[Bug Fix] The model gemini-2.5-flash with the merge_reasoning_content_in_choices parameter does not work (#13066)

* _optional_combine_thinking_block_in_choices

* test_optional_combine_thinking_block_with_none_content
This commit is contained in:
Ishaan Jaff
2025-07-28 17:42:28 -07:00
committed by GitHub
parent 16af2d9a50
commit 078096247b
3 changed files with 98 additions and 1 deletions
@@ -922,6 +922,9 @@ class CustomStreamWrapper:
)
if reasoning_content:
if self.sent_first_thinking_block is False:
# Ensure content is not None before concatenation
if model_response.choices[0].delta.content is None:
model_response.choices[0].delta.content = ""
model_response.choices[0].delta.content += (
"<think>" + reasoning_content
)
@@ -938,7 +941,7 @@ class CustomStreamWrapper:
and model_response.choices[0].delta.content
):
model_response.choices[0].delta.content = (
"</think>" + model_response.choices[0].delta.content
"</think>" + (model_response.choices[0].delta.content or "")
)
self.sent_last_thinking_block = True
+6
View File
@@ -6,6 +6,12 @@ model_list:
litellm_params:
model: openai/*
- model_name: vertex_ai/gemini-2.5-pro # Vertex AI Gemini
litellm_params:
model: vertex_ai/gemini-2.5-pro
thinking: {"type": "enabled", "budget_tokens": 1024}
merge_reasoning_content_in_choices: true
litellm_settings:
callbacks: ["datadog_llm_observability"]
@@ -725,3 +725,91 @@ def test_streaming_handler_with_stream_options(
mr_dict = mr.model_dump()
print(mr_dict)
assert "stream_options" not in mr_dict
def test_optional_combine_thinking_block_with_none_content(
initialized_custom_stream_wrapper: CustomStreamWrapper,
):
"""Test that reasoning_content is properly combined when delta.content is None"""
# Setup the wrapper to use the merge feature
initialized_custom_stream_wrapper.merge_reasoning_content_in_choices = True
# First chunk with reasoning_content and None content - should handle None gracefully
first_chunk = {
"id": "chunk1",
"object": "chat.completion.chunk",
"created": 1741037890,
"model": "deepseek-reasoner",
"choices": [
{
"index": 0,
"delta": {
"content": None, # This is None, not empty string
"reasoning_content": "Let me think about this problem",
},
"finish_reason": None,
}
],
}
# Second chunk with reasoning_content and None content
second_chunk = {
"id": "chunk2",
"object": "chat.completion.chunk",
"created": 1741037891,
"model": "deepseek-reasoner",
"choices": [
{
"index": 0,
"delta": {
"content": None, # This is None, not empty string
"reasoning_content": " step by step",
},
"finish_reason": None,
}
],
}
# Final chunk with actual content - should add </think> tag
final_chunk = {
"id": "chunk3",
"object": "chat.completion.chunk",
"created": 1741037892,
"model": "deepseek-reasoner",
"choices": [
{
"index": 0,
"delta": {
"content": "The answer is 42",
"reasoning_content": None
},
"finish_reason": None,
}
],
}
# Process first chunk - should not raise TypeError
first_response = ModelResponseStream(**first_chunk)
initialized_custom_stream_wrapper._optional_combine_thinking_block_in_choices(
first_response
)
assert first_response.choices[0].delta.content == "<think>Let me think about this problem"
assert not hasattr(first_response.choices[0].delta, "reasoning_content")
assert initialized_custom_stream_wrapper.sent_first_thinking_block is True
# Process second chunk - should work with continued reasoning
second_response = ModelResponseStream(**second_chunk)
initialized_custom_stream_wrapper._optional_combine_thinking_block_in_choices(
second_response
)
assert second_response.choices[0].delta.content == " step by step"
assert not hasattr(second_response.choices[0].delta, "reasoning_content")
# Process final chunk - should add </think> tag
final_response = ModelResponseStream(**final_chunk)
initialized_custom_stream_wrapper._optional_combine_thinking_block_in_choices(
final_response
)
assert final_response.choices[0].delta.content == "</think>The answer is 42"
assert initialized_custom_stream_wrapper.sent_last_thinking_block is True
assert not hasattr(final_response.choices[0].delta, "reasoning_content")