diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-16 03:58:02 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-16 03:58:02 +0200 |
| commit | 52938e05c1ab250cae1c19c29eaa050351559b3b (patch) | |
| tree | 85c7eab51fc4622838003e32afac1c744c1e1dcf /internal/appconfig/config_load.go | |
| parent | 9e8ca4696f4fcbc1657eb7802aa52f8684fab202 (diff) | |
Split config_load.go into config_load.go and config_env.go
Move environment variable handling functions into config_env.go (269L),
keeping config_load.go at 697L. Both well under the 1000-line limit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/appconfig/config_load.go')
| -rw-r--r-- | internal/appconfig/config_load.go | 261 |
1 files changed, 0 insertions, 261 deletions
diff --git a/internal/appconfig/config_load.go b/internal/appconfig/config_load.go index b098b48..99461ca 100644 --- a/internal/appconfig/config_load.go +++ b/internal/appconfig/config_load.go @@ -695,264 +695,3 @@ func floatPtr(v float64) *float64 { f := v return &f } - -// --- Environment overrides --- - -// loadFromEnv constructs an App containing only fields set via HEXAI_* env vars. -// These values should take precedence over file config when merged. -func loadFromEnv(logger *log.Logger) *App { - var out App - any := applyCoreEnv(&out, logger) - any = applyProviderEnv(&out, logger) || any - any = applySurfaceEnv(&out, logger) || any - any = applyIgnoreEnv(&out) || any - any = applyMCPEnv(&out) || any - if !any { - return nil - } - return &out -} - -func applyCoreEnv(out *App, logger *log.Logger) bool { - any := false - any = applyEnvInt(&out.MaxTokens, "HEXAI_MAX_TOKENS", logger) || any - any = applyEnvString(&out.ContextMode, "HEXAI_CONTEXT_MODE") || any - any = applyEnvInt(&out.ContextWindowLines, "HEXAI_CONTEXT_WINDOW_LINES", logger) || any - any = applyEnvInt(&out.MaxContextTokens, "HEXAI_MAX_CONTEXT_TOKENS", logger) || any - any = applyEnvInt(&out.LogPreviewLimit, "HEXAI_LOG_PREVIEW_LIMIT", logger) || any - any = applyEnvInt(&out.RequestTimeout, "HEXAI_REQUEST_TIMEOUT", logger) || any - any = applyEnvInt(&out.ManualInvokeMinPrefix, "HEXAI_MANUAL_INVOKE_MIN_PREFIX", logger) || any - any = applyEnvInt(&out.CompletionDebounceMs, "HEXAI_COMPLETION_DEBOUNCE_MS", logger) || any - any = applyEnvInt(&out.CompletionThrottleMs, "HEXAI_COMPLETION_THROTTLE_MS", logger) || any - any = applyEnvFloat(&out.CodingTemperature, "HEXAI_CODING_TEMPERATURE", logger) || any - any = applyEnvCSV(&out.TriggerCharacters, "HEXAI_TRIGGER_CHARACTERS") || any - any = applyEnvString(&out.InlineOpen, "HEXAI_INLINE_OPEN") || any - any = applyEnvString(&out.InlineClose, "HEXAI_INLINE_CLOSE") || any - any = applyEnvString(&out.ChatSuffix, "HEXAI_CHAT_SUFFIX") || any - any = applyEnvCSV(&out.ChatPrefixes, "HEXAI_CHAT_PREFIXES") || any - any = applyEnvString(&out.Provider, "HEXAI_PROVIDER") || any - return any -} - -func applyProviderEnv(out *App, logger *log.Logger) bool { - picker := newModelPicker(out.Provider) - any := false - any = applyEnvString(&out.OpenAIBaseURL, "HEXAI_OPENAI_BASE_URL") || any - if model, ok := picker.pick("openai", getenvTrim("HEXAI_OPENAI_MODEL")); ok { - out.OpenAIModel = model - any = true - } - any = applyEnvFloat(&out.OpenAITemperature, "HEXAI_OPENAI_TEMPERATURE", logger) || any - - any = applyEnvString(&out.OpenRouterBaseURL, "HEXAI_OPENROUTER_BASE_URL") || any - if model, ok := picker.pick("openrouter", getenvTrim("HEXAI_OPENROUTER_MODEL")); ok { - out.OpenRouterModel = model - any = true - } - any = applyEnvFloat(&out.OpenRouterTemperature, "HEXAI_OPENROUTER_TEMPERATURE", logger) || any - - any = applyEnvString(&out.OllamaBaseURL, "HEXAI_OLLAMA_BASE_URL") || any - if model, ok := picker.pick("ollama", getenvTrim("HEXAI_OLLAMA_MODEL")); ok { - out.OllamaModel = model - any = true - } - any = applyEnvFloat(&out.OllamaTemperature, "HEXAI_OLLAMA_TEMPERATURE", logger) || any - - any = applyEnvString(&out.AnthropicBaseURL, "HEXAI_ANTHROPIC_BASE_URL") || any - if model, ok := picker.pick("anthropic", getenvTrim("HEXAI_ANTHROPIC_MODEL")); ok { - out.AnthropicModel = model - any = true - } - any = applyEnvFloat(&out.AnthropicTemperature, "HEXAI_ANTHROPIC_TEMPERATURE", logger) || any - return any -} - -func applySurfaceEnv(out *App, logger *log.Logger) bool { - any := false - if entries, ok := buildSurfaceEntryFromEnv("HEXAI_MODEL_COMPLETION", "HEXAI_TEMPERATURE_COMPLETION", "HEXAI_PROVIDER_COMPLETION", logger); ok { - out.CompletionConfigs = entries - any = true - } - if entries, ok := buildSurfaceEntryFromEnv("HEXAI_MODEL_CODE_ACTION", "HEXAI_TEMPERATURE_CODE_ACTION", "HEXAI_PROVIDER_CODE_ACTION", logger); ok { - out.CodeActionConfigs = entries - any = true - } - if entries, ok := buildSurfaceEntryFromEnv("HEXAI_MODEL_CHAT", "HEXAI_TEMPERATURE_CHAT", "HEXAI_PROVIDER_CHAT", logger); ok { - out.ChatConfigs = entries - any = true - } - if entries, ok := buildSurfaceEntryFromEnv("HEXAI_MODEL_CLI", "HEXAI_TEMPERATURE_CLI", "HEXAI_PROVIDER_CLI", logger); ok { - out.CLIConfigs = entries - any = true - } - return any -} - -func applyIgnoreEnv(out *App) bool { - any := false - any = applyEnvBoolPtr(&out.IgnoreGitignore, "HEXAI_IGNORE_GITIGNORE") || any - any = applyEnvCSV(&out.IgnoreExtraPatterns, "HEXAI_IGNORE_EXTRA_PATTERNS") || any - any = applyEnvBoolPtr(&out.IgnoreLSPNotify, "HEXAI_IGNORE_LSP_NOTIFY") || any - return any -} - -func applyMCPEnv(out *App) bool { - any := false - any = applyEnvString(&out.MCPPromptsDir, "HEXAI_MCP_PROMPTS_DIR") || any - any = applyEnvBool(&out.MCPSlashCommandSync, "HEXAI_MCP_SLASHCOMMAND_SYNC") || any - any = applyEnvString(&out.MCPSlashCommandDir, "HEXAI_MCP_SLASHCOMMAND_DIR") || any - return any -} - -func buildSurfaceEntryFromEnv(modelKey, tempKey, providerKey string, logger *log.Logger) ([]SurfaceConfig, bool) { - model := getenvTrim(modelKey) - tempPtr, tempSet := parseEnvFloatPtr(tempKey, logger) - provider := getenvTrim(providerKey) - if model == "" && provider == "" && !tempSet { - return nil, false - } - entry := SurfaceConfig{Provider: provider, Model: model} - if tempSet { - entry.Temperature = tempPtr - } - return []SurfaceConfig{entry}, true -} - -func applyEnvString(target *string, key string) bool { - value := getenvTrim(key) - if value == "" { - return false - } - *target = value - return true -} - -func applyEnvInt(target *int, key string, logger *log.Logger) bool { - value, ok := parseEnvInt(key, logger) - if !ok { - return false - } - *target = value - return true -} - -func applyEnvFloat(target **float64, key string, logger *log.Logger) bool { - value, ok := parseEnvFloatPtr(key, logger) - if !ok { - return false - } - *target = value - return true -} - -func applyEnvCSV(target *[]string, key string) bool { - value := getenvTrim(key) - if value == "" { - return false - } - parts := strings.Split(value, ",") - *target = nil - for _, p := range parts { - if t := strings.TrimSpace(p); t != "" { - *target = append(*target, t) - } - } - return true -} - -func applyEnvBool(target *bool, key string) bool { - value := getenvTrim(key) - if value == "" { - return false - } - *target = value == "true" || value == "1" - return true -} - -func applyEnvBoolPtr(target **bool, key string) bool { - value := getenvTrim(key) - if value == "" { - return false - } - parsed := value == "true" || value == "1" - *target = &parsed - return true -} - -func getenvTrim(key string) string { - return strings.TrimSpace(os.Getenv(key)) -} - -func parseEnvInt(key string, logger *log.Logger) (int, bool) { - value := getenvTrim(key) - if value == "" { - return 0, false - } - n, err := strconv.Atoi(value) - if err != nil { - if logger != nil { - logger.Printf("invalid %s: %v", key, err) - } - return 0, false - } - return n, true -} - -func parseEnvFloatPtr(key string, logger *log.Logger) (*float64, bool) { - value := getenvTrim(key) - if value == "" { - return nil, false - } - f, err := strconv.ParseFloat(value, 64) - if err != nil { - if logger != nil { - logger.Printf("invalid %s: %v", key, err) - } - return nil, false - } - return &f, true -} - -type modelPicker struct { - providerLower string - modelForce string - modelGeneric string - forceUsed bool - genericUsed bool -} - -func newModelPicker(provider string) *modelPicker { - return &modelPicker{ - providerLower: strings.ToLower(strings.TrimSpace(provider)), - modelForce: getenvTrim("HEXAI_MODEL_FORCE"), - modelGeneric: getenvTrim("HEXAI_MODEL"), - } -} - -func (p *modelPicker) pick(providerName, specific string) (string, bool) { - specific = strings.TrimSpace(specific) - nameLower := strings.ToLower(strings.TrimSpace(providerName)) - if p.modelForce != "" { - if p.providerLower == nameLower { - p.forceUsed = true - return p.modelForce, true - } - if p.providerLower == "" && !p.forceUsed { - p.forceUsed = true - return p.modelForce, true - } - } - if specific != "" { - return specific, true - } - if p.modelGeneric != "" { - if p.providerLower == nameLower { - return p.modelGeneric, true - } - if p.providerLower == "" && !p.genericUsed { - p.genericUsed = true - return p.modelGeneric, true - } - } - return "", false -} |
