Skip to content

Commit 368c0d0

Browse files
refactor: let TypeAdapter handle all types uniformly, skip framework params
Remove the Pydantic model special case from _preprocess_args so TypeAdapter handles primitives, enums, Pydantic models, and containers in one path. Skip framework-managed params (_ignore_params) to avoid validating injected values like credential=None.
1 parent c6b4bc2 commit 368c0d0

File tree

2 files changed

+13
-27
lines changed

2 files changed

+13
-27
lines changed

src/google/adk/tools/function_tool.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ def _preprocess_args(
132132
validation_errors = []
133133

134134
for param_name, param in signature.parameters.items():
135-
if param_name not in args or param.annotation is inspect.Parameter.empty:
135+
if (
136+
param_name not in args
137+
or param.annotation is inspect.Parameter.empty
138+
or param_name in self._ignore_params
139+
):
136140
continue
137141

138142
target_type = param.annotation
@@ -150,27 +154,8 @@ def _preprocess_args(
150154
if args[param_name] is None and is_optional:
151155
continue
152156

153-
# Pydantic models: keep existing graceful-failure behavior
154-
if inspect.isclass(target_type) and issubclass(
155-
target_type, pydantic.BaseModel
156-
):
157-
if not isinstance(args[param_name], target_type):
158-
try:
159-
converted_args[param_name] = target_type.model_validate(
160-
args[param_name]
161-
)
162-
except Exception as e:
163-
logger.warning(
164-
"Failed to convert argument '%s' to Pydantic model %s: %s",
165-
param_name,
166-
target_type.__name__,
167-
e,
168-
)
169-
continue
170-
171-
# Validate and coerce all other annotated types using TypeAdapter.
172-
# This handles primitives (int, float, str, bool), enums, and
173-
# container types (list[int], dict[str, float], etc.).
157+
# Validate and coerce all annotated types using TypeAdapter.
158+
# This handles primitives, enums, Pydantic models, and container types.
174159
try:
175160
adapter = pydantic.TypeAdapter(target_type)
176161
converted_args[param_name] = adapter.validate_python(

tests/unittests/tools/test_function_tool_pydantic.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,18 @@ def test_preprocess_args_with_mixed_types():
184184
assert processed_args["user"].age == 40
185185

186186

187-
def test_preprocess_args_with_invalid_data_graceful_failure():
188-
"""Test _preprocess_args handles invalid data gracefully."""
187+
def test_preprocess_args_with_invalid_data_returns_error():
188+
"""Test _preprocess_args returns validation error for invalid Pydantic data."""
189189
tool = FunctionTool(sync_function_with_pydantic_model)
190190

191191
# Invalid data that can't be converted to UserModel
192192
input_args = {"user": "invalid_string"} # string instead of dict/model
193193

194-
processed_args, _ = tool._preprocess_args(input_args)
194+
_, errors = tool._preprocess_args(input_args)
195195

196-
# Should keep original value when conversion fails
197-
assert processed_args["user"] == "invalid_string"
196+
# Should return a validation error for the LLM to self-correct
197+
assert len(errors) == 1
198+
assert "user" in errors[0]
198199

199200

200201
def test_preprocess_args_with_non_pydantic_parameters():

0 commit comments

Comments
 (0)