Skip to content

Commit 1e23652

Browse files
GWealecopybara-github
authored andcommitted
fix: AttributeError and indentation in parameter processing. For issue #2776 and issue #2763
PiperOrigin-RevId: 804222317
1 parent 1979dcf commit 1e23652

File tree

2 files changed

+133
-3
lines changed

2 files changed

+133
-3
lines changed

src/google/adk/models/lite_llm.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,10 @@ def _function_declaration_to_tool_param(
392392
},
393393
}
394394

395-
if function_declaration.parameters.required:
395+
if (
396+
function_declaration.parameters
397+
and function_declaration.parameters.required
398+
):
396399
tool_params["function"]["parameters"][
397400
"required"
398401
] = function_declaration.parameters.required
@@ -596,8 +599,8 @@ def _get_completion_inputs(
596599
mapped_key = param_mapping.get(key, key)
597600
generation_params[mapped_key] = config_dict[key]
598601

599-
if not generation_params:
600-
generation_params = None
602+
if not generation_params:
603+
generation_params = None
601604

602605
return messages, tools, response_format, generation_params
603606

tests/unittests/models/test_litellm.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,52 @@ def test_maybe_append_user_content(
731731
},
732732
},
733733
),
734+
(
735+
"no_parameters",
736+
types.FunctionDeclaration(
737+
name="test_function_no_params",
738+
description="Test function with no parameters",
739+
),
740+
{
741+
"type": "function",
742+
"function": {
743+
"name": "test_function_no_params",
744+
"description": "Test function with no parameters",
745+
"parameters": {
746+
"type": "object",
747+
"properties": {},
748+
},
749+
},
750+
},
751+
),
752+
(
753+
"parameters_without_required",
754+
types.FunctionDeclaration(
755+
name="test_function_no_required",
756+
description="Test function with parameters but no required field",
757+
parameters=types.Schema(
758+
type=types.Type.OBJECT,
759+
properties={
760+
"optional_arg": types.Schema(type=types.Type.STRING),
761+
},
762+
),
763+
),
764+
{
765+
"type": "function",
766+
"function": {
767+
"name": "test_function_no_required",
768+
"description": (
769+
"Test function with parameters but no required field"
770+
),
771+
"parameters": {
772+
"type": "object",
773+
"properties": {
774+
"optional_arg": {"type": "string"},
775+
},
776+
},
777+
},
778+
},
779+
),
734780
]
735781

736782

@@ -1577,6 +1623,87 @@ def test_get_completion_inputs_generation_params():
15771623
assert "stop_sequences" not in generation_params
15781624

15791625

1626+
@pytest.mark.asyncio
1627+
def test_get_completion_inputs_empty_generation_params():
1628+
# Test that generation_params is None when no generation parameters are set
1629+
req = LlmRequest(
1630+
contents=[
1631+
types.Content(role="user", parts=[types.Part.from_text(text="hi")]),
1632+
],
1633+
config=types.GenerateContentConfig(),
1634+
)
1635+
from google.adk.models.lite_llm import _get_completion_inputs
1636+
1637+
_, _, _, generation_params = _get_completion_inputs(req)
1638+
assert generation_params is None
1639+
1640+
1641+
@pytest.mark.asyncio
1642+
def test_get_completion_inputs_minimal_config():
1643+
# Test that generation_params is None when config has no generation parameters
1644+
req = LlmRequest(
1645+
contents=[
1646+
types.Content(role="user", parts=[types.Part.from_text(text="hi")]),
1647+
],
1648+
config=types.GenerateContentConfig(
1649+
system_instruction="test instruction" # Non-generation parameter
1650+
),
1651+
)
1652+
from google.adk.models.lite_llm import _get_completion_inputs
1653+
1654+
_, _, _, generation_params = _get_completion_inputs(req)
1655+
assert generation_params is None
1656+
1657+
1658+
@pytest.mark.asyncio
1659+
def test_get_completion_inputs_partial_generation_params():
1660+
# Test that generation_params is correctly built even with only some parameters
1661+
req = LlmRequest(
1662+
contents=[
1663+
types.Content(role="user", parts=[types.Part.from_text(text="hi")]),
1664+
],
1665+
config=types.GenerateContentConfig(
1666+
temperature=0.7,
1667+
# Only temperature is set, others are None/default
1668+
),
1669+
)
1670+
from google.adk.models.lite_llm import _get_completion_inputs
1671+
1672+
_, _, _, generation_params = _get_completion_inputs(req)
1673+
assert generation_params is not None
1674+
assert generation_params["temperature"] == 0.7
1675+
# Should only contain the temperature parameter
1676+
assert len(generation_params) == 1
1677+
1678+
1679+
def test_function_declaration_to_tool_param_edge_cases():
1680+
"""Test edge cases for function declaration conversion that caused the original bug."""
1681+
from google.adk.models.lite_llm import _function_declaration_to_tool_param
1682+
1683+
# Test function with None parameters (the original bug scenario)
1684+
func_decl = types.FunctionDeclaration(
1685+
name="test_function_none_params",
1686+
description="Function with None parameters",
1687+
parameters=None,
1688+
)
1689+
result = _function_declaration_to_tool_param(func_decl)
1690+
expected = {
1691+
"type": "function",
1692+
"function": {
1693+
"name": "test_function_none_params",
1694+
"description": "Function with None parameters",
1695+
"parameters": {
1696+
"type": "object",
1697+
"properties": {},
1698+
},
1699+
},
1700+
}
1701+
assert result == expected
1702+
1703+
# Verify no 'required' field is added when parameters is None
1704+
assert "required" not in result["function"]["parameters"]
1705+
1706+
15801707
def test_gemini_via_litellm_warning(monkeypatch):
15811708
"""Test that Gemini via LiteLLM shows warning."""
15821709
# Ensure environment variable is not set

0 commit comments

Comments
 (0)