diff --git a/changelog.d/fixes/13364-zed-hosted-haiku-thinking-inflation.md b/changelog.d/fixes/13364-zed-hosted-haiku-thinking-inflation.md new file mode 100644 index 0000000000..9b6ec3a649 --- /dev/null +++ b/changelog.d/fixes/13364-zed-hosted-haiku-thinking-inflation.md @@ -0,0 +1 @@ +- **fix(providers):** stop zed-hosted `claude-haiku-4-5` extended-thinking requests from inflating `max_tokens` past the model's real 64000 output cap (#13364) — thanks @ThiagoMafra-Integrare diff --git a/open-sse/services/model.ts b/open-sse/services/model.ts index 1d36623205..0570227d91 100644 --- a/open-sse/services/model.ts +++ b/open-sse/services/model.ts @@ -73,6 +73,14 @@ const PROVIDER_MODEL_ALIASES: ProviderModelAliasMap = { "claude-sonnet-4-5": "claude-sonnet-4.5", "claude-haiku-4-5": "claude-haiku-4.5", }, + // #13364: zed-hosted's passthrough catalog exposes short hyphenated Claude ids + // that don't match modelSpecs' dotted canonical alias, so capMaxOutputTokens() + // resolves no cap and thinking+tools requests inflate max_tokens unbounded. + // Scoped to claude-haiku-4-5 (the reported/reproduced model) — add Sonnet/Opus + // entries only once confirmed against the live Zed catalog. + "zed-hosted": { + "claude-haiku-4-5": "claude-haiku-4.5", + }, }; const CROSS_PROXY_MODEL_ALIASES: Record = { diff --git a/tests/unit/issue-13364-zed-hosted-haiku-thinking-inflation.test.ts b/tests/unit/issue-13364-zed-hosted-haiku-thinking-inflation.test.ts new file mode 100644 index 0000000000..43df02bb6e --- /dev/null +++ b/tests/unit/issue-13364-zed-hosted-haiku-thinking-inflation.test.ts @@ -0,0 +1,39 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { openaiToClaudeRequest } from "../../open-sse/translator/request/openai-to-claude.ts"; + +// #13364 — zed-hosted/claude-haiku-4-5: extended thinking + tools inflates +// max_tokens to 163072 (> Anthropic's real 64000 cap for this model), causing +// Zed's proxy to reject the request in-stream with: +// "max_tokens: 163072 > 64000, which is the maximum allowed number of +// output tokens for claude-haiku-4-5-20251001" +// +// Root cause: zed-hosted's passthrough catalog exposes the short hyphenated id +// "claude-haiku-4-5", but modelSpecs' registered alias for that model is the +// dotted "claude-haiku-4.5" — a spelling mismatch (not a missing spec) means +// capMaxOutputTokens() resolves no cap for zed-hosted, so fitThinkingToMaxTokens +// adds the requested budget instead of fitting it under the model's real ceiling. +test("zed-hosted/claude-haiku-4-5 thinking+tools must not inflate max_tokens past the real 64000 output cap", () => { + const body = { + model: "claude-haiku-4-5", + max_tokens: 16000, + reasoning_effort: "high", // what claude-to-openai produces for the client's budget_tokens:15999 + tools: [ + { + type: "function", + function: { name: "read_file", parameters: { type: "object", properties: {} } }, + }, + ], + messages: [{ role: "user", content: "hello" }], + }; + const credentials = { _provider: "zed-hosted" }; + + const result = openaiToClaudeRequest("claude-haiku-4-5", body, false, credentials); + const claudeHaiku45OutputCap = 64000; // src/shared/constants/modelSpecs.ts "claude-haiku-4-5-20251001" + + assert.ok( + (result.max_tokens as number) <= claudeHaiku45OutputCap, + `max_tokens (${result.max_tokens}) must not exceed the model's real output cap ` + + `(${claudeHaiku45OutputCap}) — Anthropic/Zed rejects the request otherwise` + ); +});