From 56f42e7c9c2e2ec7908a3fc9a98df2725710ef4a Mon Sep 17 00:00:00 2001 From: lorenzbaraldi Date: Fri, 20 Mar 2026 18:00:13 +0100 Subject: [PATCH 1/2] fix: Remove newline when combining reasoning in litellm --- src/google/adk/models/lite_llm.py | 4 +++- tests/unittests/models/test_litellm.py | 32 ++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index 7d13696c96..9f612f3ca6 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -912,7 +912,9 @@ async def _content_to_message_param( ): reasoning_texts.append(_decode_inline_text_data(part.inline_data.data)) - reasoning_content = _NEW_LINE.join(text for text in reasoning_texts if text) + # Preserve reasoning deltas exactly as received. Injecting separators + # between fragments can corrupt provider-streamed thinking text. + reasoning_content = "".join(text for text in reasoning_texts if text) return ChatCompletionAssistantMessage( role=role, content=final_content, diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index ace08ad997..aafaff5dec 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -2127,6 +2127,38 @@ async def test_content_to_message_param_assistant_thought_and_content_message(): assert message["reasoning_content"] == "internal reasoning" +@pytest.mark.asyncio +async def test_content_to_message_param_preserves_chunked_reasoning_deltas(): + thought_part_1 = types.Part.from_text(text="Hel") + thought_part_1.thought = True + thought_part_2 = types.Part.from_text(text="lo") + thought_part_2.thought = True + content = types.Content( + role="assistant", parts=[thought_part_1, thought_part_2] + ) + + message = await _content_to_message_param(content) + + assert message["role"] == "assistant" + assert message["content"] is None + assert message["reasoning_content"] == "Hello" + + +@pytest.mark.asyncio +async def test_content_to_message_param_preserves_reasoning_newlines(): + thought_part_1 = types.Part.from_text(text="line 1\n") + thought_part_1.thought = True + thought_part_2 = types.Part.from_text(text="line 2") + thought_part_2.thought = True + content = types.Content( + role="assistant", parts=[thought_part_1, thought_part_2] + ) + + message = await _content_to_message_param(content) + + assert message["reasoning_content"] == "line 1\nline 2" + + @pytest.mark.asyncio async def test_content_to_message_param_function_call(): content = types.Content( From 0bfb5ab9727b8223b00681b8a65b617cda4bfa0b Mon Sep 17 00:00:00 2001 From: lorenzbaraldi Date: Mon, 23 Mar 2026 19:20:19 +0100 Subject: [PATCH 2/2] fix: formatting errors --- tests/unittests/models/test_litellm.py | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index aafaff5dec..addce21800 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -2129,34 +2129,34 @@ async def test_content_to_message_param_assistant_thought_and_content_message(): @pytest.mark.asyncio async def test_content_to_message_param_preserves_chunked_reasoning_deltas(): - thought_part_1 = types.Part.from_text(text="Hel") - thought_part_1.thought = True - thought_part_2 = types.Part.from_text(text="lo") - thought_part_2.thought = True - content = types.Content( - role="assistant", parts=[thought_part_1, thought_part_2] - ) + thought_part_1 = types.Part.from_text(text="Hel") + thought_part_1.thought = True + thought_part_2 = types.Part.from_text(text="lo") + thought_part_2.thought = True + content = types.Content( + role="assistant", parts=[thought_part_1, thought_part_2] + ) - message = await _content_to_message_param(content) + message = await _content_to_message_param(content) - assert message["role"] == "assistant" - assert message["content"] is None - assert message["reasoning_content"] == "Hello" + assert message["role"] == "assistant" + assert message["content"] is None + assert message["reasoning_content"] == "Hello" @pytest.mark.asyncio async def test_content_to_message_param_preserves_reasoning_newlines(): - thought_part_1 = types.Part.from_text(text="line 1\n") - thought_part_1.thought = True - thought_part_2 = types.Part.from_text(text="line 2") - thought_part_2.thought = True - content = types.Content( - role="assistant", parts=[thought_part_1, thought_part_2] - ) + thought_part_1 = types.Part.from_text(text="line 1\n") + thought_part_1.thought = True + thought_part_2 = types.Part.from_text(text="line 2") + thought_part_2.thought = True + content = types.Content( + role="assistant", parts=[thought_part_1, thought_part_2] + ) - message = await _content_to_message_param(content) + message = await _content_to_message_param(content) - assert message["reasoning_content"] == "line 1\nline 2" + assert message["reasoning_content"] == "line 1\nline 2" @pytest.mark.asyncio