package config import ( "fmt" "os" "strconv" "strings" "gitea.maximumdirect.net/eric/notarius/internal/core/diagnostics" ) func LoadFromEnv() (Config, error) { cfg := Default() if err := cfg.applyEnvOverridesWithLookup(os.LookupEnv); err != nil { return Config{}, err } return cfg, nil } func (c *Config) ApplyEnvOverrides() error { return c.applyEnvOverridesWithLookup(os.LookupEnv) } func (c *Config) ApplyEnvOverridesWithLookup(lookup func(string) (string, bool)) error { return c.applyEnvOverridesWithLookup(lookup) } func (c *Config) applyEnvOverridesWithLookup(lookup func(string) (string, bool)) error { if c == nil { return fmt.Errorf("config must not be nil") } if raw, ok := lookup("NOTARIUS_TOTAL_LLM_CONCURRENCY"); ok { value, err := parseIntEnv("NOTARIUS_TOTAL_LLM_CONCURRENCY", raw) if err != nil { return err } c.Concurrency.TotalLLM = value } if raw, ok := lookup("NOTARIUS_STAGE_WORKERS_EXTRACT"); ok { value, err := parseIntEnv("NOTARIUS_STAGE_WORKERS_EXTRACT", raw) if err != nil { return err } if c.Concurrency.StageWorkers == nil { c.Concurrency.StageWorkers = make(map[string]int) } c.Concurrency.StageWorkers["extract"] = value c.Concurrency.extractWorkersConfigured = true } c.Concurrency.recomputeStageWorkerDefaults() if raw, ok := lookup("NOTARIUS_WORK_DIR"); ok { c.Diagnostics.WorkDir = strings.TrimSpace(raw) } if raw, ok := lookup("NOTARIUS_DIAGNOSTICS_RETENTION"); ok { c.Diagnostics.Retention = diagnostics.RetentionMode(strings.TrimSpace(raw)) } if raw, ok := lookup("NOTARIUS_WORKSPACE_DIR"); ok { c.Workspace.Directory = strings.TrimSpace(raw) } if raw, ok := lookup("NOTARIUS_WORKSPACE_DIAGNOSTICS_ENABLED"); ok { value, err := parseBoolEnv("NOTARIUS_WORKSPACE_DIAGNOSTICS_ENABLED", raw) if err != nil { return err } c.Workspace.Diagnostics.Enabled = value c.Workspace.Diagnostics.enabledSet = true } if raw, ok := lookup("NOTARIUS_WORKSPACE_DIAGNOSTICS_RETENTION"); ok { c.Workspace.Diagnostics.Retention = diagnostics.RetentionMode(strings.TrimSpace(raw)) c.Workspace.Diagnostics.retentionSet = true } if raw, ok := lookup("NOTARIUS_WORKSPACE_RESUME_ENABLED"); ok { value, err := parseBoolEnv("NOTARIUS_WORKSPACE_RESUME_ENABLED", raw) if err != nil { return err } c.Workspace.Resume.Enabled = value } if raw, ok := lookup("NOTARIUS_WORKSPACE_DEBUG_ENABLED"); ok { value, err := parseBoolEnv("NOTARIUS_WORKSPACE_DEBUG_ENABLED", raw) if err != nil { return err } c.Workspace.Debug.Enabled = value } c.RecomputeEffectiveDiagnostics() return nil } func parseIntEnv(name string, raw string) (int, error) { value, err := strconv.Atoi(strings.TrimSpace(raw)) if err != nil { return 0, fmt.Errorf("%s: must be an integer", name) } return value, nil } func parseBoolEnv(name string, raw string) (bool, error) { value, err := strconv.ParseBool(strings.TrimSpace(raw)) if err != nil { return false, fmt.Errorf("%s: must be a boolean", name) } return value, nil }