[recipe] feat: add nemo_gym/browser — step-wise GRPO on the NeMo Gym interactive_browser environment - #138
Draft
waple0820 wants to merge 2 commits into
Draft
Conversation
…interactive_browser environment The sibling nemo_gym recipe delegates a whole rollout to NeMo Gym through RolloutCollectionHelper and receives a finished result. A browser rollout runs for tens of steps against live sites, and every step can fail for reasons that have nothing to do with the policy: the environment session is lost, a page never loads, the judge endpoint is unreachable. This recipe keeps the loop on the verl side, using verl's own ToolAgentLoop, so the trainer can bound each step, classify an infrastructure failure while it happens, and resample instead of learning from it. Both recipes target the same environments and can run side by side: they attach to different verl extension points (agent_loop_manager_class vs default_agent_loop) and share no state. Nothing under nemo_gym/ is modified. * browser_agent_loop.py — BrowserTool speaking the NeMo Gym HTTP contract (/seed_session, browser_*, /verify), one aiohttp cookie jar per rollout so a rollout maps to exactly one environment session; BrowserToolAgentLoop adds an episode deadline and per-sample env_invalid flags. * judge.py — binary LLM judge for open-ended tasks, because the environment's verifier is deterministic-only today. An unusable verdict is reported as such rather than as a zero. * group_stats.py — grpo_env_aware advantage estimator that keeps flagged samples out of the group baseline. Registered, opt-in; stock GRPO is untouched. * dataset.py — maps NeMo Gym rollout rows onto tools_kwargs. * prepare_webvoyager_data.py — task list (Hugging Face or local JSONL) to rollout inputs. No task data is committed. * tests/ — offline: no GPU, no browser, no NeMo Gym server. Requires a NeMo Gym checkout providing resources_servers/interactive_browser (NVIDIA-NeMo/Gym#1865, not yet merged) and a newer verl than the sibling recipe (V1 trainer, vLLM 0.18); pinned in its own REQUIRED_VERL.txt. Signed-off-by: waple0820 <232305951+waple0820@users.noreply.github.com>
…nvironment smoke test Registered advantage estimators only receive non_tensor_batch for gdpo, so the env_invalid flags never reached grpo_env_aware. Two routes around that, both opt-in and both documented: * NEMO_GYM_BROWSER_DROP_INVALID=1 — a rollout still invalid after its retries returns nothing, and the V1 trainer skips a sample whose agent loop returned nothing, so the failure never enters the batch. No patching involved; this is what the submit script uses. * patches.py + sitecustomize.py — wrap compute_advantage and dispatch to grpo_env_aware only for batches that actually carry the flags, forwarding everything else untouched. Ray workers are separate processes, hence sitecustomize rather than a driver-side import (same reason as nemo_gym/server_patch.py). Disable with NEMO_GYM_BROWSER_DISABLE_PATCHES=1. Both disappear once verl passes non_tensor_batch to registered estimators; the two-line change is quoted in the README. Also adds smoke_environment.py: walks one episode over the same HTTP contract the agent loop uses (seed, act, observe, verify) with no GPU and no policy, so an environment problem is distinguishable from a training problem before a node is spent on it. Tests cover the routing wrapper, including that a batch without flags is forwarded untouched. Signed-off-by: waple0820 <232305951+waple0820@users.noreply.github.com>
waple0820
force-pushed
the
recipe/nemo-gym-browser-stepwise
branch
from
August 25, 2026 03:49
fb2fc60 to
febc230
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.
What this adds
nemo_gym/browser/— step-wise GRPO training on the NeMo Gyminteractive_browserenvironment. The policy drives a live browser through one tool (observe/navigate/click/type/finish) and is scored per episode.Nothing under
nemo_gym/is modified. The only change outside the new folder is one row in the root README.Why a second path next to the existing
nemo_gymrecipeThe existing recipe hands a whole rollout to NeMo Gym through
RolloutCollectionHelperand receives a finished result. That fits short, deterministic environments.A browser rollout runs for tens of steps against live sites, and every step can fail for reasons unrelated to the policy: the environment session is lost, a page never loads, the judge endpoint is unreachable. Those failures arrive as
reward=0, indistinguishable from a policy that simply did not solve the task. We measured what that costs: on a 64-way-concurrency run, 1172 of 1280 rollouts across 20 steps failed in session reset, and because each one entered GRPO as a legitimate zero it depressed the group baseline and inflated the advantage of the survivors — the batch trained away from correct behaviour.So this recipe keeps the loop on the verl side, using verl's own
ToolAgentLoop: the trainer bounds each step, classifies an infrastructure failure while it happens, and resamples instead of learning from it.The two paths attach to different verl extension points —
agent_loop_manager_classversusdefault_agent_loop— share no symbols, and can be used side by side.Layout
browser_agent_loop.pyBrowserToolspeaking the NeMo Gym HTTP contract (/seed_session,browser_*,/verify), oneaiohttpcookie jar per rollout so a rollout maps to exactly one environment session;BrowserToolAgentLoopadds an episode deadline and per-sampleenv_invalidflagsjudge.pygroup_stats.pygrpo_env_awareadvantage estimator keeping flagged samples out of the group baselinedataset.pytools_kwargs(subclasses the existingNeMoGymJSONLDataset)prepare_webvoyager_data.pyconfigs/,submit_webvoyager.sh,config.env.exampletests/Reward
Two paths, chosen per task. A task carrying
verifier_metadata(final_url,url_contains,dom_contains,answer_equals) is scored by the environment's own/verify. Everything else is scored by a binary LLM judge in the recipe, because the environment's verifier is deterministic-only today. If NeMo Gym grows a judge-backed verifier, this should move there and the recipe should just read/verify.Environment failures, and the one upstream gap
Classification, resampling and loss masking work as-is. Keeping flagged samples out of the GRPO baseline needs the flags to reach the estimator, and registered estimators receive
non_tensor_batchonly forgdpo(verl/trainer/ppo/ray_trainer.py, theelsebranch ofcompute_advantage). The recipe ships two ways around that, both opt-in:NEMO_GYM_BROWSER_DROP_INVALID=1— a rollout still invalid after its retries returns nothing, and the V1 trainer skips a sample whose agent loop returned nothing, so the failure never enters the batch. No patching; this is what the submit script uses.patches.py+sitecustomize.py— wrapcompute_advantageand dispatch togrpo_env_awareonly for batches that actually carry the flags, forwarding everything else untouched, so other recipes and stock GRPO runs are unaffected even with the module loaded. Ray workers are separate processes, hencesitecustomizerather than a driver-side import — the same reasoning asnemo_gym/server_patch.py, which notes it is "only instantiated when [the recipe] is active, so non-recipe users are unaffected".Both disappear if verl passes
non_tensor_batchto registered estimators, which I would be happy to send as a separate two-line PR:An all-zero response mask is deliberately not used to infer invalidity: a policy that stops on its first token also produces zero tokens, and that zero is a legitimate outcome that belongs in the baseline. There is a test for exactly that case.
Dependencies
resources_servers/interactive_browser. That PR is open, not merged. Until it lands,NEMO_GYM_ROOTmust point at that branch — happy to hold this PR until then.30119a25versus695ac0eb): this path needs the V1 trainer and vLLM 0.18, so it carries its ownREQUIRED_VERL.txtand usesverlai/verl:vllm018.latest.prepare_webvoyager_data.pypulls it.Validation
Internal run, 2026-07-21, Qwen3-8B, 16×H100, 60 steps, 8 tasks × 8 trajectories per step:
critic/rewards/meanrose from ~0.105 over the first ten steps to ~0.289 over the last ten. That run used our own environment service; this PR is the port onto the upstreaminteractive_browsercontract, so the numbers are context for the recipe's shape rather than a claim about this exact code path.Offline tests pass and
pre-commit(ruff) is clean:pytest recipe/nemo_gym/browser/tests -q # 8 passedsmoke_environment.pywalks one episode over the environment's HTTP contract (seed, act, observe, verify) with no GPU and no policy, so an environment problem is separable from a training problem before a node is spent: