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 <noreply@anthropic.com>
This commit is contained in:
boengai 2026-02-16 11:53:04 +07:00
parent 341dbd3007
commit d891405c9d
2 changed files with 46 additions and 18 deletions

View file

@ -167,27 +167,27 @@ type DevicesConfig struct {
} }
type ProvidersConfig struct { type ProvidersConfig struct {
Anthropic ProviderConfig `json:"anthropic"` Anthropic ProviderConfig `json:"anthropic" envPrefix:"PICOCLAW_PROVIDERS_ANTHROPIC_"`
OpenAI ProviderConfig `json:"openai"` OpenAI ProviderConfig `json:"openai" envPrefix:"PICOCLAW_PROVIDERS_OPENAI_"`
OpenRouter ProviderConfig `json:"openrouter"` OpenRouter ProviderConfig `json:"openrouter" envPrefix:"PICOCLAW_PROVIDERS_OPENROUTER_"`
Groq ProviderConfig `json:"groq"` Groq ProviderConfig `json:"groq" envPrefix:"PICOCLAW_PROVIDERS_GROQ_"`
Zhipu ProviderConfig `json:"zhipu"` Zhipu ProviderConfig `json:"zhipu" envPrefix:"PICOCLAW_PROVIDERS_ZHIPU_"`
VLLM ProviderConfig `json:"vllm"` VLLM ProviderConfig `json:"vllm" envPrefix:"PICOCLAW_PROVIDERS_VLLM_"`
Gemini ProviderConfig `json:"gemini"` Gemini ProviderConfig `json:"gemini" envPrefix:"PICOCLAW_PROVIDERS_GEMINI_"`
Nvidia ProviderConfig `json:"nvidia"` Nvidia ProviderConfig `json:"nvidia" envPrefix:"PICOCLAW_PROVIDERS_NVIDIA_"`
Ollama ProviderConfig `json:"ollama"` Ollama ProviderConfig `json:"ollama" envPrefix:"PICOCLAW_PROVIDERS_OLLAMA_"`
Moonshot ProviderConfig `json:"moonshot"` Moonshot ProviderConfig `json:"moonshot" envPrefix:"PICOCLAW_PROVIDERS_MOONSHOT_"`
ShengSuanYun ProviderConfig `json:"shengsuanyun"` ShengSuanYun ProviderConfig `json:"shengsuanyun" envPrefix:"PICOCLAW_PROVIDERS_SHENGSUANYUN_"`
DeepSeek ProviderConfig `json:"deepseek"` DeepSeek ProviderConfig `json:"deepseek" envPrefix:"PICOCLAW_PROVIDERS_DEEPSEEK_"`
GitHubCopilot ProviderConfig `json:"github_copilot"` GitHubCopilot ProviderConfig `json:"github_copilot" envPrefix:"PICOCLAW_PROVIDERS_GITHUB_COPILOT_"`
} }
type ProviderConfig struct { type ProviderConfig struct {
APIKey string `json:"api_key" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_KEY"` APIKey string `json:"api_key" env:"API_KEY"`
APIBase string `json:"api_base" env:"PICOCLAW_PROVIDERS_{{.Name}}_API_BASE"` APIBase string `json:"api_base" env:"API_BASE"`
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_PROXY"` Proxy string `json:"proxy,omitempty" env:"PROXY"`
AuthMethod string `json:"auth_method,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_AUTH_METHOD"` AuthMethod string `json:"auth_method,omitempty" env:"AUTH_METHOD"`
ConnectMode string `json:"connect_mode,omitempty" env:"PICOCLAW_PROVIDERS_{{.Name}}_CONNECT_MODE"` //only for Github Copilot, `stdio` or `grpc` ConnectMode string `json:"connect_mode,omitempty" env:"CONNECT_MODE"` //only for Github Copilot, `stdio` or `grpc`
} }
type GatewayConfig struct { type GatewayConfig struct {

View file

@ -2,6 +2,8 @@ package config
import ( import (
"testing" "testing"
"github.com/caarlos0/env/v11"
) )
// TestDefaultConfig_HeartbeatEnabled verifies heartbeat is enabled by default // 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 // TestConfig_Complete verifies all config fields are set
func TestConfig_Complete(t *testing.T) { func TestConfig_Complete(t *testing.T) {
cfg := DefaultConfig() cfg := DefaultConfig()