Skip to content

fix: keep native structured output for Claude Opus 5.5 - #78

Merged
graphite-app[bot] merged 1 commit into
mainfrom
fix/opus-55-structured-output
Sep 25, 2026
Merged

graphite-app[bot] merged 1 commit into
mainfrom
fix/opus-55-structured-output

Conversation

@anandpant

@anandpant anandpant commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Claude Opus 5.5 rejects forced tool use, so the structuredOutputMode: 'jsonTool' that the Anthropic adapter otherwise requests fails with HTTP 400 through the Foundry proxy. Request the provider's native output format for that model instead.

  • Add requiresAnthropicNativeStructuredOutput next to the catalog entries and thread it through createFoundryAnthropic, matching on both the alias and the RID. Every other Claude model keeps the existing JSON-tool mode.
  • Set structuredOutputMode: 'outputFormat' explicitly rather than omitting the option. The SDK resolves its default 'auto' from the model ID, and this adapter addresses models by Foundry RID, so 'auto' takes the unknown-model path. On @ai-sdk/anthropic 3.0.64, 3.0.95 and 4.0.5 — all inside the declared peer range — that path reports supportsStructuredOutput: false and still sends the forced JSON tool. 'outputFormat' short-circuits that check on every version from 3.0.64 through 4.0.63.
  • The adapter's value is spread after the caller's anthropic options, so a caller passing jsonTool cannot downgrade Opus 5.5 back into the failing path.
  • @ai-sdk/anthropic 4.0.60 added an equivalent fallback upstream, but it keys on the claude-opus-5-5 model ID, which a RID never matches. The fix has to live here.

Without this, Opus 5.5 ships GA with standalone JSON and combined JSON + tools both broken by an option this package sets on the caller's behalf.

Validation

New anthropic.test.ts drives createFoundryAnthropic through the real @ai-sdk/anthropic peer with a stubbed fetch and asserts the outgoing Anthropic body: output_config.format present and no tools/tool_choice for the Opus 5.5 alias and raw RID, the forced json tool for Claude Opus 5, and no caller downgrade. Reverting to the earlier omit-based approach makes the downgrade case fail, so the coverage is load-bearing. Note that the pinned dev version 4.0.52 has a generic claude- fallback that masks the peer-floor behaviour, so the 3.0.64/4.0.5 path was verified by reading those releases' compiled getModelCapabilities directly.

Affected unit tests, lint, and typecheck passed, and pnpm run verify passed at the top of the stack. A focused live rerun of Opus 5.5's JSON and JSON + tools probes on 4.0.52 passed with output identical to the previous run, so no results table changed.

anandpant commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge - adds this PR to the back of the merge queue
  • fast - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@anandpant
anandpant marked this pull request as ready for review September 25, 2026 05:21
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@anandpant
anandpant force-pushed the fix/opus-55-structured-output branch from d427b53 to f8db945 Compare September 25, 2026 05:44
@anandpant
anandpant force-pushed the feat/model-refresh-0924 branch from f475b56 to d69e0b6 Compare September 25, 2026 05:44
@graphite-app
graphite-app Bot changed the base branch from feat/model-refresh-0924 to graphite-base/78 September 25, 2026 05:48
@graphite-app
graphite-app Bot force-pushed the fix/opus-55-structured-output branch from f8db945 to e62cce5 Compare September 25, 2026 05:50
@cursor
cursor Bot changed the base branch from graphite-base/78 to main September 25, 2026 05:51
@graphite-app
graphite-app Bot force-pushed the fix/opus-55-structured-output branch from e62cce5 to c8d09e3 Compare September 25, 2026 05:51
@graphite-app

graphite-app Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Merge activity

## Summary

Claude Opus 5.5 rejects forced tool use, so the `structuredOutputMode: 'jsonTool'` that the Anthropic adapter otherwise requests fails with HTTP 400 through the Foundry proxy. Request the provider's native output format for that model instead.

- Add `requiresAnthropicNativeStructuredOutput` next to the catalog entries and thread it through `createFoundryAnthropic`, matching on both the alias and the RID. Every other Claude model keeps the existing JSON-tool mode.
- Set `structuredOutputMode: 'outputFormat'` explicitly rather than omitting the option. The SDK resolves its default `'auto'` from the model ID, and this adapter addresses models by Foundry RID, so `'auto'` takes the unknown-model path. On `@ai-sdk/anthropic` 3.0.64, 3.0.95 and 4.0.5 — all inside the declared peer range — that path reports `supportsStructuredOutput: false` and still sends the forced JSON tool. `'outputFormat'` short-circuits that check on every version from 3.0.64 through 4.0.63.
- The adapter's value is spread after the caller's `anthropic` options, so a caller passing `jsonTool` cannot downgrade Opus 5.5 back into the failing path.
- `@ai-sdk/anthropic` 4.0.60 added an equivalent fallback upstream, but it keys on the `claude-opus-5-5` model ID, which a RID never matches. The fix has to live here.

Without this, Opus 5.5 ships GA with standalone JSON and combined JSON + tools both broken by an option this package sets on the caller's behalf.

## Validation

New `anthropic.test.ts` drives `createFoundryAnthropic` through the real `@ai-sdk/anthropic` peer with a stubbed fetch and asserts the outgoing Anthropic body: `output_config.format` present and no `tools`/`tool_choice` for the Opus 5.5 alias and raw RID, the forced `json` tool for Claude Opus 5, and no caller downgrade. Reverting to the earlier omit-based approach makes the downgrade case fail, so the coverage is load-bearing. Note that the pinned dev version 4.0.52 has a generic `claude-` fallback that masks the peer-floor behaviour, so the 3.0.64/4.0.5 path was verified by reading those releases' compiled `getModelCapabilities` directly.

Affected unit tests, lint, and typecheck passed, and `pnpm run verify` passed at the top of the stack. A focused live rerun of Opus 5.5's JSON and JSON + tools probes on 4.0.52 passed with output identical to the previous run, so no results table changed.
@graphite-app
graphite-app Bot force-pushed the fix/opus-55-structured-output branch from c8d09e3 to ffa3811 Compare September 25, 2026 05:53
@graphite-app
graphite-app Bot merged commit ffa3811 into main Sep 25, 2026
9 checks passed
@graphite-app
graphite-app Bot deleted the fix/opus-55-structured-output branch September 25, 2026 05:55
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.

1 participant