Skip to content

Commit c703a03

Browse files
committed
refactor: use Metadata map instead of dedicated proto fields for structured output
Address review feedback: - Remove JSONSchema and ResponseFormat proto fields; pass them via the existing Metadata map instead, avoiding proto changes - vLLM backend reads json_schema and response_format from request.Metadata - Add structured output support (json_schema, json_object) to Open Responses API via text_format parameter - Update docs with Open Responses structured output examples Ref: #6857 Signed-off-by: eureka928 <meobius123@gmail.com>
1 parent 480bcd8 commit c703a03

5 files changed

Lines changed: 87 additions & 7 deletions

File tree

core/backend/options.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,6 @@ func gRPCPredictOpts(c config.ModelConfig, modelPath string) *pb.PredictOptions
282282
TensorSplit: c.TensorSplit,
283283
TailFreeSamplingZ: float32(*c.TFZ),
284284
TypicalP: float32(*c.TypicalP),
285-
JSONSchema: c.JSONSchema,
286-
ResponseFormat: c.ResponseFormat,
287285
}
288286

289287
metadata := map[string]string{}
@@ -294,6 +292,12 @@ func gRPCPredictOpts(c config.ModelConfig, modelPath string) *pb.PredictOptions
294292
metadata["enable_thinking"] = "true"
295293
}
296294
}
295+
if c.ResponseFormat != "" {
296+
metadata["response_format"] = c.ResponseFormat
297+
}
298+
for k, v := range c.RequestMetadata {
299+
metadata[k] = v
300+
}
297301
pbOpts.Metadata = metadata
298302

299303
// Logprobs and TopLogprobs are set by the caller if provided

core/http/endpoints/openai/chat.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,13 @@ func ChatEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, evaluator
606606
return err
607607
}
608608

609-
// Pass raw JSON schema to backends that support native structured output
609+
// Pass raw JSON schema via metadata for backends that support native structured output
610610
schemaBytes, err := json.Marshal(d.JsonSchema.Schema)
611611
if err == nil {
612-
config.JSONSchema = string(schemaBytes)
612+
if config.RequestMetadata == nil {
613+
config.RequestMetadata = map[string]string{}
614+
}
615+
config.RequestMetadata["json_schema"] = string(schemaBytes)
613616
}
614617

615618
fs := &functions.JSONFunctionStructure{

core/http/endpoints/openai/completion.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,10 @@ func CompletionEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, eva
100100
if err := json.Unmarshal(dat, &jsr); err == nil {
101101
schemaBytes, err := json.Marshal(jsr.JsonSchema.Schema)
102102
if err == nil {
103-
config.JSONSchema = string(schemaBytes)
103+
if config.RequestMetadata == nil {
104+
config.RequestMetadata = map[string]string{}
105+
}
106+
config.RequestMetadata["json_schema"] = string(schemaBytes)
104107
}
105108
fs := &functions.JSONFunctionStructure{
106109
AnyOf: []functions.Item{jsr.JsonSchema.Schema},

core/http/endpoints/openresponses/responses.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,42 @@ func ResponsesEndpoint(cl *config.ModelConfigLoader, ml *model.ModelLoader, eval
173173
Functions: funcs,
174174
}
175175

176-
// Handle text_format -> response_format conversion
176+
// Handle text_format -> response_format conversion and structured output
177177
if input.TextFormat != nil {
178-
openAIReq.ResponseFormat = convertTextFormatToResponseFormat(input.TextFormat)
178+
responseFormat := convertTextFormatToResponseFormat(input.TextFormat)
179+
openAIReq.ResponseFormat = responseFormat
180+
181+
// Generate grammar and pass schema for structured output (like OpenAI chat/completion)
182+
if rfMap, ok := responseFormat.(map[string]interface{}); ok {
183+
if rfType, _ := rfMap["type"].(string); rfType == "json_object" {
184+
cfg.Grammar = functions.JSONBNF
185+
cfg.ResponseFormat = "json_object"
186+
} else if rfType == "json_schema" {
187+
cfg.ResponseFormat = "json_schema"
188+
d := schema.JsonSchemaRequest{}
189+
dat, err := json.Marshal(rfMap)
190+
if err == nil {
191+
if err := json.Unmarshal(dat, &d); err == nil {
192+
schemaBytes, err := json.Marshal(d.JsonSchema.Schema)
193+
if err == nil {
194+
if cfg.RequestMetadata == nil {
195+
cfg.RequestMetadata = map[string]string{}
196+
}
197+
cfg.RequestMetadata["json_schema"] = string(schemaBytes)
198+
}
199+
fs := &functions.JSONFunctionStructure{
200+
AnyOf: []functions.Item{d.JsonSchema.Schema},
201+
}
202+
g, err := fs.Grammar(cfg.FunctionsConfig.GrammarOptions()...)
203+
if err == nil {
204+
cfg.Grammar = g
205+
} else {
206+
xlog.Error("Open Responses - Failed generating grammar for json_schema", "error", err)
207+
}
208+
}
209+
}
210+
}
211+
}
179212
}
180213

181214
// Generate grammar for function calling (similar to OpenAI chat endpoint)

docs/content/features/constrained_grammars.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,43 @@ curl http://localhost:8080/v1/chat/completions -H "Content-Type: application/jso
123123
}'
124124
```
125125

126+
## Open Responses API
127+
128+
The Open Responses API (`/v1/responses`) also supports structured output via the `text_format` parameter:
129+
130+
### JSON Schema
131+
132+
```bash
133+
curl http://localhost:8080/v1/responses -H "Content-Type: application/json" -d '{
134+
"model": "my-model",
135+
"input": "Generate a person object",
136+
"text_format": {
137+
"type": "json_schema",
138+
"json_schema": {
139+
"name": "person",
140+
"schema": {
141+
"type": "object",
142+
"properties": {
143+
"name": {"type": "string"},
144+
"age": {"type": "integer"}
145+
},
146+
"required": ["name", "age"]
147+
}
148+
}
149+
}
150+
}'
151+
```
152+
153+
### JSON Object
154+
155+
```bash
156+
curl http://localhost:8080/v1/responses -H "Content-Type: application/json" -d '{
157+
"model": "my-model",
158+
"input": "Generate a person as JSON",
159+
"text_format": {"type": "json_object"}
160+
}'
161+
```
162+
126163
## Related Features
127164

128165
- [OpenAI Functions]({{%relref "features/openai-functions" %}}) - Function calling with structured outputs

0 commit comments

Comments
 (0)