Skip to content

fix(schema): expose effort-update and thinking-binding overrides on model compatibility - #52172

Merged
rekram1-node merged 2 commits into
anomalyco:v2from
argszero:compatibility-effort-flags
Oct 6, 2026
Merged

rekram1-node merged 2 commits into
anomalyco:v2from
argszero:compatibility-effort-flags

Conversation

@argszero

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #51146

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Issue #51146 reports that a Bedrock-routed Anthropic model rejects every request after an
effort update, because message-level output_config is accepted by first-party
api.anthropic.com but not by Bedrock.

The protocol layer already knows how to avoid this. packages/ai/src/protocols/anthropic-messages.ts
gates message-level effort updates behind supportsEffortUpdates(model), and that function's first
branch is a compatibility override:

const override = model.compatibility?.supportsEffortUpdates
if (override !== undefined) return override

packages/ai/src/protocols/utils/claude-model.ts has the same escape hatch for Anthropic's
thinking-binding controls. So the runtime switch exists — it is just unreachable from configuration.

That is the bug this PR fixes. A user configures the model like this:

{
  "providers": {
    "my-bedrock-gateway": {
      "models": {
        "global.anthropic.claude-opus-5-5": {
          "compatibility": { "supportsEffortUpdates": false }
        }
      }
    }
  }
}

The config is decoded as Config.Provider.Info, whose model compatibility field is
Model.Compatibility (packages/schema/src/model.ts). That struct declared neither
supportsEffortUpdates nor supportsThinkingBlockBinding, and the config decoder runs with
onExcessProperty: "ignore" (packages/core/src/config.ts). The key was therefore dropped during
decoding, before packages/core/src/config/plugin/provider.ts merged compatibility onto the model.
By the time supportsEffortUpdates read model.compatibility, the user's value was gone, so the
model-ID heuristic in claudeVersion() decided instead — and for a gateway that reuses the Anthropic
protocol against a non-Anthropic backend, that heuristic is wrong.

A provider-level gate would not have fixed the report either: a Bedrock gateway is a
provider: "anthropic" entry with a custom baseURL, so the decision has to stay per-model.

The change is two optional booleans on Model.Compatibility, matching the docs already written on
the AI-side LanguageModelCompatibility, plus the regenerated client surface (ModelCompatibility
is emitted into components.schemas). Once the fields are declared, the existing runtime override is
reachable from configuration and no change is needed in packages/ai.

How did you verify your code works?

  • packages/schema/test/model.test.ts — added a decode test asserting both new keys survive
    Schema.decodeUnknownSync(Model.Compatibility).
  • packages/core/test/config/provider.test.ts — extended the existing
    "loads configured providers and applies later model overrides" case so the two flags are present
    in the config document, and asserted they reach the resolved model's compatibility. This runs the
    real config decode plus the real plugin merge, so it fails before this change and passes after it.
  • Negative control: decoding a compatibility object containing an undeclared key (bogusFlag)
    together with supportsEffortUpdates yields only { supportsEffortUpdates: true }. That confirms
    the decoder strips undeclared keys, which is why the fields had to be declared rather than passed
    through.
  • bun test --timeout 30000 on packages/schema/test/model.test.ts,
    packages/core/test/config/provider.test.ts, packages/core/test/models.test.ts,
    packages/core/test/model-resolver.test.ts and packages/ai/test/effort-updates.test.ts — all
    pass (effort-updates.test.ts already covers the gate honoring the override in both directions).
  • tsgo --noEmit clean for packages/schema, packages/client and packages/ai; oxlint reports
    0 warnings / 0 errors on the changed files.
  • bun run generate from packages/client — the generated ModelCompatibility now carries both
    fields.

Screenshots / recordings

If this is a UI change, please include a screenshot or recording.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

If you do not follow this template your PR will be automatically rejected.

…odel compatibility

Config-level `model.compatibility` decoded through `Model.Compatibility`,
which declared neither `supportsEffortUpdates` nor
`supportsThinkingBlockBinding`. The config decoder ignores excess
properties, so both keys were silently dropped before
`config/plugin/provider.ts` merged the object onto the model, leaving the
runtime gates in `packages/ai` unreachable from configuration.

Declare both optional booleans on the schema (the runtime already reads them)
and regenerate the client surface.
@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

@rekram1-node
rekram1-node merged commit 59e5953 into anomalyco:v2 Oct 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants