fix(tools): sanitize anyOf schemas for Vertex AI function declarations#6381
Open
ShauryaaSharma wants to merge 1 commit into
Open
fix(tools): sanitize anyOf schemas for Vertex AI function declarations#6381ShauryaaSharma wants to merge 1 commit into
ShauryaaSharma wants to merge 1 commit into
Conversation
Pydantic serializes Optional[X]/Union fields as anyOf, which Vertex AI rejects because the wrapping schema has no top-level type field (googleapis/python-genai#1807), while AI Studio accepts it unmodified. Flatten the common Optional[X] -> anyOf: [X, {type: null}] pattern into X + nullable: true for parameters_json_schema and response_json_schema when targeting Vertex AI, gated on GoogleLLMVariant.VERTEX_AI so AI Studio behavior is unchanged. Fixes google#6373
Collaborator
|
Response from ADK Triaging Agent Hello @ShauryaaSharma, thank you for creating this PR! We appreciate the comprehensive description, thorough explanation of the Vertex AI schema serialization issue, and the addition of solid unit tests. However, looking at the checklist and details, there are a couple of points from our contribution guidelines that are still outstanding:
This information and verification will help reviewers review your PR more efficiently. Thanks! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pydantic serializes Optional[X]/Union fields as anyOf, which Vertex AI rejects because the wrapping schema has no top-level type field (googleapis/python-genai#1807), while AI Studio accepts it unmodified. Flatten the common Optional[X] -> anyOf: [X, {type: null}] pattern into X + nullable: true for parameters_json_schema and response_json_schema when targeting Vertex AI, gated on GoogleLLMVariant.VERTEX_AI so AI Studio behavior is unchanged.
Fixes #6373
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
1. Link to an existing issue (if applicable):
2. Or, if no issue exists, describe the change:
Problem:
Pydantic serializes
Optional[X]/Union[X, None]fields asanyOf, e.g.:{"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}], "default": null}Vertex AI rejects this with
functionDeclaration ... schema didn't specify the schema type field, because the wrapping schema itself has no top-leveltype(see googleapis/python-genai#1807). The Gemini Developer API (AI Studio) accepts the same schema unmodified — this is why the same agent definition works on AI Studio but fails on Vertex, as reported in #6373.Solution:
Add a Vertex-only sanitization pass that flattens the common
Optional[X]->anyOf: [X, {type: null}]pattern intoXmerged with"nullable": true, which Vertex accepts:{"type": "array", "items": {"type": "string"}, "default": null, "nullable": true}This is applied in
build_function_declaration_with_json_schema(src/google/adk/tools/_function_tool_declarations.py) toparameters_json_schema,response_json_schema, and the Pydantic-BaseModel-as-function path, gated strictly onget_google_llm_variant() == GoogleLLMVariant.VERTEX_AIso AI Studio's existing (working) behavior is unchanged. True multi-variant unions (e.g.Union[int, str], noNonebranch) can't be losslessly flattened this way and are intentionally left as-is.Scope note: this PR covers tool/function declaration schemas only — the exact construct reported in the issue, and fully within ADK's control. The
output_schema->GenerateContentConfig.response_schemapath hands a raw Pydantic class to thegoogle-genaiSDK, which converts it internally; that's better tracked against the upstreamgoogleapis/python-genai#1807issue rather than patched here. The second symptom in #6373 (dropped function-call results under SSE +output_schema+BuiltInPlannerthinking) was investigated separately — no bug was found in ADK's streaming aggregator (streaming_utils.py), so it's left out of this PR pending a live repro against Vertex.Testing Plan
Unit Tests:
Added
TestVertexAiAnyOfSanitizationtotests/unittests/tools/test_function_tool_declarations.py, covering:Optional[list[str]]primitive field -> flattened to array +nullable: trueOptional[Address](nested Pydantic model,$ref-based) -> flattened,$refpreservedBaseModelpassed directly (mirrorsoutput_schema-shaped classes) with anOptional[list[str]]field -> flattenedint | strunion (noNonebranch) -> left untouched, confirming we don't overreachAll new tests are gated behind an autouse fixture setting
GOOGLE_GENAI_USE_ENTERPRISE=true, so existing AI Studio-path tests (which assert rawanyOfis present) are unaffected.Ran the full
tests/unittests/tools/directory:The 5 failures (
test_edit_file_tool.py,test_read_file_tool.py) are pre-existing Windows CRLF-handling issues, unrelated to this change — confirmed identical onmainprior to this change.Also ran the full
tests/unittestssuite (excluding one pre-existing, unrelated collection error intest_verify_snippets.pythat expects a repo layout not present in this checkout — reproduces identically onmain):Spot-checked two of the 79 failures (
bigquery_agent_analytics_plugin,test_samples.py::test_sample[...parallel_functions...]) againstmainand confirmed both fail identically without this change — none of the 79 touch tool/schema declaration code.Ran
pre-commit(isort,pyink,addlicense,ruff) against the changed files — clean (isortauto-fixed import ordering; re-verified tests still pass after).Manual End-to-End (E2E) Tests:
Not completed — I don't have a GCP project/Vertex AI credentials available in my dev environment to run the exact repro from #6373 (an
LlmAgentwithoutput_schema+ a tool +BuiltInPlannerthinking, run withGOOGLE_GENAI_USE_VERTEXAI=1) against the live API. The unit tests assert the exact schema transformation Vertex requires per the linked upstream issue, but this hasn't been confirmed against a real Vertex API call yet. If a maintainer or the issue reporter can verify against their Vertex project, that would close the loop.Checklist
Additional context
toxmulti-version testing (required by CONTRIBUTING.md before a PR) is blocked in my environment by the pre-existingtest_verify_snippets.pycollection error mentioned above, present in all 5 supported Python versions independent of this change. I ran the equivalent full-suitepytestinvocation above as the closest substitute.