Skip to content
Closed
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 76 additions & 10 deletions src/google/adk/models/lite_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,24 +444,33 @@ def _extract_cached_prompt_tokens(usage: Any) -> int:


async def _content_to_message_param(
content: types.Content,
*,
provider: str = "",
content: types.Content,
*,
provider: str = "",
) -> Union[Message, list[Message]]:
"""Converts a types.Content to a litellm Message or list of Messages.

Handles multipart function responses by returning a list of
ChatCompletionToolMessage objects if multiple function_response parts exist.
This function processes a `types.Content` object, which may contain multiple
parts, and converts them into a format suitable for LiteLLM. It handles
mixed content, such as tool responses alongside text or other media, by
generating a list of messages.

- `function_response` parts are converted into `tool` role messages.
- Other parts (text, images, etc.) are grouped and converted into a
single `user` or `assistant` message.

Args:
content: The content to convert.
provider: The LLM provider name (e.g., "openai", "azure").

Returns:
A litellm Message, a list of litellm Messages.
A single litellm Message, a list of litellm Messages, or an empty list if
the content is empty.
"""

tool_messages = []
other_parts = []

# 1. Separate function responses from other content
for part in content.parts:
if part.function_response:
response = part.function_response.response
Expand All @@ -477,11 +486,68 @@ async def _content_to_message_param(
content=response_content,
)
)
if tool_messages:
else:
other_parts.append(part)

# 2. If ONLY tools are present, return them immediately
if tool_messages and not other_parts:
return tool_messages if len(tool_messages) > 1 else tool_messages[0]

# Handle user or assistant messages
role = _to_litellm_role(content.role)
# 3. Handle user or assistant messages for the remaining parts
extra_message = None
if other_parts:
role = _to_litellm_role(content.role)

if role == "user":
user_parts = [part for part in other_parts if not part.thought]
message_content = await _get_content(user_parts, provider=provider) or None
if message_content:
extra_message = ChatCompletionUserMessage(role="user", content=message_content)

else: # assistant/model
tool_calls = []
content_parts: list[types.Part] = []
reasoning_parts: list[types.Part] = []
for part in other_parts:
if part.function_call:
tool_calls.append(
ChatCompletionAssistantToolCall(
type="function",
id=part.function_call.id,
function=Function(
name=part.function_call.name,
arguments=_safe_json_serialize(part.function_call.args),
),
)
)
elif part.thought:
reasoning_parts.append(part)
else:
content_parts.append(part)

message_content = await _get_content(content_parts, provider=provider) or None
# Using p.text as requested by the code review
reasoning_content = (
"\n".join([p.text for p in reasoning_parts if p.text])
if reasoning_parts
else None
)

extra_message = ChatCompletionAssistantMessage(
role="assistant",
content=message_content,
tool_calls=tool_calls if tool_calls else None,
thought=reasoning_content,
)

# 4. Combine and Return
final_messages = tool_messages + ([extra_message] if extra_message else [])

if not final_messages:
return []

return final_messages if len(final_messages) > 1 else final_messages[0]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

After this return statement, there is a block of unreachable code from line 551 to 608. This seems to be leftover code from the refactoring that should be removed to improve code clarity and maintainability.



if role == "user":
user_parts = [part for part in content.parts if not part.thought]
Expand Down