nemo_rl.experience.rollout_manager#
Module Contents#
Classes#
How :meth: |
|
Retry budgets for one prompt, resolved from |
|
Counters describing what the retry policy has been doing. |
|
Deadlines for the blocking waits inside one rollout. |
|
|
|
Manages per-prompt multi-turn rollouts, producing a PromptGroupRecord per call. |
|
Manages per-prompt NeMo-Gym rollouts, producing a PromptGroupRecord per call. |
|
Routes to AsyncRolloutImpl (native async) or AsyncNemoGymRolloutImpl (NeMo-Gym), and pushes results to a TQReplayBuffer. |
Functions#
Wrap a generation error in the typed failure its class implies. |
|
Gather coroutines, cancelling the remainder as soon as one fails. |
Data#
API#
- nemo_rl.experience.rollout_manager.TokenizerType#
None
- class nemo_rl.experience.rollout_manager.RolloutOutcome#
Bases:
str,enum.EnumHow :meth:
RolloutManager.generate_and_pushfinished for one prompt.Initialization
Initialize self. See help(type(self)) for accurate signature.
- COMMITTED#
‘committed’
- SKIPPED#
‘skipped’
- class nemo_rl.experience.rollout_manager.RolloutRetryPolicy#
Retry budgets for one prompt, resolved from
async_rl.rollout_failure.The attempt budgets are required. They previously defaulted to 1/1/1, which contradicted
RolloutFailureConfig’s 5/2/3 and put a second set of defaults in the codebase – a reader here came away believing the shipped budget was 1. The only place a retry default lives isRolloutFailureConfig; callers that need the historical no-retry behaviour ask for it by name via :meth:single_attempt.- max_infra_attempts: int#
None
- max_data_attempts: int#
None
- max_gym_row_attempts: int#
None
- backoff_base_s: float#
1.0
- max_backoff_s: float#
30.0
- max_skipped_prompts: int#
0
- max_consecutive_dropped_prompts: int#
0
- classmethod single_attempt(
- **overrides: Any,
The historical no-retry policy, with optional overrides.
An explicit choice for callers constructing a
RolloutManagerdirectly, who must not silently gain retries – not a second set of defaults.
- __post_init__() None#
- backoff_for(attempt: int) float#
Return the delay before infra attempt
attempt+ 1 (1-based attempts).
- class nemo_rl.experience.rollout_manager.RolloutStats#
Counters describing what the retry policy has been doing.
Read by the SingleController for logging. A stall or a rising redispatch count is the only externally visible sign that the fleet is degrading, so these are not optional bookkeeping.
- committed: int#
0
- skipped: int#
0
- redispatches_by_reason: dict[str, int]#
‘field(…)’
- data_retries_by_reason: dict[str, int]#
‘field(…)’
- data_failures_by_reason: dict[str, int]#
‘field(…)’
- infra_drops_by_reason: dict[str, int]#
‘field(…)’
- max_consecutive_infra_drops: int#
0
- gym_row_redispatches: int#
0
- record_redispatch(reason: str) None#
- record_data_retry(reason: str) None#
- record_data_failure(reason: str) None#
- record_infra_drop(reason: str, consecutive: int) None#
- record_gym_row_redispatch(rows: int = 1) None#
- as_metrics() dict[str, float]#
Flatten into a metric dict for the SingleController logger.
- class nemo_rl.experience.rollout_manager.RolloutTimeouts#
Deadlines for the blocking waits inside one rollout.
Resolved from
async_rl.rollout_failure.nemo_gym.rollout_timeout_sandasync_rl.rollout_failure.native.{generation,env}_timeout_s, which own the user-facing defaults.Nonemeans no deadline, reproducing the historical behaviour of waiting indefinitely.- rollout_s: Optional[float]#
None
- generation_s: Optional[float]#
None
- env_s: Optional[float]#
None
- nemo_rl.experience.rollout_manager._classify_generation_failure(
- exc: Exception,
- *,
- prompt_idx: Any,
- traj_idx: int,
Wrap a generation error in the typed failure its class implies.
The original exception is preserved as
__cause__; the prompt and trajectory coordinates are attached because a raw generation traceback does not say which rollout it belonged to.Any Ray-boundary error is infrastructure here, by context. An exception raised inside a still-living generation worker – vLLM
EngineDeadError, a CUDA OOM – arrives as a bareRayTaskErrorwhose cause the boundary degraded, soclassify_rollout_failurecan only fall through to DATA and the prompt gets two attempts instead of the five the fleet-failure path is built for.Scoped to this call site rather than widened in
classify_rollout_failure, deliberately. Globally, “unrecognized means DATA” is the right default: it is about exceptions we can inspect and do not recognize, and flipping it would retry genuine bugs everywhere. Here we have information the classifier does not – this exception came from a generation RPC, so “that shard could not serve it” is the correct reading whatever the destroyed cause was, and re-dispatching to another shard is exactly the right response. A real bug in the worker still surfaces, chained, once the bounded infra budget runs out.This also makes the two paths agree: part 2/4’s
_generate_on_shardalready mapsray.exceptions.RayErrortoGenerationUnavailable, so without this the same exception classified INFRA there and DATA here.- Parameters:
exc – The exception raised while generating a turn.
prompt_idx – Index of the prompt whose rollout failed.
traj_idx – Index of the failing generation within the prompt group.
- Returns:
GenerationUnavailablefor infrastructure failures (retriable on another shard),RolloutDataFailureotherwise.
- async nemo_rl.experience.rollout_manager._gather_cancelling_siblings(
- coros: list[Any],
Gather coroutines, cancelling the remainder as soon as one fails.
asyncio.gatherpropagates the first exception but leaves the other awaitables running detached. On the rollout path those keep occupying generation capacity for a prompt group whose result is already being discarded, so they are cancelled and drained before unwinding.- Parameters:
coros – Coroutines to run concurrently.
- Returns:
Their results, in input order.
- class nemo_rl.experience.rollout_manager._Deadline(seconds: Optional[float], description: str)#
asyncio.timeoutthat reports expiry as a typed :class:RolloutTimeout.A bare
asyncio.timeoutsurfaces expiry asTimeoutError, which is indistinguishable from aTimeoutErrorraised by the wrapped code itself. This consultsexpired()so only a real deadline breach is relabelled, and anything else propagates untouched.seconds=Nonedisables the deadline, matchingasyncio.timeoutsemantics.Initialization
- async __aenter__() nemo_rl.experience.rollout_manager._Deadline#
- async __aexit__(exc_type, exc, tb) Optional[bool]#
- class nemo_rl.experience.rollout_manager.AsyncRolloutImpl(
- tokenizer: nemo_rl.experience.rollout_manager.TokenizerType,
- task_to_env: dict[str, nemo_rl.environments.interfaces.EnvironmentInterface],
- num_generations_per_prompt: int,
- max_seq_len: int,
- max_rollout_turns: int,
- policy_generation: nemo_rl.models.generation.interfaces.GenerationInterface,
- timeouts: nemo_rl.experience.rollout_manager.RolloutTimeouts = RolloutTimeouts(),
- **kwargs: Any,
Manages per-prompt multi-turn rollouts, producing a PromptGroupRecord per call.
Each run_rollout takes one prompt and returns num_generations_per_prompt completions generated concurrently via asyncio.gather.
Initialization
- async run_rollout(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
Run num_generations_per_prompt rollouts for one prompt.
- Parameters:
input_sample – A single prompt (one DatumSpec entry).
- Returns:
PromptGroupRecord with num_generations_per_prompt completions.
- async _run_single_rollout(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
- traj_idx: int,
Run one multi-turn rollout for a single generation index.
- async _generate_response(
- message_log: list[dict],
- stop_strings: list[str] | None,
Generate a single-turn response for one sample.
- Returns:
Tuple of (assistant_message, input_lengths, gen_metrics)
- _aggregate_rollout_metrics(
- completions: list[nemo_rl.experience.interfaces.Completion],
- all_sample_metrics: list[dict],
Aggregate per-sample metrics across all completions.
- class nemo_rl.experience.rollout_manager.AsyncNemoGymRolloutImpl(
- tokenizer: nemo_rl.experience.rollout_manager.TokenizerType,
- task_to_env: dict[str, nemo_rl.environments.interfaces.EnvironmentInterface],
- num_generations_per_prompt: int,
- max_seq_len: int,
- max_rollout_turns: int,
- generation_config: nemo_rl.models.generation.interfaces.GenerationConfig,
- mask_env_flagged_samples: bool = True,
- timeouts: Optional[nemo_rl.experience.rollout_manager.RolloutTimeouts] = None,
- retry_policy: Optional[nemo_rl.experience.rollout_manager.RolloutRetryPolicy] = None,
- stats: Optional[nemo_rl.experience.rollout_manager.RolloutStats] = None,
- **kwargs: Any,
Manages per-prompt NeMo-Gym rollouts, producing a PromptGroupRecord per call.
Each run_rollout takes one prompt and returns num_generations_per_prompt completions batched through a single NeMo-Gym run_rollouts call.
Initialization
- async run_rollout(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
Run num_generations_per_prompt rollouts for one prompt.
- Parameters:
input_sample – A single prompt (one DatumSpec entry).
- Returns:
PromptGroupRecord with num_generations_per_prompt completions.
- _validate_init_params() None#
Validate initialization parameters.
- _build_inputs(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
Build N row dicts from input_sample, applying generation config params.
- async _stream_rows(
- nemo_gym_env: Any,
- pending: list[dict],
- results: list[Optional[dict]],
- total_rows: int,
- timer_prefix: str,
Dispatch
pendingrows and fill their slots inresultsas they land.- Parameters:
nemo_gym_env – The NeMo-Gym environment actor handle.
pending – Rows still awaiting a result; each carries its original
_rowidx.results – Full-length result list, mutated in place.
total_rows – Size of the original prompt group, used to validate row indices.
timer_prefix – Timer namespace forwarded to the environment.
- Returns:
The environment’s timing metrics, or None if the stream ended without them.
- async _run_rollouts(
- inputs: list[dict],
- timer: nemo_rl.utils.timer.Timer,
- timer_prefix: str,
Dispatch rows to NeMo-Gym; return completions, prompt, and metrics.
Rows that never arrive are re-dispatched on their own rather than by redoing the whole group. NeMo-Gym’s stream dies on the first failing row, so one bad row takes every later row with it; at num_generations_per_prompt=16 a naive whole group retry pays 16 generations to recover one. Completed rows are kept across attempts, which is the same shape as the legacy collector’s pending-group retry.
- _result_to_completion(
- result: dict,
Convert one run_rollouts result dict into a Completion.
- _compute_rollout_metrics(
- completions: list[nemo_rl.experience.interfaces.Completion],
- agent_name: str,
Aggregate per-sample and per-agent metrics.
- class nemo_rl.experience.rollout_manager.RolloutManager(
- tokenizer: nemo_rl.experience.rollout_manager.TokenizerType,
- task_to_env: dict[str, nemo_rl.environments.interfaces.EnvironmentInterface],
- num_generations_per_prompt: int,
- max_seq_len: int,
- max_rollout_turns: int = 1,
- policy_generation: Optional[nemo_rl.models.generation.interfaces.GenerationInterface] = None,
- generation_config: Optional[nemo_rl.models.generation.interfaces.GenerationConfig] = None,
- use_nemo_gym: bool = False,
- mask_env_flagged_samples: bool = True,
- tq_buffer: Optional[nemo_rl.algorithms.async_utils.replay_buffer.TQReplayBuffer] = None,
- timeouts: Optional[nemo_rl.experience.rollout_manager.RolloutTimeouts] = None,
- retry_policy: Optional[nemo_rl.experience.rollout_manager.RolloutRetryPolicy] = None,
Routes to AsyncRolloutImpl (native async) or AsyncNemoGymRolloutImpl (NeMo-Gym), and pushes results to a TQReplayBuffer.
Initialization
- property stats: nemo_rl.experience.rollout_manager.RolloutStats#
Counters describing retry/skip activity so far.
- set_weight_version(version: int) None#
Set the weight_version used for rollout tags.
- Parameters:
version – Trainer weight version to stamp on future rollout tags.
- async run_rollout(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
- async generate_and_push(
- input_sample: nemo_rl.data.interfaces.DatumSpec,
- *,
- target_step: Optional[int] = None,
- inflight_registry: Optional[dict[str, tuple[asyncio.Task[None], int]]] = None,
Roll out one prompt and commit it, re-dispatching on infrastructure failure.
No prompt is discarded for infrastructure reasons. An infra failure means the fleet is unwell, not the prompt, so the attempt is retried – and because each retry re-enters generation-shard selection, it naturally lands somewhere else without this method needing to know anything about shard health. Exhausting the infra budget therefore means the failure follows the prompt across the whole fleet, which is reported as fleet-wide failure rather than absorbed.
Deterministic failures get their own, much smaller budget: another shard would reject the prompt identically, so retrying mostly burns time. One retry is still worth taking because a shard under memory pressure can return an empty generation that looks deterministic and is not.
- Parameters:
input_sample – A single prompt (one DatumSpec entry).
target_step – Training step this rollout targets; stamped on the buffer slot for StalenessSampler.force_in_order.
inflight_registry – Optional controller-owned mapping from group ID to its dispatch task and start weight version.
- Returns:
COMMITTEDwhen the group reached the buffer, orSKIPPEDwhen the prompt was given up on within a budget: its data budget withinmax_skipped_prompts, or its infra budget withinmax_consecutive_dropped_prompts. ASKIPPEDprompt committed nothing, so the caller owns both its backpressure permit and the shortfall for the training step it was stamped for.- Raises:
RolloutRedispatchExhausted – The infra budget ran out and the fleet has not committed anything since
max_consecutive_dropped_promptsdrops ago.RolloutDataFailure – The data budget ran out beyond
max_skipped_prompts.