mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-29 12:00:13 +00:00
Major new AI capabilities for infrastructure monitoring: Investigation System: - Autonomous finding investigation with configurable autonomy levels - Investigation orchestrator with rate limiting and guardrails - Safety checks for read-only mode enforcement - Chat-based investigation with approval workflows Forecasting & Remediation: - Trend forecasting for resource capacity planning - Remediation engine for generating fix proposals - Circuit breaker for AI operation protection Unified Findings: - Unified store bridging alerts and AI findings - Correlation and root cause analysis - Incident coordinator with metrics recording New Frontend: - AI Intelligence page with patrol controls - Investigation drawer for finding details - Unified findings panel with actions Supporting Infrastructure: - Learning store for user preference tracking - Proxmox event ingestion and correlation - Enhanced patrol with investigation triggers
141 lines
4.5 KiB
Go
141 lines
4.5 KiB
Go
package providers
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
// NewFromConfig creates a Provider based on the AIConfig settings
|
|
// DEPRECATED: Use NewForModel or NewForProvider for multi-provider support
|
|
func NewFromConfig(cfg *config.AIConfig) (Provider, error) {
|
|
if cfg == nil {
|
|
return nil, fmt.Errorf("Pulse Assistant config is nil")
|
|
}
|
|
|
|
if !cfg.Enabled {
|
|
return nil, fmt.Errorf("Pulse Assistant is not enabled")
|
|
}
|
|
|
|
// Try multi-provider format first (uses per-provider API keys)
|
|
provider, model := config.ParseModelString(cfg.Model)
|
|
if providerClient, err := NewForProvider(cfg, provider, model); err == nil {
|
|
return providerClient, nil
|
|
}
|
|
|
|
// Get the configured timeout
|
|
timeout := cfg.GetRequestTimeout()
|
|
|
|
// Fall back to legacy single-provider format
|
|
switch cfg.Provider {
|
|
case config.AIProviderAnthropic:
|
|
// If we have an API key (from direct entry or OAuth-created), use regular client
|
|
if cfg.APIKey != "" {
|
|
return NewAnthropicClient(cfg.APIKey, cfg.GetModel(), timeout), nil
|
|
}
|
|
// Pro/Max users without org:create_api_key will use OAuth tokens directly
|
|
if cfg.IsUsingOAuth() && cfg.OAuthAccessToken != "" {
|
|
client := NewAnthropicOAuthClient(
|
|
cfg.OAuthAccessToken,
|
|
cfg.OAuthRefreshToken,
|
|
cfg.OAuthExpiresAt,
|
|
cfg.GetModel(),
|
|
timeout,
|
|
)
|
|
return client, nil
|
|
}
|
|
return nil, fmt.Errorf("Anthropic API key is required (or use OAuth login for Pro/Max subscription)")
|
|
|
|
case config.AIProviderOpenAI:
|
|
if cfg.APIKey == "" {
|
|
return nil, fmt.Errorf("OpenAI API key is required")
|
|
}
|
|
return NewOpenAIClient(cfg.APIKey, cfg.GetModel(), cfg.GetBaseURL(), timeout), nil
|
|
|
|
case config.AIProviderOllama:
|
|
return NewOllamaClient(cfg.GetModel(), cfg.GetBaseURL(), timeout), nil
|
|
|
|
case config.AIProviderDeepSeek:
|
|
if cfg.APIKey == "" {
|
|
return nil, fmt.Errorf("DeepSeek API key is required")
|
|
}
|
|
// DeepSeek uses OpenAI-compatible API
|
|
return NewOpenAIClient(cfg.APIKey, cfg.GetModel(), cfg.GetBaseURL(), timeout), nil
|
|
|
|
case config.AIProviderGemini:
|
|
if cfg.APIKey == "" {
|
|
return nil, fmt.Errorf("Gemini API key is required")
|
|
}
|
|
return NewGeminiClient(cfg.APIKey, cfg.GetModel(), cfg.GetBaseURL(), timeout), nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown provider: %s", cfg.Provider)
|
|
}
|
|
}
|
|
|
|
// NewForProvider creates a Provider for a specific provider using multi-provider credentials
|
|
func NewForProvider(cfg *config.AIConfig, provider, model string) (Provider, error) {
|
|
if cfg == nil {
|
|
return nil, fmt.Errorf("Pulse Assistant config is nil")
|
|
}
|
|
|
|
// Get the configured timeout
|
|
timeout := cfg.GetRequestTimeout()
|
|
|
|
switch provider {
|
|
case config.AIProviderAnthropic:
|
|
// Check for OAuth first
|
|
if cfg.IsUsingOAuth() && cfg.OAuthAccessToken != "" {
|
|
return NewAnthropicOAuthClient(
|
|
cfg.OAuthAccessToken,
|
|
cfg.OAuthRefreshToken,
|
|
cfg.OAuthExpiresAt,
|
|
model,
|
|
timeout,
|
|
), nil
|
|
}
|
|
// Then check for per-provider API key
|
|
apiKey := cfg.GetAPIKeyForProvider(config.AIProviderAnthropic)
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("Anthropic API key not configured")
|
|
}
|
|
return NewAnthropicClient(apiKey, model, timeout), nil
|
|
|
|
case config.AIProviderOpenAI:
|
|
apiKey := cfg.GetAPIKeyForProvider(config.AIProviderOpenAI)
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("OpenAI API key not configured")
|
|
}
|
|
baseURL := cfg.GetBaseURLForProvider(config.AIProviderOpenAI)
|
|
return NewOpenAIClient(apiKey, model, baseURL, timeout), nil
|
|
|
|
case config.AIProviderDeepSeek:
|
|
apiKey := cfg.GetAPIKeyForProvider(config.AIProviderDeepSeek)
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("DeepSeek API key not configured")
|
|
}
|
|
baseURL := cfg.GetBaseURLForProvider(config.AIProviderDeepSeek)
|
|
return NewOpenAIClient(apiKey, model, baseURL, timeout), nil
|
|
|
|
case config.AIProviderOllama:
|
|
baseURL := cfg.GetBaseURLForProvider(config.AIProviderOllama)
|
|
return NewOllamaClient(model, baseURL, timeout), nil
|
|
|
|
case config.AIProviderGemini:
|
|
apiKey := cfg.GetAPIKeyForProvider(config.AIProviderGemini)
|
|
if apiKey == "" {
|
|
return nil, fmt.Errorf("Gemini API key not configured")
|
|
}
|
|
baseURL := cfg.GetBaseURLForProvider(config.AIProviderGemini)
|
|
return NewGeminiClient(apiKey, model, baseURL, timeout), nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("unknown provider: %s", provider)
|
|
}
|
|
}
|
|
|
|
// NewForModel creates a Provider for a specific model, automatically detecting the provider
|
|
func NewForModel(cfg *config.AIConfig, modelString string) (Provider, error) {
|
|
provider, model := config.ParseModelString(modelString)
|
|
return NewForProvider(cfg, provider, model)
|
|
}
|