nemo_rl.algorithms.async_utils.replay_buffer#
Module Contents#
Classes#
Bounded interval telemetry for checkpoint-induced mutation waits. |
|
Controller-local index for one training-ready group stored in TQ. |
|
Versioned metadata-only replay index paired with a TQ snapshot. |
|
SC metadata envelope stored with a native data-plane checkpoint. |
|
Live capability proving code runs inside a data-plane barrier cut. |
|
Allow concurrent mutations while giving live checkpoints exclusivity. |
|
Replay buffer storing per-prompt groups. |
|
Meta cache + TQ writer with reserve-then-commit slot semantics. |
Functions#
Return a deterministic JSON value or reject unsupported metadata. |
|
Canonicalize identity metadata without hashing advisory rollout metrics. |
|
Return a stable digest binding replay metadata to a TQ checkpoint. |
Data#
API#
- nemo_rl.algorithms.async_utils.replay_buffer.DATA_PLANE_CHECKPOINT_DIR#
‘data_plane’
- nemo_rl.algorithms.async_utils.replay_buffer.REPLAY_BUFFER_METADATA_FILENAME#
‘replay_buffer_metadata.pt’
- nemo_rl.algorithms.async_utils.replay_buffer.LEGACY_REPLAY_BUFFER_FILENAME#
‘replay_buffer.pt’
- nemo_rl.algorithms.async_utils.replay_buffer.REPLACEMENT_RESERVE_FILENAME#
‘replacement_reserve.pt’
- nemo_rl.algorithms.async_utils.replay_buffer.REPLAY_BUFFER_METADATA_SCHEMA_VERSION#
1
- nemo_rl.algorithms.async_utils.replay_buffer.REPLAY_BUFFER_METADATA_STORAGE: Literal[tq_checkpoint]#
‘tq_checkpoint’
- nemo_rl.algorithms.async_utils.replay_buffer.CheckpointMutationKind#
None
- nemo_rl.algorithms.async_utils.replay_buffer.CHECKPOINT_MUTATION_KINDS#
‘cast(…)’
- class nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneCheckpointBarrierTelemetry#
Bounded interval telemetry for checkpoint-induced mutation waits.
- blocked_by_kind: dict[nemo_rl.algorithms.async_utils.replay_buffer.CheckpointMutationKind, int]#
None
- wait_durations_s: tuple[float, ...]#
None
- active_mutations: int#
None
- waiting_mutations: int#
None
- max_waiting_mutations: int#
None
- checkpoint_active: bool#
None
- class nemo_rl.algorithms.async_utils.replay_buffer.TQReplayGroupMetadata#
Bases:
typing.TypedDictController-local index for one training-ready group stored in TQ.
Initialization
Initialize self. See help(type(self)) for accurate signature.
- meta: nemo_rl.data_plane.KVBatchMeta#
None
- start_weight: int#
None
- end_weight: int#
None
- target_step: Optional[int]#
None
- group_id: str#
None
- class nemo_rl.algorithms.async_utils.replay_buffer.TQReplayMetadataState#
Bases:
typing.TypedDictVersioned metadata-only replay index paired with a TQ snapshot.
Initialization
Initialize self. See help(type(self)) for accurate signature.
- schema_version: int#
None
- storage: Literal[tq_checkpoint]#
None
- partition_id: str#
None
- saved_capacity: int#
None
- manifest_digest: str#
None
- groups: list[nemo_rl.algorithms.async_utils.replay_buffer.TQReplayGroupMetadata]#
None
- class nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneCheckpointMetadata#
Bases:
typing.TypedDictSC metadata envelope stored with a native data-plane checkpoint.
The replay fields are present together in
authoritativemode and absent inshadowmode.Initialization
Initialize self. See help(type(self)) for accurate signature.
- data_plane_checkpoint_schema_version: int#
None
- single_controller_train_steps: int#
None
- single_controller_trainer_version: int#
None
- single_controller_epoch: int#
None
- partition_id: str#
None
- sampler_name: str#
None
- mode: Literal[authoritative, shadow]#
None
- replay_metadata_schema_version: NotRequired[int]#
None
- replay_manifest_digest: NotRequired[str]#
None
- replay_group_count: NotRequired[int]#
None
- rollout_recovery_schema_version: NotRequired[int]#
None
- rollout_recovery_payload_sha256: NotRequired[str]#
None
- rollout_recovery_group_count: NotRequired[int]#
None
- opd_full_teacher_checkpoints: NotRequired[list[str]]#
None
- nemo_rl.algorithms.async_utils.replay_buffer._canonical_manifest_value(
- value: Any,
- *,
- path: str,
Return a deterministic JSON value or reject unsupported metadata.
- nemo_rl.algorithms.async_utils.replay_buffer._canonical_manifest_extra_info(
- value: Any,
- *,
- path: str,
Canonicalize identity metadata without hashing advisory rollout metrics.
Rollout metrics may contain logger payloads that are not JSON-compatible. They remain in the serialized
KVBatchMetafor post-restore logging, but do not identify the replay rows bound to the native TQ checkpoint.
- nemo_rl.algorithms.async_utils.replay_buffer.replay_manifest_digest( ) str#
Return a stable digest binding replay metadata to a TQ checkpoint.
- class nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut( )#
Live capability proving code runs inside a data-plane barrier cut.
Initialization
- __slots__#
(‘_barrier’, ‘_live’)
- require_live() None#
Fail when a mutation tries to reuse an absent or expired cut.
- _invalidate() None#
- class nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneCheckpointBarrier#
Allow concurrent mutations while giving live checkpoints exclusivity.
At most one checkpoint holder is active. New mutations queue behind it, and a checkpoint waits for all active mutations before yielding. Every live canonical TQ commit/clear and native save must use this barrier so the snapshot and controller replay index describe the same rows.
Initialization
- _current_task() asyncio.Task[Any]#
Return the task entering a barrier section and reject reentrancy.
- property mutation_version: int#
Return a monotonic marker for completed outer mutation sections.
- async mutation(
- kind: nemo_rl.algorithms.async_utils.replay_buffer.CheckpointMutationKind = 'other',
Yield one task-local live cut after any active checkpoint exits.
- async drain_telemetry() nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneCheckpointBarrierTelemetry#
Return and reset interval waits while preserving current state.
- async checkpoint() collections.abc.AsyncIterator[nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut]#
Yield a live capability after blocking and draining all mutations.
- exception nemo_rl.algorithms.async_utils.replay_buffer.PostWriteEnrichmentError#
Bases:
RuntimeErrorA rollout reached TQ but failed in required post-write processing.
Initialization
Initialize self. See help(type(self)) for accurate signature.
- class nemo_rl.algorithms.async_utils.replay_buffer.ReplayBufferImpl(
- max_size: int,
- drop_incomplete_targets_on_restore: bool,
Bases:
nemo_rl.algorithms.async_utils.interfaces.ReplayBufferProtocolReplay buffer storing per-prompt groups.
A single entry corresponds to 1 prompt repeated by the algorithm’s
num_generations_per_promptsetting.Initialization
- static _rollout_metrics_turn_count_for_diagnostics(
- rm: dict[str, Any],
One scalar turn-depth per buffered trajectory for starvation diagnostics.
Supports sync multi-turn rollouts (
max_turns_per_sample/avg_turns_per_sample) and NeMo Gym (turns_per_sample/max/turns_per_sample/mean).
- add(
- trajectory: dict[str, Any],
- weight_version: int,
- target_weight_version: int,
Add a per-prompt trajectory group with metadata.
- Parameters:
trajectory – data dict
weight_version – version of the model weights used for generation
target_weight_version – version of the model weights this trajectory is intended for training
- get_debug_info() dict#
Get debug information about buffer state.
- get_last_target_weight_already_generated() int#
- get_existing_target_weights() set[int]#
Get set of target weight versions that already have trajectories.
- _remove_indices(indices: Iterable[int]) None#
Remove trajectories at the given indices.
- sample(
- num_prompt_groups: int,
- current_weight_version: int,
- max_age_steps: int,
Sample per-prompt trajectory groups intended for the current training step.
Only returns trajectories with target_weight_version == current_weight_version. If insufficient trajectories are available, returns None to stall training until the remaining trajectories are generated. This ensures no trajectory loses its last chance to be used for its intended training step.
- Returns:
Dictionary with ‘trajectories’ and ‘avg_trajectory_age’ keys, or None if insufficient data
- size() int#
Return current buffer size.
- get_held_task_indices() list[int]#
Ordinals of every prompt group currently held in the buffer.
All held groups are untrained (sampling removes trained ones). The checkpoint cut must not exceed any of these ordinals: with
checkpointing.load_replay_buffer=falsethe buffer is discarded on resume, and ordinals below the cut are never re-yielded.
- clear() None#
Clear the buffer.
- state_dict() dict[str, Any]#
Return serializable state for checkpointing.
- save_to_path(path: str) int#
Serialize inside the actor without materializing the buffer on the driver.
- load_from_path(
- path: str,
- num_prompts_per_step: int | None = None,
- current_training_step: int | None = None,
- max_age_steps: int | None = None,
Restore inside the actor and return only compact coordination metadata.
- Returns:
Mapping with
num_trajectories(pre-filter count),NEXT_NEMO_GYM_TASK_INDEX_KEY(one past the highest saved task index, computed before age/step filtering; on a legacy resume this keeps used indices from being re-issued, while a frontier-aligned resume deliberately rewinds the counter to the saved base ordinal so the covered window re-yields under its original indices), andRETAINED_TASK_INDICES_KEY(the sorted task indices of the groups that survived filtering — what a frontier-aligned resume must not regenerate).
- load_state_dict(
- state: dict[str, Any],
- num_prompts_per_step: int | None = None,
- current_training_step: int | None = None,
- max_age_steps: int | None = None,
Restore replay buffer state from a checkpoint.
- Parameters:
state – State returned by
state_dict.num_prompts_per_step – Number of prompt groups required for one training step. When provided, incomplete target steps can be removed or prepared for gap filling.
current_training_step – Step being resumed. When provided with
num_prompts_per_step, past target steps are dropped and incomplete current/future target steps are kept for gap filling.max_age_steps – Maximum allowed age for restored trajectories. When provided, stale trajectories are removed during restore.
- Raises:
ValueError – If the checkpoint is missing required fields or has inconsistent parallel list lengths.
- _prepare_for_training_step(
- current_step: int,
- num_prompts_per_step: int,
Prepare restored state so training can resume at
current_step.
- static _is_valid_for_target(
- trajectory_version: int,
- target_step: int,
- max_age_steps: int | None,
- _remove_stale_trajectories(max_age_steps: int) None#
Remove restored trajectories that are stale for their target step.
Must be called while holding
self._lock.
- _count_for_target(
- target_step: int,
- max_age_steps: int | None = None,
Count trajectories usable for
target_step.Must be called while holding
self._lock.
- _truncate_to_max_size(
- current_training_step: int | None = None,
Truncate restored state to
max_sizeafter resume cleanup.Must be called while holding
self._lock.
- get_trajectories_needed(
- target_step: int,
- num_prompts_per_step: int,
- max_age_steps: int | None = None,
Return additional trajectories needed for an unconsumed
target_step.
- has_complete_batch(
- target_step: int,
- num_prompts_per_step: int,
- max_age_steps: int | None = None,
Return whether
target_stephas enough trajectories to train.
- _remove_incomplete_target_steps(num_prompts_per_step: int) None#
Remove target steps without a complete batch.
Must be called while holding
self._lock.
- class nemo_rl.algorithms.async_utils.replay_buffer.ReplayBuffer(max_size: int, drop_incomplete_targets_on_restore: bool)#
Bases:
nemo_rl.algorithms.async_utils.replay_buffer.ReplayBufferImpl
- class nemo_rl.algorithms.async_utils.replay_buffer.TQReplayBuffer(
- dp_client: Any,
- partition_id: str,
- *,
- pad_value_dict: collections.abc.Mapping[str, int],
- include_message_violation_fields: bool,
- staging_partition_id: Optional[str] = None,
- require_routed_experts: bool = False,
Meta cache + TQ writer with reserve-then-commit slot semantics.
meta_list, weight_list, ready_list, _group_ids are parallel; a slot stays ready=False until commit fills it.
Initialization
- set_data_plane_checkpoint_barrier( ) None#
Bind the controller’s shared checkpoint/mutation barrier once.
A private fallback barrier would not coordinate with controller-owned saves and clears, so destructive operations fail loudly until the SC actor supplies its barrier.
- property data_plane_checkpoint_barrier: nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneCheckpointBarrier#
Return the shared barrier used by controller and post-commit ownership.
- set_post_write_enricher(
- enricher: Callable[[nemo_rl.data_plane.KVBatchMeta, nemo_rl.experience.interfaces.PromptGroupRecord], Awaitable[nemo_rl.data_plane.KVBatchMeta]],
Install the required enrichment stage run before slots become ready.
- property group_ids: tuple[str, ...]#
Return a stable snapshot of controller-local replay ownership.
- reserve(
- *,
- weight_version: int,
- target_step: Optional[int] = None,
- group_id: Optional[str] = None,
- rollout_ids: Optional[list[str]] = None,
Append an unready slot tagged with weight_version.
- Parameters:
weight_version – Weight version stamped on the slot.
target_step – Training step this slot targets; only consulted by StalenessSampler.force_in_order.
group_id – Pre-minted logical group ID and sample-ID prefix. The checkpoint-enabled lineage path always supplies this.
Nonecreates a fresh UUID only for untracked callers.rollout_ids – Token-capture mode: the ledger-registered rollout ids this slot dispatched, recorded so cleanup can name what it owns even before a receipt exists.
- Returns:
group_id used by the matching commit.
- async commit(
- group_id: str,
- record: nemo_rl.experience.interfaces.PromptGroupRecord,
- start_weight_version: int,
- end_weight_version: int,
Tensorize record, write N rows to TQ, and mark the slot ready.
- Parameters:
group_id – group_id returned by the matching reserve call.
record – PromptGroupRecord to tensorize.
start_weight_version – Weight version stamped on the slot before rollout. The same as the one from reserve, passed again to avoid race condition when lookup.
end_weight_version – Weight version stamped on the slot after rollout.
- Returns:
KVBatchMeta for the committed group.
- Raises:
ValueError – group_id has no live slot (removed or never reserved).
RuntimeError – router replay is enabled but the payload has no routes.
- async remove_group(group_id: str, *, remove_in_dp: bool = False) int#
Remove the live slot identified by
group_id.- Parameters:
group_id – Group identifier returned by :meth:
reserve.remove_in_dp – Whether to clear rows referenced by a committed slot.
- Returns:
One when this call removes the slot, or zero if another concurrent mutation removed it while DataPlane cleanup was awaiting.
- Raises:
ValueError –
group_idhas no live slot.
- async clear_staging_keys(
- cut: nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut,
- staging_keys: list[str],
Clear known token-capture staging rows under a caller-owned cut.
- async commit_finalized(
- cut: nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut,
- group_id: str,
- meta: nemo_rl.data_plane.KVBatchMeta,
- group_min_wv: int,
- group_max_wv: int,
- *,
- staging_keys: Optional[list[str]] = None,
Mark a slot ready from finalizer output (token-capture mode).
Unlike :meth:
commit, the canonical rows are already in TQ — the finalizer tensorized and put them — so this only fills the slot. The slot’s effective version is the group’s OLDEST call version (group_min_wv): staleness accounting stays conservative when a rollout straddles a refit.- Parameters:
cut – Live cut acquired by the owner coordinating finalization.
group_id – group_id returned by the matching reserve call.
meta – KVBatchMeta the finalizer built over its published rows.
group_min_wv – Oldest weight version any call in the group used.
group_max_wv – Newest weight version any call in the group used.
staging_keys –
The group’s staged delta keys, recorded so
- meth:
removecan clear the staging partition too.
- Raises:
ValueError – group_id has no live slot (removed or never reserved).
- abort(group_id: str) bool#
Drop an unready slot whose dispatch failed or was cancelled.
Token-capture mode; called from the failed dispatch path. No DataPlane rows are cleared here. Callers with sealed receipts must clear their deterministic canonical IDs and full staging manifests before dropping this ownership record. Before receipt sealing, orphan cleanup remains an explicit controlled-validation limitation.
- Returns:
True when a slot was dropped; False when the group_id has no live slot (already committed+consumed or never reserved).
- _delete_slot(idx: int) None#
- async remove(idxs: list[int], remove_in_dp: bool) int#
Drop entries at the given indices and optionally clear them from DataPlane.
In token-capture mode (
staging_partition_idset), clearing a group also clears its recorded staged delta rows, so eviction leaves neither canonical nor staging bytes behind.- Parameters:
idxs – Entry indices to drop. Must be within [0, size).
remove_in_dp – If True, also clear the dropped rows from DataPlane.
- Returns:
Number of group entries removed from the buffer.
- async claim_for_training(idxs: list[int]) int#
Transfer ready groups from sampler ownership to an open train step.
The canonical rows remain in TQ. Their metadata stays checkpoint-visible until :meth:
release_training_claimsruns after optimizer success and data-plane cleanup.
- training_owned_replay_groups() list[nemo_rl.algorithms.async_utils.replay_buffer.TQReplayGroupMetadata]#
Return metadata for canonical rows owned by the open train step.
- training_owned_group_ids() set[str]#
Return stable IDs currently owned by the open train step.
- release_training_claims(group_ids: list[str]) None#
Release checkpoint ownership after consumed TQ rows are cleared.
- async _remove_groups_unlocked(
- cut: nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut,
- group_ids: list[str],
- *,
- clear_data_plane: bool,
- retain_training_claims: bool = False,
Remove stable groups while the caller owns a live mutation cut.
- metadata_state_dict(
- *,
- saved_capacity: int,
- additional_groups: Optional[list[nemo_rl.algorithms.async_utils.replay_buffer.TQReplayGroupMetadata]] = None,
Capture the controller index for ready groups without tensor payloads.
The caller must hold the exclusive side of the shared data-plane checkpoint barrier through this capture and the matching TQ save. Commits and destructive clears use shared mutation slots, so the replay index and native snapshot describe one exact set of training-ready groups. Every operation that mutates the canonical or staging partitions or the controller-local replay membership must hold a mutation cut across the complete publish/index or clear/remove transition. No writer is exempt, including post-train cleanup in
_train_pump; canonical writes are not required to originate specifically from :meth:commit. The advantage stage also takes a mutation slot because the periodic checkpoint pump runs concurrently with_train_pump. In-flight reservations are intentionally omitted.additional_groupsis used by periodic snapshots to re-index rows claimed by an unfinished streamed optimizer step.
- async load_state_dict(
- state: dict[str, Any],
- *,
- max_groups: int,
- expected_partition_id: str,
- expected_group_size: int,
- expected_manifest_digest: str,
Restore the local replay index for an already-restored TQ snapshot.
The replay index never contains tensor payloads and this method never writes to the DataPlane. TQ must be restored first; the caller binds the two artifacts by passing the manifest digest returned by TQ checkpoint loading.
Staleness is intentionally NOT handled here — load only loads. The train pump’s first
sampler.evictdrops any restored group that is outside the staleness window and releases its capacity permit, keeping eviction in one place.- Parameters:
state – Envelope produced by
metadata_state_dict.max_groups – Current max_buffered_rollouts; the restored count never exceeds it.
expected_partition_id – Partition this buffer writes to; must match the envelope.
expected_group_size – num_generations_per_prompt; every group must hold exactly this many rows (a changed group size silently breaks the group-relative baseline).
expected_manifest_digest – Digest returned by the matching native TQ checkpoint load. It must match the replay metadata file.
- Returns:
Number of groups restored into the buffer.
- Raises:
ValueError – If the envelope is malformed (missing keys, partition mismatch, misaligned or wrongly sized groups, duplicate sample_ids), disagrees with the native TQ snapshot, or exceeds
max_groups.
- count_for_target_step(target_step: int) int#
Return how many slots are stamped with
target_step.
- promote_ready_group(*, to_target_step: int) Optional[int]#
Re-stamp a finished group from a later step so it lands in this one.
Fills a hole left by a dropped prompt with generation that is already done, which is the point: the step closes immediately instead of waiting out a fresh rollout. The step it was borrowed from is returned so the caller can repay it, and the caller must – an unrepaid loan is the same hole one step later, carried forward until it reaches the last step, which has nobody to borrow from.
The furthest future step is preferred because it is due last and so has the most slack to absorb the repayment. Only ready slots qualify: an unready one is a reservation whose rollout is still running, so moving its stamp would hand this step the same wait it is trying to avoid.
Promotion can only make a step fresher, never staler. Slots are appended in dispatch order and the trainer version never decreases, so a group stamped for a later step was generated at a weight version at least as new as the ones already in this step.
Synchronous on purpose.
removedeletes its indices before its own first await, so as long as nothing here yields, the index picked below cannot be shifted out from under the write by a selection running concurrently.- Parameters:
to_target_step – Training step to re-stamp the borrowed group onto – the step that lost a prompt. Must be at or ahead of the trainer version: a group re-stamped onto a step already trained is never selectable again and would only be evicted.
- Returns:
The target step the group was taken from, or None when no later step has a ready group to lend.
- size() int#
Return the number of prompt-group entries currently held.
- __len__() int#
- async _clear_samples_unlocked(
- cut: nemo_rl.algorithms.async_utils.replay_buffer.DataPlaneMutationCut,
- *,
- sample_ids: list[str],
Clear rows while the caller owns the provided live mutation cut.