From d891405c9d40fe65b21ebd1f029209222c31d6fa Mon Sep 17 00:00:00 2001 From: boengai Date: Mon, 16 Feb 2026 11:53:04 +0700 Subject: [PATCH] fix: resolve provider env vars not being applied (#66) Replace unsupported {{.Name}} template syntax in ProviderConfig env tags with envPrefix on each provider field in ProvidersConfig, so that environment variables like PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY are correctly parsed by caarlos0/env. Co-Authored-By: Claude Opus 4.6 --- pkg/config/config.go | 36 ++++++++++++++++++------------------ pkg/config/config_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index da33748d0..ab35c069c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -167,27 +167,27 @@ type DevicesConfig struct { } type ProvidersConfig struct { - Anthropic ProviderConfig `json:"anthropic"` - OpenAI ProviderConfig `json:"openai"` - OpenRouter ProviderConfig `json:"openrouter"` - Groq ProviderConfig `json:"groq"` - Zhipu ProviderConfig `json:"zhipu"` - VLLM ProviderConfig `json:"vllm"` - Gemini ProviderConfig `json:"gemini"` - Nvidia ProviderConfig `json:"nvidia"` - Ollama ProviderConfig `json:"ollama"` - Moonshot ProviderConfig `json:"moonshot"` - ShengSuanYun ProviderConfig `json:"shengsuanyun"` - DeepSeek ProviderConfig `json:"deepseek"` - GitHubCopilot ProviderConfig `json:"github_copilot"` + Anthropic ProviderConfig `json:"anthropic" envPrefix:"PICOCLAW_PROVIDERS_ANTHROPIC_"` + OpenAI ProviderConfig `json:"openai" envPrefix:"PICOCLAW_PROVIDERS_OPENAI_"` + OpenRouter ProviderConfig `json:"openrouter" envPrefix:"PICOCLAW_PROVIDERS_OPENROUTER_"` + Groq ProviderConfig `json:"groq" envPrefix:"PICOCLAW_PROVIDERS_GROQ_"` + Zhipu ProviderConfig `json:"zhipu" envPrefix:"PICOCLAW_PROVIDERS_ZHIPU_"` + VLLM ProviderConfig `json:"vllm" envPrefix:"PICOCLAW_PROVIDERS_VLLM_"` + Gemini ProviderConfig `json:"gemini" envPrefix:"PICOCLAW_PROVIDERS_GEMINI_"` + Nvidia ProviderConfig `json:"nvidia" envPrefix:"PICOCLAW_PROVIDERS_NVIDIA_"` + Ollama ProviderConfig `json:"ollama" envPrefix:"PICOCLAW_PROVIDERS_OLLAMA_"` + Moonshot ProviderConfig `json:"moonshot" envPrefix:"PICOCLAW_PROVIDERS_MOONSHOT_"` + ShengSuanYun ProviderConfig `json:"shengsuanyun" envPrefix:"PICOCLAW_PROVIDERS_SHENGSUANYUN_"` + DeepSeek ProviderConfig `json:"deepseek" envPrefix:"PICOCLAW_PROVIDERS_DEEPSEEK_"` + GitHubCopilot ProviderConfig `json:"github_copilot" envPrefix:"PICOCLAW_PROVIDERS_GITHUB_COPILOT_"` } type ProviderConfig struct { - APIKey string `json:"api_key" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_KEY"` - APIBase string `json:"api_base" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_BASE"` - Proxy string `json:"proxy,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_PROXY"` - AuthMethod string `json:"auth_method,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_AUTH_METHOD"` - ConnectMode string `json:"connect_mode,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_CONNECT_MODE"` //only for Github Copilot, `stdio` or `grpc` + APIKey string `json:"api_key" env:"API_KEY"` + APIBase string `json:"api_base" env:"API_BASE"` + Proxy string `json:"proxy,omitempty" env:"PROXY"` + AuthMethod string `json:"auth_method,omitempty" env:"AUTH_METHOD"` + ConnectMode string `json:"connect_mode,omitempty" env:"CONNECT_MODE"` //only for Github Copilot, `stdio` or `grpc` } type GatewayConfig struct { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 14618b109..a7e27117c 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -2,6 +2,8 @@ package config import ( "testing" + + "github.com/caarlos0/env/v11" ) // TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default @@ -147,6 +149,32 @@ func TestDefaultConfig_WebTools(t *testing.T) { } } +// TestProviderConfig_EnvVars verifies provider env vars are correctly parsed +func TestProviderConfig_EnvVars(t *testing.T) { + t.Setenv("PICOCLAW_PROVIDERS_ANTHROPIC_API_KEY", "test-anthropic-key") + t.Setenv("PICOCLAW_PROVIDERS_OPENAI_API_KEY", "test-openai-key") + t.Setenv("PICOCLAW_PROVIDERS_OPENAI_API_BASE", "https://custom.openai.example.com") + t.Setenv("PICOCLAW_PROVIDERS_GITHUB_COPILOT_CONNECT_MODE", "stdio") + + cfg := DefaultConfig() + if err := env.Parse(cfg); err != nil { + t.Fatalf("env.Parse failed: %v", err) + } + + if cfg.Providers.Anthropic.APIKey != "test-anthropic-key" { + t.Errorf("Anthropic API key: got %q, want %q", cfg.Providers.Anthropic.APIKey, "test-anthropic-key") + } + if cfg.Providers.OpenAI.APIKey != "test-openai-key" { + t.Errorf("OpenAI API key: got %q, want %q", cfg.Providers.OpenAI.APIKey, "test-openai-key") + } + if cfg.Providers.OpenAI.APIBase != "https://custom.openai.example.com" { + t.Errorf("OpenAI API base: got %q, want %q", cfg.Providers.OpenAI.APIBase, "https://custom.openai.example.com") + } + if cfg.Providers.GitHubCopilot.ConnectMode != "stdio" { + t.Errorf("GitHub Copilot connect mode: got %q, want %q", cfg.Providers.GitHubCopilot.ConnectMode, "stdio") + } +} + // TestConfig_Complete verifies all config fields are set func TestConfig_Complete(t *testing.T) { cfg := DefaultConfig()