feat(ai): improve AI settings first-time setup UX
- Add setup modal that appears when enabling AI without configured provider - Modal allows selecting provider (Anthropic, OpenAI, DeepSeek, Ollama) - Enter API key/URL and enable AI in one smooth flow - Reorder backend to apply API keys before enabled check - Fix Ollama to strip 'ollama:' prefix from model names - Simplify backend error message for unconfigured providers
This commit is contained in:
parent
fe20b2c55b
commit
43d658556b
3 changed files with 1085 additions and 885 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -95,6 +95,10 @@ func (c *OllamaClient) Chat(ctx context.Context, req ChatRequest) (*ChatResponse
|
||||||
|
|
||||||
// Use provided model or fall back to client default
|
// Use provided model or fall back to client default
|
||||||
model := req.Model
|
model := req.Model
|
||||||
|
// Strip "ollama:" prefix if present - callers may pass the full "provider:model" string
|
||||||
|
if strings.HasPrefix(model, "ollama:") {
|
||||||
|
model = strings.TrimPrefix(model, "ollama:")
|
||||||
|
}
|
||||||
if model == "" {
|
if model == "" {
|
||||||
model = c.model
|
model = c.model
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -359,27 +359,41 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt
|
||||||
settings.CustomContext = strings.TrimSpace(*req.CustomContext)
|
settings.CustomContext = strings.TrimSpace(*req.CustomContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle multi-provider credentials FIRST - before enabled check
|
||||||
|
// This allows the setup flow to send API key + enabled:true together
|
||||||
|
// 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.ClearOpenAIKey != nil && *req.ClearOpenAIKey {
|
||||||
|
settings.OpenAIAPIKey = ""
|
||||||
|
} else if req.OpenAIAPIKey != nil {
|
||||||
|
settings.OpenAIAPIKey = strings.TrimSpace(*req.OpenAIAPIKey)
|
||||||
|
}
|
||||||
|
if req.ClearDeepSeekKey != nil && *req.ClearDeepSeekKey {
|
||||||
|
settings.DeepSeekAPIKey = ""
|
||||||
|
} else if req.DeepSeekAPIKey != nil {
|
||||||
|
settings.DeepSeekAPIKey = strings.TrimSpace(*req.DeepSeekAPIKey)
|
||||||
|
}
|
||||||
|
if req.ClearOllamaURL != nil && *req.ClearOllamaURL {
|
||||||
|
settings.OllamaBaseURL = ""
|
||||||
|
} else if req.OllamaBaseURL != nil {
|
||||||
|
settings.OllamaBaseURL = strings.TrimSpace(*req.OllamaBaseURL)
|
||||||
|
}
|
||||||
|
if req.OpenAIBaseURL != nil {
|
||||||
|
settings.OpenAIBaseURL = strings.TrimSpace(*req.OpenAIBaseURL)
|
||||||
|
}
|
||||||
|
|
||||||
if req.Enabled != nil {
|
if req.Enabled != nil {
|
||||||
// Only allow enabling if at least one provider is configured
|
// Only allow enabling if at least one provider is configured
|
||||||
if *req.Enabled {
|
if *req.Enabled {
|
||||||
configuredProviders := settings.GetConfiguredProviders()
|
configuredProviders := settings.GetConfiguredProviders()
|
||||||
if len(configuredProviders) == 0 {
|
if len(configuredProviders) == 0 {
|
||||||
// Fall back to legacy validation for backwards compatibility
|
// No providers configured - give a helpful error
|
||||||
switch settings.Provider {
|
http.Error(w, "Please configure an AI provider (API key or Ollama URL) before enabling AI", http.StatusBadRequest)
|
||||||
case config.AIProviderAnthropic, config.AIProviderOpenAI, config.AIProviderDeepSeek:
|
return
|
||||||
if settings.APIKey == "" {
|
|
||||||
http.Error(w, "Cannot enable AI: configure at least one AI provider first", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case config.AIProviderOllama:
|
|
||||||
// Ollama doesn't need API key, but needs base URL (or will use default)
|
|
||||||
if settings.BaseURL == "" {
|
|
||||||
settings.BaseURL = config.DefaultOllamaBaseURL
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
http.Error(w, "Cannot enable AI: configure at least one AI provider first", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If we have configured providers, we're good to enable
|
// If we have configured providers, we're good to enable
|
||||||
}
|
}
|
||||||
|
|
@ -429,32 +443,6 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt
|
||||||
settings.AlertTriggeredAnalysis = *req.AlertTriggeredAnalysis
|
settings.AlertTriggeredAnalysis = *req.AlertTriggeredAnalysis
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle multi-provider credentials
|
|
||||||
// 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.ClearOpenAIKey != nil && *req.ClearOpenAIKey {
|
|
||||||
settings.OpenAIAPIKey = ""
|
|
||||||
} else if req.OpenAIAPIKey != nil {
|
|
||||||
settings.OpenAIAPIKey = strings.TrimSpace(*req.OpenAIAPIKey)
|
|
||||||
}
|
|
||||||
if req.ClearDeepSeekKey != nil && *req.ClearDeepSeekKey {
|
|
||||||
settings.DeepSeekAPIKey = ""
|
|
||||||
} else if req.DeepSeekAPIKey != nil {
|
|
||||||
settings.DeepSeekAPIKey = strings.TrimSpace(*req.DeepSeekAPIKey)
|
|
||||||
}
|
|
||||||
if req.ClearOllamaURL != nil && *req.ClearOllamaURL {
|
|
||||||
settings.OllamaBaseURL = ""
|
|
||||||
} else if req.OllamaBaseURL != nil {
|
|
||||||
settings.OllamaBaseURL = strings.TrimSpace(*req.OllamaBaseURL)
|
|
||||||
}
|
|
||||||
if req.OpenAIBaseURL != nil {
|
|
||||||
settings.OpenAIBaseURL = strings.TrimSpace(*req.OpenAIBaseURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save settings
|
// Save settings
|
||||||
if err := h.persistence.SaveAIConfig(*settings); err != nil {
|
if err := h.persistence.SaveAIConfig(*settings); err != nil {
|
||||||
log.Error().Err(err).Msg("Failed to save AI settings")
|
log.Error().Err(err).Msg("Failed to save AI settings")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue