package pipeline import ( "fmt" "log/slog" "textmachine/backend/internal/config" "textmachine/backend/internal/llm" ) // clients.go builds LLMClients from models.yaml. One client per model name // (a model carries its ExtraBody), lazily cached by the runner. // BuildClient constructs the adapter for one configured model. func BuildClient(models *config.Models, modelName string, logger *slog.Logger) (llm.LLMClient, error) { mod, ok := models.Models[modelName] if !ok { return nil, fmt.Errorf("pipeline: model %q is not defined in models.yaml", modelName) } prov := models.Providers[mod.Provider] // существование проверено fail-fast валидацией switch prov.Kind { case "openai": return llm.NewOpenAICompatClient(llm.OpenAICompatConfig{ Name: mod.Provider, BaseURL: prov.BaseURL, APIKey: prov.APIKey(), Profile: prov.Timeouts.Profile(), Reasoning: llm.ReasoningSemantics(prov.Reasoning), ExtraBody: mod.ExtraBody, }, logger), nil case "anthropic": return llm.NewAnthropicClient(llm.AnthropicConfig{ BaseURL: prov.BaseURL, APIKey: prov.APIKey(), Profile: prov.Timeouts.Profile(), CacheTTL: prov.CacheTTL, }, logger), nil case "local": return llm.NewLocalClient(llm.LocalConfig{ BaseURL: prov.BaseURL, APIKey: prov.APIKey(), Model: prov.Model, MaxTokens: prov.MaxTokens, Profile: prov.Timeouts.Profile(), }, logger), nil default: return nil, fmt.Errorf("pipeline: provider %q has unknown kind %q", mod.Provider, prov.Kind) } }