fix: preserve nested tool properties - #725
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bebd3483cd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| items: Optional[Any] = None | ||
| description: Optional[str] = None | ||
| enum: Optional[Sequence[Any]] = None | ||
| properties: Optional[Mapping[str, 'Tool.Function.Parameters.Property']] = None |
There was a problem hiding this comment.
Preserve nested schema keywords along with properties
When a nested object schema includes required—for example, address: {type: "object", required: ["city"], properties: ...}—validating each child as Property silently discards that keyword because this model only declares type, items, description, enum, and properties, while Pydantic ignores extra fields by default. The resulting chat request therefore weakens the supplied tool contract and may let the model emit incomplete arguments; other nested keywords such as additionalProperties, anyOf, constraints, and $ref are lost for the same reason. Preserve arbitrary JSON Schema fields or model the complete recursive schema rather than recursively narrowing every child to this partial type.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed the broader schema-keyword limitation, but it is not newly introduced by this field. I compared base fa85099 and head bebd348 using the same payload through Client.chat and captured the serialized request: both omit address.required and additionalProperties; base also drops the entire nested properties, while this change preserves the supported child fields. Existing array-item schemas and top-level required/$defs remain unchanged. The 26 serialization/utility tests still pass.
#724 specifically requests the recursive properties field. Arbitrary keyword preservation (or a complete JSON Schema model) would change the existing model contract beyond that focused fix, so I have clarified this limitation in the PR description and kept the implementation scoped. Nested constraints, anyOf, and $ref support remain a separate limitation.
Fixes #724.
Nested object schemas lose their
propertieswhen parsed intoTool.Function.Parameters.Property, so the nested fields never reach the chat request.Add an optional, typed recursive
propertiesfield. Use a fully qualified forward reference for the nested class, and update the existing default-inclusive serialization expectation. Regression tests cover two levels of nested objects through both model and JSON request serialization, empty objects, and existing array items, enum, top-levelrequired, and$defsbehavior. This focused change does not add support for schema keywords thatPropertydid not previously model.Validation on macOS arm64:
hatch test -i python=3.13 -p: 104 passed (full suite and doctests).uv lock --check, and exactuv exportcomparison: passed.Validation limits: the all-matrix
hatch test -acpattempt passed 104 tests on Python 3.14, then could not download the missing free-threaded 3.14t runtime (connection timeout). Whole-repository formatting with current Ruff flags existing README code-block formatting; the README is unchanged. Python 3.8 was not run.