Skip to content

Commit 1a7af76

Browse files
cliftonclaude
andauthored
fix: make optional fields nullable in strict-mode schemas (#55)
Option<T> was unsound under strict structured outputs. Option<T>::schema() deliberately returns the inner type's schema with no null union, expressing optionality via the struct-level required array. But prepare_strict_schema overwrites required with ALL property keys, as OpenAI strict mode demands. The combination made optional fields both required AND non-nullable, so under OpenAI/Grok/Anthropic constrained decoding the model was grammatically forced to fabricate values for Option fields instead of omitting them. Fix, in add_additional_properties_false: before overwriting required, capture the ORIGINAL required list (the derive macro emits only truly required fields there) and rewrite every property NOT in it to also admit null: - scalar "type" becomes a [type, "null"] union - existing type unions get "null" appended if absent - enum arrays also get null appended (enum constrains values independently of type) - anyOf/oneOf unions get a {"type": "null"} branch if none exists - bare $ref (emitted for self-referential structs) is wrapped as anyOf: [{$ref}, {"type": "null"}] - an absent required array treats ALL properties as optional The capture happens per object level inside the existing recursion, so nested objects and array item objects resolve optionality against their own required arrays. Option<T>::schema() is unchanged: non-strict mode still relies on its transparency. Serde already maps null to None, so deserialization is unaffected. Covers all prepare_strict_schema callers (OpenAI, Grok, Anthropic materialize/streaming paths and Toolbox tool-arg schemas). Adds unit tests for the scalar/union/enum/anyOf/oneOf/$ref transformations, nested object and array cases, the absent-required case, and null-to-None serde round-trip; updates the two tests that asserted the old behavior. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 6ffcffc commit 1a7af76

1 file changed

Lines changed: 438 additions & 17 deletions

File tree

0 commit comments

Comments
 (0)