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 {