diff --git a/frontend-modern/src/components/Settings/AISettings.tsx b/frontend-modern/src/components/Settings/AISettings.tsx index ebe7bdb..698c9f0 100644 --- a/frontend-modern/src/components/Settings/AISettings.tsx +++ b/frontend-modern/src/components/Settings/AISettings.tsx @@ -79,6 +79,7 @@ export const AISettings: Component = () => { model: '', chatModel: '', // Empty means use default model patrolModel: '', // Empty means use default model + autoFixModel: '', // Empty means use patrol model baseUrl: '', // Legacy - kept for compatibility clearApiKey: false, autonomousMode: false, @@ -105,6 +106,7 @@ export const AISettings: Component = () => { model: '', // Will be set when provider is configured chatModel: '', patrolModel: '', + autoFixModel: '', baseUrl: '', clearApiKey: false, autonomousMode: false, @@ -130,6 +132,7 @@ export const AISettings: Component = () => { model: data.model || '', // User must select a model chatModel: data.chat_model || '', patrolModel: data.patrol_model || '', + autoFixModel: data.auto_fix_model || '', baseUrl: data.base_url || '', clearApiKey: false, autonomousMode: data.autonomous_mode || false, @@ -280,6 +283,10 @@ export const AISettings: Component = () => { payload.patrol_model = form.patrolModel; } + if (form.autoFixModel !== (settings()?.auto_fix_model || '')) { + payload.auto_fix_model = form.autoFixModel; + } + // Include multi-provider credentials if set (non-empty) if (form.anthropicApiKey.trim()) { payload.anthropic_api_key = form.anthropicApiKey.trim(); @@ -607,6 +614,48 @@ export const AISettings: Component = () => {

+ {/* Auto-Fix Model Override */} + +
+ + 0} fallback={ + setForm('autoFixModel', e.currentTarget.value)} + placeholder="Leave empty to use patrol model" + class={controlClass()} + disabled={saving()} + /> + }> + + +

+ Model for automatic remediation actions. Consider a more capable model since it takes action. +

