From db4f73f3be6798ccfe5adcb9a0d9b855d92155bf Mon Sep 17 00:00:00 2001 From: chelsealong Date: Tue, 1 Sep 2026 23:04:35 +0000 Subject: [PATCH] fix(tools): stringify Enum values in legacy schema declarations `_parse_schema_from_parameter` declared enum parameters as `type: STRING` but copied `e.value` verbatim into `schema.enum`, so an `IntEnum` (or any enum with non-string values) produced an int list under a STRING schema. That's invalid by `types.Schema`'s own contract: constructing the same schema through the public constructor raises `ValidationError`, and serializing the ADK-built one emits `PydanticSerializationUnexpectedValue` warnings. Stringify enum values (and the default, if any) so the declaration matches the STRING type it declares. `str`-valued enums are unaffected since `str(value) == value` for them. --- .../tools/_function_parameter_parse_util.py | 5 ++++- .../tools/test_build_function_declaration.py | 22 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/google/adk/tools/_function_parameter_parse_util.py b/src/google/adk/tools/_function_parameter_parse_util.py index 32827d974a1..6b0f26ef4aa 100644 --- a/src/google/adk/tools/_function_parameter_parse_util.py +++ b/src/google/adk/tools/_function_parameter_parse_util.py @@ -304,14 +304,17 @@ def _parse_schema_from_parameter( _raise_if_schema_unsupported(variant, schema) return schema if isinstance(param.annotation, type) and issubclass(param.annotation, Enum): + # `schema.type` is always STRING here, so every enum value must be a + # string too (e.g. IntEnum members have int `.value`s otherwise). schema.type = types.Type.STRING - schema.enum = [e.value for e in param.annotation] + schema.enum = [str(e.value) for e in param.annotation] if param.default is not inspect.Parameter.empty: default_value = ( param.default.value if isinstance(param.default, Enum) else param.default ) + default_value = str(default_value) if default_value not in schema.enum: raise ValueError(default_value_error_msg) schema.default = default_value diff --git a/tests/unittests/tools/test_build_function_declaration.py b/tests/unittests/tools/test_build_function_declaration.py index 53968ed9316..7e920e67a7f 100644 --- a/tests/unittests/tools/test_build_function_declaration.py +++ b/tests/unittests/tools/test_build_function_declaration.py @@ -13,6 +13,7 @@ # limitations under the License. from enum import Enum +from enum import IntEnum from typing import Any from google.adk.features import FeatureName @@ -439,6 +440,27 @@ def simple_function_with_wrong_enum(input: InputEnum = 'WRONG_ENUM'): func=simple_function_with_wrong_enum ) + def test_int_enum(self): + + class Level(IntEnum): + LOW = 1 + HIGH = 2 + + def set_level(level: Level = Level.LOW): + return level.value + + function_decl = _automatic_function_calling_util.build_function_declaration( + func=set_level + ) + + level_schema = function_decl.parameters.properties['level'] + assert level_schema.type == 'STRING' + assert level_schema.enum == ['1', '2'] + assert level_schema.default == '1' + # The declaration must be valid for types.Schema's own contract: an + # `enum` field on a STRING schema must contain only strings. + types.Schema(type=level_schema.type, enum=level_schema.enum) + def test_basemodel_list(self): class ChildInput(BaseModel): input_str: str