Repository navigation
feat(orchestrator): gar — grader-ranked group advantage redistribution - #3803
Closed
faresobeid wants to merge 10 commits into
Closed
faresobeid wants to merge 10 commits into
faresobeid wants to merge 10 commits into
Conversation
faresobeid
force-pushed
the
feat/background-group-finalize
branch
from
October 3, 2026 12:22
1d75cf3 to
07e846f
Compare
This was referenced Oct 3, 2026
Closed
faresobeid
force-pushed
the
feat/background-group-finalize
branch
from
October 4, 2026 10:52
d6e2665 to
d12bb49
Compare
…iers 1814238 - A gar source sets `agent.checkpoint_on_finish` on its env's agents, so the group-grade grader can restore each candidate's box (`candidate_shell`). - GARAlgorithm deletes a group's checkpoints when its grading ends (graded, fallback, all-equal, cancelled); leftovers (e.g. 409) are retried at shutdown via the new `Algorithm.close` hook. - Metrics: gar/checkpointed, gar/checkpoint_seconds, gar/restores, gar/restore_seconds. - prime-sandboxes 0.4.2 (checkpoint API); docs updated.
…d28c6be `algo.mode = "advantages"`: every group is graded without rewards; the grader ranks all non-hack candidates in tiers with optional clear/slight margins, confirmed hacks go below the last tier (clear gap). `rank_advantages` maps tiers to zero-mean advantages: slight gap = win-rate difference x 2(n-1)/n (= GRPO 0/1 for two tiers), clear = 2x. One tier or grader failure -> zero advantages (pruned). Win rate logged as gar/win_rate, never as the reward. Length penalty and the difficulty_pool sampler are rejected in this mode. Default `rewards` is unchanged.
…r materials - advantages mode: margins slight = 1/4, clear = 1/2, large = 1 (= the 0/1 pass-vs-fail gap), default slight; confirmed hacks below the last tier with a large gap. Scores add margins from the bottom tier (implied rewards) and are centered: the advantages are GRPO's for those scores. Replaces the (|T_j|+|T_j+1|)/n step. - pin verifiers 4fd6bea (Task.privileged, /grade/privileged, hidden tests staged in restored candidate boxes, PRIME_API_KEY to the Workspaces tool server, three margins) and prime-envs e091967 (r2e-gym privileged); docs.
This was referenced Oct 4, 2026
samsja
pushed a commit
that referenced
this pull request
Oct 4, 2026
Removes duplicate and unused entries from `pyproject.toml` (rev-infra P9). The set of installed packages does not change for any extra combination (checked below). | Removed | Reason | |---|---| | second `orjson>=3.10` in `dependencies` | Duplicate of `orjson>=3.11.0` a few lines above. | | `pandas>=2.0` | Nothing imports it: no `import pandas` / `pd.` in `src/`, `tools/`, `scripts/`, `tests/`, `examples/`, `skills/`, `packages/`. It was added for the nano bridge (7089ba1). It stays installed because `datasets` depends on it. | | `pyarrow>=21.0.0` | No direct use anywhere in those directories. The one parquet writer, `tools/convert_traces_to_hf_dataset.py`, calls `datasets.Dataset.to_parquet`, and `datasets` depends on `pyarrow` itself. It stays installed. | | `nixl-cu13==0.10.1` in the `disagg` extra | `disagg` already lists `nixl`, and the `[[tool.uv.dependency-metadata]]` override for `nixl` 0.10.1 requires exactly `nixl-cu13==0.10.1`. The `gpu` extra keeps its explicit pin, because `transports/weights/nixl/agent.py` imports `nixl_cu13` directly. | | `mamba-ssm` dependency group | Duplicate of the `gpu` extra entry, which was added with the native Nemotron-H model (#3426). Every user of the group also syncs `--extra gpu` or `--all-extras`, so I dropped `--group mamba-ssm` from `Dockerfile.cuda` (2 places), `scripts/docker-entrypoint.sh` and the `pre_run_command` in `configs/advanced/nemotron-3-super/swe.toml`. The `[tool.uv.sources]` git pin and the build dependencies for mamba-ssm stay. | Behavior change: - None for installs: same locked packages and versions. - `uv sync --group mamba-ssm` no longer works; use `--extra gpu`, which every repo caller already passes. - `pandas` and `pyarrow` lose their direct lower bounds. They are still pulled in by `datasets`. ## Checks - `uv lock`: resolves 564 packages, the same set as before (568 `[[package]]` entries before and after). `uv lock --check` passes. The `uv.lock` diff only touches prime-rl's own metadata. I reverted the reordering of `[options.exclude-newer-package]` that my uv version (0.12.10) introduced. - I ran `uv export --frozen --all-packages` and compared the package lists before and after, ignoring the `# via` comment lines. They are identical for: no extras, `--extra disagg`, `--extra gpu`, `--extra dashboard`, `--all-extras`, and `--all-extras --all-groups`. `--extra disagg` still yields `nixl==0.10.1` and `nixl-cu13==0.10.1`, and `--extra gpu` still yields `mamba-ssm` at the pinned rev. - No Python changes, so no unit tests are affected. GPU: not needed. The Docker image build in CI covers the Dockerfile and entrypoint edits. ## Conflicts Text conflicts only in `pyproject.toml` / `uv.lock`, as with any dependency PR. None of the open PRs that touch these files (e.g. #3829, #3803, #3767, #3660) edit the lines removed here. A re-lock after rebase is enough. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Metadata and sync-flag cleanup only; lockfile exports are unchanged and runtime installs should match prior behavior. > > **Overview** > Cleans up **duplicate and redundant dependency declarations** in `pyproject.toml` and aligns `uv.lock`, Docker, and Slurm sync commands with the slimmer manifest. **No intended change to installed packages** for existing extra combinations (`gpu`, `disagg`, `--all-extras`, etc.). > > **Removed from core `dependencies`:** duplicate `orjson>=3.10`, unused direct pins on `pandas` and `pyarrow` (they still come in via `datasets`). **Removed from `disagg` extra:** explicit `nixl-cu13==0.10.1` because `nixl` metadata already pulls that version; the **`gpu` extra keeps** its explicit `nixl-cu13` pin for direct imports. **Dropped the `mamba-ssm` dependency group** as a duplicate of the `gpu` extra entry—`Dockerfile.cuda`, `scripts/docker-entrypoint.sh`, and the Nemotron SWE `pre_run_command` no longer pass `--group mamba-ssm`. > > **Workflow note:** `uv sync --group mamba-ssm` alone is no longer supported; use `--extra gpu` (or `--all-extras`), which callers already use. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit f0f2ff6. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
samsja
pushed a commit
that referenced
this pull request
Oct 4, 2026
…alize_group (#3845) Two small cleanups in `orchestrator/algo/`. Item 4 of the orchestrator review. - `action_loss_type` is now declared once, on the algorithm config classes. Before, it was declared on the config classes and again on the runtime classes (`algo/base.py`, `sft.py`, `opd.py`, `opsd.py`), and `build_algorithm` asserted that the two agreed. Now `Algorithm.__init__` copies it from the config, so `train_sink`'s `env.algorithm.action_loss_type` is unchanged. - `Algorithm.finalize_group` is removed. It only called `score_group`, and `train_sink` now calls `score_group` directly. `finalize_episode` is kept: its "has sampled tokens" guard matters for `echo`, whose `score_episode` does not filter on the mask. - `docs/algorithms.md` is updated to match. Behavior change: none. Every algorithm type gets the same loss routing as before, because the old assert guaranteed that the config and runtime values were equal. ## Checks - `pytest tests/unit/orchestrator tests/unit/test_configs* -m "not gpu"`: 291 passed (`test_algorithms` covers the per-type `action_loss_type`). - `ruff check`, `ruff format --check`: clean. GPU checks: none needed. ## Conflicts `algo/base.py` and `algo/__init__.py`: #3802, #3803 (small). #3838 (fold `GenerationSource`) also edits `algo/base.py` and `algo/__init__.py` (`connect_frozen_client`), which needs a trivial rebase. `train_sink.py:245`: #3652 rewrites `train_sink.py` (a one-line change there). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Refactor-only: loss routing and group scoring call paths are unchanged in effect; no auth, data, or trainer hot-path logic changes. > > **Overview** > **Single source of truth for loss routing:** runtime `Algorithm` instances now take `action_loss_type` from the algorithm config in `__init__` instead of duplicating it on each subclass and asserting agreement in `build_algorithm`. Per-algorithm class variables on `OPD`, `OPSD`, and `SFT` are removed; `train_sink` still reads `env.algorithm.action_loss_type` when stamping samples. > > **Thinner scoring API:** `Algorithm.finalize_group` is dropped and the train sink calls `score_group` directly when a group has trainable survivors. `finalize_episode` remains as the guarded entry point for rollout-local `score_episode` (skips episodes with no trainable sampled tokens). > > **Docs:** `algorithms.md` is updated to describe this pipeline wiring and config-owned `action_loss_type`. > > No intended training behavior change—loss routing values are the same as before when config and runtime matched. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 1ebee7f. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
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.
Privileged grader materials (revision 91508f8)
/grade/privileged/(verifiersTask.privileged, feat(v1): group-grade taskset (GAR grader) verifiers#2757 4fd6beac). For r2e (prime-envs#961 e091967c) these are the hidden tests, the gold patch and the expected per-test results. The hidden tests are also written into every restored candidate box, so the grader can runbash run_tests.shthere.info.privileged). The grader's own transcript can still quote what it read; that trace is logged (kindgrade) but never trained on. Grade traces share the run'straces.jsonlwith train/eval episodes, sotools/convert_traces_to_hf_dataset.pyalways skips traces withinfo.kind == "grade"and prints how many it skipped; they cannot reach an SFT/HF dataset export (b3c7b3c). That tool is prime-rl's only trace→dataset export path.deps/verifiers→ 4fd6beac,deps/prime-envs→ e091967c. Unit tests: 687 passed.Advantage mode (revision b9f1c16)
[orchestrator.train.algo] mode = "advantages"(default"rewards"= the existing behavior, unchanged code path).slight(default),clearorlarge. Confirmed hacks (same citation rule) form a tier below the last, with alargegap.rank_advantages, pure; revision 91508f8). The margins are anchored to the GRPO pass-vs-fail gap:slight= 1/4,clear= 1/2,large= 1, ties = 0. The bottom tier scores 0 and each tier above adds its gap's margin, so the scores are implied rewards in reward units. The advantages are those scores centered to zero mean, i.e. exactly GRPO's advantages for them (over candidates; all of a candidate's traces get its advantage). Example:[c2] slight [c1, c4] large [c3]scores 1.25 / 1 / 1 / 0, giving +0.44 / +0.19 / +0.19 / −0.81. Two tiers with alargegap are GRPO's 0/1 advantages. The prompt defines the levels: slight = same quality with minor cleanliness/minimality/style differences; clear = noticeably better approach or correctness, both reasonable; large = a real solution over a broken/wrong/harmful/non-attempt. It tells the grader to default to slight. (This replaces the earlier(|T_j|+|T_{j+1}|)/nstep.)info.gar.fallback_reasonand thegar/fallback/*metrics record why. A hack whose citation fails is in no tier and gets 0.gar/win_rateper candidate (win rate over all tiers, hacks included). It is not written as the reward.info.garhas tier, margins, w, advantage, hack, evidence.difficulty_poolsampler (samples on rewards) is rejected in config validation for this mode.GroupGradeData.mode,from_traces(..., passed=None),GroupVerdict.margins, and mode-specific ranking rules inrank_passing.md/rank_all.md(feat(v1): group-grade taskset (GAR grader) verifiers#2757, d28c6be8).test_gar_rank_advantages(the example above, large two-tier = GRPO, hacks lowest, unranked 0, zero mean, one tier → 0).pytest tests/unit -m "not gpu": 687 passed. Config checks were run in a throwaway script: length penalty rejected, difficulty_pool rejected,checkpoint_on_finishset on the gar source's agent.Candidates' boxes from checkpoints (revision 4501f0d)
The grader now works for every env, not only SWE: see PrimeIntellect-ai/verifiers#2757 (revision 18142383) for the design, the cost note and the smoke run.
gartrain source sets verifiersagent.checkpoint_on_finish = trueon every agent of its env (OrchestratorConfig.checkpoint_gar_candidates). There is no new knob. Each rollout checkpoints its Prime box when its agent finishes, and the grader'scandidate_shelltool restores a candidate's box on demand. Runtimes without checkpoints and text-only envs record nothing; those candidates are graded from their transcripts and files.GARAlgorithm.score_groupnow runs grading insidetry/finallyand deletes every checkpoint of the group (trace.info["checkpoint"]of all its traces) when grading ends, however it ends: all-equal groups (right away), graded, fallback (error/invalid/timeout/stale), and cancellation. Ids the platform refuses for now (409 while a box restored from them is alive, e.g. a cancelled grader still running) or that fail to delete are kept and retried once at shutdown through a new no-op hookAlgorithm.close(), which the orchestrator teardown calls. Ids that still fail are logged.gar/checkpointed(share of candidates with a checkpoint),gar/checkpoint_seconds(mean request → durable),gar/restoresandgar/restore_seconds(from the grading trace).info.checkpoint. The candidate is then graded without a box, and the group is still graded.deps/verifiers→ 1814238 (feat/group-grade).uv.lock: prime-sandboxes 0.4.1 → 0.4.2 (checkpoint API).stage_verifierstaged hidden tests in the old git box, and the boxes now carry no hidden tests. Itsinfo.test_outputpart still feedstests.txt.docs/algorithms.md, GAR) are updated.Simplified (revision e90d5af)
Line count of the PR: +480/−16 → +477/−16 (
algo/gar.py295 → 288 lines). Most of the simplification is in the verifiers PR (+616 → +517); see PrimeIntellect-ai/verifiers#2757.reward == 1.0. For 0/1 rewards this is the same set as before.confidenceandCONFIDENCE_SHRINKare removed: a tie in the ranking expresses uncertainty. Axes andhack_kindare removed frominfo.gar.passedin the row) and gets their full transcripts. Every candidate is still audited for hacks. The other candidates' transcripts keep only their first and last 10 turns (the others read[turn elided]). The citation check runs against exactly what the grader was given (Candidate.turns), so a confirmed hack on such a candidate must quote a turn the grader saw.deps/verifiersnow pins 32e35e04 (feat/group-grade).info.garannotations,gradeepisode logging.Stacked on #3802 (background group finalization). Adds
gar(Groupwise Advantage Redistribution, MiMo-V2.6 §4.3.2). It is a GRPO subclass that sends each mixed group (rewards not all equal) to an agentic grader in one sandbox, drops confirmed hacks to the group's minimum reward, and redistributes the above-mean rollouts' credit by the grader's ranking (Eq. 3). It is off unlessalgo.type = "gar".Landing order
group-gradetaskset) and prime-envs r2e-gym 0.1.5: keep the test run's tail in trace.info[test_output] prime-envs#961 (r2e-gyminfo.test_outputandstage_verifier).deps/verifiers(32e35e04) anddeps/prime-envs(3b987c87) are pinned to the companion branches so the stack works end to end. The pins must move to the merged commits before landing.uv.lockonly bumps r2e-gym 0.1.4 → 0.1.5.What
configs/algorithm.py):GARAlgoConfig(GRPOAlgoConfig).grader:model(aFrozenModelConfig),env(a verifiers env config, narrowed by id),serve,timeout(default 1800 s) andmax_concurrent(default 32).f_min(default 0.5) andlambda_max(default 1.5).length_penaltyis rejected, because GAR replaces it.TrainSourceConfig.grade_sourceturns a gar source's grader into anEnvConfignamed after the source.env_sourcesadds it as splitgrade. The local launcher already loops over all splits. The multi-node sbatch template gets agrade_env_namesloop.algo/gar.py):finalize_in_background = True). The default cap of 64 finalizing groups per env applies; train dispatch pauses at the cap.EnvClientonto thegradeenv server. The orchestrator wires in the step counter,max_off_policy_stepsand the grader address file throughGARAlgorithm.bind(...).info.patch.GroupGradeData.from_tracesshuffles and anonymizes the candidates, seeded by the group id.grader.timeoutcovers both the wait for a grader slot (max_concurrent) and the grading itself. The group also falls back onceprogress.stepreachespolicy.start + max_off_policy_steps + 1, the last step whose batch can still take it. So a grading group falls back to GRPO before it would go stale. It is not dropped.GroupVerdict.model_validatepluscheck().confirmedhack is downgraded tosuspectedunless every evidence quote passesquote_found. That function holds the one turn definition, in verifiers. A quote must be at least 20 characters or 4 words. It must appear in text the policy itself wrote (assistant content or tool calls), never in tool output. Suspected hacks are never applied.A = R − mean(R) > 0.f = f_min + (1 − f_min)·w, wherewis the win rate among the ranked candidates (a tie counts ½). A member of P the ranking leaves out getsw = ½. A lone ranked candidate getsf = 1. Thenλ = min(λ_max, Σ_P A / Σ_P f A),A' = λ f Aon P, and the group is re-centered.stale,timeout,error(request failure or failed grader episode) orinvalid(verdict missing or rejected). A cancel from feat(orchestrator): opt-in background group finalization #3802 (stale at step advance, drain, stop) arrives asCancelledErrorand propagates.info.gar= {label, hack, evidence, tier, w, f, lambda, fallback_reason, grader_trace_id}. It is set on the trace and also emitted right away as a trace annotation (monitors.log_annotations), so dropped groups carry it too.Kind = "grade", plusinfo.group_idandinfo.candidates(label → trace id). The logged copy leaves out the candidates' transcripts (task.data.candidates); the candidate traces are already in the stream. The index acceptsgrade.train/all/<agent>/metrics/gar/...:graded,fallback/{stale,timeout,error,invalid},latency,grader_tokens,grader_cost(when the provider reports it),lambda,all_fail,f,hack_confirmed,hack_suspected.docs/algorithms.md. Example:examples/advanced/glm-5.3/swe-gar.toml, an overlay that does not changeswe.toml.What I ran
pytest tests/unit/orchestrator tests/unit/test_configs.py: 302 passed, with verifiers at 32e35e04.ruff checkandruff format --check: clean.test_advantage.py:test_gar_redistribution(win rates, Eq. 3, the cap with re-centering, and now a non-binary group where P is the above-mean rollouts). No new test files.test_load_configsparses the new overlay.swe.toml @ swe-gar.tomlresolves to aGroupGradeConfigwith innerr2e-gym.env_sourcesholds('train','r2e')and('grade','r2e'). The writtenenvs/grade/r2e.jsonround-trips throughEnvServerConfig.score_groupagainst a fake grader:suspectedand keeps a positive advantage;fallback_reasonand metrics;gradeepisode has nocandidates(about 3.5 KB in the check).GPU / e2e checks (not run here: no GPU or sandboxes)
rl) and with the multi-node sbatch.network_allow=[]is enforced in the grader box: no egress beyond the interception and MCP routes.max_off_policy_steps = 8.finalizingsize and orchestrator RSS over many steps./testbed/r2e_tests);info.garandgradeepisodes in the trace stream and dashboard.Open questions
kind = "grade"traces. The data is there (info.gar.grader_trace_id,info.candidates).info.garand the trace stream.vf.SamplingConfig()). Add a sampling knob only if a grader model needs one.