[Bugfix][Frontend] Keep length finish_reason for max_tokens-truncated streaming tool calls (#46303)
Signed-off-by: Ting Sun <suntcrick@gmail.com>
Ting SUN committed
Sep 23, 2026 at 14:31 UTC
b33c8a657d487a1f00cfd6fc9d2bdd70424ade7c
2 files changed
+15
-5
tests/entrypoints/openai/chat_completion/test_serving_chat.py
+10
-4
@@ -2239,7 +2239,13 @@ async def test_tool_choice_validation_without_parser():
2239
2240
2241
@pytest.mark.asyncio
2242
-async def test_streaming_n_gt1_independent_tool_parsers():
2242
+@pytest.mark.parametrize(
2243
+ ("engine_finish_reason", "expected_finish_reason"),
2244
+ [("stop", "tool_calls"), ("length", "length")],
2245
+)
2246
+async def test_streaming_n_gt1_independent_tool_parsers(
2247
+ engine_finish_reason: str, expected_finish_reason: str
2248
+):
2249
"""n>1 streaming must use independent parser instances
2250
and token-id histories per choice.
2251
"""
@@ -2344,7 +2350,7 @@ async def test_streaming_n_gt1_independent_tool_parsers():
2350
token_ids=[],
2351
cumulative_logprob=0.0,
2352
logprobs=None,
2347
- finish_reason="stop",
2353
+ finish_reason=engine_finish_reason,
2354
)
2355
for choice_idx in range(num_choices)
2356
],
@@ -2408,8 +2414,8 @@ async def test_streaming_n_gt1_independent_tool_parsers():
2414
assert len(reasons) == 1, (
2415
f"Choice {choice_idx}: expected exactly 1 finish_reason, got {reasons}"
2416
)
2411
- assert reasons[0] == "tool_calls", (
2412
- f"Choice {choice_idx}: expected finish_reason='tool_calls', "
2417
+ assert reasons[0] == expected_finish_reason, (
2418
+ f"Choice {choice_idx}: expected finish_reason={expected_finish_reason!r}, "
2419
f"got '{reasons[0]}'"
2420
)
2421
vllm/entrypoints/openai/chat_completion/serving.py
+5
-1
@@ -757,7 +757,11 @@ class OpenAIServingChat(GenerateBaseServing):
757
# finish_reason is:
758
# "tool_calls" for "auto" or "required" tool calls,
759
# and "stop" for named tool calls.
760
- if tools_streamed[i] and not tool_choice_function_name:
760
+ if (
761
+ tools_streamed[i]
762
+ and not tool_choice_function_name
763
+ and output.finish_reason == "stop"
764
+ ):
765
finish_reason_ = "tool_calls"
766
else:
767
finish_reason_ = (