diff --git a/AppImage/components/notification-settings.tsx b/AppImage/components/notification-settings.tsx index d0378bd8..5fc276e4 100644 --- a/AppImage/components/notification-settings.tsx +++ b/AppImage/components/notification-settings.tsx @@ -57,7 +57,7 @@ interface NotificationConfig { channel_overrides: Record ai_enabled: boolean ai_provider: string - ai_api_key: string + ai_api_keys: Record // Per-provider API keys ai_model: string ai_language: string ai_ollama_url: string @@ -208,7 +208,13 @@ const DEFAULT_CONFIG: NotificationConfig = { }, ai_enabled: false, ai_provider: "groq", - ai_api_key: "", + ai_api_keys: { + groq: "", + gemini: "", + anthropic: "", + openai: "", + openrouter: "", + }, ai_model: "", ai_language: "en", ai_ollama_url: "http://localhost:11434", @@ -261,8 +267,19 @@ export function NotificationSettings() { const data = await fetchApi<{ success: boolean; config: NotificationConfig }>("/api/notifications/settings") if (data.success && data.config) { // Backend automatically migrates deprecated AI models to current versions - setConfig(data.config) - setOriginalConfig(data.config) + // Ensure ai_api_keys object exists (fallback for older configs) + const configWithKeys = { + ...data.config, + ai_api_keys: data.config.ai_api_keys || { + groq: "", + gemini: "", + anthropic: "", + openai: "", + openrouter: "", + } + } + setConfig(configWithKeys) + setOriginalConfig(configWithKeys) } } catch (err) { console.error("Failed to load notification settings:", err) @@ -467,7 +484,6 @@ export function NotificationSettings() { enabled: String(cfg.enabled), ai_enabled: String(cfg.ai_enabled), ai_provider: cfg.ai_provider, - ai_api_key: cfg.ai_api_key, ai_model: cfg.ai_model, ai_language: cfg.ai_language, ai_ollama_url: cfg.ai_ollama_url, @@ -479,6 +495,14 @@ export function NotificationSettings() { pve_host: cfg.pve_host, pbs_trusted_sources: cfg.pbs_trusted_sources, } + // Flatten per-provider API keys + if (cfg.ai_api_keys) { + for (const [provider, key] of Object.entries(cfg.ai_api_keys)) { + if (key) { + flat[`ai_api_key_${provider}`] = key + } + } + } // Flatten channels: { telegram: { enabled, bot_token, chat_id } } -> telegram.enabled, telegram.bot_token, ... for (const [chName, chCfg] of Object.entries(cfg.channels)) { for (const [field, value] of Object.entries(chCfg)) { @@ -633,12 +657,18 @@ export function NotificationSettings() { setTestingAI(true) setAiTestResult(null) try { + // Get the API key for the current provider + const currentApiKey = config.ai_api_keys?.[config.ai_provider] || "" + // Get the model from provider config (for non-Ollama providers) or from config for Ollama + const providerConfig = AI_PROVIDERS.find(p => p.value === config.ai_provider) + const modelToUse = config.ai_provider === 'ollama' ? config.ai_model : (providerConfig?.model || config.ai_model) + const data = await fetchApi<{ success: boolean; message: string; model: string }>("/api/notifications/test-ai", { method: "POST", body: JSON.stringify({ provider: config.ai_provider, - api_key: config.ai_api_key, - model: config.ai_model, + api_key: currentApiKey, + model: modelToUse, ollama_url: config.ai_ollama_url, openai_base_url: config.ai_openai_base_url, }), @@ -1504,14 +1534,20 @@ export function NotificationSettings() { )}
- updateConfig(p => ({ ...p, ai_api_key: e.target.value }))} - disabled={!editMode} - /> + updateConfig(p => ({ + ...p, + ai_api_keys: { + ...p.ai_api_keys, + [p.ai_provider]: e.target.value + } + }))} + disabled={!editMode} + />