+
+
+ {/* AI Provider Configuration - Configure API keys for all providers */}
diff --git a/frontend-modern/src/types/ai.ts b/frontend-modern/src/types/ai.ts index d73cde2..22a2f63 100644 --- a/frontend-modern/src/types/ai.ts +++ b/frontend-modern/src/types/ai.ts @@ -17,6 +17,7 @@ export interface AISettings { model: string; chat_model?: string; // Model for interactive chat (empty = use default) patrol_model?: string; // Model for background patrol (empty = use default) + auto_fix_model?: string; // Model for auto-fix remediation (empty = use patrol model) base_url?: string; // DEPRECATED: legacy base URL configured: boolean; // true if AI is ready to use autonomous_mode: boolean; // true if AI can execute commands without approval @@ -55,6 +56,7 @@ export interface AISettingsUpdateRequest { // Model overrides for different use cases chat_model?: string; // Model for interactive chat patrol_model?: string; // Model for background patrol + auto_fix_model?: string; // Model for auto-fix remediation // Patrol settings for token efficiency patrol_schedule_preset?: string; // DEPRECATED: use patrol_interval_minutes patrol_interval_minutes?: number; // Custom interval in minutes (0 = disabled, minimum 10) diff --git a/internal/api/ai_handlers.go b/internal/api/ai_handlers.go index febeae4..611856c 100644 --- a/internal/api/ai_handlers.go +++ b/internal/api/ai_handlers.go @@ -144,6 +144,7 @@ type AISettingsResponse struct { Model string `json:"model"` ChatModel string `json:"chat_model,omitempty"` // Model for interactive chat (empty = use default) PatrolModel string `json:"patrol_model,omitempty"` // Model for patrol (empty = use default) + AutoFixModel string `json:"auto_fix_model,omitempty"` // Model for auto-fix (empty = use patrol model) BaseURL string `json:"base_url,omitempty"` // DEPRECATED: legacy base URL Configured bool `json:"configured"` // true if AI is ready to use AutonomousMode bool `json:"autonomous_mode"` // true if AI can execute without approval @@ -154,6 +155,7 @@ type AISettingsResponse struct { // Patrol settings for token efficiency PatrolSchedulePreset string `json:"patrol_schedule_preset"` // DEPRECATED: legacy preset PatrolIntervalMinutes int `json:"patrol_interval_minutes"` // Patrol interval in minutes (0 = disabled) + PatrolAutoFix bool `json:"patrol_auto_fix"` // true if patrol can auto-fix issues AlertTriggeredAnalysis bool `json:"alert_triggered_analysis"` // true if AI analyzes when alerts fire AvailableModels []config.ModelInfo `json:"available_models"` // List of models for current provider // Multi-provider credentials - shows which providers are configured @@ -176,6 +178,7 @@ type AISettingsUpdateRequest struct { Model *string `json:"model,omitempty"` ChatModel *string `json:"chat_model,omitempty"` // Model for interactive chat PatrolModel *string `json:"patrol_model,omitempty"` // Model for background patrol + AutoFixModel *string `json:"auto_fix_model,omitempty"` // Model for auto-fix remediation BaseURL *string `json:"base_url,omitempty"` // DEPRECATED: use per-provider URLs AutonomousMode *bool `json:"autonomous_mode,omitempty"` CustomContext *string `json:"custom_context,omitempty"` // user-provided infrastructure context @@ -183,6 +186,7 @@ type AISettingsUpdateRequest struct { // Patrol settings for token efficiency PatrolSchedulePreset *string `json:"patrol_schedule_preset,omitempty"` // DEPRECATED: use patrol_interval_minutes PatrolIntervalMinutes *int `json:"patrol_interval_minutes,omitempty"` // Custom interval in minutes (0 = disabled, minimum 10) + PatrolAutoFix *bool `json:"patrol_auto_fix,omitempty"` // true if patrol can auto-fix issues AlertTriggeredAnalysis *bool `json:"alert_triggered_analysis,omitempty"` // true if AI analyzes when alerts fire // Multi-provider credentials AnthropicAPIKey *string `json:"anthropic_api_key,omitempty"` // Set Anthropic API key @@ -230,6 +234,7 @@ func (h *AISettingsHandler) HandleGetAISettings(w http.ResponseWriter, r *http.R Model: settings.GetModel(), ChatModel: settings.ChatModel, PatrolModel: settings.PatrolModel, + AutoFixModel: settings.AutoFixModel, BaseURL: settings.BaseURL, Configured: settings.IsConfigured(), AutonomousMode: settings.AutonomousMode, @@ -239,6 +244,7 @@ func (h *AISettingsHandler) HandleGetAISettings(w http.ResponseWriter, r *http.R // Patrol settings PatrolSchedulePreset: settings.PatrolSchedulePreset, PatrolIntervalMinutes: settings.PatrolIntervalMinutes, + PatrolAutoFix: settings.PatrolAutoFix, AlertTriggeredAnalysis: settings.AlertTriggeredAnalysis, AvailableModels: nil, // Now populated via /api/ai/models endpoint // Multi-provider configuration @@ -332,6 +338,14 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt settings.PatrolModel = strings.TrimSpace(*req.PatrolModel) } + if req.AutoFixModel != nil { + settings.AutoFixModel = strings.TrimSpace(*req.AutoFixModel) + } + + if req.PatrolAutoFix != nil { + settings.PatrolAutoFix = *req.PatrolAutoFix + } + if req.BaseURL != nil { settings.BaseURL = strings.TrimSpace(*req.BaseURL) } @@ -474,6 +488,7 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt Model: settings.GetModel(), ChatModel: settings.ChatModel, PatrolModel: settings.PatrolModel, + AutoFixModel: settings.AutoFixModel, BaseURL: settings.BaseURL, Configured: settings.IsConfigured(), AutonomousMode: settings.AutonomousMode, @@ -482,6 +497,7 @@ func (h *AISettingsHandler) HandleUpdateAISettings(w http.ResponseWriter, r *htt OAuthConnected: settings.OAuthAccessToken != "", PatrolSchedulePreset: settings.PatrolSchedulePreset, PatrolIntervalMinutes: settings.PatrolIntervalMinutes, + PatrolAutoFix: settings.PatrolAutoFix, AlertTriggeredAnalysis: settings.AlertTriggeredAnalysis, AvailableModels: nil, // Now populated via /api/ai/models endpoint // Multi-provider configuration diff --git a/internal/config/ai.go b/internal/config/ai.go index 7ee1018..3698973 100644 --- a/internal/config/ai.go +++ b/internal/config/ai.go @@ -47,6 +47,7 @@ type AIConfig struct { PatrolAnalyzeDocker bool `json:"patrol_analyze_docker,omitempty"` // Include Docker hosts in patrol PatrolAnalyzeStorage bool `json:"patrol_analyze_storage,omitempty"` // Include storage in patrol PatrolAutoFix bool `json:"patrol_auto_fix,omitempty"` // When true, patrol can attempt automatic remediation (default: false, observe only) + AutoFixModel string `json:"auto_fix_model,omitempty"` // Model for automatic remediation (defaults to PatrolModel, may want more capable model) // Alert-triggered AI analysis - analyze specific resources when alerts fire AlertTriggeredAnalysis bool `json:"alert_triggered_analysis,omitempty"` // Enable AI analysis when alerts fire (token-efficient) @@ -317,6 +318,16 @@ func (c *AIConfig) GetPatrolModel() string { return c.GetModel() } +// GetAutoFixModel returns the model for automatic remediation actions +// Falls back to PatrolModel, then to the main Model if AutoFixModel is not set +// Auto-fix may warrant a more capable model since it takes actions +func (c *AIConfig) GetAutoFixModel() string { + if c.AutoFixModel != "" { + return c.AutoFixModel + } + return c.GetPatrolModel() +} + // ClearOAuthTokens clears OAuth tokens (used when switching back to API key auth) func (c *AIConfig) ClearOAuthTokens() { c.OAuthAccessToken = ""