Skip to content

Commit 85295ad

Browse files
committed
Simplify fix: always remove title, drop customize_request_parameters
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.
1 parent 476e914 commit 85295ad

File tree

2 files changed

+8
-56
lines changed

2 files changed

+8
-56
lines changed

pydantic_ai_slim/pydantic_ai/models/google.py

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,14 @@
3838
VideoUrl,
3939
)
4040
from ..profiles import ModelProfileSpec
41-
from ..profiles.google import GoogleJsonSchemaTransformer, GoogleModelProfile
41+
from ..profiles.google import GoogleModelProfile
4242
from ..providers import Provider, infer_provider
4343
from ..settings import ModelSettings
4444
from ..tools import ToolDefinition
4545
from . import (
4646
Model,
4747
ModelRequestParameters,
4848
StreamedResponse,
49-
_customize_output_object,
50-
_customize_tool_def,
5149
check_allow_model_requests,
5250
download_item,
5351
get_user_agent,
@@ -247,48 +245,6 @@ def prepare_request(
247245
)
248246
return super().prepare_request(model_settings, model_request_parameters)
249247

250-
def customize_request_parameters(self, model_request_parameters: ModelRequestParameters) -> ModelRequestParameters:
251-
"""Apply schema transformations, using inlining only for tools, not for NativeOutput."""
252-
# Transform tools with inlining (default behavior of GoogleJsonSchemaTransformer)
253-
model_request_parameters = replace(
254-
model_request_parameters,
255-
function_tools=[
256-
_customize_tool_def(GoogleJsonSchemaTransformer, t) for t in model_request_parameters.function_tools
257-
],
258-
output_tools=[
259-
_customize_tool_def(GoogleJsonSchemaTransformer, t) for t in model_request_parameters.output_tools
260-
],
261-
)
262-
263-
# For NativeOutput, use GoogleJsonSchemaTransformer with prefer_inlined_defs=False to keep $ref/$defs
264-
if output_object := model_request_parameters.output_object:
265-
from .._json_schema import JsonSchema
266-
267-
# Create a subclass that doesn't inline for output objects
268-
class GoogleOutputSchemaTransformer(GoogleJsonSchemaTransformer):
269-
def __init__(
270-
self,
271-
schema: JsonSchema,
272-
*,
273-
strict: bool | None = None,
274-
simplify_nullable_unions: bool = False,
275-
) -> None:
276-
# Call JsonSchemaTransformer.__init__ directly, skipping GoogleJsonSchemaTransformer.__init__
277-
# to avoid setting prefer_inlined_defs=True
278-
super(GoogleJsonSchemaTransformer, self).__init__(
279-
schema,
280-
strict=strict,
281-
prefer_inlined_defs=False, # Don't inline for NativeOutput
282-
simplify_nullable_unions=simplify_nullable_unions,
283-
)
284-
285-
model_request_parameters = replace(
286-
model_request_parameters,
287-
output_object=_customize_output_object(GoogleOutputSchemaTransformer, output_object),
288-
)
289-
290-
return model_request_parameters
291-
292248
async def request(
293249
self,
294250
messages: list[ModelMessage],

pydantic_ai_slim/pydantic_ai/profiles/google.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ class GoogleJsonSchemaTransformer(JsonSchemaTransformer):
3737
3838
Gemini supports [a subset of OpenAPI v3.0.3](https://ai.google.dev/gemini-api/docs/function-calling#function_declarations).
3939
40-
Note: Gemini's tool calling system treats 'title' fields in nested schemas as callable function names,
40+
Note: Gemini's tool calling system treats 'title' fields as callable function names,
4141
causing MALFORMED_FUNCTION_CALL errors. This fixes issue #3483 where nested Pydantic models were
42-
treated as tool calls instead of structured output schema. We remove 'title' from nested schemas
43-
(those defined in $defs) while preserving $ref/$defs structure for better schema organization.
42+
treated as tool calls instead of structured output schema. We remove 'title' from all schemas
43+
since the function declaration name is set separately in the ToolDefinition.
4444
"""
4545

4646
def transform(self, schema: JsonSchema) -> JsonSchema:
@@ -52,14 +52,10 @@ def transform(self, schema: JsonSchema) -> JsonSchema:
5252
schema.pop('discriminator', None)
5353
schema.pop('examples', None)
5454

55-
# Remove 'title' from nested schemas - Gemini treats these as callable function names
56-
# in tool calling mode, causing MALFORMED_FUNCTION_CALL errors for nested objects.
57-
# Only keep title at the root level for the function declaration name.
58-
# We detect nested schemas by checking if the title matches a key in $defs.
59-
if self.defs and 'title' in schema:
60-
# Check if this schema's title matches any key in $defs (meaning it's a nested definition)
61-
if schema.get('title') in self.defs:
62-
schema.pop('title', None)
55+
# Remove 'title' - Gemini treats these as callable function names in tool calling mode,
56+
# causing MALFORMED_FUNCTION_CALL errors. The title for the function declaration itself
57+
# is set separately in the ToolDefinition, so we don't need it in the schema.
58+
schema.pop('title', None)
6359

6460
type_ = schema.get('type')
6561
if type_ == 'string' and (fmt := schema.pop('format', None)):

0 commit comments

Comments
 (0)