Config Package
Config registration, instantiation, validation, and extension contracts.
Where it fits: cross-cutting — not a box in the loop. Every box (rollout, reward, train, sync) is built from a config dataclass whose field checks and precision aliases this module provides. Full map:
../README.md.
What it is
unirl.config is the small shared toolkit behind UniRL's flat-recipe config flow.
It owns no config dataclasses of its own — those live next to the components
that consume them — just the two things every dataclass leans on: a
require(condition, message) precondition helper (require.py), and a
validation.py of shared field validators plus cross-component contract checks.
Why it exists
A recipe is one flat YAML wired entirely by _target_ dotpaths — there are no
Hydra config groups and no defaults: lists. That keeps every run reproducible
from a single file, but it also means Hydra type-checks nothing. This module is
where invariants get enforced instead:
- Each dataclass fails fast in
__post_init__viarequire(...), with a clearValueError. - Every precision field accepts the same aliases (
bf16/bfloat16,fp16/…,fp32/…) through one sharedvalidate_precision_type, so the rules and error message are identical everywhere. - The cross-component contracts that span multiple recipe sections (engine ↔ sync, offload, layout, batch geometry) are written down here too — though today they are documented intent, not an automatic gate (see Gotchas).
How it works
A recipe is one flat YAML marked # @package _global_. Components are _target_
dotpaths, sub-configs are nested _target_ blocks, shared values are ${...}
interpolations. There is no ConfigStore and no registration step.
Instantiation is a driver-routes / worker-materializes split:
parse_hydra_cfg(../trainer/hydra.py) resolves only the top-level_target_on the driver and passes nested blocks through as plain dicts.Worker._resolve_init_kwargs(../distributed/group/worker.py) walks the tree on the worker and builds each nested_target_withget_method(_target_)(**children)— deliberately nothydra.utils.instantiate, so already-built objects pass through unchanged and each is constructed in the worker's own CUDA context.
Validation runs in two layers:
- Per-dataclass
__post_init__— local field invariants viarequire(...)andvalidate_precision_type(...). This is the only layer that runs today (it fires at actor-build time). - Cross-component validators (
validate_weight_sync_contract,validate_rollout_layout,validate_offload_contract, …) take the wholecfg; most key offis_direct_sampling(cfg)(true when the engine_target_ends inTrainsideRolloutEngine). They encode the contracts but no live entrypoint calls them yet.
Extending it: a new component config is a plain @dataclass next to the
component (not here), with require(...) checks in __post_init__. A new
cross-component validator is a validate_<thing>(cfg) in validation.py that
branches on is_direct_sampling(cfg) — and that you must also wire in driver-side
for it to actually gate.
Gotchas
- The cross-component validators don't run today. Not one
validate_*(cfg)has a live call site (onlyis_direct_samplingis consumed); two aren't even re-exported fromconfig/__init__.py. So e.g.direct_sampling+ offload, or async:block on a trainside engine, is not rejected here — the only guard that fires is the trainer's own inlinelayout=separate requires a dedicated enginecheck. Don't assume a bad recipe is caught for you. # @package _global_on line 1 is mandatory — omit it and Hydra nests the whole recipe under a bucket key, socfg.batch_sizewon't resolve.is_direct_samplingis a_target_suffix match — renaming or relocating the trainside engine class silently flips a run into dedicated mode.validate_precision_typevalidates but does not normalize — it returns the canonical alias (bf16), but every call site invokes it as a bare statement and discards the result. Somodel_precision: bfloat16stays the raw string incfg; downstream code must re-parse it withparse_torch_dtypeitself.
Source:
unirl/config/README.md— edit the README next to the code, then runnpm run sync:readmesfromdocs/.