From 739f6ca51cc891b8206ce84eadfa45c8daa0af90 Mon Sep 17 00:00:00 2001 From: mastertyko <11311479+mastertyko@users.noreply.github.com> Date: Sun, 12 Apr 2026 14:02:07 +0200 Subject: [PATCH] fix(pi-ai): use bearer auth for MiniMax Anthropic API --- .../src/providers/anthropic-auth.test.ts | 32 +++++++++++++++++++ packages/pi-ai/src/providers/anthropic.ts | 12 ++++--- 2 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 packages/pi-ai/src/providers/anthropic-auth.test.ts diff --git a/packages/pi-ai/src/providers/anthropic-auth.test.ts b/packages/pi-ai/src/providers/anthropic-auth.test.ts new file mode 100644 index 000000000..4593e1a5d --- /dev/null +++ b/packages/pi-ai/src/providers/anthropic-auth.test.ts @@ -0,0 +1,32 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { usesAnthropicBearerAuth } from "./anthropic.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +test("usesAnthropicBearerAuth covers Bearer-only Anthropic-compatible providers (#3783)", () => { + assert.equal(usesAnthropicBearerAuth("alibaba-coding-plan"), true); + assert.equal(usesAnthropicBearerAuth("minimax"), true); + assert.equal(usesAnthropicBearerAuth("minimax-cn"), true); + assert.equal(usesAnthropicBearerAuth("anthropic"), false); +}); + +test("createClient routes Bearer-auth providers through authToken (#3783)", () => { + const source = readFileSync(join(__dirname, "..", "..", "src", "providers", "anthropic.ts"), "utf-8"); + assert.ok( + source.includes("const usesBearerAuth = usesAnthropicBearerAuth(model.provider);"), + "createClient should derive auth mode from usesAnthropicBearerAuth", + ); + assert.ok( + source.includes("apiKey: usesBearerAuth ? null : apiKey"), + "Bearer-auth providers should skip x-api-key auth", + ); + assert.ok( + source.includes("authToken: usesBearerAuth ? apiKey : undefined"), + "Bearer-auth providers should send authToken instead", + ); +}); diff --git a/packages/pi-ai/src/providers/anthropic.ts b/packages/pi-ai/src/providers/anthropic.ts index 57ee1b5be..ec9b21fde 100644 --- a/packages/pi-ai/src/providers/anthropic.ts +++ b/packages/pi-ai/src/providers/anthropic.ts @@ -44,6 +44,10 @@ function mergeHeaders(...headerSources: (Record | undefined)[]): return merged; } +export function usesAnthropicBearerAuth(provider: Model<"anthropic-messages">["provider"]): boolean { + return provider === "alibaba-coding-plan" || provider === "minimax" || provider === "minimax-cn"; +} + async function createClient( model: Model<"anthropic-messages">, apiKey: string, @@ -91,11 +95,11 @@ async function createClient( } // API key auth (Anthropic OAuth removed per TOS compliance — use API keys or Claude CLI) - // Alibaba Coding Plan uses Bearer token auth instead of x-api-key - const isAlibabaProvider = model.provider === "alibaba-coding-plan"; + // Some Anthropic-compatible providers require Bearer auth instead of x-api-key. + const usesBearerAuth = usesAnthropicBearerAuth(model.provider); const client = new AnthropicClass({ - apiKey: isAlibabaProvider ? null : apiKey, - authToken: isAlibabaProvider ? apiKey : undefined, + apiKey: usesBearerAuth ? null : apiKey, + authToken: usesBearerAuth ? apiKey : undefined, baseURL: model.baseUrl, dangerouslyAllowBrowser: true, defaultHeaders: mergeHeaders(