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
This commit is contained in:
BillionClaw 2026-03-24 10:31:09 +08:00
parent 14fffc2770
commit 7b6149ea3f
2 changed files with 50 additions and 0 deletions

View file

@ -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(

View file

@ -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"