From 494b98d7cb739ab5f3678a41924b12ed0d0e4ebc Mon Sep 17 00:00:00 2001 From: mecab Date: Sat, 12 Apr 2025 13:29:39 +0100 Subject: [PATCH] Add Anthropic option to use authToken over apiKey --- evals/packages/types/src/roo-code.ts | 1 + src/api/providers/__tests__/anthropic.test.ts | 37 +++++++++++++++++++ src/api/providers/anthropic.ts | 5 ++- src/exports/roo-code.d.ts | 1 + src/exports/types.ts | 1 + src/schemas/index.ts | 2 + .../src/components/settings/ApiOptions.tsx | 25 +++++++++---- webview-ui/src/i18n/locales/ca/settings.json | 1 + webview-ui/src/i18n/locales/de/settings.json | 1 + webview-ui/src/i18n/locales/en/settings.json | 1 + webview-ui/src/i18n/locales/es/settings.json | 1 + webview-ui/src/i18n/locales/fr/settings.json | 1 + webview-ui/src/i18n/locales/hi/settings.json | 1 + webview-ui/src/i18n/locales/it/settings.json | 1 + webview-ui/src/i18n/locales/ja/settings.json | 1 + webview-ui/src/i18n/locales/ko/settings.json | 1 + webview-ui/src/i18n/locales/pl/settings.json | 1 + .../src/i18n/locales/pt-BR/settings.json | 1 + webview-ui/src/i18n/locales/tr/settings.json | 1 + webview-ui/src/i18n/locales/vi/settings.json | 1 + .../src/i18n/locales/zh-CN/settings.json | 1 + .../src/i18n/locales/zh-TW/settings.json | 1 + 22 files changed, 79 insertions(+), 8 deletions(-) diff --git a/evals/packages/types/src/roo-code.ts b/evals/packages/types/src/roo-code.ts index 9462b7aa75..5a4082395b 100644 --- a/evals/packages/types/src/roo-code.ts +++ b/evals/packages/types/src/roo-code.ts @@ -301,6 +301,7 @@ export const providerSettingsSchema = z.object({ apiModelId: z.string().optional(), apiKey: z.string().optional(), anthropicBaseUrl: z.string().optional(), + anthropicUseAuthToken: z.boolean().optional(), // Glama glamaModelId: z.string().optional(), glamaModelInfo: modelInfoSchema.optional(), diff --git a/src/api/providers/__tests__/anthropic.test.ts b/src/api/providers/__tests__/anthropic.test.ts index fe367ea674..fe186e3d8f 100644 --- a/src/api/providers/__tests__/anthropic.test.ts +++ b/src/api/providers/__tests__/anthropic.test.ts @@ -2,8 +2,10 @@ import { AnthropicHandler } from "../anthropic" import { ApiHandlerOptions } from "../../../shared/api" +import Anthropic from "@anthropic-ai/sdk" const mockCreate = jest.fn() +const mockAnthropicConstructor = Anthropic.Anthropic as unknown as jest.Mock jest.mock("@anthropic-ai/sdk", () => { return { @@ -69,6 +71,7 @@ describe("AnthropicHandler", () => { } handler = new AnthropicHandler(mockOptions) mockCreate.mockClear() + mockAnthropicConstructor.mockClear() }) describe("constructor", () => { @@ -94,6 +97,40 @@ describe("AnthropicHandler", () => { }) expect(handlerWithCustomUrl).toBeInstanceOf(AnthropicHandler) }) + + it("use apiKey for passing token if anthropicUseAuthToken is not set", () => { + const handlerWithCustomUrl = new AnthropicHandler({ + ...mockOptions, + }) + expect(handlerWithCustomUrl).toBeInstanceOf(AnthropicHandler) + expect(mockAnthropicConstructor).toHaveBeenCalledTimes(1) + expect(mockAnthropicConstructor.mock.lastCall[0].apiKey).toEqual("test-api-key") + expect(mockAnthropicConstructor.mock.lastCall[0].authToken).toBeUndefined() + }) + + it("use apiKey for passing token if anthropicUseAuthToken is set but custom base URL is not given", () => { + const handlerWithCustomUrl = new AnthropicHandler({ + ...mockOptions, + anthropicUseAuthToken: true, + }) + expect(handlerWithCustomUrl).toBeInstanceOf(AnthropicHandler) + expect(mockAnthropicConstructor).toHaveBeenCalledTimes(1) + expect(mockAnthropicConstructor.mock.lastCall[0].apiKey).toEqual("test-api-key") + expect(mockAnthropicConstructor.mock.lastCall[0].authToken).toBeUndefined() + }) + + it("use authToken for passing token if both of anthropicBaseUrl and anthropicUseAuthToken are set", () => { + const customBaseUrl = "https://custom.anthropic.com" + const handlerWithCustomUrl = new AnthropicHandler({ + ...mockOptions, + anthropicBaseUrl: customBaseUrl, + anthropicUseAuthToken: true, + }) + expect(handlerWithCustomUrl).toBeInstanceOf(AnthropicHandler) + expect(mockAnthropicConstructor).toHaveBeenCalledTimes(1) + expect(mockAnthropicConstructor.mock.lastCall[0].authToken).toEqual("test-api-key") + expect(mockAnthropicConstructor.mock.lastCall[0].apiKey).toBeUndefined() + }) }) describe("createMessage", () => { diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 681ef2fc77..a906ad6e7e 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -20,9 +20,12 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa constructor(options: ApiHandlerOptions) { super() this.options = options + + const apiKeyFieldName = + this.options.anthropicBaseUrl && this.options.anthropicUseAuthToken ? "authToken" : "apiKey" this.client = new Anthropic({ - apiKey: this.options.apiKey, baseURL: this.options.anthropicBaseUrl || undefined, + [apiKeyFieldName]: this.options.apiKey, }) } diff --git a/src/exports/roo-code.d.ts b/src/exports/roo-code.d.ts index e137b4c482..b337e81fa2 100644 --- a/src/exports/roo-code.d.ts +++ b/src/exports/roo-code.d.ts @@ -25,6 +25,7 @@ type ProviderSettings = { apiModelId?: string | undefined apiKey?: string | undefined anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined glamaModelId?: string | undefined glamaModelInfo?: | ({ diff --git a/src/exports/types.ts b/src/exports/types.ts index 8fa340f719..05a70d133b 100644 --- a/src/exports/types.ts +++ b/src/exports/types.ts @@ -26,6 +26,7 @@ type ProviderSettings = { apiModelId?: string | undefined apiKey?: string | undefined anthropicBaseUrl?: string | undefined + anthropicUseAuthToken?: boolean | undefined glamaModelId?: string | undefined glamaModelInfo?: | ({ diff --git a/src/schemas/index.ts b/src/schemas/index.ts index 64eec0bf64..a73152773c 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -318,6 +318,7 @@ export const providerSettingsSchema = z.object({ apiModelId: z.string().optional(), apiKey: z.string().optional(), anthropicBaseUrl: z.string().optional(), + anthropicUseAuthToken: z.boolean().optional(), // Glama glamaModelId: z.string().optional(), glamaModelInfo: modelInfoSchema.nullish(), @@ -414,6 +415,7 @@ const providerSettingsRecord: ProviderSettingsRecord = { apiModelId: undefined, apiKey: undefined, anthropicBaseUrl: undefined, + anthropicUseAuthToken: undefined, // Glama glamaModelId: undefined, glamaModelInfo: undefined, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 55690d4806..cfc48e8f73 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -414,18 +414,29 @@ const ApiOptions = ({ if (!checked) { setApiConfigurationField("anthropicBaseUrl", "") + setApiConfigurationField("anthropicUseAuthToken", false) // added } }}> {t("settings:providers.useCustomBaseUrl")} {anthropicBaseUrlSelected && ( - + <> + + + {/* added */} + + {t("settings:providers.anthropicUseAuthToken")} + + )} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 390362f099..80b3ef3a48 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -116,6 +116,7 @@ "getRequestyApiKey": "Obtenir clau API de Requesty", "anthropicApiKey": "Clau API d'Anthropic", "getAnthropicApiKey": "Obtenir clau API d'Anthropic", + "anthropicUseAuthToken": "Passar la clau API d'Anthropic com a capçalera d'autorització en lloc de X-Api-Key", "deepSeekApiKey": "Clau API de DeepSeek", "getDeepSeekApiKey": "Obtenir clau API de DeepSeek", "geminiApiKey": "Clau API de Gemini", diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 59ea1f6f1e..a577199e86 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Prompts und Nachrichtenketten auf Kontextgröße komprimieren (OpenRouter Transformationen)", "anthropicApiKey": "Anthropic API-Schlüssel", "getAnthropicApiKey": "Anthropic API-Schlüssel erhalten", + "anthropicUseAuthToken": "Anthropic API-Schlüssel als Authorization-Header anstelle von X-Api-Key übergeben", "deepSeekApiKey": "DeepSeek API-Schlüssel", "getDeepSeekApiKey": "DeepSeek API-Schlüssel erhalten", "geminiApiKey": "Gemini API-Schlüssel", diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index 5ba66f34e2..9d2b6f8920 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Compress prompts and message chains to the context size (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API Key", "getAnthropicApiKey": "Get Anthropic API Key", + "anthropicUseAuthToken": "Pass Anthropic API Key as Authorization header instead of X-Api-Key", "deepSeekApiKey": "DeepSeek API Key", "getDeepSeekApiKey": "Get DeepSeek API Key", "geminiApiKey": "Gemini API Key", diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index 256c4d73f3..4b29147b59 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Comprimir prompts y cadenas de mensajes al tamaño del contexto (Transformaciones de OpenRouter)", "anthropicApiKey": "Clave API de Anthropic", "getAnthropicApiKey": "Obtener clave API de Anthropic", + "anthropicUseAuthToken": "Pasar la clave API de Anthropic como encabezado de autorización en lugar de X-Api-Key", "deepSeekApiKey": "Clave API de DeepSeek", "getDeepSeekApiKey": "Obtener clave API de DeepSeek", "geminiApiKey": "Clave API de Gemini", diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index 23d99a057d..5a064411b6 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Compresser les prompts et chaînes de messages à la taille du contexte (Transformations OpenRouter)", "anthropicApiKey": "Clé API Anthropic", "getAnthropicApiKey": "Obtenir la clé API Anthropic", + "anthropicUseAuthToken": "Passer la clé API Anthropic comme en-tête d'autorisation au lieu de X-Api-Key", "deepSeekApiKey": "Clé API DeepSeek", "getDeepSeekApiKey": "Obtenir la clé API DeepSeek", "geminiApiKey": "Clé API Gemini", diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 59556718c1..d8bf7cd72e 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "संदर्भ आकार के लिए प्रॉम्प्ट और संदेश श्रृंखलाओं को संपीड़ित करें (OpenRouter ट्रांसफॉर्म)", "anthropicApiKey": "Anthropic API कुंजी", "getAnthropicApiKey": "Anthropic API कुंजी प्राप्त करें", + "anthropicUseAuthToken": "X-Api-Key के बजाय Anthropic API कुंजी को Authorization हेडर के रूप में पास करें", "deepSeekApiKey": "DeepSeek API कुंजी", "getDeepSeekApiKey": "DeepSeek API कुंजी प्राप्त करें", "geminiApiKey": "Gemini API कुंजी", diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 30340b5e29..fb2edd63c9 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Comprimi prompt e catene di messaggi alla dimensione del contesto (Trasformazioni OpenRouter)", "anthropicApiKey": "Chiave API Anthropic", "getAnthropicApiKey": "Ottieni chiave API Anthropic", + "anthropicUseAuthToken": "Passa la chiave API Anthropic come header di autorizzazione invece di X-Api-Key", "deepSeekApiKey": "Chiave API DeepSeek", "getDeepSeekApiKey": "Ottieni chiave API DeepSeek", "geminiApiKey": "Chiave API Gemini", diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index 03e2838d09..aa5b893529 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "プロンプトとメッセージチェーンをコンテキストサイズに圧縮 (OpenRouter Transforms)", "anthropicApiKey": "Anthropic APIキー", "getAnthropicApiKey": "Anthropic APIキーを取得", + "anthropicUseAuthToken": "Anthropic APIキーをX-Api-Keyの代わりにAuthorizationヘッダーとして渡す", "deepSeekApiKey": "DeepSeek APIキー", "getDeepSeekApiKey": "DeepSeek APIキーを取得", "geminiApiKey": "Gemini APIキー", diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index b31df80304..49e253360b 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "프롬프트와 메시지 체인을 컨텍스트 크기로 압축 (OpenRouter Transforms)", "anthropicApiKey": "Anthropic API 키", "getAnthropicApiKey": "Anthropic API 키 받기", + "anthropicUseAuthToken": "X-Api-Key 대신 Authorization 헤더로 Anthropic API 키 전달", "deepSeekApiKey": "DeepSeek API 키", "getDeepSeekApiKey": "DeepSeek API 키 받기", "geminiApiKey": "Gemini API 키", diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 7f12e21360..9b70bb1dc6 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Kompresuj podpowiedzi i łańcuchy wiadomości do rozmiaru kontekstu (Transformacje OpenRouter)", "anthropicApiKey": "Klucz API Anthropic", "getAnthropicApiKey": "Uzyskaj klucz API Anthropic", + "anthropicUseAuthToken": "Przekaż klucz API Anthropic jako nagłówek Authorization zamiast X-Api-Key", "deepSeekApiKey": "Klucz API DeepSeek", "getDeepSeekApiKey": "Uzyskaj klucz API DeepSeek", "geminiApiKey": "Klucz API Gemini", diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index c19a832a57..3f238e2b37 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "Comprimir prompts e cadeias de mensagens para o tamanho do contexto (Transformações OpenRouter)", "anthropicApiKey": "Chave de API Anthropic", "getAnthropicApiKey": "Obter chave de API Anthropic", + "anthropicUseAuthToken": "Passar a chave de API Anthropic como cabeçalho Authorization em vez de X-Api-Key", "deepSeekApiKey": "Chave de API DeepSeek", "getDeepSeekApiKey": "Obter chave de API DeepSeek", "geminiApiKey": "Chave de API Gemini", diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 9ad9fedc88..4e2f5b816a 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "İstem ve mesaj zincirlerini bağlam boyutuna sıkıştır (OpenRouter Dönüşümleri)", "anthropicApiKey": "Anthropic API Anahtarı", "getAnthropicApiKey": "Anthropic API Anahtarı Al", + "anthropicUseAuthToken": "Anthropic API Anahtarını X-Api-Key yerine Authorization başlığı olarak geçir", "deepSeekApiKey": "DeepSeek API Anahtarı", "getDeepSeekApiKey": "DeepSeek API Anahtarı Al", "geminiApiKey": "Gemini API Anahtarı", diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 24eb91d4a2..0b83f89634 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -115,6 +115,7 @@ "getRequestyApiKey": "Lấy khóa API Requesty", "anthropicApiKey": "Khóa API Anthropic", "getAnthropicApiKey": "Lấy khóa API Anthropic", + "anthropicUseAuthToken": "Truyền khóa API Anthropic dưới dạng tiêu đề Authorization thay vì X-Api-Key", "deepSeekApiKey": "Khóa API DeepSeek", "getDeepSeekApiKey": "Lấy khóa API DeepSeek", "geminiApiKey": "Khóa API Gemini", diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index fd1919a5c3..e995e0101f 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "自动压缩提示词和消息链到上下文长度限制内 (OpenRouter转换)", "anthropicApiKey": "Anthropic API 密钥", "getAnthropicApiKey": "获取 Anthropic API 密钥", + "anthropicUseAuthToken": "将 Anthropic API 密钥作为 Authorization 标头传递,而不是 X-Api-Key", "deepSeekApiKey": "DeepSeek API 密钥", "getDeepSeekApiKey": "获取 DeepSeek API 密钥", "geminiApiKey": "Gemini API 密钥", diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index baa4b614d8..23a0b3ba5c 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -116,6 +116,7 @@ "openRouterTransformsText": "將提示和訊息鏈壓縮到上下文大小 (OpenRouter 轉換)", "anthropicApiKey": "Anthropic API 金鑰", "getAnthropicApiKey": "取得 Anthropic API 金鑰", + "anthropicUseAuthToken": "將 Anthropic API 金鑰作為 Authorization 標頭傳遞,而非使用 X-Api-Key", "deepSeekApiKey": "DeepSeek API 金鑰", "getDeepSeekApiKey": "取得 DeepSeek API 金鑰", "geminiApiKey": "Gemini API 金鑰",