diff --git a/open-sse/translator/concerns/thinkingUnified.js b/open-sse/translator/concerns/thinkingUnified.js index 1cf44384..d6082724 100644 --- a/open-sse/translator/concerns/thinkingUnified.js +++ b/open-sse/translator/concerns/thinkingUnified.js @@ -173,6 +173,12 @@ function applyFormat(fmt, body, cfg, caps) { } case "claude-adaptive": { if (none && canDisable) { body.thinking = { type: "disabled" }; break; } + // output_config.effort alone does NOT turn thinking on: Anthropic requires + // an explicit thinking:{type:"adaptive"} on Opus 4.6/4.7/4.8 and Sonnet 4.6 + // ("thinking is off unless you explicitly set it"), and Anthropic-compatible + // shims (e.g. GitHub Copilot /v1/messages) default thinking off even for + // Sonnet 5. Send both fields — the documented adaptive-thinking shape. + body.thinking = { type: "adaptive" }; const level = toLevel(eff); body.output_config = { effort: level === "xhigh" ? "high" : level }; break; diff --git a/tests/translator/__snapshots__/golden-request.test.js.snap b/tests/translator/__snapshots__/golden-request.test.js.snap index 17fa66dd..a7a0c219 100644 --- a/tests/translator/__snapshots__/golden-request.test.js.snap +++ b/tests/translator/__snapshots__/golden-request.test.js.snap @@ -118,6 +118,9 @@ exports[`GOLDEN request: OpenAI → Claude > reasoning_effort → adaptive outpu "type": "text", }, ], + "thinking": { + "type": "adaptive", + }, } `; diff --git a/tests/translator/thinking-unified.test.js b/tests/translator/thinking-unified.test.js index bae22c33..dad57622 100644 --- a/tests/translator/thinking-unified.test.js +++ b/tests/translator/thinking-unified.test.js @@ -55,10 +55,14 @@ describe("extractThinking", () => { }); describe("applyThinking per provider format", () => { - it("claude 4.6+ → adaptive output_config (no budget_tokens)", () => { + it("claude 4.6+ → adaptive thinking + output_config (no budget_tokens)", () => { const out = apply("claude", "claude-opus-4.7", { reasoning_effort: "high" }, "claude"); expect(out.output_config).toEqual({ effort: "high" }); - expect(out.thinking).toBeUndefined(); + // Anthropic: on Opus 4.6/4.7/4.8 and Sonnet 4.6 thinking stays OFF unless + // thinking:{type:"adaptive"} is sent explicitly; output_config alone is not + // enough (and Anthropic-compatible shims like Copilot default off even on + // Sonnet 5). Both fields together are the documented adaptive shape. + expect(out.thinking).toEqual({ type: "adaptive" }); }); it("claude haiku → enabled+budget", () => { const out = apply("claude", "claude-haiku-4.5", { reasoning_effort: "high" }, "claude");