Skip to content

fix(parse): demote composition keywords instead of dropping them - #1834

Open
Swastikbhat-lab wants to merge 2 commits into
anthropics:mainfrom
Swastikbhat-lab:fix/transform-schema-demote-composition-keywords
Open

fix(parse): demote composition keywords instead of dropping them#1834
Swastikbhat-lab wants to merge 2 commits into
anthropics:mainfrom
Swastikbhat-lab:fix/transform-schema-demote-composition-keywords

Conversation

@Swastikbhat-lab

Copy link
Copy Markdown

Summary

transform_schema silently drops user constraints when a JSON schema combines keywords that the API treats as mutually exclusive at a given level.

transform_schema pops type/anyOf/oneOf/allOf up front, then emits only the winner:

  • {"type": "string", "anyOf": [...]} → the type constraint is discarded entirely
  • {"anyOf": [...], "oneOf": [...]}oneOf is discarded (not even preserved in the description)
  • {"anyOf": [...], "allOf": [...]}allOf is discarded

The transformed schema therefore no longer matches the user's original constraints — and unlike every other unsupported property, the dropped keyword isn't even demoted to the description, so the model never "sees" the constraint either.

Fix

When a composition keyword is selected, the coexisting type and any secondary composition keyword are put back into the leftover set so they flow through the file's existing demote-to-description path (description: "{type: string}"), the same mechanism used for minimum, default, etc. The comment above that path states the intent: unsupported props are appended to the description "so that the model might follow them."

The change is strictly additive: nothing that was previously emitted changes. Schemas with a single keyword (the only case with existing tests) produce byte-identical output; only previously-dropped constraints are now preserved.

Tests

4 regression tests added to tests/lib/_parse/test_transform.py, one per dropped-keyword combination (type+anyOf, anyOf+oneOf, anyOf+allOf, type+oneOf). All 4 fail on the unpatched tree and pass with the fix; the full tests/lib/_parse + tests/lib/streaming suites (88 tests) remain green. pyright, mypy and ruff report no new issues.

transform_schema pops type/anyOf/oneOf/allOf before deciding which one to
emit, so when a schema combines `type` with a composition keyword (or two
composition keywords), the non-selected keywords were silently discarded —
the transformed schema no longer matched the user's constraints.

Keep them in the leftover set so they flow through the existing
demote-to-description path, matching how every other unsupported property
is handled. Only the previously-dropped constraint is added; output for
single-keyword schemas is unchanged.
@Swastikbhat-lab
Swastikbhat-lab requested a review from a team as a code owner August 15, 2026 23:13
Adds fuzz-style property tests (seeded random generator + exhaustive
bounded sweep) asserting every input schema key survives the transform
either as an equal-valued key or demoted into the description, per the
policy documented in _transform.py.

The property tests exposed one remaining gap: sibling keywords of a
$ref (title, enum, ...) were silently dropped by the early return.
They are now demoted to the description like every other unsupported
property, so a $ref schema carries all of its original information.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants