Repository navigation
feat(eval): add gym eval plan to write a benchmark's task list - #4131
Merged
Merged
Conversation
|
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. |
7 tasks done
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 6, 2026 22:03
1e42d30 to
7fce61c
Compare
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 6, 2026 22:48
7fce61c to
125f3ca
Compare
iguehring-nvidia
marked this pull request as ready for review
October 6, 2026 22:53
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 6, 2026 22:54
125f3ca to
63baa51
Compare
Contributor
|
🌿 Preview your docs: https://nvidia-preview-pr-4131-259e1873e9b7.docs.buildwithfern.com/nemo/gym |
7 tasks done
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 7, 2026 05:46
63baa51 to
342ca6f
Compare
marta-sd
reviewed
Oct 7, 2026
khazic
pushed a commit
to khazic/Gym-lao
that referenced
this pull request
Oct 7, 2026
NVIDIA-NeMo#4135) ## What does this PR do? `benchmarks/terminal_bench_2_1/prepare.py` wrote rows in `glob()` order (filesystem order) and without an id field. Gym's task identity (`_trajectory_identity`, `materialize_task`) therefore fell back to the row position, so "task 0" could name a different task on another machine or after a re-clone. This PR: - adds `task_id` to every row, set to the task's folder name (e.g. `path-tracing`). That is the id the Harbor task format uses for the same task (NVIDIA-NeMo#3673 takes the folder name as `task_id`; NVIDIA-NeMo#3730 keeps TB2.1's folder names), so the ids stay the same when TB2.1 moves to that format. Folder names are unique by construction; `task_name` (`terminal-bench/path-tracing`) is unchanged; - writes rows in sorted order; - checks that the 89 task ids are unique (`NUM_TASKS = 89` replaces the literal in the existing count check). Servers are unaffected: `TaskData` is `extra="allow"`, the Terminus and OpenCode run requests accept extra fields, and nothing reads `task_id` from TB2.1 rows today. Motivation: stable ids are required by `gym eval plan` (NVIDIA-NeMo#4131), which refuses position-based ids. With this change TB2.1 can be planned. No separate issue. ## Validation - Ran `prepare.py` via `gym eval plan --benchmark terminal_bench_2_1/terminus_2` (with NVIDIA-NeMo#4131 applied locally): 89 tasks, ids `adaptive-rejection-sampler`, `bn-fit-modify`, …; two runs produce byte-identical plans. Without this change the same command is refused with "89 of 89 tasks have no task id". - `pre-commit run --files benchmarks/terminal_bench_2_1/prepare.py`: pass. ## Rollouts N/A: data-only change. The added field is ignored by the TB2.1 resources server and both agents; the instruction, image and task folder of every row are unchanged. Happy to run a smoke rollout if reviewers want one. ## Compatibility and benchmark impact - Rollout outputs for TB2.1 now report the task's folder name as the task id instead of the row index, so tooling that joins TB2.1 results across runs by task id will see different keys for runs before and after this change. - Row order changes, so `_ng_task_index` values change. Don't resume (`--resume`) a TB2.1 run started before this change after re-preparing the data. - Scores are unaffected. ## Checklist - [x] I have read the [contributing guidelines](https://github.com/NVIDIA-NeMo/Gym/blob/main/CONTRIBUTING.md). - [x] The change is focused; unrelated "drive-by" edits are tracked as separate issues/PRs. - [x] Tests added or updated and pass locally, or N/A for docs-only / non-code changes (so CI unit/server checks pass when applicable). N/A: no existing tests cover this prepare script; validated by running it. - [x] Documentation added or updated, or N/A with justification. N/A: no documented behaviour changes. - [x] Pre-commit checks pass locally (`pre-commit run --all-files`) (so CI lint/format checks pass). Ran on the changed file. - [x] New source files include the required Apache-2.0 SPDX header. N/A: no new files. - [x] All commits have DCO sign-off (`git commit -s`) (so the DCO check passes). Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
sephmard
previously approved these changes
Oct 7, 2026
sephmard
left a comment
Contributor
There was a problem hiding this comment.
Looks good overall — nice refactor, solid test coverage, left one small comment on a test gap. Approving.
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 7, 2026 17:24
342ca6f to
d160556
Compare
Add `gym eval plan`, which prepares one benchmark and writes a plan file: every task once, sorted by task id, with a SHA-256 digest of the exact input a run receives. The plan stores ids and digests, not the inputs themselves. The format is versioned (nemo-gym.eval-plan.v1), only changes additively within a version, and `--schema` prints its JSON Schema. Task ids come from the row's task_id, problem_id or instance_id, the same precedence materialize_task uses; unlike collation there is no fallback to the row position, so benchmarks without ids are refused. Their taskset is the dataset's declared taskset, or the benchmark name when it declares none. Benchmarks with a prompt template are refused for now. prepare_benchmark is split into _datasets_to_prepare and _prepare_datasets so the plan command reuses them; its behaviour is unchanged. The plan considers only benchmark datasets among them. Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
…l prepare Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
…rror Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
Signed-off-by: Ingo Guehring <iguehring@nvidia.com>
iguehring-nvidia
force-pushed
the
iguehring/gym-eval-plan
branch
from
October 7, 2026 20:16
d160556 to
259e187
Compare
marta-sd
approved these changes
Oct 8, 2026
Contributor
Author
|
Needs a review from Tim Ruffles (who needs to join Nemo org before he can be assigned) before merging. |
Contributor
|
🔄 Merge queue validation started! You can track the progress here: https://github.com/NVIDIA-NeMo/Gym/actions/runs/37815277147 |
This branch was previously deployed
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 does this PR do?
Adds
gym eval plan, which prepares one benchmark and writes a plan file: every task once, sorted by task id, with a SHA-256 digest of the exact input a run receives.Why: Gym has no single command that answers "which tasks does this benchmark have, and with exactly what inputs?" in a stable form. The prepared JSONL depends on preparation details, its row order is not guaranteed, and many rows have no id, so collation falls back to the row position.
gym eval planproduces that answer as one file: stable task ids, a digest of each task's exact run input, and a versioned, documented format. The plan stores ids and digests, not the inputs themselves, so it stays small; a run re-prepares the data, and the digests show whether it still matches the plan.What the command does:
--benchmark NAME[/FLAVOR]lookup and runs itsprepare.py(shared code withgym eval prepare).task_id,problem_idorinstance_id, the same precedence asmaterialize_task(feat(data): collate declared tasksets #3818); the field list is now defined once, asTASK_ID_FIELDSinnemo_gym/task_materialization.py, and used bymaterialize_task, rollout collection and the plan. Unlike collation, there is no fallback to the row position: a position-based id names a different task once rows are reordered, so benchmarks without an id field are refused with an actionable error.{task_id: {taskset, task_id}, digest}. The digest issha256(canonical JSON of task_input), wheretask_inputis theMaterializedTaskinput with routing and bookkeeping fields (agent_ref,task_source,_ng_*) excluded. The taskset is the dataset's declaredtaskset, or the benchmark name when it declares none.--task-idsplans a subset;--schemaprints the JSON Schema; the format is labellednemo-gym.eval-plan.v1and is meant to change only additively within that version.prompt_config) are refused for now; applying the template is a follow-up.prepare_benchmarkis split into_datasets_to_prepareand_prepare_datasetsso both commands share them; its behaviour is unchanged, except that the "no preparable dataset" error no longer namesgym eval prepare(it suggests--config <config path>or--benchmark <name>). The plan considers only the benchmark datasets among the datasets to prepare.No issue filed: this is a new, additive command. Related: the Harbor task work (#3673 and follow-ups) produces the same
MaterializedTaskrows and pins, so plans for Harbor datasets should line up with it. Feedback on the file format is very welcome while this is a draft.Validation
pytest tests/unit_tests/test_eval_plan.py: 20 passed (id precedence, ids and digests only (no inputs) in the file, declared taskset used, refusal of missing and duplicate ids, refusal of already-materialized rows as a config error, routing fields excluded from input and digest, subset selection, deterministic output, schema round trip, CLI: file written, subset, prompt-template refusal, several-benchmarks refusal,--outrequired,--schema).pytest tests/unit_tests/test_cli_main.py tests/unit_tests/test_cli_compat.py tests/unit_tests/test_rollout_collection.py tests/unit_tests/test_task_materialization.py tests/unit_tests/test_benchmarks.py: all pass after rebasing ontomain, excepttest_benchmarks.py::test_every_repo_benchmark_appears_in_listing, the DNSgaierroron my machine noted below.pytest tests/unit_tests/test_benchmarks.py tests/unit_tests/test_cli_main.py tests/unit_tests/test_cli_compat.py: 335 passed, 1 failed. The failure (test_every_repo_benchmark_appears_in_listing) is a DNSgaierrorwhile resolving four configs on my machine and fails identically on unmodifiedmain.pre-commit run --all-files: all hooks pass.gym eval plan --benchmark swebench/verified/opencode --out plan.json: 500 tasks keyed byinstance_id; running it twice gives byte-identical files.--task-ids astropy__astropy-12907,django__django-11099: plan with those 2 tasks.--benchmark terminal_bench_2_1/terminus_2(fresh preparation, onmainwith fix(terminal_bench_2_1): give prepared rows a stable task_id and order #4135): 89 tasks, keyed by task folder name.--benchmark aime24: refused because of its prompt template.Rollouts
N/A: the command only prepares data and writes a file; no server, agent or model code changes.
Compatibility and benchmark impact
None. New command;
gym eval preparebehaviour unchanged.Checklist
pre-commit run --all-files) (so CI lint/format checks pass).git commit -s) (so the DCO check passes).