From 92fbe7698bc65218927f08f92c4344394d93059b Mon Sep 17 00:00:00 2001 From: Saylee Date: Mon, 12 Oct 2026 00:16:24 +0530 Subject: [PATCH] fix(openai): json_mode stop finish_reason after folding tool call When json_mode folds a tool call into JSON content, the transform sets finish_reason to "stop" but then overwrites it by re-reading the raw provider finish_reason ("tool_calls"), so the choice reports finish_reason "tool_calls" while message.tool_calls is None. Report the finish_reason the transform already computed instead. Normal tool-call responses keep the existing "tool_calls" correction. Signed-off-by: Saylee --- .../llms/openai/chat/gpt_transformation.py | 2 +- .../chat/test_openai_gpt_transformation.py | 29 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index 0479ac5c36c..a52f36e732c 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -822,7 +822,7 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig): ) translated_choice.finish_reason = map_finish_reason( - self._get_finish_reason(translated_message, choice["finish_reason"]) + self._get_finish_reason(translated_message, finish_reason) ) transformed_choices.append(translated_choice) diff --git a/tests/unit/llms/openai/chat/test_openai_gpt_transformation.py b/tests/unit/llms/openai/chat/test_openai_gpt_transformation.py index 263c2f7618d..6573fc1323f 100644 --- a/tests/unit/llms/openai/chat/test_openai_gpt_transformation.py +++ b/tests/unit/llms/openai/chat/test_openai_gpt_transformation.py @@ -2215,3 +2215,32 @@ async def test_chat_sends_a_breakpoint_by_the_deployments_own_flag_not_a_sibling "opted-in": True, "flagless": False, } + + +def test_transform_choices_json_mode_reports_stop_when_tool_call_becomes_content(): + """json_mode turns a tool call into JSON content; the choice must then report + finish_reason 'stop' instead of 'tool_calls' with no tool_calls attached.""" + choice = [ + { + "finish_reason": "tool_calls", + "index": 0, + "logprobs": None, + "message": { + "content": None, + "role": "assistant", + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": {"name": "json_tool_call", "arguments": '{"a": 1}'}, + } + ], + }, + } + ] + + out = OpenAIGPTConfig()._transform_choices(choices=choice, json_mode=True) + + assert out[0].message.content == '{"a": 1}' + assert out[0].message.tool_calls is None + assert out[0].finish_reason == "stop"