textmachine/backend/internal/pipeline/clients.go

53 lines
1.7 KiB
Go

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),
Cap: models.ResolveCapability(modelName),
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,
Temperature: prov.Temperature,
MaxTokens: prov.MaxTokens,
Profile: prov.Timeouts.Profile(),
Cap: models.ResolveCapability(modelName),
}, logger), nil
default:
return nil, fmt.Errorf("pipeline: provider %q has unknown kind %q", mod.Provider, prov.Kind)
}
}