fix(anthropic): strip all remaining output_format schema keywords rejected by Anthropic - #34319
Conversation
…ected by Anthropic
Greptile SummaryThis PR extends the Anthropic output-schema filter to strip 11 additional JSON Schema keywords that the API rejects with 400 errors (
Confidence Score: 5/5Safe to merge; the change is a pure expansion of an existing allow-list filter with no effect on schemas that do not contain the new keywords. Every added keyword is independently strip-only: schemas that do not use the new keywords pass through the filter unchanged, so there is no regression surface for existing callers. The oneOf to anyOf rewrite is the only semantic transformation and is already documented and tested. The refactor from a hard-coded set to a derived set of constraint_labels is equivalent by construction. Tests cover each new keyword, the merge ordering, and the deterministic-note invariant. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/llms/anthropic/chat/transformation.py | Extends filter_anthropic_output_schema with 11 new rejected keywords; refactors to single-source constraint_labels dict; rewrites oneOf to anyOf; fixes non-deterministic note ordering — all logic is correct and well-guarded. |
| tests/litellm/llms/anthropic/test_anthropic_schema_filter.py | Adds targeted tests for every new keyword and the deterministic-ordering guarantee; tests are pure unit tests with no network calls and cover all branches including the oneOf merge. |
| tests/test_litellm/llms/anthropic/test_anthropic_output_format_filter.py | Mirror coverage under tests/test_litellm/ for CI; all tests are mock-only with no network calls, compliant with the repo rule for this folder. |
Reviews (3): Last reviewed commit: "Merge remote-tracking branch 'origin/lit..." | Re-trigger Greptile
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…itellm_anthropic_output_format_remaining_keywords
86eee8b
into
litellm_internal_staging
Relevant issues
Follow-up to #34313 (the staging port of #33981). #33981 completed the array/object constraint keywords that were reported failing, but live probing of
api.anthropic.comshowed the strip list was still not exhaustive. This PR strips every remaining schema keyword the API rejects, so the whack-a-mole ends: probing confirmed unknown keywords are silently ignored by the API (a made-up key passes through fine), meaning the only 400 sources are the specific keywords Anthropic validates against, and all of them are now coveredLinear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Screenshots / Proof of Fix
Every keyword below was first probed directly against
api.anthropic.com(claude-opus-4-8,structured-outputs-2025-11-13beta header) to establish exactly what the API rejects:multipleOf,patternProperties,propertyNames,dependentRequired,dependentSchemas,unevaluatedProperties,if,then,else,not, andprefixItemsall 400 withproperty '<keyword>' is not supported, andoneOf400s withSchema type 'oneOf' is not supported. Accepted and left untouched:pattern,format,default,examples,readOnly,contentMediaType,unevaluatedItems, and unknown keys generallyThe e2e runs are fully live with zero mocks: a LiteLLM proxy in front of a real Azure AI Foundry Claude deployment (
azure_ai/claude-sonnet-4-6), costing real $. The before run is at46a0809a51, the head of #34313 as it went into staging via46440e2df4, so it is the exact pre-fix behavior shipping today. The after run is at this PR's head16dad256d4, the merge of the latest litellm_internal_staging into the fix. Same nine requests both times, each aresponse_format: json_schemacall whose schema carries the named keyword(s):46a0809a5116dad256d4multipleOfpatternPropertiespropertyNamesdependentRequired+dependentSchemasif+then+elsenotprefixItemsoneOfExample request (the
multipleOfcase; the other payloads differ only in which keyword they set):Raw results, before (
46a0809a51):Raw results, after (
16dad256d4); note the outputs respect the stripped constraints via the advisory description notes (25 is a multiple of 5, the dog has a sound, the color is not red, the pair is two numbers). The emptymetaobjects are the injectedadditionalProperties: falseat work oncepatternProperties/propertyNamesare stripped from a schema that declares no named properties:Type
🐛 Bug Fix
Changes
litellm/llms/anthropic/chat/transformation.py:filter_anthropic_output_schemanow stripsmultipleOf,patternProperties,propertyNames,dependentRequired,dependentSchemas,unevaluatedProperties,if,then,else,not, andprefixItemsinto advisory description notes, alongside the keywords it already handled, and rewritesoneOftoanyOfexactly as the Anthropic SDK does.unsupported_fieldsis now derived fromconstraint_labelsso there is a single source of truth, and the note builder iterates the insertion-orderedconstraint_labelsdict instead of a set, making the note order deterministic across processes; previously the order depended on PYTHONHASHSEED, so the same request could serialize differently on different proxy workers, which is hostile to request/prompt cachingTests in
tests/litellm/llms/anthropic/test_anthropic_schema_filter.pyand thetests/test_litellm/mirror cover each new keyword's removal and note text, theoneOfrewrite (including merging with an existinganyOfand recursive filtering inside variants), and a regression test asserting the exact note string for a five-constraint schema, which fails under set iteration with high probabilityFinal Attestation