diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index dda3689d..9fb02d86 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -461,7 +461,8 @@ async def _content_to_message_param( A litellm Message, a list of litellm Messages. """ - tool_messages = [] + tool_messages: list[Message] = [] + non_tool_parts: list[types.Part] = [] for part in content.parts: if part.function_response: response = part.function_response.response @@ -477,9 +478,22 @@ async def _content_to_message_param( content=response_content, ) ) - if tool_messages: + else: + non_tool_parts.append(part) + + if tool_messages and not non_tool_parts: return tool_messages if len(tool_messages) > 1 else tool_messages[0] + if tool_messages and non_tool_parts: + follow_up = await _content_to_message_param( + types.Content(role=content.role, parts=non_tool_parts), + provider=provider, + ) + follow_up_messages = ( + follow_up if isinstance(follow_up, list) else [follow_up] + ) + return tool_messages + follow_up_messages + # Handle user or assistant messages role = _to_litellm_role(content.role) diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index 01ece3f1..b3f4bd9e 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -1813,6 +1813,46 @@ async def test_content_to_message_param_multi_part_function_response(): assert messages[1]["content"] == '{"value": 123}' +@pytest.mark.asyncio +async def test_content_to_message_param_function_response_with_extra_parts(): + tool_part = types.Part.from_function_response( + name="load_image", + response={"status": "success"}, + ) + tool_part.function_response.id = "tool_call_1" + + text_part = types.Part.from_text(text="[Image: img_123.png]") + image_bytes = b"test_image_data" + image_part = types.Part.from_bytes(data=image_bytes, mime_type="image/png") + + content = types.Content( + role="user", + parts=[tool_part, text_part, image_part], + ) + + messages = await _content_to_message_param(content) + assert isinstance(messages, list) + assert messages == [ + { + "role": "tool", + "tool_call_id": "tool_call_1", + "content": '{"status": "success"}', + }, + { + "role": "user", + "content": [ + {"type": "text", "text": "[Image: img_123.png]"}, + { + "type": "image_url", + "image_url": { + "url": "data:image/png;base64,dGVzdF9pbWFnZV9kYXRh" + }, + }, + ], + }, + ] + + @pytest.mark.asyncio async def test_content_to_message_param_function_response_preserves_string(): """Tests that string responses are used directly without double-serialization.