From 7b6149ea3fbd09abf660a0275294590d49162239 Mon Sep 17 00:00:00 2001 From: BillionClaw <267901332+BillionClaw@users.noreply.github.com> Date: Tue, 24 Mar 2026 10:31:09 +0800 Subject: [PATCH] fix(gemini): handle empty response when finish_reason is STOP gemini-2.5-flash-lite can return finish_reason=STOP with empty content in long-running agentic tasks. Fixed response parsing to return empty string instead of None. Fixes #24442 --- .../vertex_and_google_ai_studio_gemini.py | 6 +++ ...test_vertex_and_google_ai_studio_gemini.py | 44 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 36f51c5b2f5..c002f6eab5d 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -2198,6 +2198,12 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): cumulative_tool_call_idx=cumulative_tool_call_index, is_function_call=is_function_call(standard_optional_params), ) + else: + # Handle case where finish_reason=STOP but no parts/content + # e.g. gemini-2.5-flash-lite in long-running agentic tasks + finish_reason = candidate.get("finishReason") + if finish_reason == "STOP": + chat_completion_message["content"] = "" if "logprobsResult" in candidate: chat_completion_logprobs = VertexGeminiConfig._transform_logprobs( diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 3102a695961..d15618d7edc 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -3825,3 +3825,47 @@ def test_sync_streaming_uses_custom_client(): # Verify that gemini_client is in the partial's keywords assert "gemini_client" in partial_make_sync_call.keywords assert partial_make_sync_call.keywords["gemini_client"] is mock_client + + +def test_vertex_ai_empty_response_with_stop_finish_reason(): + """ + Test that empty responses with finish_reason=STOP return empty string content. + + Regression test for https://github.com/BerriAI/litellm/issues/24442 + gemini-2.5-flash-lite can return finish_reason=STOP with no parts/text content + in long-running agentic tasks. Response looks like: + {"candidates":[{"content":{"role":"model"},"finishReason":"STOP","index":0}]} + """ + completion_response = { + "candidates": [ + { + "content": {"role": "model"}, + "finishReason": "STOP", + "index": 0, + } + ], + "usageMetadata": { + "promptTokenCount": 50, + "candidatesTokenCount": 0, + "totalTokenCount": 50, + }, + } + + raw_response = MagicMock() + raw_response.json.return_value = completion_response + + result = VertexGeminiConfig().transform_response( + model="gemini-2.5-flash-lite", + raw_response=raw_response, + model_response=ModelResponse(), + logging_obj=MagicMock(), + request_data={}, + messages=[], + optional_params={}, + litellm_params={}, + encoding=None, + ) + + # Should return empty string content, not None + assert result.choices[0].message.content == "" + assert result.choices[0].finish_reason == "stop"