diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a2693f1f..978eae4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## [Unreleased] + +### Fixed + +- **The AI server key reaches Anthropic when the base URL points there.** An + operator who filled the server key with an Anthropic key and + `https://api.anthropic.com/v1` got a failure on every AI call and nothing + saying why: the request was built for OpenAI's API and posted at Anthropic's, + which speaks a different shape. The base URL now picks the client. Anthropic's + host, and any subdomain of it, is called on Anthropic's Messages API and + defaults to a Claude model; every other host keeps the OpenAI-compatible API + it had before, which is also the right one for a local server or a gateway. A + base URL that is not a URL at all behaves as it did. The operator card now + says which providers the field accepts. Found in a fork by @sweidinger. + ## [1.38.3] — 2026-09-02 An account with two-factor can be deleted from the phone, and every data diff --git a/messages/de.json b/messages/de.json index 3445309ab..ae260abd6 100644 --- a/messages/de.json +++ b/messages/de.json @@ -6473,6 +6473,7 @@ "description": "Ein Betreiber-Schlüssel für Konten, die keinen eigenen hinterlegt haben.", "byokHint": "Der eigene Schlüssel (BYOK, ChatGPT-Login oder lokaler Provider) hat immer Vorrang — der Server-Schlüssel ist die letzte Stufe der Provider-Kette.", "consentHint": "Anfragen über den Server-Schlüssel erfordern die aktive KI-Einwilligung der Person; ohne Einwilligungsbeleg wird nichts gesendet.", + "baseUrlHint": "Die Basis-URL bestimmt die API: eine Anthropic-URL wie https://api.anthropic.com/v1 wird erkannt, jeder andere Host wird über die OpenAI-kompatible API angesprochen.", "statusConfigured": "Eingerichtet", "statusNotConfigured": "Nicht eingerichtet", "keyLabel": "API-Schlüssel", diff --git a/messages/en.json b/messages/en.json index 42b53a7cf..bfbb0ab32 100644 --- a/messages/en.json +++ b/messages/en.json @@ -6473,6 +6473,7 @@ "description": "An operator key for accounts that have not configured one of their own.", "byokHint": "A user's own key (BYOK, ChatGPT login or local provider) always takes precedence — the server key is the last step in the provider chain.", "consentHint": "Requests over the server key require the user's active AI consent; without a consent receipt nothing is sent.", + "baseUrlHint": "The base URL selects the API: an Anthropic URL such as https://api.anthropic.com/v1 is understood, any other host is called on the OpenAI-compatible API.", "statusConfigured": "Configured", "statusNotConfigured": "Not configured", "keyLabel": "API key", diff --git a/messages/es.json b/messages/es.json index f24a91a82..2ecddf478 100644 --- a/messages/es.json +++ b/messages/es.json @@ -6473,6 +6473,7 @@ "description": "Una clave del operador para las cuentas que no tienen una propia.", "byokHint": "La clave propia del usuario (BYOK, inicio de sesión de ChatGPT o proveedor local) siempre tiene prioridad — la clave del servidor es el último paso de la cadena de proveedores.", "consentHint": "Las solicitudes con la clave del servidor requieren el consentimiento de IA activo del usuario; sin un recibo de consentimiento no se envía nada.", + "baseUrlHint": "La URL base determina la API: se reconoce una URL de Anthropic como https://api.anthropic.com/v1, cualquier otro host se llama a través de la API compatible con OpenAI.", "statusConfigured": "Configurada", "statusNotConfigured": "Sin configurar", "keyLabel": "Clave de API", diff --git a/messages/fr.json b/messages/fr.json index 36901d982..63b62b5ff 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -6473,6 +6473,7 @@ "description": "Une clé de l'opérateur pour les comptes qui n'en ont pas configuré une.", "byokHint": "La clé personnelle de l'utilisateur (BYOK, connexion ChatGPT ou fournisseur local) a toujours la priorité — la clé du serveur est la dernière étape de la chaîne de fournisseurs.", "consentHint": "Les requêtes via la clé du serveur nécessitent le consentement IA actif de l'utilisateur ; sans justificatif de consentement, rien n'est envoyé.", + "baseUrlHint": "L'URL de base détermine l'API : une URL Anthropic telle que https://api.anthropic.com/v1 est reconnue, tout autre hôte est appelé via l'API compatible OpenAI.", "statusConfigured": "Configurée", "statusNotConfigured": "Non configurée", "keyLabel": "Clé API", diff --git a/messages/it.json b/messages/it.json index ee272c0cb..3cc1dd08e 100644 --- a/messages/it.json +++ b/messages/it.json @@ -6473,6 +6473,7 @@ "description": "Una chiave dell'operatore per gli account che non ne hanno una propria.", "byokHint": "La chiave personale dell'utente (BYOK, accesso ChatGPT o provider locale) ha sempre la precedenza — la chiave del server è l'ultimo passo della catena dei provider.", "consentHint": "Le richieste tramite la chiave del server richiedono il consenso IA attivo dell'utente; senza una ricevuta di consenso non viene inviato nulla.", + "baseUrlHint": "L'URL di base determina l'API: un URL Anthropic come https://api.anthropic.com/v1 viene riconosciuto, qualsiasi altro host viene chiamato tramite l'API compatibile con OpenAI.", "statusConfigured": "Configurata", "statusNotConfigured": "Non configurata", "keyLabel": "Chiave API", diff --git a/messages/ko.json b/messages/ko.json index ab9e42553..062b3e109 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -6473,6 +6473,7 @@ "description": "자기 키를 설정하지 않은 계정을 위한 운영자 키예요.", "byokHint": "사용자 본인 키(BYOK, ChatGPT 로그인, 로컬 제공자)가 항상 우선해요 — 서버 키는 제공자 체인의 마지막 단계예요.", "consentHint": "서버 키로 나가는 요청에는 사용자의 유효한 AI 동의가 필요해요. 동의 기록이 없으면 아무것도 전송하지 않아요.", + "baseUrlHint": "기본 URL이 API를 결정합니다. https://api.anthropic.com/v1 같은 Anthropic 주소는 인식되며, 그 밖의 호스트는 OpenAI 호환 API로 호출됩니다.", "statusConfigured": "설정됨", "statusNotConfigured": "설정 안 됨", "keyLabel": "API 키", diff --git a/messages/pl.json b/messages/pl.json index b95837dd1..de783600b 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -6473,6 +6473,7 @@ "description": "Klucz operatora dla kont, które nie mają własnego.", "byokHint": "Własny klucz użytkownika (BYOK, logowanie ChatGPT lub dostawca lokalny) zawsze ma pierwszeństwo — klucz serwera jest ostatnim ogniwem łańcucha dostawców.", "consentHint": "Żądania przez klucz serwera wymagają aktywnej zgody użytkownika na AI; bez potwierdzenia zgody nic nie jest wysyłane.", + "baseUrlHint": "Adres bazowy decyduje o API: adres Anthropic taki jak https://api.anthropic.com/v1 jest rozpoznawany, każdy inny host jest wywoływany przez API zgodne z OpenAI.", "statusConfigured": "Skonfigurowany", "statusNotConfigured": "Nieskonfigurowany", "keyLabel": "Klucz API", diff --git a/src/components/admin/ai-server-key-section.tsx b/src/components/admin/ai-server-key-section.tsx index 9fa09b63e..d9f0e6f1f 100644 --- a/src/components/admin/ai-server-key-section.tsx +++ b/src/components/admin/ai-server-key-section.tsx @@ -34,6 +34,10 @@ import { PasswordInput } from "./_shared"; * key here never silently forwards anyone's health data. * - The base URL is locked server-side to HTTPS + a hostname allowlist * (`ADMIN_AI_BASE_URL_ALLOWLIST` extends it). + * - The base URL also picks the wire: an `anthropic.com` host is called on + * Anthropic's Messages API, every other host on the OpenAI-compatible one + * (`resolveAdminProvider` in `src/lib/ai/provider.ts`). `baseUrlHint` is + * that sentence for the operator. */ interface AiServerKeyResponse { @@ -113,6 +117,7 @@ export function AiServerKeySection() {

{t("admin.aiServerKey.byokHint")}

{t("admin.aiServerKey.consentHint")}

+

{t("admin.aiServerKey.baseUrlHint")}