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 2, 01:00 UTC
[Bugfix] Honor tool_choice="none" in Chat Completions streaming (#42752)
Signed-off-by: hoobnn <111053672+hoobnn@users.noreply.github.com> Signed-off-by: sfeng33 <4florafeng@gmail.com> Co-authored-by: sfeng33 <4florafeng@gmail.com>
hoobnn committed
Jun 4, 2026 at 04:27 UTC
2b237c7a4100316de7843884d72af9c402e35e53
2 files changed
+40
tests/parser/test_streaming.py
+37
@@ -36,11 +36,24 @@ def tokenizer():
36
return get_tokenizer("Qwen/Qwen3-32B")
37
38
39
+TOOLS = [
40
+ {
41
+ "type": "function",
42
+ "function": {
43
+ "name": "get_weather",
44
+ "parameters": {"type": "object", "properties": {}},
45
+ },
46
+ }
47
+]
48
+
49
+
50
@pytest.fixture
51
def request_obj():
52
return ChatCompletionRequest(
53
model="test-model",
54
messages=[{"role": "user", "content": "hi"}],
55
+ tools=TOOLS,
56
+ tool_choice="auto",
57
)
58
59
@@ -328,3 +341,27 @@ def test_parse_delta_finished_appends_remaining_args(tokenizer, request_obj):
341
tc.function.arguments for tc in tool_calls if tc.function.arguments
342
)
343
assert tool_args.endswith(remainder)
344
+
345
+
346
+def test_parse_delta_tool_choice_none(tokenizer, request_obj):
347
+ parser = make_parser(tokenizer, reasoning=False, tool=True)
348
+ request = request_obj.model_copy(update={"tool_choice": "none"})
349
+ results = stream_text(parser, tokenizer, MODEL_OUTPUT, request, prompt_token_ids=[])
350
+ reasoning, content, tool_calls = collect_fields(results)
351
+
352
+ assert reasoning == ""
353
+ assert len(tool_calls) == 0
354
+ assert "<tool_call>" in content
355
+ assert "get_weather" in content
356
+
357
+
358
+def test_parse_delta_tool_choice_none_with_reasoning(tokenizer, request_obj):
359
+ parser = make_parser(tokenizer, reasoning=True, tool=True)
360
+ request = request_obj.model_copy(update={"tool_choice": "none"})
361
+ results = stream_text(parser, tokenizer, MODEL_OUTPUT, request, prompt_token_ids=[])
362
+ reasoning, content, tool_calls = collect_fields(results)
363
+
364
+ assert "let me think about this" in reasoning
365
+ assert len(tool_calls) == 0
366
+ assert "<tool_call>" in content
367
+ assert "get_weather" in content
vllm/parser/abstract_parser.py
+3
@@ -706,6 +706,9 @@ class DelegatingParser(Parser):
706
tool_call_id_type: str = "random",
707
function_name_returned: bool = False,
708
) -> tuple[DeltaMessage | None, bool]:
709
+ if request.tool_choice == "none":
710
+ return (DeltaMessage(content=delta_text) if delta_text else None), False
711
+
712
assert self._tool_parser is not None
713
supports_required_and_named = self._tool_parser.supports_required_and_named
714
if (