@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / be7370daf3
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 8, 08:45 UTC

[Frontend] Enable generic structured_outputs for responses API (#33709)

Signed-off-by: Alec Solder <alecs@fb.com> Co-authored-by: Alec Solder <alecs@fb.com>

Alec S committed Feb 12, 2026 at 19:15 UTC be7370daf3596da71776375b9aba6dd712646fdc
2 files changed +58 -7
tests/entrypoints/openai/responses/test_sampling_params.py
+47 -4
@@ -4,8 +4,17 @@
4 """Unit tests for ResponsesRequest.to_sampling_params() parameter mapping."""
5
6 import pytest
7 +import torch
8 +from openai.types.responses.response_format_text_json_schema_config import (
9 + ResponseFormatTextJSONSchemaConfig,
10 +)
11 +from pydantic import ValidationError
12
8 -from vllm.entrypoints.openai.responses.protocol import ResponsesRequest
13 +from vllm.entrypoints.openai.responses.protocol import (
14 + ResponsesRequest,
15 + ResponseTextConfig,
16 +)
17 +from vllm.sampling_params import StructuredOutputsParams
18
19
20 class TestResponsesRequestSamplingParams:
@@ -76,9 +85,6 @@ class TestResponsesRequestSamplingParams:
85
86 def test_seed_bounds_validation(self):
87 """Test that seed values outside torch.long bounds are rejected."""
79 - import torch
80 - from pydantic import ValidationError
81 -
88 # Test seed below minimum
89 with pytest.raises(ValidationError) as exc_info:
90 ResponsesRequest(
@@ -111,3 +117,40 @@ class TestResponsesRequestSamplingParams:
117 seed=torch.iinfo(torch.long).max,
118 )
119 assert request_max.seed == torch.iinfo(torch.long).max
120 +
121 + def test_structured_outputs_passed_through(self):
122 + """Test that structured_outputs field is passed to SamplingParams."""
123 + structured_outputs = StructuredOutputsParams(grammar="root ::= 'hello'")
124 + request = ResponsesRequest(
125 + model="test-model",
126 + input="test input",
127 + structured_outputs=structured_outputs,
128 + )
129 +
130 + sampling_params = request.to_sampling_params(default_max_tokens=1000)
131 +
132 + assert sampling_params.structured_outputs is not None
133 + assert sampling_params.structured_outputs.grammar == "root ::= 'hello'"
134 +
135 + def test_structured_outputs_and_json_schema_conflict(self):
136 + """Test that specifying both structured_outputs and json_schema raises."""
137 + structured_outputs = StructuredOutputsParams(grammar="root ::= 'hello'")
138 + text_config = ResponseTextConfig()
139 + text_config.format = ResponseFormatTextJSONSchemaConfig(
140 + type="json_schema",
141 + name="test",
142 + schema={"type": "object"},
143 + )
144 + request = ResponsesRequest(
145 + model="test-model",
146 + input="test input",
147 + structured_outputs=structured_outputs,
148 + text=text_config,
149 + )
150 +
151 + with pytest.raises(ValueError) as exc_info:
152 + request.to_sampling_params(default_max_tokens=1000)
153 +
154 + assert "Cannot specify both structured_outputs and text.format" in str(
155 + exc_info.value
156 + )
vllm/entrypoints/openai/responses/protocol.py
+11 -3
@@ -233,6 +233,10 @@ class ResponsesRequest(OpenAIBaseModel):
233 # this cannot be used in conjunction with previous_response_id
234 # TODO: consider supporting non harmony messages as well
235 previous_input_messages: list[OpenAIHarmonyMessage | dict] | None = None
236 + structured_outputs: StructuredOutputsParams | None = Field(
237 + default=None,
238 + description="Additional kwargs for structured outputs",
239 + )
240
241 repetition_penalty: float | None = None
242 seed: int | None = Field(None, ge=_LONG_INFO.min, le=_LONG_INFO.max)
@@ -319,8 +323,14 @@ class ResponsesRequest(OpenAIBaseModel):
323 stop_token_ids = default_sampling_params.get("stop_token_ids")
324
325 # Structured output
322 - structured_outputs = None
326 + structured_outputs = self.structured_outputs
327 +
328 + # Also check text.format for OpenAI-style json_schema
329 if self.text is not None and self.text.format is not None:
330 + if structured_outputs is not None:
331 + raise ValueError(
332 + "Cannot specify both structured_outputs and text.format"
333 + )
334 response_format = self.text.format
335 if (
336 response_format.type == "json_schema"
@@ -329,8 +339,6 @@ class ResponsesRequest(OpenAIBaseModel):
339 structured_outputs = StructuredOutputsParams(
340 json=response_format.schema_
341 )
332 - elif response_format.type == "json_object":
333 - raise NotImplementedError("json_object is not supported")
342
343 stop = self.stop if self.stop else []
344 if isinstance(stop, str):