summaryrefslogtreecommitdiff
path: root/internal/llm/provider.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-23 09:04:17 +0200
committerPaul Buetow <paul@buetow.org>2026-03-23 09:04:17 +0200
commit462184dff3eef32f01f06634305da1355ac1bec2 (patch)
tree026ffaaeacfe152957298c985e1df77ff661b723 /internal/llm/provider.go
parent667f2d3384643aa877de2eefcbad3923965bad09 (diff)
chore: bump version to v0.25.9v0.25.9
Code quality fixes from audit: - Log silently discarded errors in status sinks and stats.Update call sites - Fix json.Marshal errors silently ignored in LSP handlers - Replace time.Sleep in tests with channel signaling (mcp) and fake clock (stats) - Make context cancellation work in production time.Sleep sites (handlers_document, cmdentry) - Remove init()-based provider registration from llm package; use explicit RegisterAllProviders() - Add WaitGroup goroutine tracking to MCP server Run() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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.