Config Composition
NEL supports Hydra-style config composition: reusable fragments, base-config inheritance, and self-referencing interpolation — with zero external dependencies and full backward compatibility.
Existing flat YAML configs continue to work unchanged. Composition is
opt-in via the defaults: key.
Quick example
Instead of duplicating 100+ lines across variant configs, inherit from a base:
defaults: list
Add a defaults: key at the top of your config. Each entry loads and merges a
config fragment or base config:
_self_ marks where the main config body sits. Default (when omitted): last,
meaning your config has the highest priority.
Config groups
The directory name in a fragment path maps to the top-level config key:
Note:
clusters/(plural) maps tocluster:(singular). All other group names match their key directly.
Fragment file structure
A fragment file contains the content for its section, not the wrapping key:
For services/, include the service name as a key (since multiple services can
coexist):
Base-config inheritance
Use _base_: to inherit from another complete config:
The base config is loaded and recursively composed (including its own
defaults:), then the current config’s body is deep-merged on top.
Chains work too: grandparent → parent → child.
Merge semantics
- Dicts: recursive deep merge (later values win on conflict)
- Lists: replace entirely (no concatenation)
- Scalars: later value wins
null: explicitly deletes a key from the base
Self-referencing interpolation
Reference other values in the merged config with ${.path.to.value}:
The leading dot distinguishes self-references from environment variables:
${.services.model.model}→ self-reference (resolved during composition)${HF_TOKEN}→ env var (resolved later by_expand_env)
Resolution order
- Composition —
defaults:fragments are merged and_base_:inheritance applied - Self-refs —
${.path}references resolved against the merged dict - CLI overrides —
-O key=valueapplied - Pydantic validation — schema checked, types coerced
- Env-var expansion —
${HF_TOKEN}resolved at runtime (not during composition)
This means self-refs see the fully-merged config but not CLI overrides. Env vars are intentionally deferred so secrets stay out of serialized configs.
Search path
Fragments are resolved in order (first match wins):
conf/relative to the config file~/.config/nemo-evaluator/conf/- Package built-in defaults (shipped with NEL)
For _base_: references, the directory containing the referencing config is
searched first.
File extensions .yaml and .yml are tried automatically.
CLI overrides
-O key=value overrides are applied after composition, so they always win:
Creating your own fragment library
Or share team-wide fragments via ~/.config/nemo-evaluator/conf/.
Error handling
- Missing fragment:
FileNotFoundErrorwith the fragment name and all search paths that were tried - Circular reference:
ValueErrorif config A references B which references A - Invalid entry:
ValueErrorfor unrecognizeddefaults:entries