Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions src/google/adk/models/lite_llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,8 @@ def _part_has_payload(part: types.Part) -> bool:
return True
if part.file_data and (part.file_data.file_uri or part.file_data.data):
return True
if part.function_response and part.function_response.response:
return True
return False


Expand Down
146 changes: 146 additions & 0 deletions tests/unittests/models/test_litellm.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from unittest.mock import Mock
import warnings

from google.adk.models.lite_llm import _append_fallback_user_content_if_missing
from google.adk.models.lite_llm import _content_to_message_param
from google.adk.models.lite_llm import _FILE_ID_REQUIRED_PROVIDERS
from google.adk.models.lite_llm import _FINISH_REASON_MAPPING
Expand All @@ -36,6 +37,7 @@
from google.adk.models.lite_llm import _model_response_to_chunk
from google.adk.models.lite_llm import _model_response_to_generate_content_response
from google.adk.models.lite_llm import _parse_tool_calls_from_text
from google.adk.models.lite_llm import _part_has_payload
from google.adk.models.lite_llm import _redirect_litellm_loggers_to_stdout
from google.adk.models.lite_llm import _schema_to_dict
from google.adk.models.lite_llm import _split_message_content_and_tool_calls
Expand Down Expand Up @@ -1050,6 +1052,150 @@ def test_maybe_append_user_content(
assert len(llm_request.contents) == expected_output


# Tests for _part_has_payload function
class TestPartHasPayload:

def test_part_has_payload_with_text(self):
part = types.Part.from_text(text="Hello")
assert _part_has_payload(part) is True

def test_part_has_payload_with_empty_text(self):
part = types.Part.from_text(text="")
assert _part_has_payload(part) is False

def test_part_has_payload_with_inline_data(self):
part = types.Part.from_bytes(data=b"test", mime_type="text/plain")
assert _part_has_payload(part) is True

def test_part_has_payload_with_function_response(self):
part = types.Part.from_function_response(
name="test_function",
response={"result": "success"},
)
assert _part_has_payload(part) is True

def test_part_has_payload_with_function_response_empty_dict(self):
part = types.Part.from_function_response(
name="test_function",
response={},
)
# Empty dict is falsy, so this should return False
assert _part_has_payload(part) is False

def test_part_has_payload_with_empty_part(self):
part = types.Part()
assert _part_has_payload(part) is False


class TestAppendFallbackUserContentIfMissing:

def test_no_fallback_when_user_content_has_function_response(self):
"""Verify fallback is not added when user message has function_response."""
llm_request = LlmRequest(
contents=[
types.Content(
role="user",
parts=[
types.Part.from_function_response(
name="test_function",
response={"result": "success"},
)
],
)
]
)

_append_fallback_user_content_if_missing(llm_request)

# Should not add fallback message since function_response has payload
assert len(llm_request.contents) == 1
assert len(llm_request.contents[0].parts) == 1
assert llm_request.contents[0].parts[0].function_response is not None

def test_fallback_added_when_user_content_has_empty_function_response(self):
"""Verify fallback is added when function_response has empty response."""
llm_request = LlmRequest(
contents=[
types.Content(
role="user",
parts=[
types.Part.from_function_response(
name="test_function",
response={},
)
],
)
]
)

_append_fallback_user_content_if_missing(llm_request)

# Should add fallback message since empty dict is falsy
assert len(llm_request.contents) == 1
assert len(llm_request.contents[0].parts) == 2
assert (
llm_request.contents[0].parts[1].text
== "Handle the requests as specified in the System Instruction."
)

def test_no_fallback_when_user_content_has_text(self):
"""Verify fallback is not added when user message has text."""
llm_request = LlmRequest(
contents=[
types.Content(
role="user",
parts=[types.Part.from_text(text="Hello")],
)
]
)

_append_fallback_user_content_if_missing(llm_request)

assert len(llm_request.contents) == 1
assert len(llm_request.contents[0].parts) == 1
assert llm_request.contents[0].parts[0].text == "Hello"

def test_fallback_added_when_user_content_is_empty(self):
"""Verify fallback is added when user message has no parts."""
llm_request = LlmRequest(
contents=[
types.Content(
role="user",
parts=[],
)
]
)

_append_fallback_user_content_if_missing(llm_request)

assert len(llm_request.contents) == 1
assert len(llm_request.contents[0].parts) == 1
assert (
llm_request.contents[0].parts[0].text
== "Handle the requests as specified in the System Instruction."
)

def test_fallback_added_when_no_user_content(self):
"""Verify fallback user content is added when there's no user role."""
llm_request = LlmRequest(
contents=[
types.Content(
role="model",
parts=[types.Part.from_text(text="Model response")],
)
]
)

_append_fallback_user_content_if_missing(llm_request)

assert len(llm_request.contents) == 2
assert llm_request.contents[1].role == "user"
assert (
llm_request.contents[1].parts[0].text
== "Handle the requests as specified in the System Instruction."
)


function_declaration_test_cases = [
(
"simple_function",
Expand Down
Loading