[skill] evaluation: add GDPVal (NeMo Gym Stirrup agent) support - #2039
Conversation
GDPVal is an agentic AA benchmark: the Stirrup agent produces office/PDF deliverables in a per-task Apptainer code-exec sandbox, then a judge panel scores them. It runs on the 0.2.6 launcher as a `nemo_gym` task, but is standalone (one gym eval per config) and mechanically unlike the `aa/` nemo-skills tasks, so it gets its own branch in the skill. - recipes/tasks/aa_gym/gdpval.md: task recipe (standalone rule, scoring modes, canary, score extraction). - references/gym-gdpval.md: SIF sandbox, `_gym_prepare` machinery, deploy sizing, rubric-vs-comparison scoring, MLflow deliverables trap, failure modes, and the SIF<->Gym-version rebuild coupling. - recipes/examples/gym_gdpval/: self-contained SLURM + vLLM template plus the co-located `_gym_prepare.yaml` Hydra include. - scripts/gdpval-sif.sh: build-if-absent / reuse-if-present Apptainer SIF helper. Builds on the target cluster only (never copies across clusters), flock-guarded and atomic, driven by $GDPVAL_SIF_DIR. - SKILL.md / quantization-benchmarks.md: GDPVal is part of the AA suite but a different harness, so it is generated as a companion standalone config and never merged into the `aa/` task list. - env.example: TAVILY_API_KEY (agent web search) and GDPVAL_SIF_DIR. Validated end-to-end on an aarch64 GB300 cluster: deploy -> SIF build+exec -> gym head server -> 220 rollouts + deliverables -> judge scoring. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a GDPVal SIF builder and a standalone NeMo Gym evaluation path. The change includes runtime preparation, sandbox and judge configuration, environment examples, staged execution guidance, score extraction, and separate GDPVal handling in AA quantization recommendations. ChangesGDPVal evaluation
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant nel
participant vLLM
participant nemo_gym
participant GDPValJudge
nel->>vLLM: Deploy reasoning-enabled policy
nel->>nemo_gym: Start pinned GDPVal task
nemo_gym->>vLLM: Collect policy rollouts
nemo_gym->>GDPValJudge: Submit outputs for scoring
GDPValJudge-->>nemo_gym: Return scores and deliverables
nemo_gym-->>nel: Export results to MLflow
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #2039 +/- ##
==========================================
- Coverage 75.81% 74.24% -1.58%
==========================================
Files 518 520 +2
Lines 58574 62737 +4163
==========================================
+ Hits 44406 46577 +2171
- Misses 14168 16160 +1992
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Warning
CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.
Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.agents/scripts/gdpval-sif.sh:
- Around line 45-46: Update GDPVAL_GYM_COMMIT and GDPVAL_SIF_NAME defaults in
.agents/scripts/gdpval-sif.sh to dd41196f620f2af99947d776cbe5da9439d2a08d and
python-3.13.gdpval.sif. In
.agents/skills/evaluation/recipes/examples/gym_gdpval/example_gym_gdpval.yaml:30-40,
no direct change is needed once the script defaults are corrected. Update the
documented SIF path to python-3.13.gdpval.sif in
.agents/skills/evaluation/SKILL.md:64-70,
.agents/skills/evaluation/recipes/tasks/aa_gym/gdpval.md:23-28, and
.agents/skills/evaluation/references/gym-gdpval.md:22-29.
- Around line 108-123: Update both Apptainer build invocations in the fakeroot
fallback flow to include the --force option, ensuring the commands using
"$APPTAINER_BIN" overwrite an existing "$tmp" without prompting.
In @.agents/skills/evaluation/recipes/examples/gym_gdpval/_gym_prepare.yaml:
- Around line 27-70: Update the final PYTHONPATH export in gym_prepare to
preserve any inherited $PYTHONPATH while prepending /opt/Gym and the main venv
site-packages. Use a shell conditional or equivalent that references $PYTHONPATH
without ${...} syntax, and keep the existing path ordering for Gym and its
dependencies.
In
@.agents/skills/evaluation/recipes/examples/gym_gdpval/example_gym_gdpval.yaml:
- Around line 166-175: Update the pre_cmd installation flow to detect the
container architecture and install an architecture-compatible Apptainer runtime,
avoiding the hardcoded amd64 1.4.2 package on aarch64 where no matching release
exists. Ensure installation failure is not masked by the current &&/fallback
behavior: verify Apptainer is available after installation and explicitly
terminate pre_cmd if it is unavailable, preserving the existing squashfuse setup
only after Apptainer succeeds.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 963909ee-d867-411c-a876-056e093a3662
📒 Files selected for processing (8)
.agents/scripts/gdpval-sif.sh.agents/skills/evaluation/SKILL.md.agents/skills/evaluation/recipes/env.example.agents/skills/evaluation/recipes/examples/gym_gdpval/_gym_prepare.yaml.agents/skills/evaluation/recipes/examples/gym_gdpval/example_gym_gdpval.yaml.agents/skills/evaluation/recipes/tasks/aa_gym/gdpval.md.agents/skills/evaluation/references/gym-gdpval.md.agents/skills/evaluation/references/quantization-benchmarks.md
Corrections found while bringing GDPVal up on an aarch64 GB300 cluster, in
both rubric and comparison mode.
Correctness:
- _gym_prepare: run the rollout command from a script written via a QUOTED
heredoc instead of `bash -c '...'`. Gym params legitimately contain single
quotes (comparison mode's ++multistage.stages='[{num_tasks: 45}, ...]' and
++...judge_panel='[{...}]'), which closed the wrapper early so Hydra got the
value split on spaces and died with "no viable alternative at input
'[{num_tasks:'". The quoted delimiter keeps $$ and $*_API_KEY unexpanded
until run time.
- example: never put '#' comments inside a folded (>-) params scalar. YAML
keeps them as literal text; the block folds to one line and the first '#'
comments out every override after it in the shell command.
- example: arch-aware pre_cmd (Ubuntu PPA) — the hardcoded amd64 apptainer
.deb cannot install on aarch64, which is most Blackwell/Grace clusters.
- gdpval-sif.sh: align the default SIF name and gym commit with the template,
so build and run no longer disagree by default.
Scoping (rubric default; comparison is a conversion):
- The template is now rubric-only and self-consistent: no dangling
reference_elo / reference_deliverables_dir, which CONFLICT with comparison
mode's reference_models map. Comparison needs a reference set, a newer gym
image, and its own overrides -- documented, not half-wired here.
Documented traps:
- install_on_the_fly.commit is INERT on images that bake Gym as a non-git
directory (the public nemo-gym image): the prepare step logs "/opt/Gym is
not a git repo" and the pin is ignored. Do not attribute behaviour changes
to it without "=== NeMo Gym commit ===" + a SHA in the log; a port-11000
head-server hang is a transient collision, not a version symptom.
- What NEL validates and what it does not: mount dirs fail loudly before
sbatch, but `test -d` cannot see a missing/misnamed SIF *file* (silent
unsandboxed fallback) and the container is never checked; --dry-run skips
remote validation entirely. Adds `gdpval-sif.sh --check` as the preflight.
- num_repeats depends on the flow: multistage comparison uses 1 (top-level
++num_repeats works), pre-multistage rubric/single-ref used 2.
- Prefer a site-provided SIF over building, when one exists: a self-built SIF
resolves its pip stack at build time and can drift from the sandbox a
published reference set was generated in.
Validated on aws-cmh (GB300, aarch64): deploy -> SIF build+sandboxed exec ->
gym head server -> rollouts + deliverables -> judge scoring, in rubric mode
(219/220 tasks, 0 judge failures) and in comparison mode against a 9-reference
set (verify_mode=comparison, per-reference win/loss/tie).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
There was a problem hiding this comment.
Warning
CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.
Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
@.agents/skills/evaluation/recipes/examples/gym_gdpval/example_gym_gdpval.yaml:
- Around line 175-183: Update the pre_cmd dependency-installation flow to remove
“|| true” from the apt-get install command so missing squashfuse or fuse3 causes
the setup to fail. Keep apptainer installation validation and add an SIF
execution canary on the target cluster to verify mount dependencies are usable,
not merely that the binary exists.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 3adfdf25-b2a6-4f9a-bc34-7f661848dc9f
📒 Files selected for processing (5)
.agents/scripts/gdpval-sif.sh.agents/skills/evaluation/recipes/examples/gym_gdpval/_gym_prepare.yaml.agents/skills/evaluation/recipes/examples/gym_gdpval/example_gym_gdpval.yaml.agents/skills/evaluation/recipes/tasks/aa_gym/gdpval.md.agents/skills/evaluation/references/gym-gdpval.md
🚧 Files skipped from review as they are similar to previous changes (1)
- .agents/skills/evaluation/recipes/tasks/aa_gym/gdpval.md
Reading the final score off a finished GDPVal run was guesswork: the recipe just said the metric "is logged under nemo_gym.gdpval in MLflow". Two traps this fixes, both hit on a real run: - `artifacts/eval_factory_metrics.json` does NOT contain the GDPVal score. It holds only response_stats / reasoning / evaluation (request telemetry), so looking there and finding no ELO reads like the run failed to score. - A comparison run logs ~200 MLflow metrics of which ~126 are per-reference (/ref/<model>/...), so the three headline numbers are easy to miss in the UI. Documents the authoritative local file (artifacts/results.yml) with the exact metric paths for both modes, the MLflow key names (including the key_metrics/ duplicates), a copy-pasteable one-liner that reads the ELO without MLflow, and sanity checks (judged count, num_stages/num_references, unique task_id count vs 220) so a score computed on a short task set is not quoted as final. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
normalized_elo is the AA 0-1 scale and the number that is comparable across models and to the published AA index; eval_elo is the same Bradley-Terry fit on the raw Elo axis (normalized_elo = (eval_elo - 500) / 2000). Make the recipe say which one to report instead of leaving the reader to choose: normalized_elo leads the metric table, the extraction one-liner, and the MLflow key list, with eval_elo demoted to supporting detail. Also fixes three places where the recipe contradicted itself after the rubric/comparison split: - the header asserted num_repeats=2 "in the reviewed golden" while the Config section (correctly) makes it flow-dependent; - the SIF filename still said python-3.12 while the template ships 3.13; - "Scoring modes" still described the old single-reference two-step flow (mount at /gdpval/refs/test_ref, set reference_elo=1290), which no longer matches the multi-reference design and would send a reader down a path that fails at startup. It now states that comparison is a conversion, not a flag flip, and points at the conversion checklist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
…gen test Regenerating a config from the skills alone (agent blocked from seeing the known-good answer) reproduced every score-determining setting exactly, but surfaced contradictions the shared reference had been left holding. - The SIF path said `/gdpval/sif/python-3.12.gdpval.sif` while SKILL.md, the task recipe and the template all say 3.13. This is the one string the file itself stresses must match in two places, and a mismatch degrades silently to non-sandboxed exec -- the worst possible thing to be stale. - The num_repeats section still ended with "remove the sed line to keep 2 for golden-comparable / reported scores", which is wrong for the multistage comparison flow (1) that the recipe and eval-config both specify. Since SKILL.md sends readers to this reference first, it was the answer they would find. Now split by flow. Also documents three things a generating agent had to guess: - `--max-num-seqs` must come from `stirrup_agent.concurrency`, not from `parallelism` (which is gym-internal); the generic rule yields an absurd cap. - `max_new_tokens` legitimately does not apply on the gym path (the adapter puts it in params_to_remove), so the six-field template is not violated. - temperature/top_p should match whatever the reference deliverables were generated with, since a sampling difference shows up in a pairwise ELO as if it were a quality difference. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
Checked the designated golden (Qwen3.6-35B-A3B): it uses num_repeats=1, set as a plain top-level `++num_repeats=1` in the multistage block, with no `sed` patching anywhere. The `=2` in the skill came from the older MiniMax-M2.7 single-reference golden, which predates multistage. So standardise on 1 instead of making the reader work out which flow they are in: - template: drop the `sed 's/num_repeats: 2$/num_repeats: 1/'` workaround and its "the reviewed GOLDEN uses 2 / delete this line for reported scores" comment (both wrong now), and set `++num_repeats=1` the way the golden does. The pinned Gym already ships 1, so the override is belt-and-braces. - recipe + reference: collapse the two-branch explanation to "use 1", with the pre-multistage 2 kept only as a one-line historical note so nobody carries it forward from an old config. This also removes the last claim that num_repeats cannot be set via `++` -- it can, on current pins. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
The judge details were living in the internal eval-config skill, but nothing about them is internal: the panel composition, the failure modes and the throttle values are properties of GDPVal itself, useful to anyone running it. Only the concrete endpoint URL is site-specific, and the public skill already has a convention for that (`<INFERENCE_JUDGE_URL>` from `.env`). Adds a Judge section to the reference covering: - rubric uses one judge; comparison uses a 3-member panel sampled per trial, with judge_sampling_seed making the sampling reproducible; - inject the key's VALUE, not its name -- an interpolation that resolves to the literal string "INFERENCE_API_KEY" makes the proxy reply "LiteLLM Virtual Key expected", which the gym wraps as an opaque 500 that reads like a judge outage rather than a config error; - do not set judge_responses_create_params_overrides.model, which collapses the panel to a single judge and silently changes the methodology; - the judge rate-limits before the served model does, so max_concurrent_requests=10 / concurrency=220 are the golden values to raise only after clean 429 logs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
Same treatment the internal eval-config skill got: these are references an agent acts from, not essays. Cut narrative and cross-file duplication, keep every fact that prevents a mistake. reference (293 -> 225): - Merge the two SIF sections into one. The build-vs-run narrative, the privileged/rootless aside and the worked rebuild example were explanation; what remains is the rule (prefer a provided SIF, else build on the target cluster, never copy between clusters), the silent-fallback warning, the arch-aware pre_cmd note, and the diff-the-def check before a rebuild. - Scoring modes: drop the two-step baseline walkthrough, keep the rubric-vs- comparison distinction, the startup error string, and the pointer to the conversion checklist. recipe (143 -> 116): it had grown a second copy of the reference's SIF, scoring-mode and num_repeats material. Make it the thin entry point it should be -- what GDPVal is, the standalone rule, the config pointer, canary, and score extraction (the part that is genuinely unique to it) -- and delegate the mechanics to the reference in one line. Verified after the cut that the load-bearing facts survive: the silent-fallback warning, normalized_elo + results.yml extraction, reference_models, the sif helper, TRUST_PRE_CMD, num_repeats=1, the LiteLLM key-value trap, the `test -d` validation gap and the don't-pin-the-judge rule. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
) ### What does this PR do? Type of change: Bug fix (agent skill documentation) The `evaluation` skill instructed the agent to **"append `-cu130` to the image tag"** for NVFP4 checkpoints on Blackwell B300/GB300 (sm_103). That was correct for v0.19.x, but **vLLM inverted its tag convention at v0.20.0**: the *unsuffixed* tag is now the CUDA-13 build, and `-cu129` is the CUDA-12 opt-out. Consequences of the stale rule: - `v0.20.1-cu130` / `v0.24.0-cu130` / `v0.26.0-cu130` **do not exist** — following the rule literally asks for a missing tag. - The documented fallback (`cu130-nightly-<arch>`) points at ~v0.20-era builds that are *older* than several models' documented minimum vLLM version, so it can't serve as an escape hatch either. This replaces the "append a suffix" instruction with a version-keyed table plus the durable check: **select a tag whose config blob reports `CUDA_VERSION` ≥ 13**, resolving the child manifest for the platform you actually deploy on. While the PR was open the default image was also bumped, and review surfaced two follow-on corrections. Full contents: 1. **Tag-convention fix** — version-keyed table, `-cu130` fallback removed. 2. **Default image `v0.19.1` → `v0.26.0`** (latest vLLM release) everywhere it was pinned: `SKILL.md` Step 3 and the Step 7.5 table, `example_eval.yaml`, `example_eval_next.yaml`. Version specifics that the bump made stale or self-contradictory were dropped (the `e.g. v0.20.0` bump example and the MiniMax-M2.7 `≥0.20.0` anecdote, both now *below* the default; the failure-mode lesson is kept). 3. **Convention boundary corrected to v0.20.0** — the first draft said `≤ v0.20.x` suffixed / `≥ ~v0.21` unsuffixed. Off by a minor release in both rows; see Testing. 4. **Config blob resolved per deployment platform** — the check said "arm64 child". GB300/Grace is arm64, but plenty of B300 deployments are `linux/amd64`. 5. **Same corrections applied to the `deployment` skill**, which carried the original append rule untouched: its NVFP4 note, `references/support-matrix.md`, `references/benchmarking.md`, and the `:latest` pins in `references/setup.md` (now `v0.26.0`, matching the evaluation skill's never-`:latest` stance). An earlier revision of this branch also carried a `.claude/skills/benchmark-model-kernels` symlink, added automatically by `tools/precommit/sync_claude_skills.sh` — it repairs missing symlinks repo-wide on any touch of `.agents/skills/`, and #1980 landed that skill without its link. It has been dropped from this branch to keep the scope on the vLLM image guidance. Worth its own one-line PR: without the symlink, Claude Code doesn't load that skill at all. ### Usage ```bash # Durable check — resolve the child manifest for YOUR platform (arm64 for # Grace/GB300, amd64 for x86) and read CUDA_VERSION from its config blob: # v0.19.1 -> CUDA_VERSION=12.9.1 (unsuffixed = CUDA 12, old convention) # v0.19.1-cu130 -> CUDA_VERSION=13.0.1 (suffixed = CUDA 13, old convention) # v0.20.0 -> CUDA_VERSION=13.0.2 (transition release: ships both suffixes) # v0.26.0 -> CUDA_VERSION=13.0.2 (unsuffixed = CUDA 13, new convention) # v0.26.0-cu129 -> CUDA_VERSION=12.9.1 (suffixed = CUDA 12, new convention) ``` ### Testing Verified empirically against the Docker registry API for `vllm/vllm-openai` — resolved each tag's child manifests and read `CUDA_VERSION` / `TORCH_CUDA_ARCH_LIST` from the config blob. **Where the convention flips (arm64):** | release | unsuffixed | `-cu130` | `-cu129` | |---|---|---|---| | v0.18.0 | 12.9.1 | 13.0.1 | absent | | v0.19.0 / v0.19.1 | 12.9.1 | 13.0.1 | absent | | **v0.20.0** | **13.0.2** | 13.0.2 | 12.9.1 | | v0.20.1 | 13.0.2 | **absent** | 12.9.1 | | v0.20.2 | 13.0.2 | **absent** | 12.9.1 | | v0.21.0 … v0.26.0 | 13.0.2 | absent | 12.9.1 | v0.20.0 is the transition release — it publishes both suffixes *and* its unsuffixed tag is already CUDA 13. That duplication is what hid the boundary: confirming `v0.20.0-cu130` exists reads as "old convention still applies at 0.20", while `v0.20.1-cu130` and `v0.20.2-cu130` don't exist at all. **Why the platform matters.** `CUDA_VERSION` is identical across children on every tag checked (v0.26.0, v0.26.0-cu129, v0.20.0, v0.19.1, v0.19.1-cu130, kimi-k3), but `TORCH_CUDA_ARCH_LIST` is not: ``` v0.26.0 amd64 7.5 8.0 8.6 8.9 9.0 10.0 12.0 v0.26.0 arm64 8.0 8.7 8.9 9.0 10.0 11.0 12.0 v0.19.1 amd64 7.0 7.5 8.0 8.9 9.0 10.0 12.0 v0.19.1 arm64 8.7 8.9 9.0 10.0+PTX 12.0 ``` `11.0` appears only on arm64, `7.5` / `8.6` only on amd64 — so the arch check has to read the child you'll actually run. **Default bump.** The `0.26.0` family is `{,-aarch64,-x86_64} × {,-cu129} × {,-ubuntu2404}` — 12 tags, `cu129` the only CUDA axis, no `-cu130`. The new default is therefore already a CUDA-13 build, and NVFP4 on B300/GB300 needs no suffix at all. Docs-only change; no runtime code touched. `pre-commit` clean. ### Before your PR is "*Ready for review*" - Is this change backward compatible?: ✅ - If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: N/A - Did you write any new necessary tests?: N/A (agent skill documentation) - Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: N/A - Did you get Claude approval on this PR?: ❌ (not yet run) ### Additional Information Split out of the GDPVal skill work (#2039) because it is independent of GDPVal and applies to every NVFP4-on-Blackwell deployment the skill generates. Now spans both the `evaluation` and `deployment` skills, all under `.agents/`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated deployment and evaluation guidance to use vLLM v0.26.0. * Clarified CUDA image-tag conventions, including CUDA 13 defaults and CUDA 12 opt-outs. * Added validation guidance for resolved CUDA versions, platform architecture settings, and image compatibility. * Updated NVFP4 Blackwell B300/GB300 support notes, including required `sm_103` kernel availability. * Refreshed example recipes, setup commands, benchmarking notes, and serving-image requirements. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Chenjie Luo <chenjiel@nvidia.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n regen test
Regenerating a config from the compressed docs alone reproduced every
score-determining setting exactly, but surfaced three defects. All three fail
silently, which is why they survived earlier passes.
1. SKILL.md's GDPVal branch still named `python-3.12.gdpval.sif` while the
template, recipe and reference had all moved to 3.13 -- and a stale 3.12 SIF
in an otherwise-correct dir passes NEL's `test -d` preflight, so the agent
drops to unsandboxed code-exec with no error. The same step also presented
*building* a SIF as mandatory, contradicting the prefer-a-provided-SIF rule
the other two files state. Now: prefer provided, build as fallback, verify
the filename with `gdpval-sif.sh --check`.
2. The template shipped
`++...judge_responses_create_params_overrides.model=${gdpval_judge.model}`.
Harmless in rubric mode (one judge), but on conversion to comparison it
collapses the 3-member judge panel to a single judge and silently changes
the scoring methodology. A "splice these overrides in" conversion checklist
structurally cannot catch a line that must be *removed*, so remove it from
the template instead: the judge model is already set by
`openai_model=${gdpval_judge.model}`.
3. The template carried the generic `--max-num-seqs = ceil(parallelism / DP)`
rule inline. On the gym path `parallelism` is gym-internal, not an in-flight
request count, so that yields 4096. The correction existed only in the
reference; put the right rule (`ceil(stirrup_agent.concurrency / DP)`) in the
artifact people actually edit.
Also makes the score-extraction snippet's `results.yml` path explicit instead of
a bare filename.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
meenchen
left a comment
There was a problem hiding this comment.
Bot review (bedrock-claude-opus-5) — DM the bot to share feedback.
Docs/skill-only change that correctly reuses the existing aa_next/-style "branch" convention (recipe + shared reference + example dir + .agents/scripts helper), so the design question is largely settled by precedent — no competing in-repo system is introduced. Tests are legitimately N/A (no test harness exists for .agents/skills, and the license pre-commit hook only covers python/shell).
Issues found:
- Contradictory
num_repeatsguidance —references/quantization-benchmarks.mdtells the agent to "bump it back to 2 for quant validation", whilereferences/gym-gdpval.mdsays "Use 1 … Do not carry a=2into a current run" andrecipes/tasks/aa_gym/gdpval.mdsays both goldens use 1. Quant validation is the use case in this repo, so the agent will hit the contradiction on the default path. gdpval-sif.shnon-fakeroot fallback —apptainer buildrefuses to write an existing output file without--force, and the fakeroot attempt usually leaves a partial$tmp, so theeliffallback will most likely fail spuriously.- Score-extraction snippet is comparison-only while the shipped template is rubric-only — the copy-pasteable
python3 -cwillKeyErroron the default config's results. --env-fileflag appears in two places but nowhere else in the skill (which documentsset -a && source .env); please confirm 0.2.6nel runaccepts it.GDPVAL_MAX_TURNSis presented as a containerlit:env var while the effective value comes from a submit-time${oc.env:GDPVAL_MAX_TURNS,250}interpolation — the two can't both be right, and the skill elsewhere warns against${oc.env:...}for non-exported vars.
Nits: the two new YAML headers use Copyright (c) 2025, NVIDIA CORPORATION. which diverges from the canonical LICENSE_HEADER (2026, "& AFFILIATES") that gdpval-sif.sh matches exactly — worth aligning (no third-party code is vendored, so no legal concern beyond consistency). Also note the AA rule change now makes every "run AA" request additionally require TAVILY_API_KEY, a judge endpoint and a prebuilt SIF; the "let the user opt out" wording is doing a lot of work there. Size (~1000 lines) is fine given it's almost entirely prose.
| often multi-node. Generate it alongside the `aa/` config; see | ||
| `recipes/tasks/aa_gym/gdpval.md` + `references/gym-gdpval.md`. Thinking mode is | ||
| mandatory (non-thinking loses ~86% of pairwise judgements). For low-variance | ||
| quant comparisons keep the golden `num_repeats: 2` — the example template |
There was a problem hiding this comment.
Bot comment.
This contradicts the other two new docs. references/gym-gdpval.md ("## num_repeats") says "Use 1. Both current goldens do … Do not carry a =2 into a current run", and recipes/tasks/aa_gym/gdpval.md says "num_repeats=1 — already set by the template … both current goldens use it". Here the agent is told the golden is 2 and that the template lowered it to 1 "to halve cost", so on the default quant-validation path (the primary use case in this repo) it will override back to ++num_repeats=2 — doubling a multi-hour, judge-metered run against explicit guidance elsewhere. Please pick one story and state it in a single place, with the others pointing at it.
There was a problem hiding this comment.
Fixed — now says 1 everywhere. The reviewed golden uses num_repeats=1; this file was telling the primary quant-validation path to bump back to 2.
| # unprivileged build where fakeroot is unavailable. | ||
| if "$APPTAINER_BIN" build --fakeroot "$tmp" "$def_local"; then | ||
| : | ||
| elif "$APPTAINER_BIN" build "$tmp" "$def_local"; then |
There was a problem hiding this comment.
Bot comment.
The non-fakeroot fallback will probably never succeed: apptainer build errors out when the destination image file already exists unless --force is passed, and a failed --fakeroot attempt commonly leaves a partial $tmp behind. As written, a cluster without an /etc/subuid entry gets _die "apptainer build failed" even though the unprivileged build would have worked. Suggest rm -f "$tmp" (or --force) before the fallback:
if "$APPTAINER_BIN" build --fakeroot "$tmp" "$def_local"; then
:
else
rm -f "$tmp"
if "$APPTAINER_BIN" build "$tmp" "$def_local"; then ...There was a problem hiding this comment.
Fixed — clearing $tmp before the unprivileged fallback.
| echo "$sif"; exit 0 | ||
| fi | ||
| printf '\033[31mgdpval-sif: MISSING expected SIF: %s\033[0m\n' "$sif" >&2 | ||
| [[ -d "$sif_dir" ]] && { echo " dir exists but does not contain it; found:" >&2 |
There was a problem hiding this comment.
Bot comment.
Under set -e, this [[ -d "$sif_dir" ]] && { … } is a non-final command in the list, so when the dir does not exist the script exits right here and the "Build it with: …" hint on the next line never prints — which is exactly the case where the hint is most useful. Use if [[ -d … ]]; then … fi (or append || true).
Also, inside the block ls -1 … | sed … || echo " (no .sif files)" binds || to the pipeline, and sed exits 0 on empty input, so the "(no .sif files)" branch is effectively dead. Gate on the ls exit status instead.
There was a problem hiding this comment.
Fixed — if/then/fi so the "Build it with:" hint prints when the dir is missing, and the (no .sif files) branch is now gated on ls rather than the (always-0) sed.
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| # Shared prepare/teardown snippets — include via: defaults: [_gym_prepare, _self_] |
There was a problem hiding this comment.
Bot comment.
Design question worth answering in the PR body: this file describes itself as "shared … by every benchmark", but there is exactly one gym benchmark and one consumer (example_gym_gdpval.yaml), and the split creates a footgun that then has to be warned about in five separate places (SKILL.md, the recipe, the reference, the config header, the failure-modes list: "if it still hangs, the _gym_prepare.yaml include didn't travel with the config").
Inlining the two shell blocks directly into the task command: would make the example a single self-contained file like example_eval.yaml / example_eval_next.yaml and remove the whole co-location hazard. If the include exists to anticipate future aa_gym/ tasks, please say so explicitly — otherwise the simpler single-file form looks preferable.
There was a problem hiding this comment.
Agreed — inlined both blocks into the task command: and deleted this file. The example is now a single self-contained YAML and the "must travel with the config" warning is gone from all five places it appeared.
| # Drop "_cache" if you WANT them uploaded as artifacts. | ||
| PERSIST_DELIVERABLES_DIR: lit:/results/gdpval/deliverables_cache | ||
| # Stirrup agent turn cap (optional; default 100). | ||
| # GDPVAL_MAX_TURNS: lit:100 |
There was a problem hiding this comment.
Bot comment.
GDPVAL_MAX_TURNS is documented three ways that don't agree: here as an optional container env var (lit:100), in references/gym-gdpval.md's env table as lit (optional) … default 100; golden uses 250, but the value the gym actually uses comes from ++…agent_max_turns=${oc.env:GDPVAL_MAX_TURNS,250} in common_params — an OmegaConf interpolation resolved at submit time against the launching shell, not the container env. Uncommenting GDPVAL_MAX_TURNS: lit:100 therefore has no effect on agent_max_turns, and SKILL.md Step 5 explicitly warns against ${oc.env:...} for values that aren't exported with set -a. Please make it one mechanism (either drop the lit: line and document it as a host-side .env value, or hardcode 250 in the override) and fix the reference table to match.
There was a problem hiding this comment.
Fixed — removed. It could never affect agent_max_turns, which is an ${oc.env:…} resolved at submit time against the launching shell; the golden doesn't set it either.
| # workspace, don't copy the yaml alone. | ||
| # | ||
| # Canary (validates SIF sandbox + judge + gym plumbing on a couple of tasks): | ||
| # nel run --config example_gym_gdpval.yaml --env-file .env \ |
There was a problem hiding this comment.
Bot comment.
nel run … --env-file .env doesn't appear anywhere else in the skill — SKILL.md Step 8 and every other example use set -a && source .env && set +a with host: prefixes, and this is the only occurrence of --env-file in the repo. If 0.2.6 nel doesn't have that flag, the canary command as written fails immediately (it's repeated in recipes/tasks/aa_gym/gdpval.md). Please confirm against the installed CLI or switch to the documented source .env form for consistency.
There was a problem hiding this comment.
Checked: --env-file is a real nel 0.2.6 flag (verified against the installed CLI), so leaving it.
| python3 -c " | ||
| import yaml,sys | ||
| m=yaml.safe_load(open('<output_dir>/<run>/nemo_gym.0/artifacts/results.yml'))['groups']['nemo_gym']['metrics'] | ||
| for k in ('normalized_elo','eval_elo','win_rate'): |
There was a problem hiding this comment.
Bot comment.
This copy-pasteable snippet hardcodes gdpval_stirrup_agent/comparison/{k}, but the shipped template is rubric-only (gdpval.reward_mode: rubric), so on the default config it will KeyError — and the section above states flatly "The reported GDPVal score is normalized_elo", which is undefined in rubric mode per your own table row. Since rubric is what most users will run first, add the rubric extraction (mean reward over evaluator_rollouts.jsonl) alongside it, or gate the snippet with an explicit "comparison mode only" heading.
There was a problem hiding this comment.
Fixed — added the rubric extraction (mean reward over evaluator_rollouts.jsonl) beside the comparison one, each labelled by mode, so the shipped rubric-default config no longer KeyErrors.
… config
Cross-checked the open review findings against the reviewed golden GDPVal run
(Qwen3.6-35B-A3B). Two of my own claims were wrong; the rest are real bugs.
Contradicted by the golden:
- quantization-benchmarks.md still told the quant-validation path to "keep the
golden num_repeats: 2 ... bump it back to 2", while the recipe and reference
say 1. The golden uses 1, so the primary use case in this repo was being told
to double a multi-hour judge-metered run against guidance elsewhere.
- I had documented that max_new_tokens "does not apply" on the gym path. The
golden sets it alongside the adapter's params_to_remove, so the Step 3
model-card lookup applies as normal.
Real defects in code I wrote:
- gdpval-sif.sh: the unprivileged fallback could never succeed, because a failed
--fakeroot attempt leaves a partial $tmp and apptainer refuses an existing
destination. Clear it before falling back.
- gdpval-sif.sh --check: `[[ -d ... ]] && { ... }` is a non-final command under
`set -e`, so when the dir is missing the script exited before printing the
"Build it with:" hint -- exactly when the hint matters. The "(no .sif files)"
branch was also dead, since sed exits 0 on empty input.
- The template exposed GDPVAL_MAX_TURNS as a container env var, which cannot
affect agent_max_turns: that value is an ${oc.env:...} resolved at submit time
against the launching shell. The golden does not set it either.
- The score-extraction snippet was comparison-only, but the shipped template is
rubric-only, so it KeyErrors on the default config. Added the rubric form.
Checked and NOT changed: `--env-file` is a real nel 0.2.6 flag; and both
`use_reasoning` and `process_reasoning_traces` exist in the installed adapter
config, so the template's field is valid even though the golden uses the other.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
…lf-contained
The include was described as "shared by every benchmark", but there is one gym
benchmark and one consumer, and the split created a footgun that then had to be
warned about in five separate places -- SKILL.md, the recipe, the reference, the
config header, and the failure-modes list ("if it still hangs, the include
didn't travel with the config"). Copying the yaml without its sibling silently
produced an unresolvable ${gym_prepare.*} interpolation.
Inline both shell blocks into the task `command:` and delete the file. The
example is now a single self-contained yaml like example_eval.yaml and
example_eval_next.yaml, and the "copy the whole dir" instruction disappears from
every doc that carried it.
The reference keeps the *why* under a "Gym prepare / reap" heading, since the
blocks are still unobvious: the venv repair works around the eval image's
deployment-oriented packaging, and the setsid + process-group reap works around
Gym's incomplete shutdown (orphaned Ray workers otherwise hold the launcher's
stdout open and the run hangs in post-eval). Both are marked for removal once
upstream fixes land.
Verified: the template parses, the inlined command passes `bash -n` with
quote-containing params substituted, and no `#` leaked into a folded scalar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
A regen from the now-self-contained template reproduced every score-determining
setting and copied the inlined `command:` verbatim, which was the point of the
inlining. Two things it surfaced are worth fixing.
- The prepare block hardcoded `export UV_CACHE_DIR=/opt/cache/uv`, overriding the
`UV_CACHE_DIR` env var and the `/cache/uv` mount that the template sets up two
screens earlier -- so the mounted uv cache was silently bypassed for the venv
repair, and `++uv_cache_dir=$UV_CACHE_DIR` then pointed at a container-local
dir. Only set it when it is not already provided. (No `${VAR}` braces --
OmegaConf parses those.)
- Document the one case where the SIF <-> gym-commit coupling rule does not
apply: a site-provided SIF used with a site-provided gym image. Those images
bake Gym as a non-git dir, so the pin is inert and the two artifacts are
already matched -- even though the SIF filename can encode a different SHA
than `install_on_the_fly.commit`, which reads like a violation of the rule
stated just above it.
Not changed: a review claim that MLflow auto-export is rejected on aws-cmh
because `sbatch_extra_flags.qos` reaches the export sbatch. Evidence says
otherwise -- the export job ran on partition `cpu` and COMPLETED (2m20s),
precisely because those flags are not propagated to it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
…re block
The prepare block overwrote PYTHONPATH outright, discarding anything the base
image or launcher had set. Prepend instead. The usual
"${PYTHONPATH:+:$PYTHONPATH}" idiom cannot be used here because OmegaConf parses
${...} in the surrounding YAML, so this uses a plain $VAR test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
|
Thanks — worked through all of these. Summary of what changed, and what I deliberately didn't change. The four CodeRabbit inline comments were made against Fixed
Not changed, with reasons
ValidationThe skill was exercised end-to-end rather than just proofread: a GDPVal comparison-mode run on an aarch64 GB300 cluster completed and produced a real score ( |
sugunav14
left a comment
There was a problem hiding this comment.
Two line-anchored findings below. Separately, the SIF helper's flock/double-check/atomic-mv handling and the test -d vs -f validation-gap analysis are solid, and the python-3.13 filename + dd41196f... gym pin are now consistent across all five files that reference them.
Lower-severity items (no cleanup trap in gdpval-sif.sh, --check testing the local FS rather than the cluster, -f-only SIF validation, unscoped pkill -f raylet, unpinned apptainer PPA in pre_cmd, GDPVAL_MAX_TURNS missing from env.example) are in my earlier comment on this PR.
| # Writable staging dir for ref files (bind-mounted, see execution). | ||
| mkdir -p /gdpval_ref_files | ||
|
|
||
| ng_prepare_benchmark {{config.params.extra.nemo_gym.data_prep_params}} {{config.params.extra.nemo_gym.common_params}} |
There was a problem hiding this comment.
set -ex here puts HF_TOKEN / INFERENCE_API_KEY / TAVILY_API_KEY in plaintext in the eval log.
The task command: opens with set -ex (L244), and these two param blobs expand to +hf_token=$HF_TOKEN (data_prep_params) and, in common_params:
++gdpval_judge_model.responses_api_models.openai_model.openai_api_key=$INFERENCE_API_KEY
++gdpval_stirrup_agent.responses_api_agents.stirrup_agent.tavily_api_key=$TAVILY_API_KEY
set -x traces commands after expansion, so the + ng_prepare_benchmark ... trace line carries all three secret values. They land in the eval log on shared FS, and export.mlflow.log_logs: true (L369) then uploads that log to MLflow.
What suggests an oversight rather than an accepted tradeoff: the rollout call at L297 passes the same common_params, but it's written into /tmp/gym_run.sh via the quoted heredoc (L296-299) and run by a shell that never sets -x -- so $INFERENCE_API_KEY stays literal and is expanded only at run time, never traced. The right pattern is already there one line down; this call just isn't covered by it.
| ng_prepare_benchmark {{config.params.extra.nemo_gym.data_prep_params}} {{config.params.extra.nemo_gym.common_params}} | |
| set +x | |
| ng_prepare_benchmark {{config.params.extra.nemo_gym.data_prep_params}} {{config.params.extra.nemo_gym.common_params}} | |
| set -x |
(The set -ex at L171 in pre_cmd is fine -- nothing on those lines carries a secret.)
There was a problem hiding this comment.
Confirmed and fixed in 4bc6937d9 — thank you, this was a real leak, not a tradeoff.
I checked a completed run's client log rather than reasoning about it, and the traced line carries the expanded value:
hf_token=$HF_TOKEN <- what the config says
hf_token=hf_TOBgm... <- what the log actually contains
Both HF_TOKEN and INFERENCE_API_KEY were present. Applied your suggestion (set +x / set -x around that one call). Your read of why it was an oversight is exactly right: the rollout call below already gets this for free via the quoted heredoc, and this call simply wasn't covered by the same pattern.
Beyond the code fix, the already-completed runs need remediation — those logs went to MLflow via export.mlflow.log_logs: true, so the affected keys are being rotated and the uploaded artifacts purged.
|
|
||
| ```bash | ||
| nel run --config example_gym_gdpval.yaml --env-file .env \ | ||
| -o ++evaluation.nemo_evaluator_config.config.params.limit_samples=2 |
There was a problem hiding this comment.
Is limit_samples actually a canary on the gym path?
The PR description lists, among traps found during the validated run:
limit_samplesnot limiting gym rollouts
But limit_samples=2 is still presented as the canary in three places -- here, SKILL.md (GDPVal branch, step 4), and the example_gym_gdpval.yaml header -- and that trap appears nowhere in the final docs (no occurrence of limit_samples in references/gym-gdpval.md). It reads like the finding was dropped in the 436->341-line compression pass while the instruction depending on it survived.
If the trap is real, someone following this step launches the full 220-task multi-hour run believing it's a two-task smoke test -- inverting the purpose of the step, on the heaviest benchmark in the suite. Worth either documenting how to actually bound a gym canary, or stating plainly that limit_samples is inert here and describing what the canary is instead (e.g. watch the first rollouts for the SIF-fallback warning and judge auth, then cancel).
I couldn't verify this one empirically -- if limit_samples does work on the gym path, disregard.
There was a problem hiding this comment.
Confirmed — limit_samples is inert on the gym path, and your inference about how it happened is right: it was observed during the validated run (the "limit_samples=2 canary" ran all 220 tasks), made the PR description, and was then lost in the 436->341-line compression while the three instructions depending on it survived.
Fixed in 4bc6937d9 in all three places (recipe, SKILL.md GDPVal branch, template header). The gym does its own data prep and rollout collection, so the launcher-level limiter never reaches it.
Since there is no cheap sample-limited canary, the docs now describe the real procedure instead: launch, and treat the first ~20-30 minutes as the canary, with the greps to run —
grep -c "Using Apptainer container" $RD/logs/client-*.log # sandbox actually used
grep -c "falling back\|not a git repo" $RD/logs/client-*.log # unsandboxed / inert pin
grep -ciE " 401 | 403 |Internal Server Error" $RD/artifacts/nemo_gym_logs/gdpval_judge_model.log
wc -l $RD/artifacts/evaluator_rollouts.jsonl # rollouts flowing— plus a note that in comparison mode stage 1 (45 tasks) is a natural early checkpoint, since an ELO estimate lands before the 220-task stage 2 begins.
…ake canary Two findings from review, both confirmed against a real run. 1. Secret leak. The task `command:` opens with `set -ex`, and `set -x` traces commands AFTER expansion -- so the `ng_prepare_benchmark` trace line wrote $HF_TOKEN, $INFERENCE_API_KEY and $TAVILY_API_KEY in plaintext into the eval log, which lives on shared FS and is uploaded to MLflow when export.mlflow.log_logs is true. Verified in a completed run's client log: the traced line carries the expanded token, not `$HF_TOKEN`. Wrap that one call in `set +x` / `set -x`. The rollout call below it was already safe -- it goes through the quoted heredoc, so its secrets stay literal until a shell that never sets -x expands them; this call just wasn't covered by the same pattern. 2. `limit_samples` is inert on the gym path -- the gym does its own data prep and rollout collection, so the launcher-level limiter is ignored and you get the full 220-task run. This was observed during the validated run but the finding was lost in the doc-compression pass while the instruction depending on it survived in three places, so following the documented canary launches the heaviest benchmark in the suite believing it is a two-task smoke test. Replaced with the real procedure: launch, then treat the first ~20-30 minutes as the canary (SIF-sandbox line, judge auth, rollouts flowing), with the grep commands to check; note stage 1 of multistage as a natural checkpoint. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
Lower-severity items raised alongside the secret-leak and canary findings.
gdpval-sif.sh:
- Add an EXIT trap so an interrupted or failed build leaves no `.build.*` /
`.def` leftovers in the shared SIF dir. ($tmp is renamed on success, so the
trap only ever removes leftovers.)
- `--check` validated with `-f` only, which passes on a truncated or 0-byte
file -- exactly what an interrupted copy leaves behind, and the failure it is
supposed to prevent is silent. Now also requires a plausible size (>100 MB;
a real GDPVal SIF is 1-4 GB) and, when apptainer is available, that
`apptainer inspect` succeeds. Verified on-cluster: a real SIF passes, a
0-byte one is rejected with a rebuild hint.
- Document that `--check` inspects the filesystem it RUNS ON, so it must be run
on the cluster (srun/ssh) rather than the submitting box.
template:
- Scope the Ray reap to our own uid. `pkill -9 -f raylet` matches every raylet
on the node, including other users'/jobs' daemons on a shared node.
- Note that the apptainer PPA install is deliberately unpinned (it is the only
source with current arm64 builds; pinning risks unavailability) and that the
resolved version is echoed into the run log for reproducibility.
env.example:
- Document `GDPVAL_MAX_TURNS`. It is read at SUBMIT time from the launching
shell via ${oc.env:...}, so it belongs with the other exported values; setting
it as a container env var has no effect (that trap was removed earlier).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
|
@sugunav14 — both line-anchored findings were real and are fixed in The two findings. The Lower-severity items (
Thanks for the careful pass — the two anchored findings were the highest-value review input this PR has had, and both were things the end-to-end run had already brushed past without surfacing. |
After the last compression pass the two markdown files held at ~365 lines, but
the template had grown to 391 with 168 comment lines against 214 config lines --
each review fix added a three-to-six line explanation next to the line it fixed.
Cut the prose the recipe and reference already own (the 32-line header is now 16)
and reduce eight multi-line comment blocks to one or two lines each. What stays
is the part that prevents an edit mistake *at that line*: never `#` inside the
folded scalar, mount keys are not interpolated, `set +x` around the traced call,
the api_key is a VALUE not a NAME, plain `$VAR` only because OmegaConf parses
`${...}`, and the uid-scoped pkill. The reasoning behind each moves to
references/gym-gdpval.md, which is where a reader already is.
Verified after the cut: the YAML parses, the inlined command still passes
`bash -n` with quote-containing params substituted, no `#` leaked into any
folded scalar, and all nine load-bearing markers are still present.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
|
Follow-up: compressed the template in Context for why this was worth doing separately: the two markdown files have held at ~365 lines since the earlier compression pass, but the template kept growing, because each review fix in this PR added a three-to-six line explanation immediately next to the line it fixed. That is the right instinct while fixing, and the wrong end state — the file people copy ended up with more comment than config. The rule I applied: the template keeps whatever prevents an edit mistake at that line; the reasoning moves to
and the six-line explanations behind each are gone from the yaml. Verified after the cut, not just eyeballed: the YAML parses, the inlined Running totals for the docs in this PR: reference 293 → 236, recipe 143 → 129, template 391 → 360. |
|
What does this PR do?
Type of change: new feature (agent skill)
Adds GDPVal support to the
evaluationagent skill. GDPVal is an agentic AAbenchmark: the NeMo Gym "Stirrup" agent produces office/PDF deliverables inside a
per-task Apptainer code-exec sandbox, and a judge panel scores them. It runs on the
0.2.6 launcher as a
nemo_gymtask, but it is standalone (one gym eval perconfig) and mechanically unlike the
aa/nemo-skills tasks, so it gets its ownbranch in the skill rather than being merged into the
aa/task list.recipes/tasks/aa_gym/gdpval.md— task recipe: standalone rule, rubric-vs-comparisonscoring, canary, score extraction.
references/gym-gdpval.md— the machinery: Apptainer SIF sandbox, the_gym_preparevenv-repair / process-group-reap workaround, deployment sizing,scoring modes, the MLflow deliverables trap, canary failure modes, and the
SIF ↔ Gym-version rebuild coupling.
recipes/examples/gym_gdpval/— self-contained SLURM + vLLM template plus theco-located
_gym_prepare.yamlHydra include (it must travel with the config).scripts/gdpval-sif.sh— build-if-absent / reuse-if-present Apptainer SIF helper.Builds on the target cluster only (never copies across clusters), flock-guarded and
atomic, driven by
$GDPVAL_SIF_DIR.SKILL.md/references/quantization-benchmarks.md— GDPVal is part of the AAsuite but a different harness, so it is generated as a companion standalone config.
recipes/env.example—TAVILY_API_KEY(agent web search) andGDPVAL_SIF_DIR.Usage
Testing
Validated end-to-end on an aarch64 GB300 SLURM cluster with an NVFP4 MoE checkpoint:
deploy → SIF build + sandboxed exec → gym head server → 220 rollouts + deliverables →
judge scoring, producing a real rubric score with zero judge failures. Several traps
found during that run are now documented in the reference (silent unsandboxed
fallback, gym-commit/head-server hang, judge api-key value-vs-name, SIF ↔ Gym version
coupling,
limit_samplesnot limiting gym rollouts).Before your PR is "Ready for review"
CONTRIBUTING.md: N/AAdditional Information
Docs/skill-only change under
.agents/; nomodelopt/source is touched.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation