feat(nemo-gym): train on rollouts from external agent harnesses - #3407
Draft
ananthsub wants to merge 9 commits into
Draft
feat(nemo-gym): train on rollouts from external agent harnesses#3407ananthsub wants to merge 9 commits into
ananthsub wants to merge 9 commits into
Conversation
ananthsub
force-pushed
the
ananthsub/token-id-capture-rebased
branch
from
July 29, 2026 12:41
a1feb66 to
8b4f737
Compare
This was referenced Aug 5, 2026
cmunley1
reviewed
Aug 7, 2026
ananthsub
force-pushed
the
ananthsub/token-id-capture-rebased
branch
from
August 18, 2026 16:57
a6c65af to
de2d246
Compare
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
An agent harness we do not control, such as the Claude Code CLI, returns a transcript with no token ids. Training on its rollouts needs the exact ids and log probabilities the policy sampled, so Gym captures them at the model server and rebuilds each rollout's model calls into one contiguous Responses payload. This is the NeMo-RL side of that: correlate the rollouts, read the rebuilt response back, and report what the rebuild kept. In NemoGymEnvironment: - Stamp each rollout with a correlation id before dispatch, so the harness's model calls can be attributed to the rollout that produced them. Ids are derived per shard, so two actors cannot mint the same one. - After each row is yielded, replace response.output with the rebuilt items when the producing agent opted into capture. Agents that already carry token ids inline are left alone: replacing them with a reconstruction would silently train on the reconstruction wherever the two differ. - Accumulate the per-rollout capture metrics across the stream and emit them with the timing metrics, since the generator has no end-of-loop. - Retire a rollout's records once they have been read. Failure handling follows the same rule throughout: one malformed capture must degrade its own sample, never the batch. A failed rebuild keeps its records as evidence and masks the sample rather than training on a partial rollout, and the call is wrapped because the builder cannot contain a corrupt file or an unreadable directory. The recipes pin skip_tokenizer_init=false. Gym serves model calls over vLLM's OpenAI-compatible HTTP server, which needs a tokenizer to apply the chat template; without one every model call fails and the harness generates nothing. configure_generation_config already defaults this to false when expose_http_server is set, but setup_nemo_gym_config sets expose_http_server after that default is resolved, so the check never sees it. Four recipes ship: _smoke (0.5B, 2 steps, the minimum end-to-end check), _tools (3B, adds a hermes tool parser so the harness actually calls tools), _supply (3B, the same plus prefix supply), and an unsuffixed config for longer runs. The two without a tool parser produce single-turn rollouts by design. Depends on the Gym token-capture stack beginning at NVIDIA-NeMo/Gym#2124. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Gym exposes the per-record step its own rollout collection runs, so the copy of it here goes away: the build, the metrics, the mask verdict and the retention all live on that side and the two can no longer drift. The step takes the finished record and a TokenSource, so nothing here reads Gym's config. Today that source is the colocated file store; a data plane of our own passes its own source instead and nothing else in this path changes. Whether a rollout needs token ids attached is decided from the rollout, by whether its output items already carry them. The local check for the producing agent's opt-in flag is gone with it, which is what makes a batch mixing native agents and external harnesses work: each rollout gets what it needs, and a native agent that later moves onto capture starts being rebuilt with no change here. That check could not have worked anyway, since it needed Gym's resolved server configs and this actor only ever holds the env block. Rollout identity moves to Gym's dedicated _ng_rollout_id key. It was synthesized by writing a per-actor random base into the task index and a counter into the rollout index, overloading fields that mean "which dataset row" everywhere else. The id is unchanged in shape and still unique per actor and across steps. mask_sample is read from the top of the rollout record as well as from the instance config. Gym's token capture uses the former, because rollout collection does not own the agent's instance config. An agent that judges its own rollout unusable still sets the latter, and either one masks. The example configs move to the nested token_id_capture block. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
The harness builds its own requests and never reads the row, so its calls reach the engine with no sampling params and create_chat_completion refuses them. Set sampling_overrides on that server, interpolated from policy.generation so the value has one source. Scoped to the blackbox run configs rather than the shared Gym training config: a native agent's request already carries the right params from the row, and replacing them would discard a validation profile the framework chose deliberately. Also drops the two per-parameter publication keys, which were read through defaults that happened to equal the configured values, so a run could not tell a wired pin from an unwired one. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Adopt Gym's finalized capture lifecycle and pluggable source contract, preserve rollout grouping and multimodal inputs, and make the nightly smoke test reliably exercise optimizer updates. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Exclude local validation recipes and remove deployment-specific filesystem path checks from the production integration. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Let the framework construct its TokenSource inside the rollout consumer actor while preserving the default file-backed source. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Keep NeMo-RL responsible only for actor-local source construction now that Gym rejects configured framework sources at its schema boundary. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Carry source prompt identities through dynamic sampling so harness-specific prompt changes cannot collapse GRPO advantages into singleton groups. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
Pin the declared Gym dependency stack and adapt rebased GRPO and rollout tests to the current async generation interfaces. Signed-off-by: Ananth Subramaniam <ansubramania@nvidia.com>
ananthsub
force-pushed
the
ananthsub/token-id-capture-rebased
branch
from
August 27, 2026 22:27
8335e15 to
93f23d3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Trains on rollouts from an agent harness that drives its own model calls and returns no token IDs.
Gym captures the exact token IDs and log probabilities inside its model server and rebuilds each rollout into a trainable Responses payload. NeMo-RL assigns rollout identity, constructs the
TokenSourceinside its rollout actor, consumes the rebuilt result, and retires the frozen capture only after the rollout consumer accepts it.Depends on the Gym stack: NVIDIA-NeMo/Gym#2190, #2124, #2125, #2126, #2341, #2180, #2181, and #2349.
Control and data flow
sequenceDiagram autonumber participant RL as NemoGym actor participant G as Gym run_examples participant H as Agent harness participant M as Gym model-server worker participant K as TokenSink participant S as TokenSource participant T as Trainer consumer RL->>RL: Run token_source_factory inside actor RL->>RL: Assign _ng_rollout_id RL->>G: Dispatch selected rows G->>H: Run agent H->>M: Model calls under /training-token-capture/{rollout_id} M->>K: Append TokenEntry records G-->>RL: Return rollout without token metadata RL->>S: Freeze and read snapshot RL->>RL: Rebuild response and preserve original media items RL-->>T: Yield trainable rollout T-->>RL: Request next rollout RL->>S: Conditionally retire accepted snapshotIntegration behavior
token_id_capture.all_agents: trueopts every agent into capture for training-only runs._build_token_capture_sourceuses a localTokenCaptureStorewhentoken_id_capture.diris configured.token_source_factorytospinup_nemo_gym_actor. Ray serializes the factory with the actor constructor, and the factory runs inside the actor process and virtual environment.TokenSourceprotocol. It may wrap a TransferQueue handle or any other framework-owned transport client.TokenSinkandLineageStoreproxies in their own processes. Their adapter package must be installed in the model-server environment.shutdown()closes the actor-owned source before stopping Gym servers.Source construction
The default file adapter is selected with
token_id_capture.dir. A framework data plane injects an actor-local factory instead of naming a source class in Gym configuration.The source must implement Gym's
TokenSourceprotocol:freeze, conditionaldrop, and idempotentclose. The model-server side uses the correspondingTokenSinkandLineageStoreprotocols supplied by the framework.Metrics
The integration reports
token_capture/rebuilt_fraction,rollouts_rebuilt,rollouts_unbuilt,calls_per_rollout_mean,chains_per_rollout_mean,delivered_fraction_mean,quarantined_fraction_mean,empty_generation_calls,parent_link_failures,masked_rollouts, andincomplete_rollouts.