fix(core): require all nested properties for strict tool schemas - #39306
Conversation
Nested object schemas (e.g. from MCP tools) only had their top-level properties added to `required` when `strict=True`. OpenAI's strict mode requires every property at every nesting level to be listed in `required`, so nested optional fields caused the API to reject the request outright. Closes #33869 Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Merging this PR will not alter performance
Comparing Footnotes
|
Address review feedback on #39306: the recursive strict-mode walk only followed anyOf/properties/items, so nested object schemas referenced via $ref into a top-level $defs/definitions map (as raw JSON-schema tool input can represent them) were skipped, leaving them without required/additionalProperties and still rejected under strict mode. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Fixes CI failure on the pydantic ~=2.7.0 matrix job: Pydantic <2.9 wraps a referenced model field in "allOf" when it has sibling keys (e.g. a Field description), instead of merging them directly like 2.9+ does. The strict-mode recursive walk didn't follow "allOf", so the referenced object schema never got its required/additionalProperties completed under older pydantic versions, leaving the regression test for #33869 failing with a KeyError. Co-authored-by: open-swe[bot] <open-swe@users.noreply.github.com>
Sydney Runkle (sydney-runkle)
left a comment
There was a problem hiding this comment.
left a few questions, thanks!
| _recursive_set_additional_properties_false(schema["items"]) | ||
| # Raw JSON schemas may keep nested objects in `$defs` and reference them via | ||
| # `$ref`; walk those definitions too so they're made strict. | ||
| for defs_key in ("$defs", "definitions"): |
There was a problem hiding this comment.
why $defs and definitions?
| # OpenAI strict mode requires every property to appear in `required` at every | ||
| # level of nesting. Without this, nested object schemas are rejected. | ||
| properties = schema.get("properties") | ||
| if isinstance(properties, dict) and properties: |
There was a problem hiding this comment.
should we instead be rejecting these tools then?
ccurme (@ccurme) are we in agreement that we should be mutating schemas?
Closes #33869, #38223
Issue:
OpenAI strict tool schemas fail when nested objects contain properties that are optional
Throws:
Fix:
convert_to_openai_function(strict=True)now recursively adds all object properties torequired, matching OpenAI's strict schema requirements.