Settings resolution
Every package configures itself through a typed ModuleSettings subclass — not scattered
os.getenv calls. That gives one predictable resolution order and one place for secrets.
The precedence (highest wins)
Section titled “The precedence (highest wins)”- Explicit overrides passed in code.
KaosContextvalues (per-request config).KAOS_<MOD>_*environment variables (e.g.KAOS_AGENT_MAX_COST_USD).- Documented legacy-alias env vars (for backward compatibility).
.envfile.- Field defaults.
The first level that provides a value wins; everything below is a fallback.
flowchart TD
start([resolve a setting]) --> o{"1 · explicit<br/>override?"}
o -->|yes| win([✅ use this value])
o -->|no| c{"2 · KaosContext?"}
c -->|yes| win
c -->|no| e{"3 · KAOS_*_ env?"}
e -->|yes| win
e -->|no| a{"4 · legacy alias?"}
a -->|yes| win
a -->|no| f{"5 · .env file?"}
f -->|yes| win
f -->|no| d([6 · field default])
classDef good fill:#f0fdf4,stroke:#22c55e,color:#14532d;
class win good;
Why it matters
Section titled “Why it matters”- Predictable. You always know why a setting has its value and how to override it.
- Secret-safe. API keys are typed as
pydantic.SecretStr, so they’re redacted from logs, errors, CLI/JSON output, and serialized settings — you can’t accidentally print a key. - Per-request when needed. Level 2 lets a single process serve many tenants/sessions with different config without mutating globals.
See the environment variables reference for the per-package prefixes, and add typed settings for the hands-on pattern.