From dfb86df6fc08bf76761a5070c28d364ccee4d4d4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 26 May 2026 23:06:07 +0100 Subject: [PATCH] fix: keep Cloudflare Anthropic provider auth header --- src/llm/providers/anthropic.test.ts | 61 +++++++++++++++++++++++++++++ src/llm/providers/anthropic.ts | 4 +- 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/llm/providers/anthropic.test.ts diff --git a/src/llm/providers/anthropic.test.ts b/src/llm/providers/anthropic.test.ts new file mode 100644 index 00000000000..781c283aa20 --- /dev/null +++ b/src/llm/providers/anthropic.test.ts @@ -0,0 +1,61 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import type { Context, Model } from "../types.js"; + +const anthropicMockState = vi.hoisted(() => ({ + configs: [] as unknown[], +})); + +vi.mock("@anthropic-ai/sdk", () => ({ + default: class MockAnthropic { + messages = { + create: vi.fn(() => { + throw new Error("stop after constructor"); + }), + }; + + constructor(config: unknown) { + anthropicMockState.configs.push(config); + } + }, +})); + +import { streamAnthropic } from "./anthropic.js"; + +describe("Anthropic provider", () => { + beforeEach(() => { + anthropicMockState.configs = []; + }); + + it("keeps Cloudflare AI Gateway upstream provider auth on the Anthropic API key", async () => { + const model = { + id: "claude-sonnet-4-6", + provider: "cloudflare-ai-gateway", + api: "anthropic-messages", + baseUrl: + "https://gateway.ai.cloudflare.com/v1/account/gateway/anthropic/v1/messages", + maxTokens: 4096, + headers: { + "cf-aig-authorization": "Bearer gateway-token", + }, + } satisfies Model<"anthropic-messages">; + const context = { + messages: [{ role: "user", content: "hello" }], + } satisfies Context; + + streamAnthropic(model, context, { + apiKey: "sk-ant-provider", + }); + + await vi.waitFor(() => expect(anthropicMockState.configs).toHaveLength(1)); + const config = anthropicMockState.configs[0] as { + apiKey?: string | null; + authToken?: string | null; + defaultHeaders?: Record; + }; + + expect(config.apiKey).toBe("sk-ant-provider"); + expect(config.authToken).toBeNull(); + expect(config.defaultHeaders?.["x-api-key"]).toBeUndefined(); + expect(config.defaultHeaders?.["cf-aig-authorization"]).toBe("Bearer gateway-token"); + }); +}); diff --git a/src/llm/providers/anthropic.ts b/src/llm/providers/anthropic.ts index 785a8b88a54..d6bdac63934 100644 --- a/src/llm/providers/anthropic.ts +++ b/src/llm/providers/anthropic.ts @@ -833,7 +833,7 @@ function createClient( if (model.provider === "cloudflare-ai-gateway") { const client = new Anthropic({ - apiKey: null, + apiKey, authToken: null, baseURL: resolveCloudflareBaseUrl(model), dangerouslyAllowBrowser: true, @@ -841,8 +841,6 @@ function createClient( { accept: "application/json", "anthropic-dangerous-direct-browser-access": "true", - "cf-aig-authorization": `Bearer ${apiKey}`, - "x-api-key": null, Authorization: null, ...(betaFeatures.length > 0 ? { "anthropic-beta": betaFeatures.join(",") } : {}), },