package llm import ( "context" "fmt" "log/slog" "github.com/enmanuel/agents/internal/config" coretypes "github.com/enmanuel/agents/pkg/llm" ) // FromConfig builds a CompleteFunc from an LLMProviderCfg. func FromConfig(cfg config.LLMProviderCfg, log *slog.Logger) (coretypes.CompleteFunc, error) { log.Info("llm_provider_init", "provider", cfg.Provider, "model", cfg.Model) switch cfg.Provider { case "anthropic": return NewAnthropicComplete(cfg.APIKeyEnv, cfg.BaseURL, log), nil case "openai": return NewOpenAIComplete(cfg.APIKeyEnv, cfg.BaseURL, log), nil case "ollama": base := cfg.BaseURL if base == "" { base = "http://localhost:11434/v1" } return NewOpenAIComplete("OLLAMA_API_KEY", base, log), nil case "claude-code": return NewClaudeCodeComplete(cfg.ClaudeCode, log), nil default: return nil, fmt.Errorf("unknown LLM provider: %s", cfg.Provider) } } // WithFallback wraps primary with a fallback CompleteFunc. // If primary returns an error, fallback is tried with the fallback config's model. func WithFallback(primary, fallback coretypes.CompleteFunc, fallbackCfg config.LLMProviderCfg, log *slog.Logger) coretypes.CompleteFunc { return func(ctx context.Context, req coretypes.CompletionRequest) (coretypes.CompletionResponse, error) { resp, err := primary(ctx, req) if err != nil { log.Warn("llm_fallback_triggered", "primary_err", err) // Override request fields with fallback config values if fallbackCfg.Model != "" { req.Model = fallbackCfg.Model } if fallbackCfg.MaxTokens > 0 { req.MaxTokens = fallbackCfg.MaxTokens } return fallback(ctx, req) } return resp, nil } }