package config import ( "errors" "fmt" "os" "strconv" "time" ) // ServerConfig holds the runtime options for the proxy server. Fields map to // CLI flags and (for LogLevel and BaseURL) the OLLAMA_PROXY_* environment // variables, so a systemd unit can set defaults without changing the binary. type ServerConfig struct { Addr string // listen address, e.g. "127.0.0.1:11435" BaseURL string // upstream Ollama Cloud root, e.g. "https://ollama.com" Cooldown time.Duration // per-account 429 cooldown window Retries int // max failover attempts (= number of accounts) LogLevel string // debug | info | warn | error } // DefaultServerConfig returns the canonical defaults used when a flag or env // var is not set. func DefaultServerConfig() ServerConfig { return ServerConfig{ Addr: "127.0.0.1:11435", BaseURL: "https://ollama.com", Cooldown: 60 * time.Second, Retries: 3, LogLevel: "info", } } // ApplyEnv overlays OLLAMA_PROXY_* environment variables on top of the current // config. Empty / unparsable env vars are ignored (the existing value wins). func (c *ServerConfig) ApplyEnv() { if v := os.Getenv("OLLAMA_PROXY_ADDR"); v != "" { c.Addr = v } if v := os.Getenv("OLLAMA_PROXY_BASE_URL"); v != "" { c.BaseURL = v } if v := os.Getenv("OLLAMA_PROXY_COOLDOWN"); v != "" { if d, err := time.ParseDuration(v); err == nil { c.Cooldown = d } } if v := os.Getenv("OLLAMA_PROXY_RETRIES"); v != "" { if n, err := strconv.Atoi(v); err == nil && n > 0 { c.Retries = n } } if v := os.Getenv("OLLAMA_PROXY_LOG_LEVEL"); v != "" { switch v { case "debug", "info", "warn", "error": c.LogLevel = v } } } // Validate returns an error when a field has an obviously invalid value. func (c *ServerConfig) Validate() error { if c.Addr == "" { return errors.New("addr is required") } if c.BaseURL == "" { return errors.New("base_url is required") } if !startsWithScheme(c.BaseURL, "http://") && !startsWithScheme(c.BaseURL, "https://") { return fmt.Errorf("base_url must start with http:// or https://, got %q", c.BaseURL) } if c.Cooldown < 0 { return errors.New("cooldown must be >= 0") } if c.Retries < 1 { return errors.New("retries must be >= 1") } switch c.LogLevel { case "debug", "info", "warn", "error": default: return fmt.Errorf("log_level must be one of debug|info|warn|error, got %q", c.LogLevel) } return nil } func startsWithScheme(s, scheme string) bool { if len(s) < len(scheme) { return false } return s[:len(scheme)] == scheme }