diff --git a/messages/de.json b/messages/de.json index 7f976efef..ad6935b2c 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1025,6 +1025,46 @@ "regenerateSuccess": "Reports wurden neu generiert", "regenerateRateLimit": "Bitte warte — maximal 2 Analysen pro Stunde.", "lastGeneratedAt": "Zuletzt generiert", + "ai": { + "title": "KI-Insights", + "description": "Anbieter, Modell, Key.", + "chatgptConnectedBadge": "ChatGPT verbunden", + "adminAiActiveBadge": "Admin-KI aktiv", + "connectionExpiredBadge": "Verbindung abgelaufen", + "chatgptConnectedHeading": "ChatGPT verbunden", + "chatgptConnectedBody": "Insights werden über dein ChatGPT-Abo generiert — keine zusätzlichen Kosten.", + "connectedSince": "Verbunden seit {when}.", + "disconnect": "Trennen", + "connectChatgptHeading": "Mit ChatGPT verbinden", + "connectChatgptBody": "Verbinde dein ChatGPT Pro/Max-Konto um KI-gestützte Gesundheitsanalysen basierend auf aktuellen medizinischen Leitlinien zu erhalten. Keine zusätzlichen API-Kosten.", + "connectChatgptCta": "Mit ChatGPT verbinden", + "oauthNotConfigured": "ChatGPT-OAuth ist auf dieser Instanz nicht konfiguriert — nutze stattdessen einen eigenen API-Key unten.", + "adminFallback": "Alternativ nutzt HealthLog den vom Administrator konfigurierten KI-Anbieter.", + "personalProviderHeading": "KI-Provider (persönlich)", + "personalProviderBody": "Eigener KI-Anbieter überschreibt die Admin-Einstellung. Leer lassen für Standard. Für ChatGPT-Pro/Max-OAuth nutze den Verbinden-Button oben.", + "providerLabel": "Provider", + "providerOptionDefault": "— Standard (Admin/Codex) —", + "providerOptionOpenai": "OpenAI (API-Key)", + "providerOptionAnthropic": "Anthropic (Claude)", + "providerOptionLocal": "Lokal (OpenAI-kompatibel)", + "modelLabel": "Modell", + "modelOptionDefault": "— Standard —", + "modelOptionCustom": "Eigenes…", + "customModelLabel": "Eigener Modellname", + "openaiKeyLabel": "OpenAI API-Key", + "openaiKeyHelp": "Eigener Key — keine Verbindung zu deinem ChatGPT-Abo. Für das Abo nutze den Verbinden-Button oben.", + "anthropicKeyLabel": "Anthropic API-Key", + "baseUrlLabel": "Base URL", + "localKeyLabel": "API-Key (optional)", + "savedPreview": "(gespeichert {preview})", + "savedShort": "(gespeichert)", + "saveCta": "Speichern", + "testCta": "Verbindung testen", + "saved": "Gespeichert", + "saveFailed": "Speichern fehlgeschlagen", + "testFailed": "Test fehlgeschlagen", + "errorGeneric": "Fehler" + }, "withings": "Withings", "withingsDescription": "Verbinde deine Withings-Waage und Blutdruckmessgeräte.", "withingsCredentials": "API-Zugangsdaten", diff --git a/messages/en.json b/messages/en.json index fc32179f2..64a015e51 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1025,6 +1025,46 @@ "regenerateSuccess": "Reports regenerated successfully", "regenerateRateLimit": "Please wait — max 2 analyses per hour.", "lastGeneratedAt": "Last generated", + "ai": { + "title": "AI Insights", + "description": "Provider, model, key.", + "chatgptConnectedBadge": "ChatGPT connected", + "adminAiActiveBadge": "Admin AI active", + "connectionExpiredBadge": "Connection expired", + "chatgptConnectedHeading": "ChatGPT connected", + "chatgptConnectedBody": "Insights are generated through your ChatGPT subscription — no extra cost.", + "connectedSince": "Connected since {when}.", + "disconnect": "Disconnect", + "connectChatgptHeading": "Connect with ChatGPT", + "connectChatgptBody": "Link your ChatGPT Pro/Max account to get AI-powered health analyses based on current medical guidelines. No additional API costs.", + "connectChatgptCta": "Connect with ChatGPT", + "oauthNotConfigured": "ChatGPT OAuth is not configured on this instance — use your own API key below instead.", + "adminFallback": "Alternatively, HealthLog will use the AI provider configured by the administrator.", + "personalProviderHeading": "AI provider (personal)", + "personalProviderBody": "Your own AI provider overrides the admin setting. Leave blank for the default. For ChatGPT Pro/Max OAuth use the connect button above.", + "providerLabel": "Provider", + "providerOptionDefault": "— Default (Admin/Codex) —", + "providerOptionOpenai": "OpenAI (API key)", + "providerOptionAnthropic": "Anthropic (Claude)", + "providerOptionLocal": "Local (OpenAI-compatible)", + "modelLabel": "Model", + "modelOptionDefault": "— Default —", + "modelOptionCustom": "Custom…", + "customModelLabel": "Custom model name", + "openaiKeyLabel": "OpenAI API key", + "openaiKeyHelp": "Your own key — not connected to your ChatGPT subscription. For the subscription use the connect button above.", + "anthropicKeyLabel": "Anthropic API key", + "baseUrlLabel": "Base URL", + "localKeyLabel": "API key (optional)", + "savedPreview": "(saved {preview})", + "savedShort": "(saved)", + "saveCta": "Save", + "testCta": "Test connection", + "saved": "Saved", + "saveFailed": "Save failed", + "testFailed": "Test failed", + "errorGeneric": "Error" + }, "withings": "Withings", "withingsDescription": "Connect your Withings scale and blood pressure monitors.", "withingsCredentials": "API Credentials", diff --git a/src/components/settings/ai-section.tsx b/src/components/settings/ai-section.tsx index 096f1df19..fafb8727d 100644 --- a/src/components/settings/ai-section.tsx +++ b/src/components/settings/ai-section.tsx @@ -46,7 +46,11 @@ interface UserAIProvider { * the underlying API speaks (e.g. preview models, fine-tunes). */ const MODEL_PRESETS: Record> = { - OPENAI: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo", "gpt-5", "o3-mini"], + // P15: dropped `gpt-5` (not a released model — would surface as + // model_not_found) and `o3-mini` (uses the o-series param contract: + // `max_completion_tokens` instead of `max_tokens`, no `temperature` + // override — would 400 with the current OpenAIClient body shape). + OPENAI: ["gpt-4o", "gpt-4o-mini", "gpt-4-turbo"], ANTHROPIC: [ "claude-sonnet-4-6", "claude-opus-4-7", @@ -234,17 +238,17 @@ function InsightsSettingsCard({
{settings?.codexStatus === "connected" && ( - ChatGPT verbunden + {t("settings.ai.chatgptConnectedBadge")} )} {settings?.codexStatus !== "connected" && settings?.hasAdminKey && ( - Admin-KI aktiv + {t("settings.ai.adminAiActiveBadge")} )} {settings?.codexStatus === "expired" && ( - Verbindung abgelaufen + {t("settings.ai.connectionExpiredBadge")} )} {settings?.lastInsightAt && ( @@ -264,15 +268,17 @@ function InsightsSettingsCard({ {settings?.codexStatus === "connected" ? (
-

ChatGPT verbunden

+

+ {t("settings.ai.chatgptConnectedHeading")} +

- Insights werden über dein ChatGPT-Abo generiert — keine - zusätzlichen Kosten. + {t("settings.ai.chatgptConnectedBody")} {settings.codexConnectedAt && ( <> {" "} - Verbunden seit {formatDateTime(settings.codexConnectedAt)} - . + {t("settings.ai.connectedSince", { + when: formatDateTime(settings.codexConnectedAt), + })} )}

@@ -292,17 +298,17 @@ function InsightsSettingsCard({ ) : ( )} - Trennen + {t("settings.ai.disconnect")}
) : (
-

Mit ChatGPT verbinden

+

+ {t("settings.ai.connectChatgptHeading")} +

- Verbinde dein ChatGPT Pro/Max-Konto um KI-gestützte - Gesundheitsanalysen basierend auf aktuellen medizinischen - Leitlinien zu erhalten. Keine zusätzlichen API-Kosten. + {t("settings.ai.connectChatgptBody")}

{settings?.codexOauthConfigured ? ( @@ -312,18 +318,16 @@ function InsightsSettingsCard({ className="w-full sm:w-auto" > - Mit ChatGPT verbinden + {t("settings.ai.connectChatgptCta")} ) : (

- ChatGPT-OAuth ist auf dieser Instanz nicht konfiguriert — - nutze stattdessen einen eigenen API-Key unten. + {t("settings.ai.oauthNotConfigured")}

)} {settings?.hasAdminKey && (

- Alternativ nutzt HealthLog den vom Administrator - konfigurierten KI-Anbieter. + {t("settings.ai.adminFallback")}

)}
@@ -388,6 +392,7 @@ function InsightsSettingsCard({ } function UserAIProviderSubsection() { + const { t } = useTranslations(); const queryClient = useQueryClient(); const [provider, setProvider] = useState(""); const [modelChoice, setModelChoice] = useState(""); @@ -470,10 +475,10 @@ function UserAIProviderSubsection() { body: JSON.stringify(body), }); const json = await res.json(); - if (!res.ok) throw new Error(json.error || "Save failed"); + if (!res.ok) throw new Error(json.error || t("settings.ai.saveFailed")); }, onSuccess: () => { - setSaveMsg("Gespeichert"); + setSaveMsg(t("settings.ai.saved")); setSaveOk(true); setAnthropicKey(""); setLocalKey(""); @@ -482,7 +487,7 @@ function UserAIProviderSubsection() { queryClient.invalidateQueries({ queryKey: ["insights"] }); }, onError: (e) => { - setSaveMsg(e instanceof Error ? e.message : "Fehler"); + setSaveMsg(e instanceof Error ? e.message : t("settings.ai.errorGeneric")); setSaveOk(false); }, }); @@ -517,7 +522,9 @@ function UserAIProviderSubsection() { ); setTestOk(true); } catch (e) { - setTestMsg(e instanceof Error ? e.message : "Test fehlgeschlagen"); + setTestMsg( + e instanceof Error ? e.message : t("settings.ai.testFailed"), + ); setTestOk(false); } finally { setTesting(false); @@ -529,32 +536,36 @@ function UserAIProviderSubsection() { return (
-

KI-Provider (persönlich)

+

+ {t("settings.ai.personalProviderHeading")} +

- Eigener KI-Anbieter überschreibt die Admin-Einstellung. Leer lassen - für Standard. Für ChatGPT-Pro/Max-OAuth nutze den Verbinden-Button - oben. + {t("settings.ai.personalProviderBody")}

- +
- +
{modelChoice === CUSTOM_MODEL_SENTINEL && provider && (
- + @@ -611,8 +628,7 @@ function UserAIProviderSubsection() { className="mt-1" />

- Eigener Key — keine Verbindung zu deinem ChatGPT-Abo. Für das - Abo nutze den Verbinden-Button oben. + {t("settings.ai.openaiKeyHelp")}

)} @@ -620,10 +636,12 @@ function UserAIProviderSubsection() { {provider === "ANTHROPIC" && (
@@ -640,7 +658,9 @@ function UserAIProviderSubsection() { {provider === "LOCAL" && ( <>
- +
- + setLocalKey(e.target.value)} - placeholder={data?.hasLocalKey ? "(gespeichert)" : ""} + placeholder={ + data?.hasLocalKey ? t("settings.ai.savedShort") : "" + } className="mt-1" />
@@ -674,7 +698,7 @@ function UserAIProviderSubsection() { ) : ( )} - Speichern + {t("settings.ai.saveCta")}