You've already forked adk-python
mirror of
https://github.com/encounter/adk-python.git
synced 2026-03-30 10:57:20 -07:00
fix: Filter out thought parts in lite_llm._get_content
Thought parts represent internal model reasoning and should not be included in the content sent back to the model in subsequent turns Close #3948 Co-authored-by: George Weale <gweale@google.com> PiperOrigin-RevId: 852965417
This commit is contained in:
committed by
Copybara-Service
parent
084fcfaba5
commit
1ace8fc678
@@ -584,9 +584,12 @@ async def _get_content(
|
||||
parts: Iterable[types.Part],
|
||||
*,
|
||||
provider: str = "",
|
||||
) -> Union[OpenAIMessageContent, str]:
|
||||
) -> OpenAIMessageContent:
|
||||
"""Converts a list of parts to litellm content.
|
||||
|
||||
Thought parts represent internal model reasoning and are always dropped so
|
||||
they are not replayed back to the model in subsequent turns.
|
||||
|
||||
Args:
|
||||
parts: The parts to convert.
|
||||
provider: The LLM provider name (e.g., "openai", "azure").
|
||||
@@ -595,11 +598,25 @@ async def _get_content(
|
||||
The litellm content.
|
||||
"""
|
||||
|
||||
parts_without_thought = [part for part in parts if not part.thought]
|
||||
if len(parts_without_thought) == 1:
|
||||
part = parts_without_thought[0]
|
||||
if part.text:
|
||||
return part.text
|
||||
if (
|
||||
part.inline_data
|
||||
and part.inline_data.data
|
||||
and part.inline_data.mime_type
|
||||
and part.inline_data.mime_type.startswith("text/")
|
||||
):
|
||||
return _decode_inline_text_data(part.inline_data.data)
|
||||
|
||||
content_objects = []
|
||||
for part in parts:
|
||||
for part in parts_without_thought:
|
||||
# Skip thought parts to prevent reasoning from being replayed in subsequent
|
||||
# turns. Thought parts are internal model reasoning and should not be sent
|
||||
# back to the model.
|
||||
if part.text:
|
||||
if len(parts) == 1:
|
||||
return part.text
|
||||
content_objects.append({
|
||||
"type": "text",
|
||||
"text": part.text,
|
||||
@@ -611,8 +628,6 @@ async def _get_content(
|
||||
):
|
||||
if part.inline_data.mime_type.startswith("text/"):
|
||||
decoded_text = _decode_inline_text_data(part.inline_data.data)
|
||||
if len(parts) == 1:
|
||||
return decoded_text
|
||||
content_objects.append({
|
||||
"type": "text",
|
||||
"text": decoded_text,
|
||||
|
||||
@@ -2086,6 +2086,45 @@ def test_split_message_content_prefers_existing_structured_calls():
|
||||
assert tool_calls == [tool_call]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_content_filters_thought_parts():
|
||||
"""Test that thought parts are filtered from content.
|
||||
|
||||
Thought parts contain model reasoning that should not be sent back to
|
||||
the model in subsequent turns. This test verifies that _get_content
|
||||
skips parts with thought=True.
|
||||
|
||||
See: https://github.com/google/adk-python/issues/3948
|
||||
"""
|
||||
# Create a thought part (reasoning) and a regular text part
|
||||
thought_part = types.Part(text="Internal reasoning...", thought=True)
|
||||
regular_part = types.Part.from_text(text="Visible response")
|
||||
parts = [thought_part, regular_part]
|
||||
|
||||
content = await _get_content(parts)
|
||||
|
||||
# The thought part should be filtered out, leaving only the regular text
|
||||
assert content == "Visible response"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_content_filters_all_thought_parts():
|
||||
"""Test that all thought parts are filtered when only thoughts present.
|
||||
|
||||
When all parts are thought parts, _get_content should return an empty list.
|
||||
|
||||
See: https://github.com/google/adk-python/issues/3948
|
||||
"""
|
||||
thought_part1 = types.Part(text="First reasoning...", thought=True)
|
||||
thought_part2 = types.Part(text="Second reasoning...", thought=True)
|
||||
parts = [thought_part1, thought_part2]
|
||||
|
||||
content = await _get_content(parts)
|
||||
|
||||
# All thought parts should be filtered out
|
||||
assert content == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_content_text():
|
||||
parts = [types.Part.from_text(text="Test text")]
|
||||
|
||||
Reference in New Issue
Block a user