summaryrefslogtreecommitdiff
path: root/internal/llm/provider.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/llm/provider.go')
-rw-r--r--internal/llm/provider.go29
1 files changed, 19 insertions, 10 deletions
diff --git a/internal/llm/provider.go b/internal/llm/provider.go
index 3c72181..6c0c04b 100644
--- a/internal/llm/provider.go
+++ b/internal/llm/provider.go
@@ -103,20 +103,17 @@ type ProviderKeys struct {
// ProviderFactory builds an LLM client for a named provider.
type ProviderFactory func(cfg Config, keys ProviderKeys) (Client, error)
-// providerRegistry is a package-level singleton populated by init() calls in
-// each provider file (anthropic.go, openai.go, etc.). It must be a
-// package-level var — rather than a constructor argument — because Go's
-// init() mechanism runs before any application code, and the alternative
-// (an explicit RegisterAll() in main) would require every binary that uses
-// the llm package to manually enumerate all providers. The RWMutex makes the
-// map safe for the rare case where RegisterProvider is called from a test
-// goroutine after init() has completed.
+// providerRegistry is a package-level singleton populated via RegisterAllProviders.
+// Callers (binaries and tests) must call RegisterAllProviders before creating any
+// clients. The RWMutex makes the map safe for concurrent reads once populated.
var (
- providerRegistryMu sync.RWMutex
- providerRegistry = map[string]ProviderFactory{}
+ providerRegistryMu sync.RWMutex
+ providerRegistry = map[string]ProviderFactory{}
+ registerProvidersOnce sync.Once
)
// RegisterProvider registers a provider factory by normalized name.
+// Panics on empty name, nil factory, or duplicate registration.
func RegisterProvider(name string, factory ProviderFactory) {
normalized := normalizeProvider(name)
if normalized == "" {
@@ -133,6 +130,18 @@ func RegisterProvider(name string, factory ProviderFactory) {
providerRegistry[normalized] = factory
}
+// RegisterAllProviders registers all built-in LLM providers (anthropic, openai,
+// openrouter, ollama). It is safe to call from multiple entry points because the
+// actual registration runs only once via sync.Once.
+func RegisterAllProviders() {
+ registerProvidersOnce.Do(func() {
+ RegisterProvider("anthropic", anthropicProviderFactory)
+ RegisterProvider("openai", openAIProviderFactory)
+ RegisterProvider("openrouter", openRouterProviderFactory)
+ RegisterProvider("ollama", ollamaProviderFactory)
+ })
+}
+
// NewFromConfig creates an LLM client using only the supplied configuration.
// The OpenAI API key is supplied separately and may be read from the environment
// by the caller; other environment-based configuration is not used.