mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-06 06:24:16 +00:00
fix(gemini): Add reasoning_content to streaming responses with tools (#16854)
Fixes #16805 When using Gemini models (2.5/3.0) with streaming + tools enabled, the reasoning_content field was missing from stream chunks, even though thinking_blocks were present in non-streaming responses. Changes: - Convert thinking_blocks to reasoning_content for streaming responses - Extract "thinking" field from each thinking_block - Concatenate multiple thinking parts with newlines - Assign to reasoning_content in chat_completion_message for streaming Testing: - Added test_streaming_chunk_with_tool_calls_includes_reasoning_content - Test verifies reasoning_content appears with tool calls in streaming - All 39 existing Gemini tests pass
This commit is contained in:
@@ -1673,6 +1673,19 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
|
||||
if thinking_blocks is not None:
|
||||
chat_completion_message["thinking_blocks"] = thinking_blocks # type: ignore
|
||||
|
||||
# Convert thinking_blocks to reasoning_content for streaming
|
||||
# This ensures reasoning_content is available in streaming responses
|
||||
if isinstance(model_response, ModelResponseStream) and reasoning_content is None:
|
||||
reasoning_content_parts = []
|
||||
for block in thinking_blocks:
|
||||
thinking_text = block.get("thinking")
|
||||
if thinking_text:
|
||||
reasoning_content_parts.append(thinking_text)
|
||||
|
||||
if reasoning_content_parts:
|
||||
reasoning_content = "\n".join(reasoning_content_parts)
|
||||
chat_completion_message["reasoning_content"] = reasoning_content
|
||||
|
||||
if isinstance(model_response, ModelResponseStream):
|
||||
choice = VertexGeminiConfig._create_streaming_choice(
|
||||
chat_completion_message=chat_completion_message,
|
||||
|
||||
@@ -390,6 +390,58 @@ def test_streaming_chunk_includes_reasoning_content():
|
||||
)
|
||||
|
||||
|
||||
def test_streaming_chunk_with_tool_calls_includes_reasoning_content():
|
||||
"""
|
||||
Test for issue #16805: Ensure that when Gemini returns a streaming chunk with
|
||||
tool calls AND thoughtSignature, the reasoning_content is included in the delta.
|
||||
|
||||
Previously, thinking_blocks were only added to non-streaming responses, causing
|
||||
reasoning_content to be missing in streaming mode when tools were enabled.
|
||||
"""
|
||||
from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import (
|
||||
ModelResponseIterator,
|
||||
)
|
||||
|
||||
litellm_logging = MagicMock()
|
||||
|
||||
chunk = {
|
||||
"candidates": [
|
||||
{
|
||||
"content": {
|
||||
"parts": [
|
||||
{
|
||||
"functionCall": {
|
||||
"name": "get_current_time",
|
||||
"args": {"timezone": "America/New_York"},
|
||||
},
|
||||
"thoughtSignature": "EsEDCr4DAdHtim...", # Base64 signature
|
||||
}
|
||||
]
|
||||
},
|
||||
"finishReason": "STOP",
|
||||
}
|
||||
],
|
||||
"usageMetadata": {
|
||||
"promptTokenCount": 68,
|
||||
"candidatesTokenCount": 120,
|
||||
"totalTokenCount": 188,
|
||||
},
|
||||
}
|
||||
|
||||
iterator = ModelResponseIterator(
|
||||
streaming_response=[], sync_stream=True, logging_obj=litellm_logging
|
||||
)
|
||||
streaming_chunk = iterator.chunk_parser(chunk)
|
||||
|
||||
# Verify that reasoning_content is present in the streaming delta
|
||||
assert streaming_chunk.choices[0].delta.reasoning_content is not None
|
||||
|
||||
# Verify tool calls are also present
|
||||
assert streaming_chunk.choices[0].delta.tool_calls is not None
|
||||
assert len(streaming_chunk.choices[0].delta.tool_calls) == 1
|
||||
assert streaming_chunk.choices[0].delta.tool_calls[0].function.name == "get_current_time"
|
||||
|
||||
|
||||
def test_check_finish_reason():
|
||||
finish_reason_mappings = VertexGeminiConfig.get_finish_reason_mapping()
|
||||
for k, v in finish_reason_mappings.items():
|
||||
|
||||
Reference in New Issue
Block a user