diff --git a/agent/provider.go b/agent/provider.go index 6ce4f8a..005f542 100644 --- a/agent/provider.go +++ b/agent/provider.go @@ -203,7 +203,9 @@ func compactionConfig(windowTokens int) *compaction.Config { // ARTEX_LLM_PROXY = proxy URL (optional; http/https/socks5) // ANTHROPIC_API_KEY / OPENAI_API_KEY = credentials (unused for ollama) func FromEnv() (Config, bool) { - prov := os.Getenv("ARTEX_LLM_PROVIDER") + // Case- and space-insensitive: "Ollama" used to miss every case below and + // fall through to anthropic, sending ANTHROPIC_API_KEY to the Ollama base URL. + prov := strings.ToLower(strings.TrimSpace(os.Getenv("ARTEX_LLM_PROVIDER"))) anthKey := os.Getenv("ANTHROPIC_API_KEY") oaiKey := os.Getenv("OPENAI_API_KEY") diff --git a/agent/provider_env_test.go b/agent/provider_env_test.go new file mode 100644 index 0000000..6d58003 --- /dev/null +++ b/agent/provider_env_test.go @@ -0,0 +1,37 @@ +package agent + +import ( + "testing" + + "github.com/Autumn-27/norma/llm" +) + +// ARTEX_LLM_PROVIDER is matched regardless of case and surrounding spaces. A +// miss used to fall through to anthropic: "Ollama" then sent ANTHROPIC_API_KEY +// to the Ollama base URL. +func TestFromEnvProviderIsCaseInsensitive(t *testing.T) { + t.Setenv("ANTHROPIC_API_KEY", "sk-ant-secret") + t.Setenv("OPENAI_API_KEY", "sk-oai") + t.Setenv("ARTEX_LLM_BASE_URL", "") + t.Setenv("ARTEX_LLM_MODEL", "") + for _, tc := range []struct { + env string + want llm.Format + key string + }{ + {"Ollama", FormatOllama, ""}, + {" OLLAMA ", FormatOllama, ""}, + {"OpenAI", llm.FormatOpenAI, "sk-oai"}, + {"OpenAI-Responses", llm.FormatOpenAIResponses, "sk-oai"}, + {"Anthropic", llm.FormatAnthropic, "sk-ant-secret"}, + } { + t.Setenv("ARTEX_LLM_PROVIDER", tc.env) + c, ok := FromEnv() + if !ok { + t.Fatalf("%q: FromEnv returned ok=false", tc.env) + } + if c.Format != tc.want || c.APIKey != tc.key { + t.Fatalf("%q: format=%v key=%q, want format=%v key=%q", tc.env, c.Format, c.APIKey, tc.want, tc.key) + } + } +}