summaryrefslogtreecommitdiff
path: root/internal/llmutils/client.go
blob: 16a633830cb070d79149b6fe97df3ff18231c165 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package llmutils

import (
	"os"
	"strings"

	"codeberg.org/snonux/hexai/internal/appconfig"
	"codeberg.org/snonux/hexai/internal/llm"
)

// CanonicalProvider normalizes provider names and defaults to openai.
func CanonicalProvider(name string) string {
	provider := strings.ToLower(strings.TrimSpace(name))
	if provider == "" {
		return "openai"
	}
	return provider
}

// DefaultModelForProvider returns the configured default model for a provider.
func DefaultModelForProvider(cfg appconfig.App, provider string) string {
	switch CanonicalProvider(provider) {
	case "openrouter":
		return strings.TrimSpace(cfg.OpenRouterModel)
	case "ollama":
		return strings.TrimSpace(cfg.OllamaModel)
	case "anthropic":
		return strings.TrimSpace(cfg.AnthropicModel)
	default:
		return strings.TrimSpace(cfg.OpenAIModel)
	}
}

// ConfigForProvider returns cfg adjusted for the selected provider/model.
func ConfigForProvider(cfg appconfig.App, provider, modelOverride string) appconfig.App {
	derived := cfg
	if strings.TrimSpace(provider) == "" {
		provider = cfg.Provider
	}
	normalized := CanonicalProvider(provider)
	derived.Provider = normalized
	model := strings.TrimSpace(modelOverride)
	if model == "" {
		return derived
	}
	switch normalized {
	case "openrouter":
		derived.OpenRouterModel = model
	case "ollama":
		derived.OllamaModel = model
	case "anthropic":
		derived.AnthropicModel = model
	default:
		derived.OpenAIModel = model
	}
	return derived
}

// NewClientFromAppForProvider builds a client for a specific provider/model.
func NewClientFromAppForProvider(cfg appconfig.App, provider, modelOverride string) (llm.Client, error) {
	return NewClientFromApp(ConfigForProvider(cfg, provider, modelOverride))
}

// NewClientFromApp builds an llm.Client using app config and environment keys.
func NewClientFromApp(cfg appconfig.App) (llm.Client, error) {
	llmCfg := llm.Config{
		Provider:              cfg.Provider,
		RequestTimeout:        cfg.RequestTimeout,
		OpenAIBaseURL:         cfg.OpenAIBaseURL,
		OpenAIModel:           cfg.OpenAIModel,
		OpenAITemperature:     cfg.OpenAITemperature,
		OpenRouterBaseURL:     cfg.OpenRouterBaseURL,
		OpenRouterModel:       cfg.OpenRouterModel,
		OpenRouterTemperature: cfg.OpenRouterTemperature,
		OllamaBaseURL:         cfg.OllamaBaseURL,
		OllamaModel:           cfg.OllamaModel,
		OllamaTemperature:     cfg.OllamaTemperature,
		AnthropicBaseURL:      cfg.AnthropicBaseURL,
		AnthropicModel:        cfg.AnthropicModel,
		AnthropicTemperature:  cfg.AnthropicTemperature,
	}
	oaKey := os.Getenv("HEXAI_OPENAI_API_KEY")
	if strings.TrimSpace(oaKey) == "" {
		oaKey = os.Getenv("OPENAI_API_KEY")
	}
	orKey := os.Getenv("HEXAI_OPENROUTER_API_KEY")
	if strings.TrimSpace(orKey) == "" {
		orKey = os.Getenv("OPENROUTER_API_KEY")
	}
	anKey := os.Getenv("HEXAI_ANTHROPIC_API_KEY")
	if strings.TrimSpace(anKey) == "" {
		anKey = os.Getenv("ANTHROPIC_API_KEY")
	}
	return llm.NewFromConfig(llmCfg, oaKey, orKey, anKey)
}