-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Fix Gemini nested tool argument schemas by removing title from $defs
#3487
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
DouweM
merged 6 commits into
pydantic:main
from
conradlee:fix/google-nested-models-issue-3483
Nov 20, 2025
Merged
Fix Gemini nested tool argument schemas by removing title from $defs
#3487
DouweM
merged 6 commits into
pydantic:main
from
conradlee:fix/google-nested-models-issue-3483
Nov 20, 2025
Conversation
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
When using nested Pydantic models with Gemini without NativeOutput, the model treats 'title' fields in nested schemas as callable function names, resulting in MALFORMED_FUNCTION_CALL errors. Gemini's tool calling system doesn't properly handle JSON Schema $ref/$defs and interprets nested model titles as Python constructors instead of schema definitions. The fix applies different schema transformations for tool calling versus structured output modes. For tool schemas, we inline $ref definitions and remove title fields from nested schemas, which allows Gemini to correctly parse the schema structure. For NativeOutput mode, we preserve $ref/$defs to maintain support for complex and recursive schemas as established in previous work. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
pydantic#3483) ## Problem Gemini's function calling parser incorrectly interprets 'title' fields in nested JSON Schema definitions ($defs) as callable Python functions, causing MALFORMED_FUNCTION_CALL errors when using nested Pydantic models. ## Root Cause When a schema like this is sent to Gemini: ```json { "$defs": { "MiddleModel": { "title": "MiddleModel", // ← Gemini treats this as a callable "type": "object" } } } ``` Gemini tries to generate Python constructor calls like: `MiddleModel(title="...", items=[...])` Instead of JSON: `{"title": "...", "items": [...]}` ## Solution Remove 'title' from nested schemas (those in $defs) while preserving: - $ref/$defs structure (better schema organization) - Top-level title (needed for function declaration name) This is simpler than the original approach of inlining all $ref definitions. ## Testing - Added test_google_nested_models_without_native_output (reproduces issue) - Added test_google_nested_models_with_native_output (workaround verification) - All 81 Google model tests pass - Verified with real Gemini API calls ## Related Issues - Fixes pydantic#3483 - Reported to Google SDK: googleapis/python-genai#1732 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Closed
2 tasks
DouweM
requested changes
Nov 20, 2025
Address reviewer feedback from @DouweM: - Always remove 'title' from all schemas (like OpenAI does) - Remove unnecessary customize_request_parameters() method - Remove unused imports (GoogleJsonSchemaTransformer, _customize_tool_def, _customize_output_object) The original approach of conditionally removing title based on $defs keys had an edge case where users could customize titles. The simpler approach of always removing title is safer and cleaner, since the function declaration name is set separately in ToolDefinition anyway.
Contributor
Author
|
Thanks for the speedy review @DouweM!:
My understanding is that when pydanitc models are transformed into json schemas, the title field is quite redundant:
Is that right? Then we don't have to feel bad about stripping titles.
|
The GoogleJsonSchemaTransformer now removes all 'title' fields to fix Gemini's MALFORMED_FUNCTION_CALL bug. Updated test snapshots to reflect this change - all 'title' fields are now absent from expected outputs. This completes the fix for issue pydantic#3483. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Reference the underlying issue in googleapis/python-genai instead of repeating the detailed explanation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
title from $defs
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.
Problem
When using nested Pydantic models with Gemini without
NativeOutput, the model incorrectly treatstitlefields in nested JSON Schema definitions ($defs) as callable Python functions, resulting inMALFORMED_FUNCTION_CALLerrors.Example: Using these Pydantic models:
Schema sent to Gemini (simplified):
{ "name": "final_result", "parameters_json_schema": { "title": "TopModel", "type": "object", "properties": { "name": {"type": "string"}, "pages": { "type": "array", "items": {"$ref": "#/$defs/MiddleModel"} } }, "$defs": { "MiddleModel": { "title": "MiddleModel", // ← Gemini treats this as a callable function! "type": "object", "properties": { "title": {"type": "string"}, "items": { "type": "array", "items": {"$ref": "#/$defs/NestedModel"} } } }, "NestedModel": { "title": "NestedModel", // ← Also treated as callable! "type": "object", "properties": { "name": {"type": "string"}, "value": {"type": "integer"} } } } } }What Gemini incorrectly generates:
→ Error:
Unknown tool name: 'MiddleModel'. Available tools: 'final_result'What we want Gemini to generate:
{"title": "Page 1", "items": [{"name": "Item", "value": 1}]}Root Cause
Gemini's function calling parser has a bug where it treats the JSON Schema
titlefield in$defsentries as callable function names, violating the JSON Schema spec wheretitleis purely descriptive metadata. When it encounters"title": "MiddleModel"inside a definition, it thinks "MiddleModel is a function I can call" rather than "this is just metadata describing the schema."This only affects nested schemas in
$defs, not the top-leveltitle(which correctly represents the tool name).Solution
Remove
titlefields from nested schemas (those in$defs) while preserving:$ref/$defsstructure (for better schema organization)title(needed for the function declaration name)Transformed schema (after fix):
{ "$defs": { "MiddleModel": { // "title" removed! "type": "object", "properties": {...} }, "NestedModel": { // "title" removed! "type": "object", "properties": {...} } }, "title": "TopModel", // ← Kept at root level ... }Key insight: We initially thought we needed to inline all
$refdefinitions, but testing with real API calls revealed that Gemini can handle$ref/$defscorrectly—the problem is only with thetitlefields. This makes the fix much simpler and cleaner.Implementation
Modified
GoogleJsonSchemaTransformer.transform()to detect and removetitlefrom nested schemas:Testing
test_google_nested_models_without_native_output(reproduces the exact failing case from Nested Pydantic Models Treated as Tool Calls Instead of Structured Output for Gemini Models #3483)test_google_nested_models_with_native_output(verifies theNativeOutputworkaround)Related Issues