diff --git a/src/google/adk/models/lite_llm.py b/src/google/adk/models/lite_llm.py index 74a1ecd6..975ffd9b 100644 --- a/src/google/adk/models/lite_llm.py +++ b/src/google/adk/models/lite_llm.py @@ -1233,7 +1233,7 @@ async def _content_to_message_param( *, provider: str = "", model: str = "", -) -> Union[Message, list[Message]]: +) -> Union[Message, list[Message]] | None: """Converts a types.Content to a litellm Message or list of Messages. Handles multipart function responses by returning a list of @@ -1245,14 +1245,18 @@ async def _content_to_message_param( model: The LiteLLM model string, used for provider-specific behavior. Returns: - A litellm Message, a list of litellm Messages. + A litellm Message, a list of litellm Messages, or None if skipped. """ _ensure_litellm_imported() + # Skip content if there are no parts to avoid LiteLLM adapter errors. + parts = content.parts or [] + if not parts: + return None + tool_messages: list[Message] = [] non_tool_parts: list[types.Part] = [] - content_parts_or_empty = content.parts or [] - for part in content_parts_or_empty: + for part in parts: if part.function_response: function_response = part.function_response response = function_response.response @@ -1300,7 +1304,7 @@ async def _content_to_message_param( role = _to_litellm_role(content.role) if role == "user": - user_parts = [part for part in content_parts_or_empty if not part.thought] + user_parts = [part for part in parts if not part.thought] message_content = ( await _get_content(user_parts, provider=provider, model=model) or None ) @@ -1312,7 +1316,7 @@ async def _content_to_message_param( tool_calls: list[_OutboundToolCall] = [] content_parts: list[types.Part] = [] reasoning_parts: list[types.Part] = [] - for part in content_parts_or_empty: + for part in parts: if part.function_call: function_call = part.function_call if not function_call.name: diff --git a/tests/unittests/models/test_litellm.py b/tests/unittests/models/test_litellm.py index 0b89d04f..68b4135f 100644 --- a/tests/unittests/models/test_litellm.py +++ b/tests/unittests/models/test_litellm.py @@ -2085,6 +2085,23 @@ async def test_content_to_message_param_user_message(): assert message["content"] == "Test prompt" +@pytest.mark.asyncio +@pytest.mark.parametrize("parts", [None, []]) +async def test_content_to_message_param_user_message_without_parts(parts): + # parts must not raise. + content = types.Content(role="user", parts=parts) + message = await _content_to_message_param(content) + assert message is None + + +@pytest.mark.asyncio +@pytest.mark.parametrize("parts", [None, []]) +async def test_content_to_message_param_assistant_message_without_parts(parts): + content = types.Content(role="assistant", parts=parts) + message = await _content_to_message_param(content) + assert message is None + + @pytest.mark.asyncio @pytest.mark.parametrize("file_uri,mime_type", FILE_URI_TEST_CASES) async def test_content_to_message_param_user_message_with_file_uri(