Skip to content

Commit 04c9cab

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 825f3b9 commit 04c9cab

2 files changed

Lines changed: 13 additions & 27 deletions

File tree

src/google/adk/tools/function_tool.py

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

131131
for param_name, param in signature.parameters.items():
132-
if param_name not in args or param.annotation is inspect.Parameter.empty:
132+
if (
133+
param_name not in args
134+
or param.annotation is inspect.Parameter.empty
135+
or param_name in self._ignore_params
136+
):
133137
continue
134138

135139
target_type = param.annotation
@@ -147,27 +151,8 @@ def _preprocess_args(
147151
if args[param_name] is None and is_optional:
148152
continue
149153

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