Skip to content
Open
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
9 changes: 9 additions & 0 deletions src/google/adk/tools/_automatic_function_calling_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,15 @@ def from_function_with_options(
type='OBJECT',
properties=parameters_json_schema,
)
# Determine required fields from the function signature directly.
# _get_required_fields() relies on nullable/default metadata in Schema
# objects, which is not preserved by the json_schema fallback path.
declaration.parameters.required = [
name
for name, param in inspect.signature(func).parameters.items()
if name in parameters_json_schema
and param.default is inspect.Parameter.empty
]

if variant == GoogleLLMVariant.GEMINI_API:
return declaration
Expand Down
38 changes: 38 additions & 0 deletions tests/unittests/tools/test_from_function_with_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,3 +319,41 @@ async def test_function(param: str) -> AsyncGenerator[Dict[str, str], None]:
# VERTEX_AI should extract yield type (Dict[str, str]) from AsyncGenerator
assert declaration.response is not None
assert declaration.response.type == types.Type.OBJECT


def test_from_function_with_options_required_fields_with_complex_union():
"""Test that required fields are set when complex union types trigger the json_schema fallback.

Regression test for https://github.com/google/adk-python/issues/4798

When a function has parameters with types that cause
_parse_schema_from_parameter to raise ValueError (e.g. list[str] | None),
from_function_with_options falls back to the parameters_json_schema path.
This fallback must still set the `required` field for parameters that have
no default value.
"""

def tool_with_complex_types(
query: str,
mode: str = 'default',
tags: list[str] | None = None,
) -> str:
"""A tool with a required param and a complex union type param."""
return query

declaration = _automatic_function_calling_util.from_function_with_options(
tool_with_complex_types, GoogleLLMVariant.GEMINI_API
)

assert declaration.name == 'tool_with_complex_types'
assert declaration.parameters is not None
assert declaration.parameters.type == 'OBJECT'
assert 'query' in declaration.parameters.properties
assert 'mode' in declaration.parameters.properties
assert 'tags' in declaration.parameters.properties
# The critical assertion: `query` must be in required because it has no default
assert declaration.parameters.required is not None
assert 'query' in declaration.parameters.required
# `mode` and `tags` have defaults, so they must NOT be required
assert 'mode' not in declaration.parameters.required
assert 'tags' not in declaration.parameters.required