fix(anthropic): keep enum and const as structured-output constraints - #7558
Conversation
The SDK's transformJSONSchema pops every keyword it recognises and stringifies whatever is left into the node's description. It has no branch for enum or const, so both were silently demoted from grammar constraints into prose, leaving native structured outputs unenforced for them on every Anthropic and Azure Anthropic agent block. Lift enum and const out before the transform and re-attach them after, so the transform's sanitising is preserved verbatim. Only lift the shapes the API grammar-checks: a non-empty enum of primitives matching the node's declared type, and a primitive const. Anything else stays in place and is demoted exactly as before, since sending it would make the API reject the whole request.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryThis PR preserves supported
Confidence Score: 5/5The PR appears safe to merge, with no outstanding correctness, security, or repository-rule issues. The latest change clones the caller-provided schema before extracting constraints, preventing mutation across repeated or parallel uses. The previously reported test typing violation was manually resolved after being corrected, and no new actionable failures remain.
|
| Filename | Overview |
|---|---|
| apps/sim/providers/anthropic/structured-output-schema.ts | Adds a non-mutating wrapper around the SDK transform that preserves supported primitive enum and const constraints. |
| apps/sim/providers/anthropic/core.ts | Routes native structured-output schemas through the new preservation helper. |
| apps/sim/providers/anthropic/core.request.test.ts | Adds comprehensive wire-payload coverage, including verification that caller schemas remain unchanged and reusable. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
A[Caller JSON schema] --> B[Clone schema]
B --> C[Extract supported enum and const constraints]
C --> D[Anthropic SDK schema transform]
D --> E[Restore constraints at transformed paths]
E --> F[output_config.format.schema]
Reviews (3): Last reviewed commit: "test(anthropic): lock the structured-out..." | Re-trigger Greptile
Replaces the two Record<string, any> casts in the new test helper with a WireSchemaNode interface describing the subset of the wire schema the assertions read back, per the repo's no-any rule.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…iant The builder deep-clones before walking so it never deletes enum/const from the caller's schema object. Agent blocks inside a parallel or loop reuse the same responseFormat object across iterations, so losing that clone would strip the constraints from every iteration after the first. Verified the test fails when the clone is removed.
|
@cubic-dev-ai review this PR |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
Summary
transformJSONSchemafrom the Anthropic SDK has no branch forenumorconst, so both were swept into the node'sdescriptionas prose instead of staying real grammar constraints. Native structured outputs have been silently unenforced for them on every Anthropic and Azure Anthropic agent block.enum/constout before the transform and re-attach them after, so all of the transform's sanitising (unknown stringformat, numeric bounds,minItems > 1,additionalProperties) is preserved verbatim.enumof primitives all matching the node's declared type, and a primitiveconst. Everything else stays put and is demoted exactly as today — sending it would make the API reject the whole request, turning a working block into a hard 400.Type of Change
Testing
Verified against the live Anthropic API on
claude-sonnet-5:enum: [...]and a prompt explicitly instructing the model to emit an out-of-enum value, the old path returns the out-of-enum string and the new path returns a valid enum member. Same for number, integer, boolean andconst.null, mixed-type enum, emptyenum, enum disagreeing with the declared type,constwith object/array values, plus the primitive shapes. All return 200 on both paths.output_config.format.schemadirectly, and cover a property literally namedenum,$defs,oneOf→anyOf, and both falsyconstvalues (0,false).providers/pass; verified the new tests fail when the fix is reverted.bun run lint,bun run check:audits(45 audits),docs-manifest:checkandtype-checkall clean.Checklist