Skip to content

refactor(utils): migrate CheckpointingConfig and PretrainedCheckpointConfig to BaseModel - #4152

Open
lonexreb wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
lonexreb:refactor/config-v2-checkpointing
Open

lonexreb wants to merge 1 commit into
NVIDIA-NeMo:mainfrom
lonexreb:refactor/config-v2-checkpointing

Conversation

@lonexreb

Copy link
Copy Markdown
Contributor

What does this PR do ?

Second PR in the utils-layer config v1 → v2 series (after #3916 / ClusterConfig; pattern from #2518, running alongside #3197 policy-config and #3289 generation-config series). Migrates CheckpointingConfig and PretrainedCheckpointConfig (nemo_rl/utils/checkpoint.py) from TypedDict to BaseModel(extra="allow").

Schema decisions (each mirrors today's effective behavior — no user-visible default changes):

  • Hidden call-site defaults are centralized on the class: save_optimizer=True, save_data_plane=False, save_consolidated=False, model_cache_dir="", model_repo_id="", is_peft=False, is_async=False — this removes the .get(key, default) forbidden-pattern instances in CheckpointManager.__init__.
  • model_save_format stays Optional[str] = None: None is load-bearing (the DTensor v1 worker rejects any non-None value); the automodel layers keep applying their own "safetensors" fallback, now written explicitly in CheckpointManager instead of hiding in a .get default.
  • load_replay_buffer stays tri-state (Optional[bool] = None): the async-GRPO restore only opts out on an explicit False, so an omitted key must remain distinguishable from true.
  • Configs that omit save_optimizer / keep_top_k previously hit a latent KeyError (config["save_optimizer"] on a NotRequired key); they now get the documented defaults.

Call sites: ~85 across all algorithms, SC utils, and lm_policy move to attribute access. Two dict boundaries are deliberately preserved via model_dump() so neighboring migration lanes stay untouched:

  • policy_config["pretrained_checkpoint"] hand-off (PolicyConfig is still TypedDict-shaped; megatron/setup.py consumes dict keys — @NolenLiang's lane), and
  • checkpointing_cfg forwarded to save_checkpoint workers (the automodel checkpoint layer consumes .items()/.get()).

Tests: fixtures built via model_construct bypass validation, so checkpointing dict literals are wrapped in CheckpointingConfig.model_construct(**{...}); CheckpointManager unit tests validate their full config dicts via model_validate.

No exemplar YAML or reference-config changes; tests/unit/test_config_v2.py is unaffected.

Issues

Part of the TypedDict → BaseModel migration tracked by tests/unit/test_config_v2.py (see also #1675 / #2102).

Usage

No user-facing YAML change. Unknown/legacy keys under checkpointing: continue to load; missing required keys (enabled, checkpoint_dir, metric_name, higher_is_better, save_period) now fail at config validation time with a clear pydantic error.

Before your PR is "Ready for review"

  • Standalone pydantic semantics checks (exemplar blocks round-trip; sft.yaml block keeps model_save_format=None; nested pretrained_checkpoint coerces and dumps back; extra keys preserved; model_construct default-fill)
  • ruff format + check clean (repo-pinned 0.9.9); py_compile on all touched files; zero 3rdparty/ changes (gitlinks verified against main)
  • CI unit tests (will trigger)

…Config to BaseModel

Second PR in the utils-layer config v1 -> v2 series (NVIDIA-NeMo#3916 migrated
ClusterConfig; pattern from NVIDIA-NeMo#2518, alongside NVIDIA-NeMo#3197/NVIDIA-NeMo#3289).

Schema:
- Both classes become BaseModel(extra="allow"); NotRequired fields
  become Optional[...] = None.
- Hidden call-site defaults are centralized on the class exactly as
  they behave today: save_optimizer=True, save_data_plane=False,
  save_consolidated=False, model_cache_dir="", model_repo_id="",
  is_peft=False, is_async=False.
- model_save_format stays Optional[str] = None on purpose: the DTensor
  v1 worker rejects any non-None value, and the automodel layers apply
  their own "safetensors" fallback (CheckpointManager keeps that
  resolution, now explicit instead of a .get default).
- load_replay_buffer stays tri-state (Optional[bool] = None): the
  async-GRPO restore path only opts out on an explicit False.

Call sites (~85 across all algorithms, SC utils, lm_policy) move from
key access / .get() to attribute access. Two deliberate dict
boundaries are preserved with model_dump():
- policy_config["pretrained_checkpoint"] hand-off (PolicyConfig is
  still TypedDict-shaped; Megatron setup consumes dict keys), and
- checkpointing_cfg forwarded to save_checkpoint workers (the
  automodel checkpoint layer consumes .items()/.get()).

Tests: fixtures built via model_construct bypass validation, so
checkpointing dict literals are wrapped in
CheckpointingConfig.model_construct(**{...}) (missing required fields
now raise AttributeError where they raised KeyError before);
CheckpointManager unit tests validate their full config dicts via
model_validate.

No exemplar YAML or reference-config changes; class defaults mirror
the documented/exemplar values, so tests/unit/test_config_v2.py is
unaffected.

Verified: py_compile on all touched files; standalone pydantic
semantics checks (exemplar blocks round-trip, sft.yaml block keeps
model_save_format=None, nested pretrained_checkpoint coerces and dumps
back, extra keys preserved, model_construct default-fill); ruff format
+ check clean.

Signed-off-by: Shubhankar Tripathy <reach2shubhankar@gmail.com>
@lonexreb
lonexreb requested review from a team as code owners September 16, 2026 07:00
@copy-pr-bot

copy-pr-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@lonexreb

Copy link
Copy Markdown
Contributor Author

/ok to test d99873c

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant