fix: honor field defaults in nested Pydantic model function schemas - #2782
NishchayMahor wants to merge 1 commit into
Conversation
1a805a9 to
3637ea3
Compare
When generating a function-calling schema for a parameter that is a Pydantic model, each sub-field was built from an inspect.Parameter without a default, so its schema default stayed unset and _get_required_fields marked every non-Optional field as required — even fields with a default. This forced the model to supply values for optional parameters. Forward the field's default (resolving default_factory) into the sub-field parameter so defaulted fields are correctly optional and their default is emitted in the schema.
3637ea3 to
3f91ac2
Compare
|
Rebased onto current Worth flagging one thing I checked rather than assumed: so it's still pinning the behaviour rather than passing incidentally. The change itself is unchanged: a nested Pydantic model's fields with defaults were being emitted as class Inner(BaseModel):
a: str
b: int = 3 # has a default
class Outer(BaseModel):
inner: Innerproduced All checks are green and the CLA is signed. Is there anything you'd like changed, or a different reviewer I should tag? Happy to close it if the direction isn't wanted — I'd just rather that be a decision than a timeout. |
Summary
When automatic function calling generates a schema for a parameter that is a Pydantic model, sub-fields that have a default are incorrectly marked as required. This forces the model to supply values for parameters that should be optional.
Root cause
In
_automatic_function_calling_util._parse_schema_from_parameter, the Pydantic-model branch builds each sub-field viainspect.Parameter(field_name, ..., annotation=field_info.annotation)but never passesfield_info.default. The recursive call therefore sees no default, the sub-schema'sdefaultstaysNone, and_get_required_fields(not nullable and default is None) lists the field as required. Top-level parameters already work because their defaults flow throughparam.default; only nested-model fields were affected.Fix
Forward each field's default into the sub-field
inspect.Parameter, resolvingdefault_factoryviafield_info.get_default(call_default_factory=True), and only when the field is not required. This leavesOptional-without-default fields (already handled vianullable) untouched and does not modify the shared_get_required_fieldshelper.Verified across concrete defaults (
b=5), factory defaults (d: list = Field(default_factory=list)), falsy defaults (f: int = 0), andOptional[int]without a default — only genuinely-required fields remain inrequired.Testing
Added
test_pydantic_model_with_default_fieldstogoogle/genai/tests/types/test_types.py(fails on main, passes with the fix). Existing schema/callable tests unchanged:(2 pre-existing ReplayApiClient fixture errors in afc_thoughts are unrelated — they fail identically on clean main.)
This change was developed with AI assistance; I verified the root cause and behavior against the cited code paths, reviewed every line, and ran the tests above.