Skip to content

Commit f5695e2

Browse files
committed
fix: emit additional_properties in dict branch of _parse_schema_from_parameter
The dict origin branch in _parse_schema_from_parameter previously returned Schema(type=OBJECT) without any value type information for parameterized dict types like dict[str, str]. This caused Gemini to receive a bare {"type": "object"} with no constraints, leading to empty tool arguments and infinite retry loops when combined with output_schema. Now the dict branch parses the value type argument and sets additional_properties with the corresponding schema, so dict[str, V] correctly emits {"type": "object", "additionalProperties": <V schema>}. Fixes #4868
1 parent f973673 commit f5695e2

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

src/google/adk/tools/_function_parameter_parse_util.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,17 @@ def _parse_schema_from_parameter(
294294
args = get_args(param.annotation)
295295
if origin is dict:
296296
schema.type = types.Type.OBJECT
297+
if len(args) == 2:
298+
value_type = args[1]
299+
schema.additional_properties = _parse_schema_from_parameter(
300+
variant,
301+
inspect.Parameter(
302+
'value',
303+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
304+
annotation=value_type,
305+
),
306+
func_name,
307+
)
297308
if param.default is not inspect.Parameter.empty:
298309
if not _is_default_value_compatible(param.default, param.annotation):
299310
raise ValueError(default_value_error_msg)

tests/unittests/tools/test_build_function_declaration.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,53 @@ def simple_function(input_str: dict[str, str]) -> str:
102102
assert function_decl.name == 'simple_function'
103103
assert function_decl.parameters.type == 'OBJECT'
104104
assert function_decl.parameters.properties['input_str'].type == 'OBJECT'
105+
assert (
106+
function_decl.parameters.properties[
107+
'input_str'
108+
].additional_properties.type
109+
== 'STRING'
110+
)
111+
112+
113+
def test_dict_input_with_int_values():
114+
def simple_function(input_str: dict[str, int]) -> str:
115+
return {'result': input_str}
116+
117+
function_decl = _automatic_function_calling_util.build_function_declaration(
118+
func=simple_function
119+
)
120+
121+
assert function_decl.name == 'simple_function'
122+
assert function_decl.parameters.type == 'OBJECT'
123+
assert function_decl.parameters.properties['input_str'].type == 'OBJECT'
124+
assert (
125+
function_decl.parameters.properties[
126+
'input_str'
127+
].additional_properties.type
128+
== 'INTEGER'
129+
)
130+
131+
132+
def test_list_of_dict_input():
133+
"""Test list[dict[str, str]] emits proper schema with additional_properties."""
134+
135+
def simple_function(fruits: list[dict[str, str]]) -> str:
136+
return str(fruits)
137+
138+
function_decl = _automatic_function_calling_util.build_function_declaration(
139+
func=simple_function
140+
)
141+
142+
assert function_decl.name == 'simple_function'
143+
assert function_decl.parameters.type == 'OBJECT'
144+
assert function_decl.parameters.properties['fruits'].type == 'ARRAY'
145+
assert function_decl.parameters.properties['fruits'].items.type == 'OBJECT'
146+
assert (
147+
function_decl.parameters.properties[
148+
'fruits'
149+
].items.additional_properties.type
150+
== 'STRING'
151+
)
105152

106153

107154
def test_basemodel_input():
@@ -279,6 +326,12 @@ def simple_function(
279326
assert function_decl.parameters.properties['input_str'].items.type == 'STRING'
280327
assert function_decl.parameters.properties['input_dir'].type == 'ARRAY'
281328
assert function_decl.parameters.properties['input_dir'].items.type == 'OBJECT'
329+
assert (
330+
function_decl.parameters.properties[
331+
'input_dir'
332+
].items.additional_properties.type
333+
== 'STRING'
334+
)
282335

283336

284337
def test_enums():

0 commit comments

Comments
 (0)