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 10, 06:30 UTC
[BugFix] Fix engine crash caused by chat tools + response_format (#32127)
Signed-off-by: Nick Hill <nickhill123@gmail.com>
Nick Hill committed
Jan 12, 2026 at 18:33 UTC
c6bb5b56030df628697bcccad551276f81d2019e
3 files changed
+47
tests/tool_use/test_chat_completions.py
+42
@@ -151,3 +151,45 @@ async def test_chat_completion_with_tools(
151
assert chunk.choices[0].finish_reason != "tool_calls"
152
assert len(chunks)
153
assert "".join(chunks) == output_text
154
+
155
+
156
+# Regression test for https://github.com/vllm-project/vllm/issues/32006
157
+# Engine crash when combining response_format: json_object with
158
+# tool_choice: required
159
+@pytest.mark.asyncio
160
+@pytest.mark.timeout(120)
161
+async def test_response_format_with_tool_choice_required(
162
+ client: openai.AsyncOpenAI, server_config: ServerConfig
163
+):
164
+ """
165
+ Test that combining response_format: json_object with tool_choice: required
166
+ doesn't crash the engine.
167
+
168
+ Before the fix, this would cause a validation error:
169
+ "You can only use one kind of structured outputs constraint but multiple
170
+ are specified" because both json_object and json (from tool schema) would
171
+ be set in StructuredOutputsParams.
172
+ """
173
+ models = await client.models.list()
174
+ model_name: str = models.data[0].id
175
+
176
+ # This combination previously crashed the engine
177
+ chat_completion = await client.chat.completions.create(
178
+ messages=ensure_system_prompt(
179
+ [{"role": "user", "content": "What is the weather in Dallas, Texas?"}],
180
+ server_config,
181
+ ),
182
+ temperature=0,
183
+ max_completion_tokens=150,
184
+ model=model_name,
185
+ tools=[WEATHER_TOOL],
186
+ tool_choice="required",
187
+ response_format={"type": "json_object"},
188
+ )
189
+
190
+ # The fix clears response_format when tool_choice forces tool calling,
191
+ # so the request should complete successfully with tool calls
192
+ choice = chat_completion.choices[0]
193
+ assert choice.finish_reason == "tool_calls"
194
+ assert choice.message.tool_calls is not None
195
+ assert len(choice.message.tool_calls) > 0
vllm/tool_parsers/abstract_tool_parser.py
+1
@@ -67,6 +67,7 @@ class ToolParser:
67
# tool_choice: "Forced Function" or "required" will override
68
# structured output json settings to make tool calling work correctly
69
request.structured_outputs.json = json_schema_from_tool
70
+ request.response_format = None
71
if isinstance(request, ResponsesRequest):
72
request.text = ResponseTextConfig()
73
request.text.format = ResponseFormatTextJSONSchemaConfig(
vllm/v1/engine/input_processor.py
+4
@@ -370,6 +370,10 @@ class InputProcessor:
370
# Remember that this backend was set automatically
371
params.structured_outputs._backend_was_auto = True
372
373
+ # Run post-init validation. This is also important to ensure subsequent
374
+ # roundtrip serialization/deserialization won't fail.
375
+ params.structured_outputs.__post_init__()
376
+
377
def _maybe_build_mm_uuids(
378
self,
379
request_id: str,