G
YouTube TikTok Games
Skip to content

fix(tools): declare non-string Enum values as strings in the legacy fallback - #8071

Open
hyeonsang010716 wants to merge 1 commit into
google:mainfrom
hyeonsang010716:fix/legacy-fallback-enum-string-values
Open

hyeonsang010716 wants to merge 1 commit into
google:mainfrom
hyeonsang010716:fix/legacy-fallback-enum-string-values

Conversation

@hyeonsang010716

Copy link
Copy Markdown
Contributor

Link to Issue or Description of Change

Problem:

With JSON_SCHEMA_FOR_FUNC_DECL disabled, a function with a parameter that
_parse_schema_from_parameter cannot parse (such as tuple[str, int]) has the
schema of every parameter generated by pydantic. Pydantic declares an Enum with
non-string values as, for an IntEnum, {'enum': [1, 2], 'type': 'integer'},
and types.Schema.model_validate rejects it because Schema.enum only takes
strings. Building the declaration raised a ValueError, so the agent failed on
its first model request. #6981 fixed this only in _parse_schema_from_parameter.

Solution:

Generate the fallback schemas with _StringEnumGenerateJsonSchema, a
GenerateJsonSchema whose enum_schema declares every Enum as a STRING schema
with string values, as _parse_schema_from_parameter does. This also covers an
Enum nested in another type, such as list[Level] or a pydantic model field.
The values are rendered from their JSON form, so an Enum of dates still gets the
same values as before.

The default of an Enum parameter is rendered the same way, so it is one of the
enum values ('2' rather than Level.HIGH). For the existing date Enum test,
the default is now '1970-01-01' rather than the member, the value it was
already serialized to.

Literal schemas are not changed: Literal[1, 2] is still rejected, since the
legacy path only supports string literals.

Testing Plan

Unit Tests:

  • I have added or updated unit tests for my change.

  • All unit tests pass locally.

  • test_int_enum_falling_back_to_pydantic (both variants): an IntEnum
    parameter next to a tuple parameter is a STRING schema with
    enum == ['1', '2'] and default == '2'.

  • test_nested_int_enum_falling_back_to_pydantic: the same for list[Level].

  • test_enum_fallback_with_member_default now checks the enum and the rendered
    default.

The new tests and the updated one fail without the fix.

$ pytest tests/unittests/tools tests/unittests/flows
4786 passed

The full tests/unittests run has one failure,
test_release_dependencies.py::test_inclusive_upper_bounds_name_an_installed_release,
which fails the same way on main.

pre-commit run passes on the changed files, and mypy reports no issues in the
two changed source files.

Manual End-to-End (E2E) Tests:

Ran the reproduction from the issue: both functions now declare level as
STRING with ['1', '2'].

Also ran an LlmAgent with a mocked model through a Runner with the flag
disabled, with a tool taking level: Level and a tuple parameter, and the
model calling it with {"level": "2"}. Before the change, building the first
request raised the ValueError. Now the declared schema is
{'type': 'STRING', 'enum': ['1', '2'], 'default': '1'}, and the tool receives
Level.HIGH.

Checklist

  • I have read the CONTRIBUTING.md document.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have manually tested my changes end-to-end.
  • Any dependent changes have been merged and published in downstream modules (N/A; no dependencies).

…allback

When a parameter cannot be parsed by _parse_schema_from_parameter, the
legacy declaration path generates the schema of every parameter with
pydantic. Pydantic declares an Enum with non-string values as, for an
IntEnum, {'enum': [1, 2], 'type': 'integer'}, and types.Schema rejects
it because its enum only takes strings, so building the declaration
raised a ValueError.

Generate these schemas with a GenerateJsonSchema that declares every
Enum as a STRING schema with string values, as
_parse_schema_from_parameter does, and render an Enum default the same
way, so it is one of the enum values.

Fixes google#8070
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Legacy function declaration rejects an IntEnum parameter when the function falls back to pydantic schemas

2 participants