mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 22:32:22 +03:00
fix(claude): forward client-negotiated thinking-binding-controls and thinking-display-updates betas (#12989)
* fix(claude): forward client-negotiated thinking-binding-controls and thinking-display-updates betas Gateways drops anthropic-beta tokens not on FORWARDABLE_CLIENT_BETAS, so @ai-sdk/anthropic Fable 5.1 requests carrying thinking.block_binding were rejected upstream with thinking.adaptive.block_binding: Extra inputs are not permitted even when the client negotiated thinking-binding-controls-2026-08-01 correctly. Same class for thinking.display updates (thinking-display-updates-2026-08-18). Regression guard: tests/unit/thinking-binding-controls-beta-forward.test.ts * chore(changelog): fragment for #12989 * chore: trim comments
This commit is contained in:
@@ -0,0 +1 @@
|
||||
- **fix(claude):** forward client-negotiated `thinking-binding-controls-2026-08-01` and `thinking-display-updates-2026-08-18` betas so Fable 5.1 `thinking.block_binding` / `thinking.display` requests are no longer rejected upstream with `Extra inputs are not permitted` ([#12989](https://github.com/diegosouzapw/OmniRoute/pull/12989))
|
||||
@@ -65,6 +65,10 @@ export const FORWARDABLE_CLIENT_BETAS = Object.freeze([
|
||||
// gate (#9505), so a client that sent it must keep it through the merge —
|
||||
// otherwise its effort negotiation is silently dropped.
|
||||
"effort-2025-11-24",
|
||||
// Fable 5.1 betas (@ai-sdk/anthropic sends both automatically): without them
|
||||
// upstream rejects `thinking.block_binding` / `thinking.display` with 400.
|
||||
"thinking-binding-controls-2026-08-01",
|
||||
"thinking-display-updates-2026-08-18",
|
||||
]);
|
||||
|
||||
/**
|
||||
|
||||
69
tests/unit/thinking-binding-controls-beta-forward.test.ts
Normal file
69
tests/unit/thinking-binding-controls-beta-forward.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Fable 5.1 sends `thinking.block_binding` + `thinking-binding-controls-2026-08-01`
|
||||
* (and `thinking.display` + `thinking-display-updates-2026-08-18`) automatically.
|
||||
* The gateway stripped both betas, so upstream 400'd with `Extra inputs are not
|
||||
* permitted`. Both must be forwarded when the client negotiates them.
|
||||
*/
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
const { ANTHROPIC_BETA_API_KEY, mergeClientAnthropicBeta, FORWARDABLE_CLIENT_BETAS } =
|
||||
await import("../../open-sse/config/anthropicHeaders.ts");
|
||||
|
||||
const BINDING_CONTROLS = "thinking-binding-controls-2026-08-01";
|
||||
const DISPLAY_UPDATES = "thinking-display-updates-2026-08-18";
|
||||
|
||||
// ── allowlist membership ────────────────────────────────────────────────────
|
||||
|
||||
test("FORWARDABLE_CLIENT_BETAS must include thinking-binding-controls beta", () => {
|
||||
assert.ok(FORWARDABLE_CLIENT_BETAS.includes(BINDING_CONTROLS));
|
||||
});
|
||||
|
||||
test("FORWARDABLE_CLIENT_BETAS must include thinking-display-updates beta", () => {
|
||||
assert.ok(FORWARDABLE_CLIENT_BETAS.includes(DISPLAY_UPDATES));
|
||||
});
|
||||
|
||||
// ── client-negotiated beta forwarding ───────────────────────────────────────
|
||||
|
||||
test("mergeClientAnthropicBeta must forward client-negotiated thinking-binding-controls beta", () => {
|
||||
const out = mergeClientAnthropicBeta(
|
||||
ANTHROPIC_BETA_API_KEY,
|
||||
`claude-code-20250219,${BINDING_CONTROLS}`
|
||||
);
|
||||
const tokens = out.split(",").map((s) => s.trim());
|
||||
assert.ok(tokens.includes(BINDING_CONTROLS), `client beta dropped: ${out}`);
|
||||
});
|
||||
|
||||
test("mergeClientAnthropicBeta must forward client-negotiated thinking-display-updates beta", () => {
|
||||
const out = mergeClientAnthropicBeta(
|
||||
ANTHROPIC_BETA_API_KEY,
|
||||
`claude-code-20250219,${DISPLAY_UPDATES}`
|
||||
);
|
||||
const tokens = out.split(",").map((s) => s.trim());
|
||||
assert.ok(tokens.includes(DISPLAY_UPDATES), `client beta dropped: ${out}`);
|
||||
});
|
||||
|
||||
test("mergeClientAnthropicBeta forwards both betas together without duplication", () => {
|
||||
const out = mergeClientAnthropicBeta(
|
||||
`${ANTHROPIC_BETA_API_KEY},${BINDING_CONTROLS}`,
|
||||
`claude-code-20250219,${BINDING_CONTROLS},${DISPLAY_UPDATES}`
|
||||
);
|
||||
const tokens = out.split(",").map((s) => s.trim());
|
||||
assert.ok(tokens.includes(BINDING_CONTROLS), `binding-controls missing: ${out}`);
|
||||
assert.ok(tokens.includes(DISPLAY_UPDATES), `display-updates missing: ${out}`);
|
||||
assert.equal(
|
||||
tokens.filter((t) => t.toLowerCase() === BINDING_CONTROLS).length,
|
||||
1,
|
||||
`binding-controls duplicated: ${out}`
|
||||
);
|
||||
});
|
||||
|
||||
// ── guard: allowlist still closed ───────────────────────────────────────────
|
||||
|
||||
test("mergeClientAnthropicBeta still ignores non-allowlisted client betas", () => {
|
||||
const out = mergeClientAnthropicBeta(
|
||||
ANTHROPIC_BETA_API_KEY,
|
||||
"some-random-future-beta-2099-01-01"
|
||||
);
|
||||
assert.ok(!out.includes("some-random-future-beta"), `unknown beta leaked: ${out}`);
|
||||
});
|
||||
Reference in New Issue
Block a user