Configuration system

July 15, 2026 · View on GitHub

Primus experiments are described in YAML. The loader resolves environment variables, extends: inheritance, and module/model/platform presets before training starts. This document focuses on the Python configuration pipeline (primus/core/config/ and primus/core/launcher/parser.py).

Related documentation

TopicLocation
CLI launcher and --configCLI Reference
Backend parameter referencesMegatron parameters, TorchTitan parameters, MaxText parameters

Overview: Three-layer YAML

A typical experiment ties together:

  1. Experiment YAML—your run: identity, workspace, and a modules section naming framework presets plus overrides.
  2. Module preset—training defaults for a backend (optimizer, schedule, parallelism hooks) under primus/configs/modules/<framework>/.
  3. Model preset—architecture and tokenizer metadata under primus/configs/models/<framework>/.

All of these are deep-merged (see primus/core/config/yaml_loader.py and primus/core/config/merge_utils.py). A platform preset (primus/configs/platforms/) maps distributed environment variable names and logging defaults; if omitted, the parser injects platform_azure.yaml (see PrimusParser.parse_platform in primus/core/launcher/parser.py).


Experiment config structure

work_group: ${PRIMUS_TEAM:amd}
user_name: ${PRIMUS_USER:root}
exp_name: ${PRIMUS_EXP_NAME:my_experiment}
workspace: ${PRIMUS_WORKSPACE:./output}

modules:
  pre_trainer:
    framework: megatron          # backend name (megatron, torchtitan, maxtext, megatron_bridge, …)
    config: pre_trainer.yaml     # module preset file under primus/configs/modules/<framework>/
    model: llama3_8B.yaml        # model preset file under primus/configs/models/<framework>/
    overrides:                   # training overrides (deep-merged last)
      train_iters: 50
      micro_batch_size: 4

Required top-level keys are validated in PrimusParser.parse_meta_info: work_group, user_name, exp_name, workspace.


Module presets

  • Location: primus/configs/modules/<framework>/ (for example primus/configs/modules/megatron/pre_trainer.yaml).
  • Purpose: Default training behavior (iterations, batching, optimizer, logging, parallelism-related flags for that backend).
  • Inheritance: Use extends: to compose files in the same directory (or relative paths). Multiple entries merge in order; the current file wins on conflicts (_apply_extends in yaml_loader.py).

Example chain (excerpt): pre_trainer.yaml extends trainer_base.yaml, which extends ../module_base.yaml and other shared fragments (primus/configs/modules/megatron/trainer_base.yaml).


Model presets

  • Location: primus/configs/models/<framework>/ (for example primus/configs/models/megatron/llama3_8B.yaml).
  • Purpose: Architecture dimensions, tokenizer identifiers, and other model metadata.
  • Inheritance: Same extends: mechanism as modules (for example llama3_8B.yamlllama3_base.yaml → …).

Platform presets

  • Location: primus/configs/platforms/ (for example primus/configs/platforms/platform_azure.yaml).
  • Purpose: Names of environment variables used for distributed launch (NNODES, NODE_RANK, MASTER_ADDR, …) and defaults such as master_sink_level and workspace.
  • Default: If the experiment omits platform, the parser sets config: platform_azure.yaml (primus/core/launcher/parser.py).

Environment variable substitution

primus/core/config/yaml_loader.py expands:

PatternBehavior
${VAR}Required. Raises if VAR is unset.
${VAR:default}Uses default when VAR is unset.

After substitution, purely numeric strings may be converted to int or float.


extends: inheritance

For each YAML file:

  1. Each path in extends: is resolved relative to the directory of the current file.
  2. Presets are loaded recursively (each may have its own extends:).
  3. Merge order: earlier presets in the list are merged first; later presets override earlier ones; the current file overrides all (_apply_extends).

PresetLoader.load (primus/core/config/preset_loader.py) resolves primus/configs/<config_type>/<framework>/<name>.yaml and runs the same parse_yaml pipeline.


CLI overrides (training)

