diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.test.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.test.ts new file mode 100644 index 00000000000..ebfc9c926d8 --- /dev/null +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.test.ts @@ -0,0 +1,88 @@ +import { describe, it, expect } from "bun:test" +import { supports1MContext } from "./anthropic" + +describe("supports1MContext", () => { + //#given a model string + //#when checking if it supports 1M context + //#then return true for Opus 4.6 models + + it("returns true for claude-opus-4-6", () => { + expect(supports1MContext("claude-opus-4-6")).toBe(true) + }) + + it("returns true for claude-opus-4-6 with snapshot date suffix", () => { + expect(supports1MContext("claude-opus-4-6-20260101")).toBe(true) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return true for Sonnet 4.5 models + + it("returns true for claude-sonnet-4-5", () => { + expect(supports1MContext("claude-sonnet-4-5")).toBe(true) + }) + + it("returns true for claude-sonnet-4-5 with snapshot date suffix", () => { + expect(supports1MContext("claude-sonnet-4-5-20250929")).toBe(true) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return true for Sonnet 4 models (prefix match) + + it("returns true for claude-sonnet-4 (canonical)", () => { + expect(supports1MContext("claude-sonnet-4")).toBe(true) + }) + + it("returns true for claude-sonnet-4-20250514", () => { + expect(supports1MContext("claude-sonnet-4-20250514")).toBe(true) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return false for Opus 4.5 models (not supported) + + it("returns false for claude-opus-4-5-20251101", () => { + expect(supports1MContext("claude-opus-4-5-20251101")).toBe(false) + }) + + it("returns false for claude-opus-4-5", () => { + expect(supports1MContext("claude-opus-4-5")).toBe(false) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return false for older Opus models + + it("returns false for claude-opus-4-1-20250805", () => { + expect(supports1MContext("claude-opus-4-1-20250805")).toBe(false) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return false for Haiku models + + it("returns false for claude-haiku-4-5-20251001", () => { + expect(supports1MContext("claude-haiku-4-5-20251001")).toBe(false) + }) + + it("returns false for claude-3-haiku-20240307", () => { + expect(supports1MContext("claude-3-haiku-20240307")).toBe(false) + }) + + //#given a model string + //#when checking if it supports 1M context + //#then return false for unknown models + + it("returns false for unknown model", () => { + expect(supports1MContext("some-random-model")).toBe(false) + }) + + //#given an empty string + //#when checking if it supports 1M context + //#then return false + + it("returns false for empty string", () => { + expect(supports1MContext("")).toBe(false) + }) +}) diff --git a/packages/console/app/src/routes/zen/util/provider/anthropic.ts b/packages/console/app/src/routes/zen/util/provider/anthropic.ts index a5f92a29acf..8abec3de4f2 100644 --- a/packages/console/app/src/routes/zen/util/provider/anthropic.ts +++ b/packages/console/app/src/routes/zen/util/provider/anthropic.ts @@ -16,11 +16,17 @@ type Usage = { } } +const CONTEXT_1M_BETA = "context-1m-2025-08-07" + +export function supports1MContext(model: string): boolean { + const prefixes = ["claude-opus-4-6", "claude-sonnet-4-5", "claude-sonnet-4"] + return prefixes.some((prefix) => model.startsWith(prefix)) +} + export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => { const isBedrockModelArn = providerModel.startsWith("arn:aws:bedrock:") const isBedrockModelID = providerModel.startsWith("global.anthropic.") const isBedrock = isBedrockModelArn || isBedrockModelID - const isSonnet = reqModel.includes("sonnet") return { format: "anthropic", modifyUrl: (providerApi: string, isStream?: boolean) => @@ -33,8 +39,8 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => } else { headers.set("x-api-key", apiKey) headers.set("anthropic-version", headers.get("anthropic-version") ?? "2023-06-01") - if (body.model.startsWith("claude-sonnet-")) { - headers.set("anthropic-beta", "context-1m-2025-08-07") + if (supports1MContext(body.model)) { + headers.set("anthropic-beta", CONTEXT_1M_BETA) } } }, @@ -43,7 +49,7 @@ export const anthropicHelper: ProviderHelper = ({ reqModel, providerModel }) => ...(isBedrock ? { anthropic_version: "bedrock-2023-05-31", - anthropic_beta: isSonnet ? "context-1m-2025-08-07" : undefined, + anthropic_beta: supports1MContext(reqModel) ? CONTEXT_1M_BETA : undefined, model: undefined, stream: undefined, }