diff --git a/frontend-modern/src/components/Settings/AISettings.tsx b/frontend-modern/src/components/Settings/AISettings.tsx index 0cb3d6a..bdb4e06 100644 --- a/frontend-modern/src/components/Settings/AISettings.tsx +++ b/frontend-modern/src/components/Settings/AISettings.tsx @@ -341,6 +341,41 @@ export const AISettings: Component = () => { } }; + const handleClearProvider = async (provider: string) => { + if (!confirm(`Clear ${provider} credentials? You'll need to re-enter them to use this provider.`)) { + return; + } + + setSaving(true); + try { + const clearPayload: Record = {}; + if (provider === 'anthropic') clearPayload.clear_anthropic_key = true; + if (provider === 'openai') clearPayload.clear_openai_key = true; + if (provider === 'deepseek') clearPayload.clear_deepseek_key = true; + if (provider === 'ollama') clearPayload.clear_ollama_url = true; + + await AIAPI.updateSettings(clearPayload); + + // Reload settings to reflect the change + const newSettings = await AIAPI.getSettings(); + setSettings(newSettings); + + // Clear the local form field + if (provider === 'anthropic') setForm('anthropicApiKey', ''); + if (provider === 'openai') setForm('openaiApiKey', ''); + if (provider === 'deepseek') setForm('deepseekApiKey', ''); + if (provider === 'ollama') setForm('ollamaBaseUrl', ''); + + notificationStore.success(`${provider} credentials cleared`); + } catch (error) { + logger.error(`[AISettings] Clear ${provider} failed:`, error); + const message = error instanceof Error ? error.message : 'Failed to clear credentials'; + notificationStore.error(message); + } finally { + setSaving(false); + } + }; + // OAuth handlers removed - OAuth is currently unavailable from Anthropic for third-party apps // When OAuth becomes available, handlers can be added back to the Anthropic accordion section @@ -589,14 +624,25 @@ export const AISettings: Component = () => { Get API key โ†’

- +
+ + +
@@ -654,14 +700,25 @@ export const AISettings: Component = () => { Get API key โ†’

- +
+ + +
@@ -711,14 +768,25 @@ export const AISettings: Component = () => { Get API key โ†’

- +
+ + +
@@ -769,14 +837,25 @@ export const AISettings: Component = () => { ยท Free & local

- +
+ + +
diff --git a/frontend-modern/src/types/ai.ts b/frontend-modern/src/types/ai.ts index 6782690..60adffe 100644 --- a/frontend-modern/src/types/ai.ts +++ b/frontend-modern/src/types/ai.ts @@ -61,6 +61,11 @@ export interface AISettingsUpdateRequest { deepseek_api_key?: string; // Set DeepSeek API key ollama_base_url?: string; // Set Ollama server URL openai_base_url?: string; // Set custom OpenAI base URL + // Clear flags for removing credentials + clear_anthropic_key?: boolean; // Clear Anthropic API key + clear_openai_key?: boolean; // Clear OpenAI API key + clear_deepseek_key?: boolean; // Clear DeepSeek API key + clear_ollama_url?: boolean; // Clear Ollama URL } diff --git a/internal/api/ai_handlers.go b/internal/api/ai_handlers.go index 2a7c4f8..a5e254d 100644 --- a/internal/api/ai_handlers.go +++ b/internal/api/ai_handlers.go @@ -152,6 +152,11 @@ type AISettingsUpdateRequest struct { DeepSeekAPIKey *string `json:"deepseek_api_key,omitempty"` // Set DeepSeek API key OllamaBaseURL *string `json:"ollama_base_url,omitempty"` // Set Ollama server URL OpenAIBaseURL *string `json:"openai_base_url,omitempty"` // Set custom OpenAI base URL + // Clear flags for removing credentials + ClearAnthropicKey *bool `json:"clear_anthropic_key,omitempty"` // Clear Anthropic API key + ClearOpenAIKey *bool `json:"clear_openai_key,omitempty"` // Clear OpenAI API key + ClearDeepSeekKey *bool `json:"clear_deepseek_key,omitempty"` // Clear DeepSeek API key + ClearOllamaURL *bool `json:"clear_ollama_url,omitempty"` // Clear Ollama URL } // HandleGetAISettings returns the current AI settings (GET /api/settings/ai) @@ -335,16 +340,25 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt } // Handle multi-provider credentials - if req.AnthropicAPIKey != nil { + // Clear flags take priority over setting new values + if req.ClearAnthropicKey != nil && *req.ClearAnthropicKey { + settings.AnthropicAPIKey = "" + } else if req.AnthropicAPIKey != nil { settings.AnthropicAPIKey = strings.TrimSpace(*req.AnthropicAPIKey) } - if req.OpenAIAPIKey != nil { + if req.ClearOpenAIKey != nil && *req.ClearOpenAIKey { + settings.OpenAIAPIKey = "" + } else if req.OpenAIAPIKey != nil { settings.OpenAIAPIKey = strings.TrimSpace(*req.OpenAIAPIKey) } - if req.DeepSeekAPIKey != nil { + if req.ClearDeepSeekKey != nil && *req.ClearDeepSeekKey { + settings.DeepSeekAPIKey = "" + } else if req.DeepSeekAPIKey != nil { settings.DeepSeekAPIKey = strings.TrimSpace(*req.DeepSeekAPIKey) } - if req.OllamaBaseURL != nil { + if req.ClearOllamaURL != nil && *req.ClearOllamaURL { + settings.OllamaBaseURL = "" + } else if req.OllamaBaseURL != nil { settings.OllamaBaseURL = strings.TrimSpace(*req.OllamaBaseURL) } if req.OpenAIBaseURL != nil {