After the main arguments are parsed, unknown tokens are interpreted as key=value overrides and deep-merged into the active training module namespace (module_cfg.params). The core runtime applies them in PrimusRuntime._apply_overrides (primus/core/runtime/train_runtime.py) using parse_cli_overrides (primus/core/utils/arg_utils.py) followed by deep_merge. Both key=value and --key value forms are accepted. Unknown keys are merged in (not rejected) and forwarded to the backend; the stricter key-existence check in parse_args / _check_keys_exist (primus/core/launcher/parser.py) belongs to a legacy path that the train subcommand does not exercise.

Example (conceptual):

./runner/primus-cli direct -- train pretrain --config exp.yaml \
  --train_iters 100 --micro_batch_size 2

Merge priority (training config)

The effective ordering of training parameters is:

  1. CLI overrides (key=value after the main train arguments)—highest. Applied last by the runtime (PrimusRuntime._apply_overrides), after preset and experiment merging.
  2. modules.pre_trainer.overrides in the experiment YAML (applied in PrimusParser.parse_trainer_module).
  3. Module preset with model preset additions: the module preset is loaded first, then the model preset is merged in with allow_override=False, so duplicate top-level module keys are preserved while non-duplicate model keys are added (merge_namespace in parse_trainer_module).
  4. Preset chains via extends: inside those files—base layers first, specialized layers later, file body last.

A concise mental model:

CLI overrides > experiment overrides > module preset with model preset additions > each preset's own extends: chain > shared bases such as module_base.yaml.


Config resolution walkthrough: llama2_7B-BF16-pretrain.yaml

Example experiment: examples/megatron/configs/MI300X/llama2_7B-BF16-pretrain.yaml.

  1. Load experimentparse_yaml reads the file; ${PRIMUS_*:…} placeholders resolve.
  2. Metawork_group, user_name, exp_name, workspace are checked.
  3. Platform—If not present, default platform_azure.yaml loads from primus/configs/platforms/.
  4. Module presetPresetLoader.load("pre_trainer.yaml", "megatron", "modules") loads primus/configs/modules/megatron/pre_trainer.yaml and applies its extends: chain (for example trainer_base.yaml → …).
  5. Model presetPresetLoader.load("llama2_7B.yaml", "megatron", "models") loads primus/configs/models/megatron/llama2_7B.yaml (which extends llama2_base.yamlllama_base.yaml → …).
  6. Merge—Module and model namespaces are merged for pre_trainer.
  7. Experiment overrides—Keys under modules.pre_trainer.overrides in the example file (for example mock_data: true, parallelism, LR) are applied on top.
  8. CLI overrides—Any key=value pairs from the command line are merged last.

How to write a new config for a new model

  1. Add or reuse a model preset under primus/configs/models/<framework>/, using extends: from the closest existing architecture (for example copy llama3_8B.yaml and adjust hidden size, layers, tokenizer).
  2. Point an experiment YAML at it—set modules.pre_trainer.framework, config: <module_preset>.yaml, and model: <your_model>.yaml.
  3. Set overrides in the experiment file for run-specific values (batch sizes, paths, mock_data, parallelism). Prefer small experiment files that reference presets instead of duplicating hundreds of keys.
  4. Validate with --dry-run and by tracing the referenced experiment, module, and model presets (see below).
  5. Optional: add an example under examples/<backend>/configs/<platform>/ for others to copy.

Debugging config issues

TechniqueWhat it does
--export_config PATHParsed by the training config parser, but the default core PrimusRuntime path does not currently write the resolved YAML. Treat this as legacy/future functionality unless your deployment implements it.
./runner/primus-cli --dry-run …Shows the launcher command without executing (shell layer).
--debugEnables verbose logging for launcher and Python (PRIMUS_LOG_LEVEL=DEBUG).
Inspect presetsOpen the resolved extends: chain under primus/configs/modules/ and primus/configs/models/ for the framework you use.

If ${VAR} substitution fails, set the variable or switch to ${VAR:default} in the YAML.


Cross-references

  • Default launcher YAML: runner/.primus.yaml
  • YAML loader (env + extends): primus/core/config/yaml_loader.py
  • Parser and merge: primus/core/launcher/parser.py
  • Preset paths: primus/core/config/preset_loader.py