fix: guard against Content with no parts in _content_to_message_param

Merge https://github.com/google/adk-python/pull/6312

Avoid raising TypeError when types.Content has parts=None or parts=[] in LiteLLM adapter.

PiperOrigin-RevId: 967357294
This commit is contained in:
Anas Khan
2026-08-19 12:47:50 -07:00
committed by Copybara-Service
parent 924d802f5b
commit 1ed8d48620
2 changed files with 27 additions and 6 deletions
+10 -6
View File
@@ -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:
+17
View File
@@ -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(