From c8645f881f55226c671fc156666624ad555649cd Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Sun, 15 Mar 2026 03:44:03 +0800 Subject: [PATCH] Correct LLM provider naming to follow common terminology Standardize provider naming in the UI and codebase so OpenAI API, Anthropic API, and Azure OpenAI use consistent API-facing terminology, while ChatGPT and Claude remain the web product names. Update related locale strings, config keys, and migration handling to preserve existing settings and avoid duplicate API markers in custom deployment labels. --- src/_locales/de/main.json | 43 +++++- src/_locales/en/main.json | 43 +++++- src/_locales/es/main.json | 43 +++++- src/_locales/fr/main.json | 43 +++++- src/_locales/in/main.json | 43 +++++- src/_locales/it/main.json | 43 +++++- src/_locales/ja/main.json | 43 +++++- src/_locales/ko/main.json | 43 +++++- src/_locales/pt/main.json | 43 +++++- src/_locales/ru/main.json | 43 +++++- src/_locales/tr/main.json | 43 +++++- src/_locales/zh-hans/main.json | 43 +++++- src/_locales/zh-hant/main.json | 43 +++++- src/background/index.mjs | 9 +- src/config/index.mjs | 118 +++++++++------ src/popup/sections/AdvancedPart.jsx | 6 +- src/popup/sections/GeneralPart.jsx | 9 +- src/popup/sections/import-data-cleanup.mjs | 34 +++++ src/services/apis/aiml-api.mjs | 4 +- src/services/apis/chatglm-api.mjs | 4 +- src/services/apis/claude-api.mjs | 4 +- src/services/apis/deepseek-api.mjs | 4 +- src/services/apis/moonshot-api.mjs | 4 +- src/services/apis/ollama-api.mjs | 4 +- src/services/apis/openai-api.mjs | 6 +- src/services/apis/openrouter-api.mjs | 4 +- src/utils/model-name-convert.mjs | 6 +- tests/setup/browser-shim.mjs | 11 ++ tests/unit/config/user-config.test.mjs | 113 ++++++++++++++ tests/unit/popup/import-data-cleanup.test.mjs | 138 ++++++++++++++++++ .../services/apis/openai-api-compat.test.mjs | 56 +++---- tests/unit/utils/model-name-convert.test.mjs | 17 ++- 32 files changed, 981 insertions(+), 129 deletions(-) create mode 100644 src/popup/sections/import-data-cleanup.mjs create mode 100644 tests/unit/popup/import-data-cleanup.test.mjs diff --git a/src/_locales/de/main.json b/src/_locales/de/main.json index 9e5df2a55..ad4a25d91 100644 --- a/src/_locales/de/main.json +++ b/src/_locales/de/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-Turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-Turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Benutzerdefiniertes Modell", @@ -132,7 +134,8 @@ "Generating...": "Generieren...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "Moonshot-Token erforderlich, bitte zuerst bei https://kimi.com anmelden und dann auf die Schaltfläche Wiederholen klicken", "Hide context menu of this extension": "Kontextmenü dieser Erweiterung ausblenden", - "Custom Claude API Url": "Benutzerdefinierte Claude-API-URL", + "Custom Anthropic API Url": "Benutzerdefinierte Anthropic-API-URL", + "Anthropic API Key": "Anthropic-API-Schlüssel", "Cancel": "Abbrechen", "Name is required": "Name ist erforderlich", "Prompt template should include {{selection}}": "Die Vorlage sollte {{selection}} enthalten", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Typ", "Mode": "Modus", - "Custom": "Benutzerdefiniert" + "Custom": "Benutzerdefiniert", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/en/main.json b/src/_locales/en/main.json index 44981fbcb..0bc75df81 100644 --- a/src/_locales/en/main.json +++ b/src/_locales/en/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Custom Model", @@ -132,7 +134,8 @@ "Generating...": "Generating...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "moonshot token required, please login at https://kimi.com first, and then click the retry button", "Hide context menu of this extension": "Hide context menu of this extension", - "Custom Claude API Url": "Custom Claude API Url", + "Custom Anthropic API Url": "Custom Anthropic API Url", + "Anthropic API Key": "Anthropic API Key", "Cancel": "Cancel", "Name is required": "Name is required", "Prompt template should include {{selection}}": "Prompt template should include {{selection}}", @@ -159,5 +162,41 @@ "Type": "Type", "Mode": "Mode", "Custom": "Custom", - "Crop Text to ensure the input tokens do not exceed the model's limit": "Crop Text to ensure the input tokens do not exceed the model's limit" + "Crop Text to ensure the input tokens do not exceed the model's limit": "Crop Text to ensure the input tokens do not exceed the model's limit", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/es/main.json b/src/_locales/es/main.json index f44525355..4ea2c2081 100644 --- a/src/_locales/es/main.json +++ b/src/_locales/es/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Modelo personalizado", @@ -132,7 +134,8 @@ "Generating...": "Generando...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "se requiere un token de moonshot, por favor inicie sesión en https://kimi.com primero, y luego haga clic en el botón Reintentar", "Hide context menu of this extension": "Ocultar menú contextual de esta extensión", - "Custom Claude API Url": "URL personalizada de la API de Claude", + "Custom Anthropic API Url": "URL personalizada de la API de Anthropic", + "Anthropic API Key": "Clave API de Anthropic", "Cancel": "Cancelar", "Name is required": "Se requiere un nombre", "Prompt template should include {{selection}}": "La plantilla de sugerencias debe incluir {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Tipo", "Mode": "Modo", - "Custom": "Personalizado" + "Custom": "Personalizado", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/fr/main.json b/src/_locales/fr/main.json index 7fbc4ecb0..e718e2a71 100644 --- a/src/_locales/fr/main.json +++ b/src/_locales/fr/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Modèle personnalisé", @@ -132,7 +134,8 @@ "Generating...": "Génération...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "jeton moonshot requis, veuillez vous connecter d'abord sur https://kimi.com, puis cliquez sur le bouton Réessayer", "Hide context menu of this extension": "Masquer le menu contextuel de cette extension", - "Custom Claude API Url": "URL API Claude personnalisée", + "Custom Anthropic API Url": "URL API Anthropic personnalisée", + "Anthropic API Key": "Clé API Anthropic", "Cancel": "Annuler", "Name is required": "Le nom est requis", "Prompt template should include {{selection}}": "Le modèle de suggestion doit inclure {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Type", "Mode": "Mode", - "Custom": "Personnalisé" + "Custom": "Personnalisé", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/in/main.json b/src/_locales/in/main.json index 69bc49bd3..0e19c9f09 100644 --- a/src/_locales/in/main.json +++ b/src/_locales/in/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Model Kustom", @@ -132,7 +134,8 @@ "Generating...": "Menghasilkan...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "diperlukan token moonshot, silakan masuk di https://kimi.com terlebih dahulu, lalu klik tombol coba lagi", "Hide context menu of this extension": "Sembunyikan menu konteks ekstensi ini", - "Custom Claude API Url": "URL API Claude Kustom", + "Custom Anthropic API Url": "URL API Anthropic Kustom", + "Anthropic API Key": "Kunci API Anthropic", "Cancel": "Batal", "Name is required": "Nama diperlukan", "Prompt template should include {{selection}}": "Template prompt harus mencakup {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Jenis", "Mode": "Mode", - "Custom": "Kustom" + "Custom": "Kustom", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/it/main.json b/src/_locales/it/main.json index 1b81c251e..60cf3f6db 100644 --- a/src/_locales/it/main.json +++ b/src/_locales/it/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Modello personalizzato", @@ -132,7 +134,8 @@ "Generating...": "Generazione...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "richiesto token moonshot, effettua il login su https://kimi.com prima, quindi fai clic sul pulsante Riprova", "Hide context menu of this extension": "Nascondi il menu contestuale di questa estensione", - "Custom Claude API Url": "URL API Claude personalizzato", + "Custom Anthropic API Url": "URL API Anthropic personalizzato", + "Anthropic API Key": "Chiave API Anthropic", "Cancel": "Annulla", "Name is required": "Il nome è obbligatorio", "Prompt template should include {{selection}}": "Il modello di prompt dovrebbe includere {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Tipo", "Mode": "Modalità", - "Custom": "Personalizzato" + "Custom": "Personalizzato", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/ja/main.json b/src/_locales/ja/main.json index 92d6c62fc..75010c900 100644 --- a/src/_locales/ja/main.json +++ b/src/_locales/ja/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "カスタムモデル", @@ -132,7 +134,8 @@ "Generating...": "生成中...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "moonshotトークンが必要です。最初に https://kimi.com にログインしてから、再試行ボタンをクリックしてください", "Hide context menu of this extension": "この拡張機能のコンテキストメニューを非表示", - "Custom Claude API Url": "カスタムClaude APIのURL", + "Custom Anthropic API Url": "カスタムAnthropic APIのURL", + "Anthropic API Key": "Anthropic API キー", "Cancel": "キャンセル", "Name is required": "名前は必須です", "Prompt template should include {{selection}}": "プロンプトテンプレートには {{selection}} を含める必要があります", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "タイプ", "Mode": "モード", - "Custom": "カスタム" + "Custom": "カスタム", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/ko/main.json b/src/_locales/ko/main.json index 3f01a2d67..856614e48 100644 --- a/src/_locales/ko/main.json +++ b/src/_locales/ko/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "사용자 정의 모델", @@ -132,7 +134,8 @@ "Generating...": "생성 중...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "moonshot 토큰이 필요합니다. https://kimi.com 에서 로그인한 다음 재시도 버튼을 클릭하세요.", "Hide context menu of this extension": "이 확장 프로그램의 컨텍스트 메뉴 숨기기", - "Custom Claude API Url": "사용자 정의 Claude API URL", + "Custom Anthropic API Url": "사용자 정의 Anthropic API URL", + "Anthropic API Key": "Anthropic API 키", "Cancel": "취소", "Name is required": "이름은 필수입니다", "Prompt template should include {{selection}}": "프롬프트 템플릿에는 {{selection}} 이 포함되어야 합니다", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (웹)", "Type": "유형", "Mode": "모드", - "Custom": "사용자 정의" + "Custom": "사용자 정의", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/pt/main.json b/src/_locales/pt/main.json index 3cfc84274..c71da47b5 100644 --- a/src/_locales/pt/main.json +++ b/src/_locales/pt/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Modelo Personalizado", @@ -132,7 +134,8 @@ "Generating...": "Gerando...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "token moonshot necessário, faça login em https://kimi.com primeiro e depois clique no botão de tentar novamente", "Hide context menu of this extension": "Ocultar menu de contexto desta extensão", - "Custom Claude API Url": "URL da API Personalizada do Claude", + "Custom Anthropic API Url": "URL da API Personalizada do Anthropic", + "Anthropic API Key": "Chave API Anthropic", "Cancel": "Cancelar", "Name is required": "Nome é obrigatório", "Prompt template should include {{selection}}": "O modelo de prompt deve incluir {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Tipo", "Mode": "Modo", - "Custom": "Personalizado" + "Custom": "Personalizado", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/ru/main.json b/src/_locales/ru/main.json index 6e0ff84fe..45f233a50 100644 --- a/src/_locales/ru/main.json +++ b/src/_locales/ru/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Веб, GPT-4)", "Bing (Web, GPT-4)": "Bing (Веб, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-турбо)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-турбо)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8к)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8к)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32к)", "GPT-3.5": "GPT-3.5", "Custom Model": "Пользовательская модель", @@ -132,7 +134,8 @@ "Generating...": "Генерация...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "требуется токен moonshot, пожалуйста, сначала войдите на https://kimi.com, а затем нажмите кнопку повтора", "Hide context menu of this extension": "Скрыть контекстное меню этого расширения", - "Custom Claude API Url": "Пользовательский URL API Claude", + "Custom Anthropic API Url": "Пользовательский URL API Anthropic", + "Anthropic API Key": "Ключ API Anthropic", "Cancel": "Отмена", "Name is required": "Имя обязательно", "Prompt template should include {{selection}}": "Шаблон запроса должен включать {{selection}}", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Веб)", "Type": "Тип", "Mode": "Режим", - "Custom": "Пользовательский" + "Custom": "Пользовательский", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/tr/main.json b/src/_locales/tr/main.json index 99e1b3641..042879bc4 100644 --- a/src/_locales/tr/main.json +++ b/src/_locales/tr/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (Web, GPT-4)", "Bing (Web, GPT-4)": "Bing (Web, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "Özel Model", @@ -132,7 +134,8 @@ "Generating...": "Üretiliyor...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "moonshot jetonu gereklidir, lütfen önce https://kimi.com adresinde oturum açın ve ardından yeniden dene düğmesine tıklayın", "Hide context menu of this extension": "Bu uzantının bağlam menüsünü gizle", - "Custom Claude API Url": "Özel Claude API Url'si", + "Custom Anthropic API Url": "Özel Anthropic API Url'si", + "Anthropic API Key": "Anthropic API Anahtarı", "Cancel": "İptal", "Name is required": "İsim gereklidir", "Prompt template should include {{selection}}": "Prompt şablonu {{selection}} içermelidir", @@ -158,5 +161,41 @@ "Gemini (Web)": "Gemini (Web)", "Type": "Tür", "Mode": "Mod", - "Custom": "Özel" + "Custom": "Özel", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/zh-hans/main.json b/src/_locales/zh-hans/main.json index fd3263fb9..55d2446e4 100644 --- a/src/_locales/zh-hans/main.json +++ b/src/_locales/zh-hans/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (网页版, GPT-4)", "Bing (Web, GPT-4)": "Bing (网页版, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "自定义模型", @@ -132,7 +134,8 @@ "Generating...": "正在生成...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "请先登录Kimi: https://kimi.com, 然后点击重试按钮", "Hide context menu of this extension": "隐藏此扩展的右键菜单", - "Custom Claude API Url": "自定义的Claude API地址", + "Custom Anthropic API Url": "自定义的Anthropic API地址", + "Anthropic API Key": "Anthropic API 密钥", "Cancel": "取消", "Name is required": "名称是必须的", "Prompt template should include {{selection}}": "提示模板应该包含 {{selection}}", @@ -165,5 +168,41 @@ "ChatGLM (Emohaa)": "ChatGLM (Emohaa, 专业情绪咨询)", "ChatGLM (CharGLM-3)": "ChatGLM (CharGLM-3, 角色扮演)", "Crop Text to ensure the input tokens do not exceed the model's limit": "裁剪文本以确保输入token不超过模型限制", - "Thinking Content": "思考内容" + "Thinking Content": "思考内容", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/_locales/zh-hant/main.json b/src/_locales/zh-hant/main.json index 87b41242a..e1c7c603a 100644 --- a/src/_locales/zh-hant/main.json +++ b/src/_locales/zh-hant/main.json @@ -73,7 +73,9 @@ "ChatGPT (Web, GPT-4)": "ChatGPT (網頁版, GPT-4)", "Bing (Web, GPT-4)": "Bing (網頁版, GPT-4)", "ChatGPT (GPT-3.5-turbo)": "ChatGPT (GPT-3.5-turbo)", + "OpenAI (GPT-3.5-turbo)": "OpenAI (GPT-3.5-turbo)", "ChatGPT (GPT-4-8k)": "ChatGPT (GPT-4-8k)", + "OpenAI (GPT-4-8k)": "OpenAI (GPT-4-8k)", "ChatGPT (GPT-4-32k)": "ChatGPT (GPT-4-32k)", "GPT-3.5": "GPT-3.5", "Custom Model": "自訂模型", @@ -132,7 +134,8 @@ "Generating...": "產生中...", "moonshot token required, please login at https://kimi.com first, and then click the retry button": "需要 moonshot token,請先在 https://kimi.com 登入,然後點擊重試按鈕", "Hide context menu of this extension": "隱藏此擴充功能的右鍵選單", - "Custom Claude API Url": "自訂 Claude API 網址", + "Custom Anthropic API Url": "自訂 Anthropic API 網址", + "Anthropic API Key": "Anthropic API 金鑰", "Cancel": "取消", "Name is required": "名稱是必填的", "Prompt template should include {{selection}}": "提示範本應該包含 {{selection}}", @@ -160,5 +163,41 @@ "Mode": "模式", "Custom": "自訂", "Crop Text to ensure the input tokens do not exceed the model's limit": "裁剪文本以確保輸入token不超過模型限制", - "Thinking Content": "思考內容" + "Thinking Content": "思考內容", + "OpenAI (API)": "OpenAI (API)", + "Anthropic (API)": "Anthropic (API)", + "Azure OpenAI (API)": "Azure OpenAI (API)", + "OpenAI (GPT-3.5-turbo-16k)": "OpenAI (GPT-3.5-turbo-16k)", + "OpenAI (GPT-4o, 128k)": "OpenAI (GPT-4o, 128k)", + "OpenAI (GPT-4o mini)": "OpenAI (GPT-4o mini)", + "OpenAI (GPT-4-Turbo 128k)": "OpenAI (GPT-4-Turbo 128k)", + "OpenAI (GPT-4-Turbo 128k Preview)": "OpenAI (GPT-4-Turbo 128k Preview)", + "OpenAI (GPT-4-Turbo 128k 1106 Preview)": "OpenAI (GPT-4-Turbo 128k 1106 Preview)", + "OpenAI (GPT-4-Turbo 128k 0125 Preview)": "OpenAI (GPT-4-Turbo 128k 0125 Preview)", + "OpenAI (GPT-5 latest)": "OpenAI (GPT-5 latest)", + "OpenAI (GPT-5.1 latest)": "OpenAI (GPT-5.1 latest)", + "OpenAI (GPT-4.1)": "OpenAI (GPT-4.1)", + "OpenAI (GPT-4.1 mini)": "OpenAI (GPT-4.1 mini)", + "OpenAI (GPT-4.1 nano)": "OpenAI (GPT-4.1 nano)", + "Anthropic (Claude 3 Haiku)": "Anthropic (Claude 3 Haiku)", + "Anthropic (Claude 3.5 Haiku)": "Anthropic (Claude 3.5 Haiku)", + "Anthropic (Claude 3.7 Sonnet)": "Anthropic (Claude 3.7 Sonnet)", + "Anthropic (Claude Opus 4)": "Anthropic (Claude Opus 4)", + "Anthropic (Claude Opus 4.1)": "Anthropic (Claude Opus 4.1)", + "Anthropic (Claude Opus 4.5)": "Anthropic (Claude Opus 4.5)", + "Anthropic (Claude Opus 4.6)": "Anthropic (Claude Opus 4.6)", + "Anthropic (Claude Sonnet 4)": "Anthropic (Claude Sonnet 4)", + "Anthropic (Claude Sonnet 4.5)": "Anthropic (Claude Sonnet 4.5)", + "Anthropic (Claude Haiku 4.5)": "Anthropic (Claude Haiku 4.5)", + "OpenAI (GPT-3.5-turbo 1106)": "OpenAI (GPT-3.5-turbo 1106)", + "OpenAI (GPT-3.5-turbo 0125)": "OpenAI (GPT-3.5-turbo 0125)", + "OpenAI (GPT-4-8k 0613)": "OpenAI (GPT-4-8k 0613)", + "Azure OpenAI": "Azure OpenAI", + "OpenAI (GPT-5)": "OpenAI (GPT-5)", + "OpenAI (GPT-5.1)": "OpenAI (GPT-5.1)", + "OpenAI (GPT-5.2 latest)": "OpenAI (GPT-5.2 latest)", + "OpenAI (GPT-5.2)": "OpenAI (GPT-5.2)", + "OpenAI (GPT-5.3 latest)": "OpenAI (GPT-5.3 latest)", + "OpenAI (GPT-5.4)": "OpenAI (GPT-5.4)", + "Anthropic (Claude Sonnet 4.6)": "Anthropic (Claude Sonnet 4.6)" } diff --git a/src/background/index.mjs b/src/background/index.mjs index ec5092fde..7fcaa4286 100644 --- a/src/background/index.mjs +++ b/src/background/index.mjs @@ -6,7 +6,7 @@ import { } from '../services/apis/chatgpt-web' import { generateAnswersWithBingWebApi } from '../services/apis/bing-web.mjs' import { - generateAnswersWithChatgptApi, + generateAnswersWithOpenAiApi, generateAnswersWithGptCompletionApi, } from '../services/apis/openai-api' import { generateAnswersWithCustomApi } from '../services/apis/custom-api.mjs' @@ -64,7 +64,6 @@ const RECONNECT_CONFIG = { BACKOFF_MULTIPLIER: 2, // Multiplier for exponential backoff STABLE_CONNECT_RESET_DELAY_MS: 3000, // Reset retries only after connection stays stable } - function setPortProxy(port, proxyTabId) { try { console.debug(`[background] Attempting to connect to proxy tab: ${proxyTabId}`) @@ -509,10 +508,10 @@ async function executeApi(session, port, config) { const cookies = await getBardCookies() await generateAnswersWithBardWebApi(port, session.question, session, cookies) } else if (isUsingChatgptApiModel(session)) { - console.debug('[background] Using ChatGPT API Model') - await generateAnswersWithChatgptApi(port, session.question, session, config.apiKey) + console.debug('[background] Using OpenAI API Model') + await generateAnswersWithOpenAiApi(port, session.question, session, config.apiKey) } else if (isUsingClaudeApiModel(session)) { - console.debug('[background] Using Claude API Model') + console.debug('[background] Using Anthropic API Model') await generateAnswersWithClaudeApi(port, session.question, session) } else if (isUsingMoonshotApiModel(session)) { console.debug('[background] Using Moonshot API Model') diff --git a/src/config/index.mjs b/src/config/index.mjs index 42366aa2f..d425b78bf 100644 --- a/src/config/index.mjs +++ b/src/config/index.mjs @@ -166,11 +166,11 @@ export const ModelGroups = { chatgptApiModelKeys: { value: chatgptApiModelKeys, - desc: 'ChatGPT (API)', + desc: 'OpenAI (API)', }, claudeApiModelKeys: { value: claudeApiModelKeys, - desc: 'Claude.ai (API)', + desc: 'Anthropic (API)', }, moonshotApiModelKeys: { value: moonshotApiModelKeys, @@ -186,7 +186,7 @@ export const ModelGroups = { }, azureOpenAiApiModelKeys: { value: azureOpenAiApiModelKeys, - desc: 'ChatGPT (Azure API)', + desc: 'Azure OpenAI (API)', }, gptApiModelKeys: { value: gptApiModelKeys, @@ -230,85 +230,85 @@ export const Models = { chatgptPlus4: { value: 'gpt-4', desc: 'ChatGPT (Web, GPT-4)' }, chatgptPlus4Browsing: { value: 'gpt-4', desc: 'ChatGPT (Web, GPT-4)' }, // for compatibility - chatgptApi35: { value: 'gpt-3.5-turbo', desc: 'ChatGPT (GPT-3.5-turbo)' }, - chatgptApi35_16k: { value: 'gpt-3.5-turbo-16k', desc: 'ChatGPT (GPT-3.5-turbo-16k)' }, + chatgptApi35: { value: 'gpt-3.5-turbo', desc: 'OpenAI (GPT-3.5-turbo)' }, + chatgptApi35_16k: { value: 'gpt-3.5-turbo-16k', desc: 'OpenAI (GPT-3.5-turbo-16k)' }, - chatgptApi4o_128k: { value: 'gpt-4o', desc: 'ChatGPT (GPT-4o, 128k)' }, - chatgptApi4oMini: { value: 'gpt-4o-mini', desc: 'ChatGPT (GPT-4o mini)' }, - chatgptApi4_8k: { value: 'gpt-4', desc: 'ChatGPT (GPT-4-8k)' }, + chatgptApi4o_128k: { value: 'gpt-4o', desc: 'OpenAI (GPT-4o, 128k)' }, + chatgptApi4oMini: { value: 'gpt-4o-mini', desc: 'OpenAI (GPT-4o mini)' }, + chatgptApi4_8k: { value: 'gpt-4', desc: 'OpenAI (GPT-4-8k)' }, chatgptApi4_128k: { value: 'gpt-4-turbo', - desc: 'ChatGPT (GPT-4-Turbo 128k)', + desc: 'OpenAI (GPT-4-Turbo 128k)', }, chatgptApi4_128k_preview: { value: 'gpt-4-turbo-preview', - desc: 'ChatGPT (GPT-4-Turbo 128k Preview)', + desc: 'OpenAI (GPT-4-Turbo 128k Preview)', }, chatgptApi4_128k_1106_preview: { value: 'gpt-4-1106-preview', - desc: 'ChatGPT (GPT-4-Turbo 128k 1106 Preview)', + desc: 'OpenAI (GPT-4-Turbo 128k 1106 Preview)', }, chatgptApi4_128k_0125_preview: { value: 'gpt-4-0125-preview', - desc: 'ChatGPT (GPT-4-Turbo 128k 0125 Preview)', + desc: 'OpenAI (GPT-4-Turbo 128k 0125 Preview)', }, - chatgptApi5Latest: { value: 'gpt-5-chat-latest', desc: 'ChatGPT (ChatGPT-5 latest)' }, - chatgptApi5: { value: 'gpt-5', desc: 'ChatGPT (GPT-5)' }, - chatgptApi5_1Latest: { value: 'gpt-5.1-chat-latest', desc: 'ChatGPT (ChatGPT-5.1 latest)' }, - chatgptApi5_1: { value: 'gpt-5.1', desc: 'ChatGPT (GPT-5.1)' }, - chatgptApi5_2Latest: { value: 'gpt-5.2-chat-latest', desc: 'ChatGPT (ChatGPT-5.2 latest)' }, - chatgptApi5_2: { value: 'gpt-5.2', desc: 'ChatGPT (GPT-5.2)' }, - chatgptApi5_3Latest: { value: 'gpt-5.3-chat-latest', desc: 'ChatGPT (ChatGPT-5.3 latest)' }, - chatgptApi5_4: { value: 'gpt-5.4', desc: 'ChatGPT (GPT-5.4)' }, + chatgptApi5Latest: { value: 'gpt-5-chat-latest', desc: 'OpenAI (GPT-5 latest)' }, + chatgptApi5: { value: 'gpt-5', desc: 'OpenAI (GPT-5)' }, + chatgptApi5_1Latest: { value: 'gpt-5.1-chat-latest', desc: 'OpenAI (GPT-5.1 latest)' }, + chatgptApi5_1: { value: 'gpt-5.1', desc: 'OpenAI (GPT-5.1)' }, + chatgptApi5_2Latest: { value: 'gpt-5.2-chat-latest', desc: 'OpenAI (GPT-5.2 latest)' }, + chatgptApi5_2: { value: 'gpt-5.2', desc: 'OpenAI (GPT-5.2)' }, + chatgptApi5_3Latest: { value: 'gpt-5.3-chat-latest', desc: 'OpenAI (GPT-5.3 latest)' }, + chatgptApi5_4: { value: 'gpt-5.4', desc: 'OpenAI (GPT-5.4)' }, - chatgptApi4_1: { value: 'gpt-4.1', desc: 'ChatGPT (GPT-4.1)' }, - chatgptApi4_1_mini: { value: 'gpt-4.1-mini', desc: 'ChatGPT (GPT-4.1 mini)' }, - chatgptApi4_1_nano: { value: 'gpt-4.1-nano', desc: 'ChatGPT (GPT-4.1 nano)' }, + chatgptApi4_1: { value: 'gpt-4.1', desc: 'OpenAI (GPT-4.1)' }, + chatgptApi4_1_mini: { value: 'gpt-4.1-mini', desc: 'OpenAI (GPT-4.1 mini)' }, + chatgptApi4_1_nano: { value: 'gpt-4.1-nano', desc: 'OpenAI (GPT-4.1 nano)' }, claude2WebFree: { value: '', desc: 'Claude.ai (Web)' }, claude3HaikuApi: { value: 'claude-3-haiku-20240307', - desc: 'Claude.ai (API, Claude 3 Haiku)', + desc: 'Anthropic (Claude 3 Haiku)', }, claude35HaikuApi: { value: 'claude-3-5-haiku-20241022', - desc: 'Claude.ai (API, Claude 3.5 Haiku)', + desc: 'Anthropic (Claude 3.5 Haiku)', }, claude37SonnetApi: { value: 'claude-3-7-sonnet-20250219', - desc: 'Claude.ai (API, Claude 3.7 Sonnet)', + desc: 'Anthropic (Claude 3.7 Sonnet)', }, claudeOpus4Api: { value: 'claude-opus-4-20250514', - desc: 'Claude.ai (API, Claude Opus 4)', + desc: 'Anthropic (Claude Opus 4)', }, claudeOpus41Api: { value: 'claude-opus-4-1-20250805', - desc: 'Claude.ai (API, Claude Opus 4.1)', + desc: 'Anthropic (Claude Opus 4.1)', }, claudeOpus45Api: { value: 'claude-opus-4-5', - desc: 'Claude.ai (API, Claude Opus 4.5)', + desc: 'Anthropic (Claude Opus 4.5)', }, claudeOpus46Api: { value: 'claude-opus-4-6', - desc: 'Claude.ai (API, Claude Opus 4.6)', + desc: 'Anthropic (Claude Opus 4.6)', }, claudeSonnet4Api: { value: 'claude-sonnet-4-20250514', - desc: 'Claude.ai (API, Claude Sonnet 4)', + desc: 'Anthropic (Claude Sonnet 4)', }, claudeSonnet45Api: { value: 'claude-sonnet-4-5-20250929', - desc: 'Claude.ai (API, Claude Sonnet 4.5)', + desc: 'Anthropic (Claude Sonnet 4.5)', }, claudeSonnet46Api: { value: 'claude-sonnet-4-6', - desc: 'Claude.ai (API, Claude Sonnet 4.6)', + desc: 'Anthropic (Claude Sonnet 4.6)', }, claudeHaiku45Api: { value: 'claude-haiku-4-5-20251001', - desc: 'Claude.ai (API, Claude Haiku 4.5)', + desc: 'Anthropic (Claude Haiku 4.5)', }, bingFree4: { value: '', desc: 'Bing (Web, GPT-4)' }, @@ -331,15 +331,15 @@ export const Models = { chatgptFree35Mobile: { value: 'text-davinci-002-render-sha-mobile', desc: 'ChatGPT (Mobile)' }, chatgptPlus4Mobile: { value: 'gpt-4-mobile', desc: 'ChatGPT (Mobile, GPT-4)' }, - chatgptApi35_1106: { value: 'gpt-3.5-turbo-1106', desc: 'ChatGPT (GPT-3.5-turbo 1106)' }, - chatgptApi35_0125: { value: 'gpt-3.5-turbo-0125', desc: 'ChatGPT (GPT-3.5-turbo 0125)' }, - chatgptApi4_8k_0613: { value: 'gpt-4', desc: 'ChatGPT (GPT-4-8k 0613)' }, + chatgptApi35_1106: { value: 'gpt-3.5-turbo-1106', desc: 'OpenAI (GPT-3.5-turbo 1106)' }, + chatgptApi35_0125: { value: 'gpt-3.5-turbo-0125', desc: 'OpenAI (GPT-3.5-turbo 0125)' }, + chatgptApi4_8k_0613: { value: 'gpt-4', desc: 'OpenAI (GPT-4-8k 0613)' }, gptApiInstruct: { value: 'gpt-3.5-turbo-instruct', desc: 'GPT-3.5-turbo Instruct' }, customModel: { value: '', desc: 'Custom Model' }, ollamaModel: { value: '', desc: 'Ollama API' }, - azureOpenAi: { value: '', desc: 'ChatGPT (Azure)' }, + azureOpenAi: { value: '', desc: 'Azure OpenAI' }, waylaidwandererApi: { value: '', desc: 'Waylaidwanderer API (Github)' }, poeAiWebSage: { value: 'Assistant', desc: 'Poe AI (Web, Assistant)' }, @@ -513,7 +513,7 @@ export const defaultConfig = { poeCustomBotName: '', - claudeApiKey: '', + anthropicApiKey: '', chatglmApiKey: '', moonshotApiKey: '', deepSeekApiKey: '', @@ -543,7 +543,7 @@ export const defaultConfig = { customChatGptWebApiUrl: 'https://chatgpt.com', customChatGptWebApiPath: '/backend-api/conversation', customOpenAiApiUrl: 'https://api.openai.com', - customClaudeApiUrl: 'https://api.anthropic.com', + customAnthropicApiUrl: 'https://api.anthropic.com', disableWebModeHistory: true, hideContextMenu: false, cropText: true, @@ -759,9 +759,45 @@ export async function getPreferredLanguageKey() { * @returns {Promise} */ export async function getUserConfig() { - const options = await Browser.storage.local.get(Object.keys(defaultConfig)) + // Also fetch old keys for migration + const options = await Browser.storage.local.get([ + ...Object.keys(defaultConfig), + 'claudeApiKey', + 'customClaudeApiUrl', + ]) if (options.customChatGptWebApiUrl === 'https://chat.openai.com') options.customChatGptWebApiUrl = 'https://chatgpt.com' + + // Migrate legacy Claude-named keys to Anthropic-named keys. + // If both old/new keys coexist (for example after a partial migration), + // keep the Anthropic-named keys and clean up the legacy Claude-named keys. + if (options.claudeApiKey !== undefined) { + if (options.anthropicApiKey === undefined) { + options.anthropicApiKey = options.claudeApiKey + try { + await Browser.storage.local.set({ anthropicApiKey: options.claudeApiKey }) + await Browser.storage.local.remove('claudeApiKey') + } catch { + // Retry the legacy-key cleanup on the next config read. + } + } else { + await Browser.storage.local.remove('claudeApiKey').catch(() => {}) + } + } + if (options.customClaudeApiUrl !== undefined) { + if (options.customAnthropicApiUrl === undefined) { + options.customAnthropicApiUrl = options.customClaudeApiUrl + try { + await Browser.storage.local.set({ customAnthropicApiUrl: options.customClaudeApiUrl }) + await Browser.storage.local.remove('customClaudeApiUrl') + } catch { + // Retry the legacy-key cleanup on the next config read. + } + } else { + await Browser.storage.local.remove('customClaudeApiUrl').catch(() => {}) + } + } + return defaults(options, defaultConfig) } diff --git a/src/popup/sections/AdvancedPart.jsx b/src/popup/sections/AdvancedPart.jsx index 4149528bf..bfd211504 100644 --- a/src/popup/sections/AdvancedPart.jsx +++ b/src/popup/sections/AdvancedPart.jsx @@ -104,13 +104,13 @@ function ApiUrl({ config, updateConfig }) { /> diff --git a/src/popup/sections/GeneralPart.jsx b/src/popup/sections/GeneralPart.jsx index 30e6c20a4..11e72a408 100644 --- a/src/popup/sections/GeneralPart.jsx +++ b/src/popup/sections/GeneralPart.jsx @@ -30,6 +30,7 @@ import { languageList } from '../../config/language.mjs' import PropTypes from 'prop-types' import { config as menuConfig } from '../../content-script/menu-tools' import { PencilIcon } from '@primer/octicons-react' +import { importDataIntoStorage } from './import-data-cleanup.mjs' GeneralPart.propTypes = { config: PropTypes.object.isRequired, @@ -212,11 +213,11 @@ export function GeneralPart({ config, updateConfig, setTabIndex }) { { const apiKey = e.target.value - updateConfig({ claudeApiKey: apiKey }) + updateConfig({ anthropicApiKey: apiKey }) }} /> )} @@ -572,7 +573,7 @@ export function GeneralPart({ config, updateConfig, setTabIndex }) { reader.onload = (e) => resolve(JSON.parse(e.target.result)) reader.readAsText(file) }) - await Browser.storage.local.set(data) + await importDataIntoStorage(Browser.storage.local, data) window.location.reload() }} > diff --git a/src/popup/sections/import-data-cleanup.mjs b/src/popup/sections/import-data-cleanup.mjs new file mode 100644 index 000000000..19fbea4b6 --- /dev/null +++ b/src/popup/sections/import-data-cleanup.mjs @@ -0,0 +1,34 @@ +const conflictingKeyPairs = [ + ['claudeApiKey', 'anthropicApiKey'], + ['customClaudeApiUrl', 'customAnthropicApiUrl'], +] + +export function prepareImportData(data) { + const normalizedData = { ...data } + const keysToRemove = [] + + for (const [legacyKey, anthropicKey] of conflictingKeyPairs) { + const hasLegacyKey = Object.hasOwn(data, legacyKey) + const hasAnthropicKey = Object.hasOwn(data, anthropicKey) + + if (hasLegacyKey && !hasAnthropicKey) { + normalizedData[anthropicKey] = data[legacyKey] + keysToRemove.push(legacyKey) + } else if (hasAnthropicKey && !hasLegacyKey) { + normalizedData[legacyKey] = data[anthropicKey] + keysToRemove.push(legacyKey) + } + } + + return { normalizedData, keysToRemove } +} + +export async function importDataIntoStorage(storageArea, data) { + const { normalizedData, keysToRemove } = prepareImportData(data) + + await storageArea.set(normalizedData) + + if (keysToRemove.length > 0) { + await storageArea.remove(keysToRemove) + } +} diff --git a/src/services/apis/aiml-api.mjs b/src/services/apis/aiml-api.mjs index b1699052b..fb10be47b 100644 --- a/src/services/apis/aiml-api.mjs +++ b/src/services/apis/aiml-api.mjs @@ -1,4 +1,4 @@ -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' /** * @param {Browser.Runtime.Port} port @@ -8,5 +8,5 @@ import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' */ export async function generateAnswersWithAimlApi(port, question, session, apiKey) { const baseUrl = 'https://api.aimlapi.com/v1' - return generateAnswersWithChatgptApiCompat(baseUrl, port, question, session, apiKey) + return generateAnswersWithOpenAiApiCompat(baseUrl, port, question, session, apiKey) } diff --git a/src/services/apis/chatglm-api.mjs b/src/services/apis/chatglm-api.mjs index 8307c3c51..fa6899abe 100644 --- a/src/services/apis/chatglm-api.mjs +++ b/src/services/apis/chatglm-api.mjs @@ -1,6 +1,6 @@ import { getUserConfig } from '../../config/index.mjs' // import { getToken } from '../../utils/jwt-token-generator.mjs' -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' /** * @param {Runtime.Port} port @@ -10,5 +10,5 @@ import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' export async function generateAnswersWithChatGLMApi(port, question, session) { const baseUrl = 'https://open.bigmodel.cn/api/paas/v4' const config = await getUserConfig() - return generateAnswersWithChatgptApiCompat(baseUrl, port, question, session, config.chatglmApiKey) + return generateAnswersWithOpenAiApiCompat(baseUrl, port, question, session, config.chatglmApiKey) } diff --git a/src/services/apis/claude-api.mjs b/src/services/apis/claude-api.mjs index 9931e4e75..04a87fe2a 100644 --- a/src/services/apis/claude-api.mjs +++ b/src/services/apis/claude-api.mjs @@ -13,7 +13,7 @@ import { getModelValue } from '../../utils/model-name-convert.mjs' export async function generateAnswersWithClaudeApi(port, question, session) { const { controller, messageListener, disconnectListener } = setAbortController(port) const config = await getUserConfig() - const apiUrl = config.customClaudeApiUrl + const apiUrl = config.customAnthropicApiUrl const model = getModelValue(session) const prompt = getConversationPairs( @@ -29,7 +29,7 @@ export async function generateAnswersWithClaudeApi(port, question, session) { headers: { 'Content-Type': 'application/json', 'anthropic-version': '2023-06-01', - 'x-api-key': config.claudeApiKey, + 'x-api-key': config.anthropicApiKey, 'anthropic-dangerous-direct-browser-access': true, }, body: JSON.stringify({ diff --git a/src/services/apis/deepseek-api.mjs b/src/services/apis/deepseek-api.mjs index d0538ea15..9e91b97a8 100644 --- a/src/services/apis/deepseek-api.mjs +++ b/src/services/apis/deepseek-api.mjs @@ -1,4 +1,4 @@ -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' /** * @param {Browser.Runtime.Port} port @@ -8,5 +8,5 @@ import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' */ export async function generateAnswersWithDeepSeekApi(port, question, session, apiKey) { const baseUrl = 'https://api.deepseek.com' - return generateAnswersWithChatgptApiCompat(baseUrl, port, question, session, apiKey) + return generateAnswersWithOpenAiApiCompat(baseUrl, port, question, session, apiKey) } diff --git a/src/services/apis/moonshot-api.mjs b/src/services/apis/moonshot-api.mjs index c3cc187b3..157c3ecd4 100644 --- a/src/services/apis/moonshot-api.mjs +++ b/src/services/apis/moonshot-api.mjs @@ -1,4 +1,4 @@ -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' /** * @param {Browser.Runtime.Port} port @@ -8,5 +8,5 @@ import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' */ export async function generateAnswersWithMoonshotCompletionApi(port, question, session, apiKey) { const baseUrl = 'https://api.moonshot.cn/v1' - return generateAnswersWithChatgptApiCompat(baseUrl, port, question, session, apiKey) + return generateAnswersWithOpenAiApiCompat(baseUrl, port, question, session, apiKey) } diff --git a/src/services/apis/ollama-api.mjs b/src/services/apis/ollama-api.mjs index 2bf5753e6..01634fc64 100644 --- a/src/services/apis/ollama-api.mjs +++ b/src/services/apis/ollama-api.mjs @@ -1,5 +1,5 @@ import { getUserConfig } from '../../config/index.mjs' -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' import { getModelValue } from '../../utils/model-name-convert.mjs' /** @@ -10,7 +10,7 @@ import { getModelValue } from '../../utils/model-name-convert.mjs' export async function generateAnswersWithOllamaApi(port, question, session) { const config = await getUserConfig() const model = getModelValue(session) - return generateAnswersWithChatgptApiCompat( + return generateAnswersWithOpenAiApiCompat( config.ollamaEndpoint + '/v1', port, question, diff --git a/src/services/apis/openai-api.mjs b/src/services/apis/openai-api.mjs index 752a2a21c..0131d8f7d 100644 --- a/src/services/apis/openai-api.mjs +++ b/src/services/apis/openai-api.mjs @@ -96,9 +96,9 @@ export async function generateAnswersWithGptCompletionApi(port, question, sessio * @param {Session} session * @param {string} apiKey */ -export async function generateAnswersWithChatgptApi(port, question, session, apiKey) { +export async function generateAnswersWithOpenAiApi(port, question, session, apiKey) { const config = await getUserConfig() - return generateAnswersWithChatgptApiCompat( + return generateAnswersWithOpenAiApiCompat( config.customOpenAiApiUrl + '/v1', port, question, @@ -109,7 +109,7 @@ export async function generateAnswersWithChatgptApi(port, question, session, api ) } -export async function generateAnswersWithChatgptApiCompat( +export async function generateAnswersWithOpenAiApiCompat( baseUrl, port, question, diff --git a/src/services/apis/openrouter-api.mjs b/src/services/apis/openrouter-api.mjs index 1fe9c8ad7..f514e1e29 100644 --- a/src/services/apis/openrouter-api.mjs +++ b/src/services/apis/openrouter-api.mjs @@ -1,4 +1,4 @@ -import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' +import { generateAnswersWithOpenAiApiCompat } from './openai-api.mjs' /** * @param {Browser.Runtime.Port} port @@ -8,5 +8,5 @@ import { generateAnswersWithChatgptApiCompat } from './openai-api.mjs' */ export async function generateAnswersWithOpenRouterApi(port, question, session, apiKey) { const baseUrl = 'https://openrouter.ai/api/v1' - return generateAnswersWithChatgptApiCompat(baseUrl, port, question, session, apiKey) + return generateAnswersWithOpenAiApiCompat(baseUrl, port, question, session, apiKey) } diff --git a/src/utils/model-name-convert.mjs b/src/utils/model-name-convert.mjs index 3f2062326..04eeb4226 100644 --- a/src/utils/model-name-convert.mjs +++ b/src/utils/model-name-convert.mjs @@ -18,7 +18,11 @@ export function modelNameToDesc(modelName, t, extraCustomModelName = '') { desc = `${t(Models[presetPart].desc)} (${t(ModelMode[customPart])})` else desc = `${t(Models[presetPart].desc)} (${customPart})` } else if (presetPart in ModelGroups) { - desc = `${t(ModelGroups[presetPart].desc)} (${customPart})` + const baseDesc = + presetPart === 'azureOpenAiApiModelKeys' + ? Models.azureOpenAi.desc + : ModelGroups[presetPart].desc + desc = `${t(baseDesc)} (${customPart})` } } return desc diff --git a/tests/setup/browser-shim.mjs b/tests/setup/browser-shim.mjs index 2685d46de..cd6d5380c 100644 --- a/tests/setup/browser-shim.mjs +++ b/tests/setup/browser-shim.mjs @@ -76,6 +76,17 @@ const storageLocal = { } return Promise.resolve() }, + remove(keys, callback) { + const keyList = Array.isArray(keys) ? keys : [keys] + for (const key of keyList) { + delete storageState[key] + } + if (typeof callback === 'function') { + queueMicrotask(() => callback()) + return + } + return Promise.resolve() + }, clear(callback) { storageState = createStorageState() if (typeof callback === 'function') { diff --git a/tests/unit/config/user-config.test.mjs b/tests/unit/config/user-config.test.mjs index 95a6b06b2..f22d73609 100644 --- a/tests/unit/config/user-config.test.mjs +++ b/tests/unit/config/user-config.test.mjs @@ -1,5 +1,6 @@ import assert from 'node:assert/strict' import { beforeEach, test } from 'node:test' +import Browser from 'webextension-polyfill' import { clearOldAccessToken, getUserConfig, setAccessToken } from '../../../src/config/index.mjs' const THIRTY_DAYS_MS = 30 * 24 * 3600 * 1000 @@ -28,6 +29,118 @@ test('getUserConfig keeps modern chatgpt.com URL unchanged', async () => { assert.equal(config.customChatGptWebApiUrl, 'https://chatgpt.com') }) +test('getUserConfig migrates legacy Claude keys to Anthropic keys and removes old keys', async () => { + globalThis.__TEST_BROWSER_SHIM__.replaceStorage({ + claudeApiKey: 'legacy-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + const config = await getUserConfig() + const storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(config.anthropicApiKey, 'legacy-key') + assert.equal(config.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(storage.anthropicApiKey, 'legacy-key') + assert.equal(storage.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(Object.hasOwn(storage, 'claudeApiKey'), false) + assert.equal(Object.hasOwn(storage, 'customClaudeApiUrl'), false) +}) + +test('getUserConfig prefers Anthropic keys when both legacy and Anthropic keys exist', async () => { + globalThis.__TEST_BROWSER_SHIM__.replaceStorage({ + anthropicApiKey: 'new-key', + claudeApiKey: 'legacy-key', + customAnthropicApiUrl: 'https://new.anthropic.example', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + const config = await getUserConfig() + const storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(config.anthropicApiKey, 'new-key') + assert.equal(config.customAnthropicApiUrl, 'https://new.anthropic.example') + assert.equal(storage.anthropicApiKey, 'new-key') + assert.equal(storage.customAnthropicApiUrl, 'https://new.anthropic.example') + assert.equal(Object.hasOwn(storage, 'claudeApiKey'), false) + assert.equal(Object.hasOwn(storage, 'customClaudeApiUrl'), false) +}) + +test('getUserConfig keeps Anthropic keys unchanged when legacy Claude keys are absent', async () => { + globalThis.__TEST_BROWSER_SHIM__.replaceStorage({ + anthropicApiKey: 'new-key', + customAnthropicApiUrl: 'https://new.anthropic.example', + }) + + const config = await getUserConfig() + const storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(config.anthropicApiKey, 'new-key') + assert.equal(config.customAnthropicApiUrl, 'https://new.anthropic.example') + assert.equal(storage.anthropicApiKey, 'new-key') + assert.equal(storage.customAnthropicApiUrl, 'https://new.anthropic.example') + assert.equal(Object.hasOwn(storage, 'claudeApiKey'), false) + assert.equal(Object.hasOwn(storage, 'customClaudeApiUrl'), false) +}) + +test('getUserConfig returns migrated Anthropic values when storage.set fails', async (t) => { + globalThis.__TEST_BROWSER_SHIM__.replaceStorage({ + claudeApiKey: 'legacy-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + const removeCalls = [] + t.mock.method(Browser.storage.local, 'set', async () => { + throw new Error('quota exceeded') + }) + t.mock.method(Browser.storage.local, 'remove', async (key) => { + removeCalls.push(key) + }) + + const config = await getUserConfig() + const storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(config.anthropicApiKey, 'legacy-key') + assert.equal(config.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(Object.hasOwn(storage, 'anthropicApiKey'), false) + assert.equal(Object.hasOwn(storage, 'customAnthropicApiUrl'), false) + assert.equal(storage.claudeApiKey, 'legacy-key') + assert.equal(storage.customClaudeApiUrl, 'https://legacy.anthropic.example') + assert.deepEqual(removeCalls, []) +}) + +test('getUserConfig returns migrated Anthropic values when storage.remove fails', async (t) => { + globalThis.__TEST_BROWSER_SHIM__.replaceStorage({ + claudeApiKey: 'legacy-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + const originalRemove = Browser.storage.local.remove + let removeCalls = 0 + t.mock.method(Browser.storage.local, 'remove', async (key) => { + removeCalls += 1 + if (removeCalls === 1) throw new Error('remove failed') + return originalRemove.call(Browser.storage.local, key) + }) + + const config = await getUserConfig() + let storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(config.anthropicApiKey, 'legacy-key') + assert.equal(config.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(storage.anthropicApiKey, 'legacy-key') + assert.equal(storage.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(storage.claudeApiKey, 'legacy-key') + assert.equal(Object.hasOwn(storage, 'customClaudeApiUrl'), false) + + const nextConfig = await getUserConfig() + storage = globalThis.__TEST_BROWSER_SHIM__.getStorage() + + assert.equal(nextConfig.anthropicApiKey, 'legacy-key') + assert.equal(nextConfig.customAnthropicApiUrl, 'https://legacy.anthropic.example') + assert.equal(Object.hasOwn(storage, 'claudeApiKey'), false) + assert.equal(Object.hasOwn(storage, 'customClaudeApiUrl'), false) +}) + test('clearOldAccessToken clears expired token older than 30 days', async (t) => { const now = 1_700_000_000_000 t.mock.method(Date, 'now', () => now) diff --git a/tests/unit/popup/import-data-cleanup.test.mjs b/tests/unit/popup/import-data-cleanup.test.mjs new file mode 100644 index 000000000..d21639726 --- /dev/null +++ b/tests/unit/popup/import-data-cleanup.test.mjs @@ -0,0 +1,138 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { + importDataIntoStorage, + prepareImportData, +} from '../../../src/popup/sections/import-data-cleanup.mjs' + +test('prepareImportData normalizes a legacy-only backup to Anthropic keys and removes legacy keys later', () => { + const { normalizedData, keysToRemove } = prepareImportData({ + claudeApiKey: 'legacy-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + assert.deepEqual(normalizedData, { + claudeApiKey: 'legacy-key', + anthropicApiKey: 'legacy-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + customAnthropicApiUrl: 'https://legacy.anthropic.example', + }) + assert.deepEqual(keysToRemove, ['claudeApiKey', 'customClaudeApiUrl']) +}) + +test('prepareImportData normalizes an Anthropic-only backup and still removes legacy keys later', () => { + const { normalizedData, keysToRemove } = prepareImportData({ + anthropicApiKey: 'new-key', + customAnthropicApiUrl: 'https://new.anthropic.example', + }) + + assert.deepEqual(normalizedData, { + claudeApiKey: 'new-key', + anthropicApiKey: 'new-key', + customClaudeApiUrl: 'https://new.anthropic.example', + customAnthropicApiUrl: 'https://new.anthropic.example', + }) + assert.deepEqual(keysToRemove, ['claudeApiKey', 'customClaudeApiUrl']) +}) + +test('prepareImportData resolves each conflicting field pair independently', () => { + const { normalizedData, keysToRemove } = prepareImportData({ + anthropicApiKey: 'new-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + }) + + assert.deepEqual(normalizedData, { + claudeApiKey: 'new-key', + anthropicApiKey: 'new-key', + customClaudeApiUrl: 'https://legacy.anthropic.example', + customAnthropicApiUrl: 'https://legacy.anthropic.example', + }) + assert.deepEqual(keysToRemove, ['claudeApiKey', 'customClaudeApiUrl']) +}) + +test('prepareImportData keeps imported values unchanged when both key families are already present', () => { + const input = { + anthropicApiKey: 'new-key', + claudeApiKey: 'legacy-key', + customAnthropicApiUrl: 'https://new.anthropic.example', + customClaudeApiUrl: 'https://legacy.anthropic.example', + } + const { normalizedData, keysToRemove } = prepareImportData(input) + + assert.deepEqual(normalizedData, input) + assert.deepEqual(keysToRemove, []) +}) + +test('prepareImportData leaves unrelated imports untouched', () => { + const { normalizedData, keysToRemove } = prepareImportData({ + apiKey: 'openai-key', + }) + + assert.deepEqual(normalizedData, { apiKey: 'openai-key' }) + assert.deepEqual(keysToRemove, []) +}) + +test('importDataIntoStorage writes normalized data before removing legacy keys', async () => { + const calls = [] + const storageArea = { + async set(data) { + calls.push(['set', data]) + }, + async remove(keys) { + calls.push(['remove', keys]) + }, + } + + await importDataIntoStorage(storageArea, { + claudeApiKey: 'legacy-key', + }) + + assert.deepEqual(calls, [ + ['set', { claudeApiKey: 'legacy-key', anthropicApiKey: 'legacy-key' }], + ['remove', ['claudeApiKey']], + ]) +}) + +test('importDataIntoStorage does not remove existing keys when set fails', async () => { + const calls = [] + const storageArea = { + async set() { + calls.push(['set']) + throw new Error('quota exceeded') + }, + async remove(keys) { + calls.push(['remove', keys]) + }, + } + + await assert.rejects(async () => { + await importDataIntoStorage(storageArea, { + claudeApiKey: 'legacy-key', + }) + }, /quota exceeded/) + + assert.deepEqual(calls, [['set']]) +}) + +test('importDataIntoStorage leaves normalized values in storage when remove fails after set', async () => { + const storageState = {} + const storageArea = { + async set(data) { + Object.assign(storageState, data) + }, + async remove() { + throw new Error('remove failed') + }, + } + + await assert.rejects(async () => { + await importDataIntoStorage(storageArea, { + anthropicApiKey: 'new-key', + }) + }, /remove failed/) + + assert.deepEqual(storageState, { + claudeApiKey: 'new-key', + anthropicApiKey: 'new-key', + }) +}) diff --git a/tests/unit/services/apis/openai-api-compat.test.mjs b/tests/unit/services/apis/openai-api-compat.test.mjs index 87834819a..89292f1b7 100644 --- a/tests/unit/services/apis/openai-api-compat.test.mjs +++ b/tests/unit/services/apis/openai-api-compat.test.mjs @@ -1,8 +1,8 @@ import assert from 'node:assert/strict' import { beforeEach, test } from 'node:test' import { - generateAnswersWithChatgptApi, - generateAnswersWithChatgptApiCompat, + generateAnswersWithOpenAiApi, + generateAnswersWithOpenAiApiCompat, generateAnswersWithGptCompletionApi, } from '../../../../src/services/apis/openai-api.mjs' import { createFakePort } from '../../helpers/port.mjs' @@ -29,7 +29,7 @@ beforeEach(() => { globalThis.__TEST_BROWSER_SHIM__.clearStorage() }) -test('generateAnswersWithChatgptApiCompat sends expected request and aggregates SSE deltas', async (t) => { +test('generateAnswersWithOpenAiApiCompat sends expected request and aggregates SSE deltas', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -55,7 +55,7 @@ test('generateAnswersWithChatgptApiCompat sends expected request and aggregates ]) }) - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -93,7 +93,7 @@ test('generateAnswersWithChatgptApiCompat sends expected request and aggregates assert.deepEqual(session.conversationRecords.at(-1), { question: 'CurrentQ', answer: 'Hello' }) }) -test('generateAnswersWithChatgptApiCompat uses max_completion_tokens for OpenAI latest gpt-5 compat models', async (t) => { +test('generateAnswersWithOpenAiApiCompat uses max_completion_tokens for OpenAI latest gpt-5 compat models', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -117,7 +117,7 @@ test('generateAnswersWithChatgptApiCompat uses max_completion_tokens for OpenAI } const port = createFakePort() - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -133,7 +133,7 @@ test('generateAnswersWithChatgptApiCompat uses max_completion_tokens for OpenAI } }) -test('generateAnswersWithChatgptApiCompat uses latest mapped gpt-5 API model values', async (t) => { +test('generateAnswersWithOpenAiApiCompat uses latest mapped gpt-5 API model values', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -157,7 +157,7 @@ test('generateAnswersWithChatgptApiCompat uses latest mapped gpt-5 API model val } const port = createFakePort() - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -174,7 +174,7 @@ test('generateAnswersWithChatgptApiCompat uses latest mapped gpt-5 API model val } }) -test('generateAnswersWithChatgptApi uses OpenAI token params for a latest mapped gpt-5 model', async (t) => { +test('generateAnswersWithOpenAiApi uses OpenAI token params for a latest mapped gpt-5 model', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ customOpenAiApiUrl: 'https://api.openai.example.com', @@ -200,7 +200,7 @@ test('generateAnswersWithChatgptApi uses OpenAI token params for a latest mapped ]) }) - await generateAnswersWithChatgptApi(port, 'CurrentQ', session, 'sk-test') + await generateAnswersWithOpenAiApi(port, 'CurrentQ', session, 'sk-test') const body = JSON.parse(capturedInit.body) assert.equal(capturedInput, 'https://api.openai.example.com/v1/chat/completions') @@ -209,7 +209,7 @@ test('generateAnswersWithChatgptApi uses OpenAI token params for a latest mapped assert.equal(Object.hasOwn(body, 'max_tokens'), false) }) -test('generateAnswersWithChatgptApiCompat keeps max_tokens for latest mapped gpt-5 models in compat provider', async (t) => { +test('generateAnswersWithOpenAiApiCompat keeps max_tokens for latest mapped gpt-5 models in compat provider', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -233,7 +233,7 @@ test('generateAnswersWithChatgptApiCompat keeps max_tokens for latest mapped gpt } const port = createFakePort() - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -250,7 +250,7 @@ test('generateAnswersWithChatgptApiCompat keeps max_tokens for latest mapped gpt } }) -test('generateAnswersWithChatgptApiCompat removes conflicting token key from extraBody', async (t) => { +test('generateAnswersWithOpenAiApiCompat removes conflicting token key from extraBody', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -273,7 +273,7 @@ test('generateAnswersWithChatgptApiCompat removes conflicting token key from ext ]) }) - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -291,7 +291,7 @@ test('generateAnswersWithChatgptApiCompat removes conflicting token key from ext assert.equal(body.top_p, 0.9) }) -test('generateAnswersWithChatgptApiCompat removes max_tokens from extraBody for OpenAI gpt-5 models', async (t) => { +test('generateAnswersWithOpenAiApiCompat removes max_tokens from extraBody for OpenAI gpt-5 models', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -315,7 +315,7 @@ test('generateAnswersWithChatgptApiCompat removes max_tokens from extraBody for } const port = createFakePort() - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -335,7 +335,7 @@ test('generateAnswersWithChatgptApiCompat removes max_tokens from extraBody for } }) -test('generateAnswersWithChatgptApiCompat allows max_tokens override for compat provider', async (t) => { +test('generateAnswersWithOpenAiApiCompat allows max_tokens override for compat provider', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -358,7 +358,7 @@ test('generateAnswersWithChatgptApiCompat allows max_tokens override for compat ]) }) - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -376,7 +376,7 @@ test('generateAnswersWithChatgptApiCompat allows max_tokens override for compat assert.equal(body.top_p, 0.75) }) -test('generateAnswersWithChatgptApiCompat allows max_completion_tokens override for OpenAI gpt-5 models', async (t) => { +test('generateAnswersWithOpenAiApiCompat allows max_completion_tokens override for OpenAI gpt-5 models', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -400,7 +400,7 @@ test('generateAnswersWithChatgptApiCompat allows max_completion_tokens override } const port = createFakePort() - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -420,7 +420,7 @@ test('generateAnswersWithChatgptApiCompat allows max_completion_tokens override } }) -test('generateAnswersWithChatgptApiCompat throws on non-ok response with JSON error body', async (t) => { +test('generateAnswersWithOpenAiApiCompat throws on non-ok response with JSON error body', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -445,7 +445,7 @@ test('generateAnswersWithChatgptApiCompat throws on non-ok response with JSON er ) await assert.rejects(async () => { - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -457,7 +457,7 @@ test('generateAnswersWithChatgptApiCompat throws on non-ok response with JSON er assert.deepEqual(port.listenerCounts(), { onMessage: 0, onDisconnect: 0 }) }) -test('generateAnswersWithChatgptApiCompat throws on network error', async (t) => { +test('generateAnswersWithOpenAiApiCompat throws on network error', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -477,7 +477,7 @@ test('generateAnswersWithChatgptApiCompat throws on network error', async (t) => }) await assert.rejects(async () => { - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -489,7 +489,7 @@ test('generateAnswersWithChatgptApiCompat throws on network error', async (t) => assert.deepEqual(port.listenerCounts(), { onMessage: 0, onDisconnect: 0 }) }) -test('generateAnswersWithChatgptApiCompat falls back to status text when JSON error parsing fails', async (t) => { +test('generateAnswersWithOpenAiApiCompat falls back to status text when JSON error parsing fails', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 3, @@ -516,7 +516,7 @@ test('generateAnswersWithChatgptApiCompat falls back to status text when JSON er ) await assert.rejects(async () => { - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', @@ -528,7 +528,7 @@ test('generateAnswersWithChatgptApiCompat falls back to status text when JSON er assert.deepEqual(port.listenerCounts(), { onMessage: 0, onDisconnect: 0 }) }) -test('generateAnswersWithChatgptApiCompat supports message.content fallback', async (t) => { +test('generateAnswersWithOpenAiApiCompat supports message.content fallback', async (t) => { t.mock.method(console, 'debug', () => {}) setStorage({ maxConversationContextLength: 2, @@ -549,7 +549,7 @@ test('generateAnswersWithChatgptApiCompat supports message.content fallback', as ]), ) - await generateAnswersWithChatgptApiCompat( + await generateAnswersWithOpenAiApiCompat( 'https://api.example.com/v1', port, 'CurrentQ', diff --git a/tests/unit/utils/model-name-convert.test.mjs b/tests/unit/utils/model-name-convert.test.mjs index 8601418a3..73e00dbf0 100644 --- a/tests/unit/utils/model-name-convert.test.mjs +++ b/tests/unit/utils/model-name-convert.test.mjs @@ -119,10 +119,10 @@ test('modelNameToDesc returns desc for a known model name without t function', ( }) test('modelNameToDesc returns desc for GPT-5 stable presets', () => { - assert.equal(modelNameToDesc('chatgptApi5'), 'ChatGPT (GPT-5)') - assert.equal(modelNameToDesc('chatgptApi5_1'), 'ChatGPT (GPT-5.1)') - assert.equal(modelNameToDesc('chatgptApi5_2'), 'ChatGPT (GPT-5.2)') - assert.equal(modelNameToDesc('chatgptApi5_4'), 'ChatGPT (GPT-5.4)') + assert.equal(modelNameToDesc('chatgptApi5'), 'OpenAI (GPT-5)') + assert.equal(modelNameToDesc('chatgptApi5_1'), 'OpenAI (GPT-5.1)') + assert.equal(modelNameToDesc('chatgptApi5_2'), 'OpenAI (GPT-5.2)') + assert.equal(modelNameToDesc('chatgptApi5_4'), 'OpenAI (GPT-5.4)') }) test('modelNameToDesc appends extraCustomModelName for customModel', () => { @@ -145,6 +145,15 @@ test('modelNameToDesc handles custom model with presetPart in ModelGroups', () = assert.equal(desc, 'Bing (Web) (customVariant)') }) +test('modelNameToDesc shows Azure OpenAI deployment without duplicate API label', () => { + const desc = modelNameToDesc('azureOpenAiApiModelKeys-deployment-a') + assert.equal(desc, 'Azure OpenAI (deployment-a)') +}) + +test('Azure OpenAI group label remains unchanged', () => { + assert.equal(ModelGroups.azureOpenAiApiModelKeys.desc, 'Azure OpenAI (API)') +}) + test('modelNameToCustomPart returns modelName when not custom', () => { assert.equal(modelNameToCustomPart('chatgptFree35'), 'chatgptFree35') })