From 59f10a250a33f9598675b78be9aa4fcaf4ff0d22 Mon Sep 17 00:00:00 2001 From: Aldo Date: Thu, 8 Oct 2026 05:09:45 +0000 Subject: [PATCH 1/2] fix(claude): stop downgrading adaptive thinking on claude-haiku-5-5 The Haiku guard matched the /haiku/ family name and rewrote adaptive thinking to a manual thinking.type "enabled" for every Haiku model. claude-haiku-5-5 is adaptive-only, so Anthropic rejected the rewritten request with a 400 ("thinking.type.enabled is not supported ... use thinking.type.adaptive and output_config.effort"). - Add a claude-haiku-5-5 model spec (adaptiveThinkingOnly, 1M context, 128K max output, from the gateway catalog). - The Haiku guard now defers to the spec: adaptive-only models are never rejected by the family regex. Haiku 4.5 and earlier keep the manual contract. - Regression tests: 5.5 passes adaptive+effort through untouched; 4.5 still downgrades. Without the fix the 5.5 pass-through test fails. Live probe (before): claude-haiku-5-5 + adaptive -> 400 "enabled not supported"; claude-sonnet-5 + adaptive -> 200. --- open-sse/services/claudeHaikuConstraints.ts | 13 +++++++--- src/shared/constants/modelSpecs.ts | 15 ++++++++++++ .../claude-haiku-no-adaptive-thinking.test.ts | 24 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/open-sse/services/claudeHaikuConstraints.ts b/open-sse/services/claudeHaikuConstraints.ts index 23c6f912dad5..e79e3f6e924d 100644 --- a/open-sse/services/claudeHaikuConstraints.ts +++ b/open-sse/services/claudeHaikuConstraints.ts @@ -1,9 +1,13 @@ +import { isAdaptiveThinkingOnly } from "../../src/shared/constants/modelSpecs.ts"; + type JsonRecord = Record; // Models that reject `thinking.type:"adaptive"` and `output_config.effort` — -// today, all Claude Haiku-tier models (4.5 / 3.5 / dated and aliased). Adaptive -// thinking + the effort knob landed on Sonnet 4.6 / Opus 4.5+ only; sending -// either on Haiku is a hard 400 from the Messages API. +// Claude Haiku-tier models that predate adaptive thinking (4.5 / 3.5 / dated and +// aliased). Haiku 5.5+ is adaptive-only (spec `adaptiveThinkingOnly`), so it +// must NOT be rewritten here: the family regex alone wrongly downgrades it. +// Adaptive thinking + the effort knob landed on Sonnet 4.6 / Opus 4.5+ and +// Haiku 5.5; sending `enabled` to those is a hard 400 from the Messages API. const HAIKU_CONSTRAINT_PATTERN = /haiku/i; // Default budget when collapsing `thinking.type:"adaptive"` to a manual shape @@ -18,6 +22,9 @@ function asRecord(value: unknown): JsonRecord | null { function modelRejectsAdaptiveAndEffort(modelId: string | null | undefined): boolean { if (typeof modelId !== "string" || modelId.length === 0) return false; + // Spec is authoritative: an adaptive-only model accepts adaptive+effort, so + // it is never rejected here regardless of its family name. + if (isAdaptiveThinkingOnly(modelId)) return false; return HAIKU_CONSTRAINT_PATTERN.test(modelId); } diff --git a/src/shared/constants/modelSpecs.ts b/src/shared/constants/modelSpecs.ts index cb1a45a06df2..7f410c9418f9 100644 --- a/src/shared/constants/modelSpecs.ts +++ b/src/shared/constants/modelSpecs.ts @@ -405,6 +405,21 @@ export const MODEL_SPECS: Record = { aliases: BEDROCK_CLAUDE_ALIASES("claude-sonnet-5"), }, + // ── Claude Haiku 5.5 ──────────────────────────────────────────── + // Adaptive-thinking-only, like Sonnet 5.5: `thinking.type:"enabled"` returns + // 400 ("use thinking.type.adaptive and output_config.effort"). Haiku 4.5 and + // earlier still accept manual budgets, so this spec must NOT be widened to + // the /haiku/ family. Limits from the gateway catalog (context 1M, output 128K). + "claude-haiku-5-5": { + maxOutputTokens: 128000, + contextWindow: 1000000, + supportsThinking: true, + supportsTools: true, + supportsVision: true, + adaptiveThinkingOnly: true, + aliases: BEDROCK_CLAUDE_ALIASES("claude-haiku-5-5"), + }, + // ── Claude Sonnet 5.5 ─────────────────────────────────────────── "claude-sonnet-5-5": { // Same shape as Sonnet 5, but it rejects thinking.type:"disabled" diff --git a/tests/unit/claude-haiku-no-adaptive-thinking.test.ts b/tests/unit/claude-haiku-no-adaptive-thinking.test.ts index 56c7b6bc332f..6e59a024dc34 100644 --- a/tests/unit/claude-haiku-no-adaptive-thinking.test.ts +++ b/tests/unit/claude-haiku-no-adaptive-thinking.test.ts @@ -72,6 +72,30 @@ describe("normalizeClaudeHaikuConstraints", () => { assert.strictEqual(out, input); }); + // Regression: Haiku 5.5 is adaptive-only (spec adaptiveThinkingOnly). The + // family regex used to rewrite its adaptive+effort to manual `enabled`, which + // Anthropic rejects with a 400 ("use thinking.type.adaptive ..."). + it("leaves claude-haiku-5-5 adaptive thinking + effort untouched", () => { + const input = { + model: "claude-haiku-5-5", + thinking: { type: "adaptive" }, + output_config: { effort: "high" }, + }; + const out = normalizeClaudeHaikuConstraints(input, input.model); + assert.strictEqual(out, input); + assert.deepEqual(out.thinking, { type: "adaptive" }); + assert.deepEqual(out.output_config, { effort: "high" }); + }); + + it("still downgrades adaptive on pre-5 Haiku (4.5 keeps the manual-only contract)", () => { + const input = { + model: "claude-haiku-4-5-20251001", + thinking: { type: "adaptive" }, + }; + const out = normalizeClaudeHaikuConstraints(input, input.model); + assert.deepEqual(out.thinking, { type: "enabled", budget_tokens: 10000 }); + }); + it("leaves haiku bodies that have no adaptive thinking or effort untouched", () => { const input = { model: "claude-haiku-4-5-20251001", From b283e2109b9904c19dbed5724ae4c9678d7be800 Mon Sep 17 00:00:00 2001 From: Aldo Date: Thu, 8 Oct 2026 10:16:25 +0000 Subject: [PATCH 2/2] docs(changelog): add fragment for #15841 Haiku 5.5 adaptive thinking --- changelog.d/fixes/15841-haiku-5-5-adaptive-thinking.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/fixes/15841-haiku-5-5-adaptive-thinking.md diff --git a/changelog.d/fixes/15841-haiku-5-5-adaptive-thinking.md b/changelog.d/fixes/15841-haiku-5-5-adaptive-thinking.md new file mode 100644 index 000000000000..6ba5b71ec750 --- /dev/null +++ b/changelog.d/fixes/15841-haiku-5-5-adaptive-thinking.md @@ -0,0 +1 @@ +- **fix(claude):** `claude-haiku-5-5` requests keep `thinking.type: "adaptive"` instead of being downgraded to `"enabled"`, which Anthropic rejected with HTTP 400 ([#15841](https://github.com/diegosouzapw/OmniRoute/pull/15841)) — thanks @aldoeliacim