How to Manage Configuration Using pydantic BaseSettings in DimOS
DimOS centralizes all runtime configuration in a singleton GlobalConfig class that inherits from pydantic_settings.BaseSettings, merging environment variables, dynamic CLI flags, and programmatic overrides into a single typed source of truth.
DimOS (dimensionalOS/dimos) implements a robust configuration management pattern using pydantic BaseSettings. The recommended approach relies on a global singleton instance that lazy-loads values from .env files, automatically exposes every field as a CLI option, and supports runtime mutations without requiring application restarts.
The GlobalConfig Singleton Pattern
All configuration resides in dimos/core/global_config.py within the GlobalConfig class. This class subclasses pydantic_settings.BaseSettings and instantiates a singleton global_config = GlobalConfig() that is imported across the codebase.
Because BaseSettings parses environment variables lazily on first access, the singleton reflects the final merged configuration throughout the process tree. Modules receive consistent values whether they import global_config directly or accept it as a default parameter.
# dimos/core/global_config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
class GlobalConfig(BaseSettings):
robot_ip: str = "localhost"
simulation: bool = False
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
global_config = GlobalConfig()
Configuration Sources and Precedence
DimOS merges configuration from three sources in the following priority order:
- Environment variables loaded from a
.envfile viaSettingsConfigDict(env_file=".env") - CLI flags generated dynamically from
GlobalConfig.model_fields - Programmatic overrides via
global_config.update(...)
Environment Variables and .env Files
The model_config specifies env_file=".env" and extra="ignore", allowing users to define defaults in a repository-root .env file without code changes. Variable names are case-insensitive and support standard pydantic type coercion.
# .env
ROBOT_IP=192.168.1.42
SIMULATION=true
VIEWER=rerun-web
CLI Flags (Dynamic Typer Integration)
The CLI entry point in dimos/robot/cli/dimos.py inspects GlobalConfig.model_fields to generate a Typer option for every configuration field. Adding a new field to GlobalConfig automatically exposes it as a --kebab-case flag without additional boilerplate.
Programmatic Overrides
For runtime mutations, GlobalConfig.update(**kwargs) merges new values into the existing singleton instance. This allows the CLI or custom scripts to inject overrides after initialization.
Declaring New Configuration Fields
Add typed fields to GlobalConfig to extend configuration. Each field requires a Python type annotation (bool, int, float, Literal[...]) to enable IDE autocompletion and static analysis.
# dimos/core/global_config.py
from typing import Literal, TypeAlias
ViewerBackend: TypeAlias = Literal["rerun", "rerun-web", "rexconnect", "foxglove", "none"]
class GlobalConfig(BaseSettings):
viewer: ViewerBackend = "rerun"
navigation_timeout: int | None = None
# Existing fields...
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
Any new field immediately becomes:
- An environment variable (e.g.,
NAVIGATION_TIMEOUT=30) - A CLI flag (e.g.,
--navigation-timeout 30)
Runtime Configuration Updates
Use the update() method to modify configuration after initialization. This pattern supports CLI flag injection and dynamic reconfiguration without recreating the singleton.
# Custom script or CLI callback
from dimos.core.global_config import global_config
# Override specific values
global_config.update(viewer="foxglove", simulation=True)
print(global_config.viewer) # → "foxglove"
print(global_config.simulation) # → True
Modules should import global_config to access current values:
# dimos/perception/perceive_loop_skill.py
from dimos.core.global_config import GlobalConfig, global_config
class PerceiveLoopSkill:
def __init__(self, cfg: GlobalConfig = global_config):
self._cfg = cfg
def start(self):
if self._cfg.simulation:
self._setup_simulated_camera()
CLI Integration
Running dimos --help reveals dynamically generated options mapping to every GlobalConfig field:
$ dimos --help
Options:
--robot-ip TEXT Override robot_ip in GlobalConfig
--simulation / --no-simulation Override simulation in GlobalConfig
--viewer TEXT Override viewer in GlobalConfig
--navigation-timeout INTEGER Override navigation_timeout in GlobalConfig
The dynamic callback in dimos/robot/cli/dimos.py constructs these options by iterating over GlobalConfig.model_fields, ensuring new configuration fields are instantly available to users.
Key Implementation Files
dimos/core/global_config.py— CentralGlobalConfigdefinition and singleton instantiationdimos/robot/cli/dimos.py— Dynamic Typer CLI option generation fromGlobalConfig.model_fieldsdimos/core/module_coordinator.py— Example consumer importing theglobal_configsingleton
Summary
- Use the singleton
global_configimported fromdimos.core.global_configfor consistent configuration access across modules. - Declare typed fields on
GlobalConfigto add new configuration options; pydantic handles validation and.envloading automatically. - Leverage
update()for runtime mutations without rebuilding the configuration object. - Rely on automatic CLI generation—new fields appear as flags immediately without modifying
dimos/robot/cli/dimos.py. - Set defaults in
.envusing theenv_fileloading built intoSettingsConfigDict.
Frequently Asked Questions
How does GlobalConfig handle environment variables?
GlobalConfig uses SettingsConfigDict(env_file=".env", extra="ignore") to load variables from a .env file at the repository root. Pydantic matches environment variable names to field names case-insensitively and coerces values to the declared Python types (e.g., parsing "true" as a boolean).
Can I override configuration values at runtime?
Yes. Call global_config.update(**kwargs) to merge new values into the existing singleton instance. This method is used by the CLI to inject flag overrides after parsing arguments, and can be called from any module to change configuration dynamically.
How do I add a new configuration option to DimOS?
Add a typed field to the GlobalConfig class in dimos/core/global_config.py. For example, new_param: str = "default". The field immediately becomes available as an environment variable NEW_PARAM, a CLI flag --new-param, and a mutable attribute on the global_config singleton.
Where is the CLI option generation handled?
The CLI dynamically builds options in dimos/robot/cli/dimos.py by inspecting GlobalConfig.model_fields. The code creates a Typer.Option for each field, mapping configuration keys to command-line flags without requiring manual updates when fields are added or removed.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →