diff --git a/nemotron_recipes/lightning-3.5/base/README.md b/nemotron_recipes/lightning-3.5/base/README.md new file mode 100644 index 00000000000..43091f9cb06 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/base/README.md @@ -0,0 +1,297 @@ +# Nemotron 3.5 Lightning 30B A3B Base evaluation recipes + +These are `nemo-evaluator-launcher` configs run with `nel run`, not Gym recipes — see +[`../reproducibility.md`](../reproducibility.md) for how they sit alongside the instruct +recipes. + +Evaluation configs for the **base (pretraining)** benchmarks of +[`nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16`](https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16) +on `nemo-evaluator-launcher` 0.2.6. + +There are two configs: + +- **Base suite** — [`base-suite.yaml`](./base-suite.yaml), + the 21 short-context tasks across knowledge, math, code, commonsense, reading + comprehension and multilingual. Runs against an OpenAI-compatible endpoint **you + provide** (`deployment: none`) — see + [Running against an existing endpoint](#running-against-an-existing-endpoint). The + endpoint must support `echo` + `logprobs` for 9 of the tasks. +- **Long context (RULER)** — [`ruler.yaml`](./ruler.yaml), + RULER at 64k / 128k / 256k / 512k / 1M. Also endpoint-based, but it needs a + **separately served, long-context endpoint**. Also + needs a pre-generated dataset. **Results are provisional** — see [RULER](#ruler). + +**No judge is required.** Unlike several of the instruct recipes, nothing in the base +suite is LLM-graded. What you need is: + +- one OpenAI-compatible **`/v1/completions`** endpoint serving the base model, which + **must honour `echo` together with `logprobs`** (9 of the 21 tasks are scored by + log-likelihood). + +### Reproducing the reference numbers + +Reproduction depends on the *serving* configuration as much as on the eval configuration: +vLLM version, tensor parallelism, cache dtype, prefix caching and mamba cache mode all +move scores. We recommend serving the model using the command provided in +[Serving the model](#serving-the-model). + +For the instruct-model recipes, see [`../instruct/`](../instruct/). + + +--- + +## Common setup + +### 0. Prerequisites + +- **Docker**, running and usable by your user. `execution: local` runs each benchmark in + a container, so the launcher shells out to Docker. +- **Python 3.10+** for the launcher itself. +- Outbound network access to `huggingface.co` (datasets, tokenizer) and `nvcr.io` + (benchmark containers). +- No NGC login is required — the `nvcr.io/nvidia/eval-factory/*` containers used here + pull anonymously. +- GPUs are **not** needed to run these two configs: the model is served elsewhere. You + need GPUs only to serve it — see [Serving the model](#serving-the-model). + +### 1. Install `nemo-evaluator-launcher` + +```bash +python3 -m venv .venv +.venv/bin/pip install "nemo-evaluator-launcher[all]==0.2.6" +``` + +### 2. Credentials + +Two environment variables are used: + +| variable | needed for | +|---|---| +| `HF_TOKEN` | benchmark datasets and the tokenizer, both fetched from the Hub. Some datasets are gated — accept their terms on the Hub first, and make sure the token has access. | +| `POLICY_API_KEY` | the bearer token your endpoint expects. A self-hosted vLLM usually ignores it, so any placeholder works there. | + +Export them, or put them in a `.env` file and pass `--env-file .env` — every example +below does the latter: + +```bash +# .env +HF_TOKEN=hf_... +POLICY_API_KEY=... +``` + +The variable name for the endpoint key is set by `target.api_endpoint.api_key_name` in +the config; rename it there if you prefer a different one. + +### 3. Bind mounts (optional but recommended) + +Caching datasets and wheels across runs: + +```yaml +execution: + extra_docker_args: >- + -v /home/you/.cache/huggingface:/cache/huggingface + -v /home/you/.cache/uv:/cache/uv +``` + +--- + +## Serving the model + +Both configs run against an endpoint you provide (`deployment: none`), so serve the model +first and point them at it. **They need different serving configurations** — see +[Long context](#long-context-serving) below for RULER. + +Model card: +[`nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16`](https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16) +— check it for the authoritative serving guidance; the command below is what this suite +was validated with. + +```bash +vllm serve nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ + --tensor-parallel-size=4 --pipeline-parallel-size=1 --data-parallel-size=1 \ + --port 8000 --served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ + --trust-remote-code --gpu-memory-utilization 0.9 --max-num-seqs 512 \ + --no-enable-prefix-caching \ + --model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 96}' +``` + +Container: `vllm/vllm-openai:v0.19.1-cu130` (multi-arch, amd64 + arm64). + +**Sizing.** ~31.6B parameters in BF16, so roughly **62 GB of weights** plus KV cache. +TP=4 is what was validated; smaller TP works wherever the weights and cache fit. The +checkpoint declares `max_position_embeddings: 262144`. + +**The endpoint must expose `/v1/completions`, and must honour `echo` together with +`logprobs`** — see [Running against an existing endpoint](#running-against-an-existing-endpoint) +for why, and for a one-command check. vLLM supports both out of the box. + +### Long context serving + +RULER will not run against the endpoint above. The checkpoint declares +`max_position_embeddings: 262144`, so anything past 256k is refused unless the server was +started to allow it. Serve a **second** endpoint for RULER: + +```bash +VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 \ +vllm serve nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ + --tensor-parallel-size=4 --port 8000 \ + --served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ + --trust-remote-code --gpu-memory-utilization 0.9 --max-num-seqs 512 \ + --max-model-len 1100000 --mamba-cache-mode align \ + --model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 96}' \ + --compilation-config '{"pass_config": {"fuse_allreduce_rms": false}}' +``` + +A 1.1M-token context costs a great deal of KV cache, which is why you would not serve the +short-context suite this way. + +--- + +## Running + +### Base suite + +21 pretraining tasks against your endpoint: + +```bash +nel run --config base-suite.yaml --env-file .env +nel run --config base-suite.yaml --env-file .env --dry-run # preview +nel run --config base-suite.yaml --env-file .env -t adlr_mmlu # one benchmark +``` + +| Category | Tasks | +|---|---| +| General knowledge | `adlr_mmlu`, `adlr_mmlu_pro_5_shot_base`, `adlr_agieval_en_cot`, `adlr_gpqa_diamond_cot_5_shot` | +| Math | `adlr_gsm8k_cot_8_shot`, `adlr_minerva_math_nemo_4_shot`, `adlr_math_500_4_shot_sampled` | +| Code | `adlr_humaneval_greedy`, `adlr_humaneval_sampled`, `adlr_mbpp_sanitized_3_shot_greedy`, `adlr_mbpp_sanitized_3_shot_sampled` | +| Commonsense | `adlr_commonsense_qa_7_shot`, `adlr_arc_challenge_llama_25_shot`, `hellaswag`, `openbookqa`, `piqa`, `social_iqa`, `adlr_winogrande_5_shot` | +| Reading comprehension | `adlr_race` | +| Multilingual | `adlr_global_mmlu_lite_5_shot`, `adlr_mgsm_native_cot_8_shot` | + +The `*_sampled` tasks repeat requests with the same seed, so response caching is +disabled for them in the config (lm-eval does its own caching). + +### Running against an existing endpoint + +If you already have the model served somewhere, use +[`base-suite.yaml`](./base-suite.yaml) +— it deploys nothing. Point `target.api_endpoint.url` at your endpoint's +`/v1/completions` route, set `model_id` to the served name, and export +`POLICY_API_KEY` alongside `HF_TOKEN` (the tokenizer is still fetched from the Hub): + +```bash +nel run --config base-suite.yaml --env-file .env +``` + +**This is a base model, so the suite uses the completions API, not chat.** A chat +endpoint is not a substitute — the base model has no chat template, and wrapping the +prompts in one changes what is measured. + +The nine log-likelihood tasks send: + +```json +{"prompt": "...", "max_tokens": 1, "logprobs": 1, "echo": true, "temperature": 0, "seed": 1234} +``` + +| | tasks | +|---|---| +| **Need `echo` + `logprobs`** | `adlr_arc_challenge_llama_25_shot`, `adlr_commonsense_qa_7_shot`, `adlr_global_mmlu_lite_5_shot`, `adlr_race`, `adlr_winogrande_5_shot`, `hellaswag`, `openbookqa`, `piqa`, `social_iqa` | +| **Plain completions** | `adlr_agieval_en_cot`, `adlr_gpqa_diamond_cot_5_shot`, `adlr_gsm8k_cot_8_shot`, `adlr_humaneval_greedy`, `adlr_humaneval_sampled`, `adlr_math_500_4_shot_sampled`, `adlr_mbpp_sanitized_3_shot_greedy`, `adlr_mbpp_sanitized_3_shot_sampled`, `adlr_mgsm_native_cot_8_shot`, `adlr_minerva_math_nemo_4_shot`, `adlr_mmlu`, `adlr_mmlu_pro_5_shot_base` | + +Some hosted services accept the request but silently ignore `echo`, which produces +**wrong scores rather than an error** — check before trusting the multiple-choice +numbers. Self-hosted vLLM supports both; the exact serve command this suite was +validated with is in the header of the endpoint config. + +A quick way to check your endpoint before running anything: + +```bash +curl -s "$URL" -H "Authorization: Bearer $POLICY_API_KEY" -H "Content-Type: application/json" \ + -d '{"model":"","prompt":"Question: how to boil water\nAnswer: Put water in a pot", + "max_tokens":1,"logprobs":1,"echo":true,"temperature":0}' \ + | python3 -c "import json,sys; c=json.load(sys.stdin)['choices'][0]; lp=c.get('logprobs') or {}; \ + print('echo:', c['text'][:40]); print('logprobs:', len(lp.get('tokens') or []))" +``` + +The response must echo the prompt back and return one logprob per prompt token. If +`logprobs` is empty or the text does not start with your prompt, the nine tasks above +will mis-score. + +### RULER + +```bash +nel run --config ruler.yaml --env-file .env +``` + +**Prepare the dataset — required, and not included.** RULER runs from a pre-generated +dataset rather than building it per request (generating in-run is slow and gives fewer +configuration options). You must generate it yourself with the RULER preparation +tooling in [nemo-skills](https://github.com/NVIDIA/NeMo-Skills), then bind-mount it: + +```yaml +execution: + extra_docker_args: >- + -v /path/to/your/ruler_data:/ruler_data +``` + +Each task selects its slice through `extra.ruler.setup`, which the configs set to +`nanonext__100` (100 samples per length). Rename these to match whatever your +generated data provides — the names are just keys into your dataset directory, and the +run will fail if they do not resolve. + +**Endpoint.** Point this config at a long-context endpoint served as described in +[Long context serving](#long-context-serving) — not at the one used for the base suite. +Expect the 512k and 1M tasks to be substantially slower and more memory-hungry than +everything else; budget time accordingly. + +### Output + +Each run creates a timestamped invocation directory under `execution.output_dir`, with +one subdirectory per task: + +``` +/-/./ +├── artifacts/ +│ ├── results.yml # headline scores + the exact command that ran +│ ├── report.json / report.html # rendered summary +│ ├── eval_factory_metrics.json # request counts, latencies, HTTP status codes +│ └── / +│ └── results_.json # raw lm-eval output +└── logs/ # client + server logs +``` + +Start with `results.yml` for scores, and **check `eval_factory_metrics.json` before +trusting them** — `response_stats.status_codes` should be `{'200': N}` with +`count == successful_count`. Anything else means some requests failed and the score was +computed on partial data. + +Override the location with `-o "execution.output_dir=/absolute/path"`. For +`resume_from_cache` to survive retries, `output_dir` must be an absolute path on stable +storage (not tmpfs). + +--- + +## Monitoring & customization + +```bash +nel status ; nel logs # status / logs +nel run --config -o execution.output_dir=/abs/path # override output +nel run --config -o evaluation.nemo_evaluator_config.config.params.limit_samples=10 # smoke test only — never for reported numbers +``` + +## Troubleshooting + +- **Gated HF datasets** — accept the terms on the Hub; `HF_TOKEN` must have access. +- **Timeouts** — `request_timeout` is 3600s; on rate limits lower `parallelism`. +- **Out of memory on RULER** — lower `--max-num-seqs`, or raise + `tensor_parallel_size`; the 512k/1M tasks are the ones that will hit it first. +- **`limit_samples` results** — never report them; they exist to validate setup only. + +## Expected results + +Comparable to the model card; expect slight variation on the sampled tasks +(`temperature > 0`). Never report `limit_samples` runs. + +## License + +Apache 2.0 — see the repository `LICENSE`. diff --git a/nemotron_recipes/lightning-3.5/base/base-suite.yaml b/nemotron_recipes/lightning-3.5/base/base-suite.yaml new file mode 100644 index 00000000000..dfca44c9074 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/base/base-suite.yaml @@ -0,0 +1,207 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# Base (pretraining) suite for NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16, run +# against an EXISTING OpenAI-compatible endpoint. Nothing is deployed by this config. +# +# For the variant that deploys its own local vLLM, use +# `base-suite.yaml` instead. +# +# How to use: +# +# 1. copy this file locally or clone the repository +# 2. point target.api_endpoint.url at your endpoint's /v1/completions route and set +# model_id to the name it serves +# 3. export POLICY_API_KEY (endpoint auth) and HF_TOKEN (tokenizer + datasets) +# +# POLICY_API_KEY is whatever bearer token your endpoint expects. A self-hosted +# vLLM usually needs no real key, so any placeholder works. For NVIDIA-hosted +# endpoints use a personal API key in the `nvapi-...` form — a legacy base64 NGC key +# authenticates management calls but returns 401 on invocation, which reads like a +# broken endpoint rather than a wrong credential. +# 4. run `nemo-evaluator-launcher run --config path/to/base-suite.yaml` +# +# ─── ENDPOINT REQUIREMENTS — READ BEFORE RUNNING ───────────────────────────── +# +# This is a BASE model, so the suite drives the COMPLETIONS API, not chat. A chat +# endpoint is not a substitute: the base model has no chat template, and wrapping the +# prompts in one changes what is being measured. +# +# 9 of the 21 tasks are multiple-choice and scored by log-likelihood. They send: +# +# {"prompt": ..., "max_tokens": 1, "logprobs": 1, "echo": true, +# "temperature": 0, "seed": 1234} +# +# so the endpoint MUST honour `echo: true` together with `logprobs`. Many hosted +# inference services accept the request but silently ignore `echo`, which yields wrong +# scores rather than an error — verify before trusting results. Self-hosted vLLM +# supports both (see the serve command below). +# +# Need echo + logprobs : adlr_arc_challenge_llama_25_shot, adlr_commonsense_qa_7_shot, +# adlr_global_mmlu_lite_5_shot, adlr_race, +# adlr_winogrande_5_shot, hellaswag, openbookqa, piqa, +# social_iqa +# Plain completions only : adlr_agieval_en_cot, adlr_gpqa_diamond_cot_5_shot, +# adlr_gsm8k_cot_8_shot, adlr_humaneval_greedy, +# adlr_humaneval_sampled, adlr_math_500_4_shot_sampled, +# adlr_mbpp_sanitized_3_shot_greedy, +# adlr_mbpp_sanitized_3_shot_sampled, +# adlr_mgsm_native_cot_8_shot, adlr_minerva_math_nemo_4_shot, +# adlr_mmlu, adlr_mmlu_pro_5_shot_base +# +# If your endpoint cannot do echo+logprobs, run only the second group with `-t `. +# +# ─── SELF-HOSTING THE ENDPOINT ─────────────────────────────────────────────── +# +# The following serve command is the one this suite was validated with (4x GPU node, +# TP=4, container vllm/vllm-openai:v0.19.1-cu130): +# +# vllm serve nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ +# --tensor-parallel-size=4 --pipeline-parallel-size=1 --data-parallel-size=1 \ +# --port 8000 --served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ +# --trust-remote-code --gpu-memory-utilization 0.9 --max-num-seqs 512 \ +# --no-enable-prefix-caching --enable-log-requests \ +# --model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 96}' +# +# The model is ~31.6B params in BF16 (~62 GB of weights) and declares +# max_position_embeddings 262144. TP=4 is what was validated; smaller TP works if the +# weights and KV cache fit. +# +# ⚠️ WARNING: +# Always run full evaluations (without limit_samples) for actual benchmark results. +# Using a subset of samples is solely for testing configuration and setup. +# Results from such test runs should NEVER be used to compare models or +# report benchmark performance. +defaults: + - execution: local + - deployment: none + - _self_ + +execution: + output_dir: NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16-endpoint + # Caching datasets/tokenizer across runs is optional but recommended. + # mounts: + # evaluation: + # ./hf_cache: /root/.cache/huggingface + +# ─── FILL THESE IN ─────────────────────────────────────────────────────────── +# +# url the /v1/completions route of your endpoint. Examples: +# self-hosted vLLM http://localhost:8000/v1/completions +# hosted service https:///v1/completions +# It must be the COMPLETIONS route, not chat — see the note above. +# +# model_id the name your endpoint SERVES. This is often NOT the Hub repo id: a hosted +# deployment may serve it under a short name such as +# `nemotron-3.5-lightning-30b-a3b-base`, while a self-hosted vLLM started with +# `--served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16` +# serves it under that. Query `GET /v1/models` on your endpoint if unsure. +# The `tokenizer` under evaluation.extra stays the Hub id either way — it is +# fetched from the Hub, not from the endpoint. +target: + api_endpoint: + url: https:///v1/completions + model_id: + api_key_name: POLICY_API_KEY # pragma: allowlist secret + type: completions + +evaluation: + env_vars: + HF_TOKEN: host:HF_TOKEN + TRUST_REMOTE_CODE: lit:1 + nemo_evaluator_config: + config: + required_capabilities: [] + params: + max_retries: 5 + parallelism: 32 # raise if your endpoint tolerates more concurrency + request_timeout: 3600 + temperature: 0.01 + # limit_samples: 10 # uncomment to limit number of samples for quick testing + extra: + # Tokenizer is pulled from the Hub even though the model is remote. + tokenizer: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + tokenizer_backend: huggingface + model_trust_remote_code: true + + tasks: + # General Knowledge + - name: adlr_mmlu + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_mmlu_pro_5_shot_base + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_agieval_en_cot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_gpqa_diamond_cot_5_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + # Math + - name: adlr_gsm8k_cot_8_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + # NOTE: adlr_minerva_math_nemo_4_shot currently fails on the 26.03 container — + # see the "Known issue" section of the README. + - name: adlr_minerva_math_nemo_4_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_math_500_4_shot_sampled + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + nemo_evaluator_config: + target: + api_endpoint: + adapter_config: + # NOTE: Disable caching for tasks that repeat requests with the same seed (i.e. sampled). + # Also, lm-eval has its own caching + use_caching: false + # Code + - name: adlr_humaneval_greedy + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_humaneval_sampled + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + nemo_evaluator_config: + target: + api_endpoint: + adapter_config: + use_caching: false + - name: adlr_mbpp_sanitized_3_shot_greedy + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_mbpp_sanitized_3_shot_sampled + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + nemo_evaluator_config: + target: + api_endpoint: + adapter_config: + use_caching: false + # Commonsense Understanding (all of these need echo + logprobs) + - name: adlr_commonsense_qa_7_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_arc_challenge_llama_25_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: hellaswag + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: openbookqa + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: piqa + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: social_iqa + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_winogrande_5_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + # Reading comprehension (needs echo + logprobs) + - name: adlr_race + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + # Multilingual + - name: adlr_global_mmlu_lite_5_shot # needs echo + logprobs + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 + - name: adlr_mgsm_native_cot_8_shot + container: nvcr.io/nvidia/eval-factory/lm-evaluation-harness:26.03 diff --git a/nemotron_recipes/lightning-3.5/base/ruler.yaml b/nemotron_recipes/lightning-3.5/base/ruler.yaml new file mode 100644 index 00000000000..c48ac4dec8e --- /dev/null +++ b/nemotron_recipes/lightning-3.5/base/ruler.yaml @@ -0,0 +1,140 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +# RULER (long-context) for the Nemotron 3.5 Lightning 30B A3B Base model, run against an +# EXISTING OpenAI-compatible endpoint. Nothing is deployed by this config. +# +# ─── YOUR ENDPOINT MUST BE SERVED FOR LONG CONTEXT ─────────────────────────── +# +# This is a separate config from the short-context suite because RULER needs a different +# serving configuration. A normal deployment of this model will NOT do: the checkpoint +# declares max_position_embeddings 262144, so anything past 256k is refused unless the +# server was started to allow it. +# +# Serve the endpoint you point this at with: +# +# VLLM_ALLOW_LONG_MAX_MODEL_LEN=1 \ +# vllm serve nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ +# --tensor-parallel-size=4 --port 8000 \ +# --served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 \ +# --trust-remote-code --gpu-memory-utilization 0.9 --max-num-seqs 512 \ +# --max-model-len 1100000 --mamba-cache-mode align \ +# --model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 96}' \ +# --compilation-config '{"pass_config": {"fuse_allreduce_rms": false}}' +# +# Container: vllm/vllm-openai:v0.19.1-cu130. A 1.1M-token context costs a great deal of +# KV cache, which is why you would not serve the short-context suite this way. +# +# How to use: +# +# 1. copy this file locally or clone the repository +# 2. serve the model as above, then fill in target.api_endpoint.url and model_id +# 3. pre-generate the RULER data and mount it at /ruler_data (see README, "RULER") +# 4. export POLICY_API_KEY and HF_TOKEN +# 5. run `nemo-evaluator-launcher run --config path/to/ruler.yaml` +# +# ⚠️ Long-context results from this config are PROVISIONAL — see the "RULER" section +# of the README for the open discrepancy against the reference numbers. +defaults: + - execution: local + - deployment: none + - _self_ + +execution: + output_dir: NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16-ruler + # RULER needs the pre-generated dataset mounted into the evaluation container. + extra_docker_args: >- + -v /path/to/your/ruler_data:/ruler_data + +# The model under test. Serve it for long context first (see the header), then fill in. +target: + api_endpoint: + url: https:///v1/completions + model_id: + api_key_name: POLICY_API_KEY # pragma: allowlist secret + type: completions + +evaluation: + env_vars: + HF_TOKEN: host:HF_TOKEN + POLICY_API_KEY: host:POLICY_API_KEY # pragma: allowlist secret + TRUST_REMOTE_CODE: lit:1 + nemo_evaluator_config: + config: + required_capabilities: [] + params: + max_retries: 5 + parallelism: 512 + request_timeout: 3600 + temperature: 0.00001 + top_p: 0.99 + max_new_tokens: 128 + extra: + tokenizer: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + tokenizer_backend: huggingface + model_trust_remote_code: true + + tasks: + - name: ns_ruler_64k + container: nvcr.io/nvidia/eval-factory/nemo-skills:26.05.1 + nemo_evaluator_config: + config: + params: + extra: + ruler: + tokenizer_path: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + data_dir: /ruler_data + setup: nanonext_64k_100 + - name: ns_ruler_128k + container: nvcr.io/nvidia/eval-factory/nemo-skills:26.05.1 + nemo_evaluator_config: + config: + params: + extra: + ruler: + tokenizer_path: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + data_dir: /ruler_data + setup: nanonext_128k_100 + - name: ns_ruler_256k + container: nvcr.io/nvidia/eval-factory/nemo-skills:26.05.1 + nemo_evaluator_config: + config: + params: + extra: + ruler: + tokenizer_path: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + data_dir: /ruler_data + setup: nanonext_256k_100 + - name: ns_ruler_512k + container: nvcr.io/nvidia/eval-factory/nemo-skills:26.05.1 + nemo_evaluator_config: + config: + params: + extra: + ruler: + tokenizer_path: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + data_dir: /ruler_data + setup: nanonext_512k_100 + - name: ns_ruler_1m + container: nvcr.io/nvidia/eval-factory/nemo-skills:26.05.1 + nemo_evaluator_config: + config: + params: + extra: + ruler: + tokenizer_path: nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16 + data_dir: /ruler_data + setup: nanonext_1m_100 diff --git a/nemotron_recipes/lightning-3.5/instruct/README.md b/nemotron_recipes/lightning-3.5/instruct/README.md new file mode 100644 index 00000000000..72ba705117e --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/README.md @@ -0,0 +1,285 @@ +# Instruct model recipes + +Recipes for `nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16`. Most benchmarks ship as +a Gym recipe in [`gym/`](./gym/): a script that wraps `gym eval run` with the settings +the published run used. Terminal-Bench and the two SWE-bench suites are NeMo Evaluator +configs in [`nemo-evaluator/`](./nemo-evaluator/) — see +[that section](#nemo-evaluator-recipes) below. + +To run a Gym recipe, copy the two templates, fill them in, and run from the Gym repo root: + +```bash +cp nemotron_recipes/lightning-3.5/instruct/gym/env.yaml.example env.yaml # policy endpoint and judges +cp nemotron_recipes/lightning-3.5/instruct/gym/.env.example .env # keys and paths +set -a; source .env; set +a +nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh +``` + +Run from the repo root, not from this directory: each recipe resolves its dataset and +prepare script relative to your working directory. + +Every recipe accepts `LIMIT` for a quick smoke, `OUT` for the output directory, +`PARALLEL` for concurrency and `RESUME` to continue an interrupted run. Results land in +`./results/`. + +Each recipe first rolls the repo back to the Gym commit its tech report number was +produced with, leaving the recipes alone. It overwrites and deletes tracked files, so +commit or stash your work first; `git restore .` puts everything back. Set `PIN_GYM=0` +to run against your current checkout instead. + +## Prerequisites + +- **Python 3.13.14 or newer** +- **[uv](https://docs.astral.sh/uv/getting-started/installation/) on your `PATH`** — Gym + builds every server's virtualenv with it, so nothing starts without it. +- **Gym installed**, from the repo root: + + ```bash + uv venv --python 3.13.14 && source .venv/bin/activate + uv sync + ``` + +## Serving the model + +Reproducing these numbers depends on the model being served the right way. Check the +[model card](https://huggingface.co/nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16) +for the authoritative serving guidance; the command below is what these recipes were +validated with, on `vllm/vllm-openai:v0.26.0`. + +```bash +vllm serve nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16 \ + --served-model-name nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16 --port 8000 \ + --tensor-parallel-size 2 --pipeline-parallel-size 1 --data-parallel-size 2 \ + --data-parallel-backend ray --data-parallel-size-local 2 --api-server-count 1 \ + --trust-remote-code --gpu-memory-utilization 0.85 \ + --enable-prefix-caching --enable-chunked-prefill \ + --enable-auto-tool-choice --tool-call-parser qwen3_coder \ + --reasoning-parser nemotron_v3 \ + --kv-cache-dtype fp8 --mamba-cache-mode align \ + --max-num-batched-tokens 131072 \ + --model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 96}' +``` + +The NeMo Evaluator recipes were served with `--gpu-memory-utilization 0.95`, +`--max-num-seqs 32` and an explicit `--max-model-len 262144` instead of +`--max-num-batched-tokens` — throughput knobs rather than output ones, so one endpoint +serves every recipe here. + +## Benchmarks needing extra setup + +Most recipes need nothing beyond the prerequisites above. These do: + +### SciCode + +Scoring needs `test_data.h5` (~1 GB) — the numeric ground-truth the test cases are +checked against. It is not downloaded automatically. Get it from the official SciCode +[Google Drive folder](https://drive.google.com/drive/folders/1W5GZW6_bdiDAiipuFMqdUhvUaHIj6-pR), +save it anywhere, and point `TEST_DATA` at it: + +```bash +sha256sum /path/to/test_data.h5 +# 48b0272a88b17dbd29777c217e1b4fb2b019b92e11cc2add847409db9541b890 + +TEST_DATA=/path/to/test_data.h5 nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh +``` + +It must be readable by the user running the recipe — otherwise every test case fails +and the run scores 0 with no error. + +### GDPval + +The agent writes real work products (spreadsheets, documents, code output) and runs +its code inside an Apptainer sandbox. Three things must exist before a run. + +**1. The sandbox.** The definition ships with Gym; build the image once: + +```bash +nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh /abs/path/gdpval.sif +export GDPVAL_CONTAINER_PATH=/abs/path/gdpval.sif +``` + +You do **not** need to install the document-generation stack yourself. The agent writes +deliverables inside the sandbox, and the image already carries LibreOffice plus +`python-docx`, `python-pptx`, `openpyxl`, `fpdf2`, `reportlab`, `weasyprint` and the +rest. + +**2. Root.** The sandbox is started with `--home /root`, which an ordinary user cannot +enter. Office deliverables are also converted to PDF on the host — not in the sandbox — +and the resources server prepares LibreOffice on startup by running: + +```bash +apt-get install -y libreoffice fonts-liberation default-jre-headless libreoffice-java-common +``` + +That call needs root and returns early if it fails, so pre-installing the packages +doesn't substitute. Either way the run still exits 0: if the sandbox cannot start no +task runs at all, and if only the conversion fails the deliverables reach the judge +unconverted and score lower. Run as root, or in a container where you are. + +**3. A judge endpoint serving all three panel models.** Deliverables are graded by a +panel — GPT-5.5, Gemini 3.1 Pro and Claude Opus 4.8, one sampled per call — all routed +through the `gdpval_judge_model` block in `env.yaml`, so one endpoint has to serve all +three. + +For a full run give `TAVILY_API_KEY` several keys — `'[tvly-1,tvly-2]'` — since a single +key rate-limits and the agent then cannot search. + +#### Rubric mode (default) + +A judge scores each deliverable against its task rubric. + +```bash +nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh +``` + +#### Comparison mode + +Scores deliverables pairwise against reference models **you generate yourself**. Point +`GDPVAL_REFS` at a directory holding one subdirectory per reference, named after the +model: + +``` +refs/ + gptoss_120b/ task_/repeat_0/... + gemma4_26b/ task_/repeat_0/... +``` + +Produce each one with an execute-only run of that model — point the policy in +`env.yaml` at it, then: + +```bash +EXECUTE_ONLY=true OUT=./tmp nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh && mv ./tmp/deliverables ./refs/gptoss_120b +``` + +Then score your model against whatever you collected: + +```bash +GDPVAL_REWARD_MODE=comparison GDPVAL_REFS=./refs nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh +``` + +The recipe recognises these names and anchors each at the +[Artificial Analysis GDPval-AA v2](https://artificialanalysis.ai/evaluations/gdpval-aa) +rating the published figures were fitted against. The live board has moved since, so +these deliberately no longer match it: + +| Reference | ELO | | Reference | ELO | +|---|---|---|---|---| +| `deepseek_v4_pro` | 1307 | | `qwen35_397b` | 962 | +| `glm51_fp8` | 1257 | | `gptoss_120b` | 799 | +| `kimi_k26` | 1191 | | `gemma4_26b` | 761 | +| `nemotron3_ultra` | 1164 | | `qwen3_30b_thinking` | 308 | +| `qwen36_35b` | 1049 | | | | + +Supply **two or more** and the run switches to the two-stage fit used for the published +numbers: a 45-task pass over all of them to place the model, then the full task set +against the four nearest. Supply all nine and the method matches the reference run. + +`JUDGE_ONLY=true` re-scores deliverables you already have, and needs neither the +sandbox nor a search key. + +One caveat on `RESUME`: a stage is journalled complete once it finishes, whether or not +any rollout in it succeeded, and a resume then skips it for good. If a stage produces +zero rollouts — an endpoint that was down the whole time, a bad key — delete +`evaluator_rollouts_multistage_state.jsonl` from `OUT` before resuming, or that stage +contributes nothing to the fit and the ELO comes out wrong with no error. Only at zero; +a stage that partly succeeded resumes correctly. + +#### Reproducing our numbers + +We ran comparison mode: deliverables judged against all nine reference models above, +fitted in two stages, graded by the three-judge panel. The scale is Artificial Analysis' +GDPval-AA v2 ELO, anchored to a human baseline of 1000 — so roughly 1000 is human-level +on these tasks. + +The closest you can get is to generate those nine reference sets yourself and run the +same way: same method, same anchors, same scale. It will land near our number rather +than on it — a gap of tens of points is noise here, not a regression. + +That is also the expensive path: nine reference sets means nine full 220-task agentic +runs before you score your own. Rubric mode is one run and no references. It gives a +self-contained score rather than an ELO, which is enough if you are comparing your own +runs rather than positioning against published models. + +### PinchBench + +Each task runs in its own sandbox, built from the image definition that ships with Gym. +Build it once — this needs **Docker**, because the Apptainer path converts a Docker +image rather than building directly: + +```bash +export PINCHBENCH_SIF=/abs/path/pinchbench.sif +bash responses_api_agents/pinchbench/setup_scripts/build_image.sh --apptainer +``` + +The build script writes to `PINCHBENCH_SIF` when it is set, so exporting it first points +both the build and the recipe at the same file. + +On a host without Docker, build the image elsewhere and copy the `.sif` across. + +The policy endpoint is given to the recipe directly rather than through `env.yaml`, +because OpenClaw calls it itself: set `PINCHBENCH_MODEL_BASE_URL`, +`PINCHBENCH_MODEL_API_KEY` and `PINCHBENCH_MODEL_NAME` alongside the judge and search +keys — all listed in `.env.example`. + +### CritPt + +The Artificial Analysis API scores in batches of exactly 70 distinct problems, so a full +run — 70 problems repeated five times — costs five scoring calls. + +That shapes three things. `LIMIT` counts rollouts and repeats are grouped, so a limited +run never gathers 70 different problems and is never scored. Each key carries a daily scoring quota +and rotates only once a key is rate-limited, so pass several to get through all five calls: +`ARTIFICIAL_ANALYSIS_API_KEY='[key-1,key-2]'`. And concurrency should stay at its default +of 350, since a rollout waiting to be scored holds its slot — lower values starve the +batches and the run wedges until it times out. + +If 350 is more than your endpoint can take, five single-repeat runs at 70 give the same +result. Average their `mean/reward`: + +```bash +for i in 1 2 3 4 5; do + CRITPT_REPEATS=1 PARALLEL=70 OUT=./results/critpt/run_$i nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh +done +``` + +Every submission and grader response is cached under `/critpt_cache`. If a run dies +on quota, re-score it once the quota resets without repeating any inference: + +```bash +python -m resources_servers.critpt.replay --cache-dir /critpt_cache +``` + +## NeMo Evaluator recipes + +Terminal-Bench 2.1, SWE-bench Verified and SWE-bench Multilingual are still being migrated +to Gym and for now still belong to +[NeMo Evaluator](https://github.com/NVIDIA-NeMo/Evaluator). Until that migration lands +they are published here as YAML configs in [`nemo-evaluator/`](./nemo-evaluator/) and are +run with `nel eval run` rather than as Gym recipes. + +Two things have to be in place first. + +**1. NeMo Evaluator, pinned to the commit these numbers were produced with.** + +```bash +pip install "nemo-evaluator[harbor] @ git+https://github.com/NVIDIA-NeMo/Evaluator.git@230c8411fff82fa581195b7d088d7fb67d3bc98c" +``` + +Do not use the 0.3.0 release on PyPI: it does not recognise `sandbox.scrub_git_history`, +which the SWE-bench Multilingual config sets, and refuses to load the file. Do not delete +that line to silence the error either — it strips each task repository's later commits, +and without it the agent can read the official fix straight out of the git history and +score far too high. + +**2. An AWS sandbox.** Every task runs in its own ECS Fargate container, and that +infrastructure is not created for you. Apply the reference Terraform stack from the +[NeMo Evaluator repository](https://github.com/NVIDIA-NeMo/Evaluator/tree/main/terraform) +in your own AWS account, in the region you plan to run in, then point +`sandbox.ecr_repository` in the config at that account's harbor ECR. Export +`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` next to `POLICY_API_KEY`. + +Then run whichever config you want, adding `--resume` to continue an interrupted run: + +```bash +nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml +``` diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/.env.example b/nemotron_recipes/lightning-3.5/instruct/gym/.env.example new file mode 100644 index 00000000000..04aaa6c1123 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/.env.example @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Copy to `.env`, fill in what you need, and load it before running a recipe: +# +# set -a; source .env; set +a +# +# Only the vars for the recipes you actually run matter — leave the rest as `???`. +# Endpoint URLs and judge model ids otherwise live in `env.yaml` (see +# env.yaml.example); PinchBench is the exception and takes its endpoints from here, +# because the agent that calls them never reads env.yaml. + +# === Shared === +# Dataset access. Accept the terms on the Hub first for gated datasets. +HF_TOKEN=??? +# Policy key (env.yaml -> policy_api_key). +NVIDIA_API_KEY=??? +# Judge gateway key (env.yaml -> judge_key). Also read directly by gdpval. +JUDGE_API_KEY=??? +# Tau3's user simulator (env.yaml). Set it to the judge key if that is what you have. +SIM_KEY=??? + +# === Judge models === +# These recipes share env.yaml's judge slot but each needs a different model, so the +# id is named per recipe rather than set once. +# aa-lcr — used: Qwen3-235B-A22B-Instruct-2507 +AALCR_JUDGE_MODEL=??? +# browsecomp — used: GLM-5.1 +BROWSECOMP_JUDGE_MODEL=??? + +# === Web search (browsecomp, gdpval) === +# One key, or a bracketed list to rotate across: [tvly-1,tvly-2]. Unbracketed commas +# are Hydra sweep syntax and abort the run at startup. Prefer the list for full runs: +# a rate-limited agent quietly makes fewer tool calls and scores lower. +TAVILY_API_KEY=??? + +# === BrowseComp === +# Optional: the list of domains search skips. Defaults to the exclude_domains.json +# shipped next to the recipe. Swapping in a different list changes search coverage, +# so scores move with it. +# EXCLUDE_JSON= + +# === CritPt === +# Artificial Analysis grading API. One key, or a bracketed list to spread the daily +# quota — a full run needs one scoring per repeat. +ARTIFICIAL_ANALYSIS_API_KEY=??? +# Optional. Submissions and grader responses are cached here (default +# /critpt_cache) so a run that dies on quota can be re-scored without repeating +# inference. CRITPT_REPEATS is how many passes the interleaved dataset holds. +# CRITPT_CACHE_DIR= +# CRITPT_REPEATS=5 + +# === SciCode === +# Path to the ~1 GB test_data.h5. Must be readable by whoever runs the recipe — an +# unreadable copy scores 0 with no error. +TEST_DATA=??? + +# === Tau3 === +# All optional. GYM_ROOT defaults to $PWD (run from the Gym repo root), TAU2_VENV to +# $GYM_ROOT/responses_api_agents/tau2/.venv, and the data ref to the pinned fork branch. +# GYM_ROOT= +# TAU2_VENV= +# NEMO_GYM_TAU2_BENCH_DATA_REF= + +# === GDPval === +# The sandbox image from build-gdpval-sif.sh. +GDPVAL_CONTAINER_PATH=??? +# Optional. Rubric is the default; comparison additionally needs GDPVAL_REFS pointing +# at a directory of reference deliverables. EXECUTE_ONLY produces deliverables without +# judging (that is how you build a reference set); JUDGE_ONLY re-scores existing ones. +# GDPVAL_REWARD_MODE=comparison +# GDPVAL_REFS= +# EXECUTE_ONLY=true +# JUDGE_ONLY=true +# Where deliverables are written (default /deliverables) and the seed the judge +# panel is drawn with (default 42). GDPVAL_DEF only matters if the image definition is +# not at its usual place in your Gym checkout. +# PERSIST_DELIVERABLES_DIR= +# JUDGE_SAMPLING_SEED=42 +# GDPVAL_DEF= + +# === PinchBench === +# This recipe never reads env.yaml — OpenClaw calls the endpoints itself — so its +# model and judge are configured here. +# Sandbox image. build_image.sh writes to this path too, so export it before building +# and one value covers both. +PINCHBENCH_SIF=??? +# The endpoint OpenClaw calls. No default for the name: a wrong one silently scores a +# different model. +PINCHBENCH_MODEL_BASE_URL=??? +PINCHBENCH_MODEL_API_KEY=??? +PINCHBENCH_MODEL_NAME=??? +# Judge — used: claude-haiku-4-5. +PINCHBENCH_JUDGE_MODEL=??? +PINCHBENCH_JUDGE_BASE_URL=??? +PINCHBENCH_JUDGE_API_KEY=??? +# ONE key only: this field is a plain string, so a bracketed list is taken literally. +PINCHBENCH_TAVILY_API_KEY=??? + +# === Run control (all recipes) === +# Opt-in resume: continue an interrupted run in the same OUT dir. Gym resumes by +# skipping rows already present and fingerprints nothing — not the sampling params, +# not the dataset — so resuming into a dir written under a different config silently +# mixes the two. +# RESUME=1 +# Rollout retry budget, Gym's default is 3. Only PinchBench's published run raised it, +# and its recipe sets that itself — override here only if you want a different budget. +# NEMO_GYM_MAX_ROLLOUT_ATTEMPTS= +# Recipes pin Gym to the commit their tech report number was produced with, which +# rewrites tracked files (never the recipes) — commit or stash your own work first, and +# run `git restore .` to undo. Set PIN_GYM=0 to run against your current checkout, or +# GYM_PIN to a different commit. +# PIN_GYM=0 +# GYM_PIN= diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh b/nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh new file mode 100755 index 00000000000..edd2aa352cd --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# AA-LCR (long-context reasoning). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY +# and JUDGE_API_KEY). Run from the Gym repo root — the benchmark's dataset and +# prepare script resolve relative to your working directory. Results land in +# ./results/aa-lcr. +# +# nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh # full benchmark (100 tasks x 16) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/aa-lcr.sh # output dir, concurrency +# +# Note: top_p 1.0 overrides the 0.95 in env.yaml — intentional for this benchmark. + +# Used judge: Qwen3-235B-A22B-Instruct-2507 (non-reasoning) +AALCR_JUDGE_MODEL="${AALCR_JUDGE_MODEL:?}" + +POLICY=policy_model.responses_api_models.vllm_model + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark aalcr + +gym eval run \ + --benchmark aalcr \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/aa-lcr}/evaluator_rollouts.jsonl" \ + --top-p 1.0 \ + "++Qwen3-235B-A22B-Instruct-2507-FP8.responses_api_models.vllm_model.model=$AALCR_JUDGE_MODEL" \ + "++$POLICY.sequential_reasoning_allowed=false" \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={skip_special_tokens: false}" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh b/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh new file mode 100755 index 00000000000..f106d570dc5 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# BrowseComp (web-browsing agent). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY, +# JUDGE_API_KEY and TAVILY_API_KEY). Run from the Gym repo root — the benchmark's +# dataset and prepare script resolve relative to your working directory. Results +# land in ./results/browsecomp. +# +# nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh # full benchmark (1266 tasks x 1) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/browsecomp.sh # output dir, concurrency + +# Runs all 1266 problems. Unset for prepare.py's default 400-problem subset. +export BROWSECOMP_RUN_FULL=1 + +# Used judge: GLM-5.1 +BROWSECOMP_JUDGE_MODEL="${BROWSECOMP_JUDGE_MODEL:?}" +TAVILY_API_KEY="${TAVILY_API_KEY:?export TAVILY_API_KEY (one key, or [k1,k2] for several)}" + +# The domain list search skips. Which domains are on it changes search coverage, +# so results shift if you swap in a different list. +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +EXCLUDE_JSON="${EXCLUDE_JSON:-$HERE/exclude_domains.json}" +[ -r "$EXCLUDE_JSON" ] || { echo "exclude list not readable at $EXCLUDE_JSON" >&2; exit 1; } + +QWEN=Qwen3-235B-A22B-Instruct-2507-FP8.responses_api_models.vllm_model +HARNESS=browsecomp_benchmark_resources_server.resources_servers.browsecomp_advanced_harness +AGENT=browsecomp_benchmark_agent.responses_api_agents.browsecomp_agent +# The agent runs on this derived node, not policy_model, so thinking is set here. +POLICY=policy_model_no_interleaved_reasoning.responses_api_models.vllm_model + +# prepare has no --model-type flag, so vllm_model.yaml is composed via --config. +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark browsecomp \ + --config responses_api_models/vllm_model/configs/vllm_model.yaml + +gym eval run \ + --benchmark browsecomp \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/browsecomp}/evaluator_rollouts.jsonl" \ + --max-output-tokens 32768 \ + "++$QWEN.model=$BROWSECOMP_JUDGE_MODEL" \ + "++$HARNESS.judge_model_server.name=Qwen3-235B-A22B-Instruct-2507-FP8" \ + "++$HARNESS.tavily_api_key=$TAVILY_API_KEY" \ + "++$HARNESS.exclude_domains_file_path=$EXCLUDE_JSON" \ + "++$AGENT.save_model_call_using_vllm_tokenize_endpoint=false" \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={skip_special_tokens: false}" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/exclude_domains.json b/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/exclude_domains.json new file mode 100644 index 00000000000..eca7d4574c2 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/browsecomp/exclude_domains.json @@ -0,0 +1,196 @@ +{ + "notices": [ + { + "properties": [ + { + "type": "domain", + "value": "ad-italia.it" + }, + { + "type": "domain", + "value": "ad-magazin.de" + }, + { + "type": "domain", + "value": "admagazine.fr" + }, + { + "type": "domain", + "value": "allure.com" + }, + { + "type": "domain", + "value": "architecturaldigest.com" + }, + { + "type": "domain", + "value": "arstechnica.com" + }, + { + "type": "domain", + "value": "bonappetit.com" + }, + { + "type": "domain", + "value": "cntraveler.com" + }, + { + "type": "domain", + "value": "cntraveller.com" + }, + { + "type": "domain", + "value": "cntraveller.de" + }, + { + "type": "domain", + "value": "epicurious.com" + }, + { + "type": "domain", + "value": "glamour.com" + }, + { + "type": "domain", + "value": "glamour.de" + }, + { + "type": "domain", + "value": "glamour.es" + }, + { + "type": "domain", + "value": "glamourmagazine.co.uk" + }, + { + "type": "domain", + "value": "gq-magazine.co.uk" + }, + { + "type": "domain", + "value": "gq.com" + }, + { + "type": "domain", + "value": "gqitalia.it" + }, + { + "type": "domain", + "value": "gqmagazine.fr" + }, + { + "type": "domain", + "value": "johansens.com" + }, + { + "type": "domain", + "value": "lacucinaitaliana.com" + }, + { + "type": "domain", + "value": "newyorker.com" + }, + { + "type": "domain", + "value": "nyt.com" + }, + { + "type": "domain", + "value": "nytimes.com" + }, + { + "type": "domain", + "value": "nytimescooking.com" + }, + { + "type": "domain", + "value": "pitchfork.com" + }, + { + "type": "domain", + "value": "revistaad.es" + }, + { + "type": "domain", + "value": "revistagq.com" + }, + { + "type": "domain", + "value": "revistavanityfair.es" + }, + { + "type": "domain", + "value": "self.com" + }, + { + "type": "domain", + "value": "tatler.com" + }, + { + "type": "domain", + "value": "teenvogue.com" + }, + { + "type": "domain", + "value": "theathletic.com" + }, + { + "type": "domain", + "value": "them.us" + }, + { + "type": "domain", + "value": "vanityfair.com" + }, + { + "type": "domain", + "value": "vanityfair.fr" + }, + { + "type": "domain", + "value": "vanityfair.it" + }, + { + "type": "domain", + "value": "vogue.co.uk" + }, + { + "type": "domain", + "value": "vogue.com" + }, + { + "type": "domain", + "value": "vogue.de" + }, + { + "type": "domain", + "value": "vogue.fr" + }, + { + "type": "domain", + "value": "vogue.it" + }, + { + "type": "domain", + "value": "voguebusiness.com" + }, + { + "type": "domain", + "value": "wirecutter.com" + }, + { + "type": "domain", + "value": "wired.com" + }, + { + "type": "domain", + "value": "wired.it" + }, + { + "type": "domain", + "value": "worldofinteriors.com" + } + ] + } + ] +} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh b/nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh new file mode 100755 index 00000000000..b81bccec146 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# CritPt (research-level physics problems). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY and +# ARTIFICIAL_ANALYSIS_API_KEY). Run from the Gym repo root — the benchmark's dataset +# and prepare script resolve relative to your working directory. Results land in +# ./results/critpt. +# +# nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh # full benchmark (70 problems x 5) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh # rollouts only, never scored (see below) +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/critpt.sh # output dir, concurrency +# +# Note: num_repeats 5 is set through the dataset override below — --num-repeats +# multiplies the config default instead of replacing it. +# +# Note: the grader needs 70 different problems per call. LIMIT counts rollouts and +# repeats are grouped, so a limited run never gets there and is never scored. +# +# Note: keep concurrency at 350 (problems x repeats). A rollout waiting to be scored +# holds its slot, so anything lower wedges the run until it times out. + +ARTIFICIAL_ANALYSIS_API_KEY="${ARTIFICIAL_ANALYSIS_API_KEY:?export ARTIFICIAL_ANALYSIS_API_KEY (one key, or [k1,k2] to fail over when a key is rate-limited)}" + +AGENT=critpt_benchmark_agent.responses_api_agents.critpt_agent +CRITPT=critpt_resources_server.resources_servers.critpt +POLICY=policy_model.responses_api_models.vllm_model + +# Persist every submission and grader response. Without it a run that exhausts the daily +# quota loses all of its inference; with it you can re-score once the quota resets: +# python -m resources_servers.critpt.replay --cache-dir +export CRITPT_CACHE_DIR="${CRITPT_CACHE_DIR:-$(realpath -m "${OUT:-./results/critpt}")/critpt_cache}" + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark critpt + +gym eval run \ + --benchmark critpt \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/critpt}/evaluator_rollouts.jsonl" \ + "++$AGENT.datasets=[{name: critpt, type: benchmark, jsonl_fpath: benchmarks/critpt/data/critpt_benchmark.jsonl, prompt_config: benchmarks/critpt/prompts/turn1.yaml, prepare_script: benchmarks/critpt/prepare.py, num_repeats: ${CRITPT_REPEATS:-5}}]" \ + "++artificial_analysis_api_key=$ARTIFICIAL_ANALYSIS_API_KEY" \ + "++$CRITPT.verify_timeout_seconds=21600" \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={skip_special_tokens: false}" \ + "++$POLICY.sequential_reasoning_allowed=false" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + --concurrency "${PARALLEL:-350}" diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/env.yaml.example b/nemotron_recipes/lightning-3.5/instruct/gym/env.yaml.example new file mode 100644 index 00000000000..28909c7b40e --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/env.yaml.example @@ -0,0 +1,109 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Endpoint configuration for the recipes here. Copy to ./env.yaml in the directory +# you run them from (Gym auto-loads ./env.yaml from the cwd) and fill in the +# placeholders. Never commit the filled-in copy. +# +# Gym merges env.yaml over the benchmark config — only command-line ++ beats it — so +# these blocks wire each recipe's servers. Unused blocks are harmless: a benchmark +# only starts the servers it references. +# +# URLs and model ids go HERE; API keys come from your shell via ${oc.env:VAR}, listed +# in .env.example. A key you don't need resolves empty and only surfaces as a 401 if +# that judge is actually called. +# +# PinchBench is the exception: it reads none of this. OpenClaw calls the endpoints +# itself, so its model and judge are set in .env instead. + +# ========================= FILL THESE IN ========================= + +# The model under test. Base URL ends in /v1 — Gym appends /chat/completions. +# local vLLM / SGLang: http://localhost:8000/v1 +# Serving it yourself is the coherent default: the score-determining serving flags +# (--reasoning-parser, --kv-cache-dtype fp8, --max-model-len) can only be set that way. +policy_base_url: http://localhost:8000/v1 +policy_api_key: ${oc.env:NVIDIA_API_KEY} +# Whatever name your endpoint serves the model under — the recipes pass it straight +# through, so it must match your deployment exactly. +# +# Serve with --reasoning-parser nemotron_v3: enable_thinking and skip_special_tokens +# only behave as in the published runs on that template plus parser. GDPval is the +# exception — its reference used a parser plugin that is not public, so its numbers +# depend on something you cannot reproduce exactly here. +policy_model_name: + +# Default sampling for the model under test. Some recipes adjust this per benchmark +# (aa-lcr's top_p, browsecomp's and omniscience's token caps). +responses_create_params: + temperature: 1.0 + top_p: 0.95 + +# Judge gateway. The blocks below inherit these two, so set them once. The endpoint +# must be OpenAI-compatible and serve the judge models pinned below; if your judges +# live on different providers, replace ${judge_url}/${judge_key} per block. +judge_url: "" # e.g. https://api.openai.com/v1 +judge_key: ${oc.env:JUDGE_API_KEY,""} + +# ===================== WIRING (don't rename) ===================== +# These block names are Gym server ids fixed by the benchmark configs. Each pins the +# judge MODEL the published runs used — keep the model, adjusting only the id to +# however your provider routes it. + +# HLE judge — used: gpt-4o. Without this block HLE grades with the model under test, +# which is the benchmark config's default. +judge_model: + responses_api_models: + openai_model: + openai_base_url: ${judge_url} + openai_model: gpt-4o + openai_api_key: ${judge_key} + +# AA-Omniscience judge — used: gemini-3-flash-preview. +genrm_model: + responses_api_models: + openai_model: + openai_base_url: ${judge_url} + openai_model: gemini-3-flash-preview + openai_api_key: ${judge_key} + +# Shared judge slot for aa-lcr and browsecomp, which need DIFFERENT models — each +# recipe pins its own via AALCR_JUDGE_MODEL / BROWSECOMP_JUDGE_MODEL, so only the +# endpoint and key belong here. +Qwen3-235B-A22B-Instruct-2507-FP8: + responses_api_models: + vllm_model: + base_url: ${judge_url} + api_key: ${judge_key} + +# Tau3 user simulator — plays the customer, so its identity is part of the benchmark. +# Used: gpt-5.4-mini. Key comes from SIM_KEY. +gpt-5_4-mini-2026-03-17: + responses_api_models: + openai_model: + openai_base_url: ${judge_url} + openai_model: gpt-5.4-mini + openai_api_key: ${oc.env:SIM_KEY,""} + +# GDPval judges. Deliverables are graded by a panel — GPT-5.5, Gemini 3.1 Pro and +# Claude Opus 4.8, one sampled per call — and all three route through this one block, +# so the endpoint has to serve all of them. The model id below is only the fallback +# used when the panel is disabled. +gdpval_judge_model: + responses_api_models: + openai_model: + openai_base_url: ${judge_url} + openai_model: gemini-3.1-pro-preview + openai_api_key: ${judge_key} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh b/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh new file mode 100755 index 00000000000..31c4f2afb80 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Builds the Apptainer sandbox GDPval runs deliverables in — once, before +# gdpval.sh. Needs apptainer on the host; the definition ships with Gym, so run +# this from the Gym repo root (or set GYM_ROOT). +# +# nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh # writes ./gdpval.sif +# nemotron_recipes/lightning-3.5/instruct/gym/gdpval/build-gdpval-sif.sh /abs/out.sif # writes somewhere else +# +# Then export the path gdpval.sh reads: +# export GDPVAL_CONTAINER_PATH=/abs/out.sif +# +# Note: building inside Docker needs --privileged --device /dev/fuse, a subuid +# mapping for the build user (echo "root:100000:65536" >> /etc/subuid, same for +# /etc/subgid), and an output path on a container-local dir rather than a host +# bind-mount. On a Linux host, VM or cluster node it runs natively. +set -euo pipefail + +GYM_ROOT="${GYM_ROOT:-$PWD}" +OUT="${1:-$PWD/gdpval.sif}" +DEF="${GDPVAL_DEF:-$GYM_ROOT/responses_api_agents/stirrup_agent/containers/gdpval.def}" + +command -v apptainer >/dev/null || { echo "apptainer not installed (https://apptainer.org/docs/admin/main/installation.html)" >&2; exit 1; } +[ -r "$DEF" ] || { echo "gdpval.def not readable at $DEF — set GYM_ROOT to your Gym checkout (or GDPVAL_DEF to the .def)" >&2; exit 1; } + +echo "Building $OUT from $DEF (multi-GB; several minutes)…" +apptainer build --fakeroot "$OUT" "$DEF" +echo +echo "Built: $OUT" +echo "Next: export GDPVAL_CONTAINER_PATH=$OUT" diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh b/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh new file mode 100755 index 00000000000..2883cb69d37 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh @@ -0,0 +1,138 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# GDPval (real-world work products, judged). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY, +# JUDGE_API_KEY and TAVILY_API_KEY). Also needs the Apptainer sandbox built by +# build-gdpval-sif.sh, pointed at by GDPVAL_CONTAINER_PATH (see +# instruct/README.md). Run from the Gym repo root — the benchmark's dataset and +# prepare script resolve relative to your working directory. Results land in +# ./results/gdpval. +# +# nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh # full benchmark (220 tasks x 1) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/gdpval/gdpval.sh # output dir, concurrency +# +# Scores each deliverable against its rubric. Comparison mode instead scores against +# reference deliverables you generate yourself, one subdirectory per reference model; +# see instruct/README.md for the layout and how to produce them. +# +# Note: PARALLEL is applied twice on purpose — the agent caps its own concurrent runs +# at 32 regardless of --concurrency, so raising only one of them does nothing. + +# Used judges: Gym's default panel — GPT-5.5, Gemini 3.1 Pro and Claude Opus 4.8, +# one sampled per call. All three route through gdpval_judge_model in env.yaml, so +# that endpoint has to serve every one of them. + +# JUDGE_ONLY re-scores existing deliverables without running the agent, so it +# needs neither the sandbox nor a search key. +if [ "${JUDGE_ONLY:-false}" = "true" ]; then + TAVILY_API_KEY="" +else + GDPVAL_CONTAINER_PATH="${GDPVAL_CONTAINER_PATH:?export GDPVAL_CONTAINER_PATH (gdpval.sif from build-gdpval-sif.sh)}" + [ -r "$GDPVAL_CONTAINER_PATH" ] || { echo "gdpval.sif not readable at $GDPVAL_CONTAINER_PATH" >&2; exit 1; } + TAVILY_API_KEY="${TAVILY_API_KEY:?export TAVILY_API_KEY (one key, or [k1,k2] for several)}" +fi + +STIR=gdpval_stirrup_agent.responses_api_agents.stirrup_agent +GDR=gdpval_resources_server.resources_servers.gdpval +JUDGE=gdpval_judge_model.responses_api_models.openai_model +POLICY=policy_model.responses_api_models.vllm_model + +# Absolute path required. A comparison run points GDPVAL_REFS at one of these. +DELIVERABLES="${PERSIST_DELIVERABLES_DIR:-$(realpath -m "${OUT:-./results/gdpval}/deliverables")}" + +# Reference ELOs — the Artificial Analysis GDPval-AA v2 ratings the published figures +# were fitted against. A reference set is a subdirectory of GDPVAL_REFS named after one +# of these keys; supply as many as you have. Comparing against several opponents of +# known rating is what puts the result on the published scale — a single opponent only +# fixes an arbitrary offset. +# +# Pinned on purpose, and they no longer match the live board. These anchors define the +# scale, so refreshing them moves your score off the one the published figures sit on. +REF_ELOS="deepseek_v4_pro=1307 glm51_fp8=1257 kimi_k26=1191 nemotron3_ultra=1164 + qwen36_35b=1049 qwen35_397b=962 gptoss_120b=799 gemma4_26b=761 + qwen3_30b_thinking=308" + +MODE="${GDPVAL_REWARD_MODE:-rubric}" +[ "$MODE" = rubric ] || [ "$MODE" = comparison ] || + { echo "GDPVAL_REWARD_MODE must be rubric or comparison (got '$MODE')" >&2; exit 1; } + +MODE_OVR=() # rubric is the config default and needs nothing added +if [ "$MODE" = comparison ]; then + GDPVAL_REFS="${GDPVAL_REFS:?export GDPVAL_REFS (dir of reference deliverables)}" + MODE_OVR=("++$GDR.reward_mode=comparison") + found=0 + + for kv in $REF_ELOS; do + name="${kv%%=*}" + [ -d "$GDPVAL_REFS/$name" ] || continue + MODE_OVR+=("++$GDR.reference_models.$name.deliverables_dir=$GDPVAL_REFS/$name" + "++$GDR.reference_models.$name.elo=${kv##*=}") + found=$((found + 1)) + done + + if [ "$found" -eq 0 ]; then + echo "no reference sets found in $GDPVAL_REFS. Expected subdirectories named:" >&2 + for kv in $REF_ELOS; do echo " ${kv%%=*}" >&2; done + exit 1 + fi + + # Two or more opponents: stage 1 places the model, stage 2 spends the full task + # budget on the nearest of them. With one there is nothing to narrow to. + if [ "$found" -ge 2 ]; then + MODE_OVR+=("++multistage.enabled=true" + "++multistage.stages=[{num_tasks: 45}, {num_models: $((found < 4 ? found : 4))}]") + fi + + echo "gdpval: comparison against $found rated reference(s)" >&2 +fi + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-57c15a22f8b82d3d859b71468fe3329f4e2093b4}" +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark gdpval + +gym eval run \ + --benchmark gdpval \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/gdpval}/evaluator_rollouts.jsonl" \ + "++$STIR.tavily_api_key=$TAVILY_API_KEY" \ + "++$STIR.persist_deliverables_dir=$DELIVERABLES" \ + "++$GDR.judge_sampling_seed=${JUDGE_SAMPLING_SEED:-42}" \ + "++$GDR.persist_raw_judge_responses=true" \ + "++$GDR.preconvert_max_concurrent=30" \ + "++$JUDGE.max_concurrent_requests=10" \ + ${PARALLEL:+"++$STIR.concurrency=$PARALLEL"} \ + ${MODE_OVR[@]+"${MODE_OVR[@]}"} \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={skip_special_tokens: false}" \ + "++$POLICY.sequential_reasoning_allowed=false" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh b/nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh new file mode 100755 index 00000000000..28efca35426 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# GPQA Diamond (no tools). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN and +# NVIDIA_API_KEY). Run from the Gym repo root — the benchmark's dataset and +# prepare script resolve relative to your working directory. Results land in +# ./results/gpqa. +# +# nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh # full benchmark (198 questions x 8) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/gpqa.sh # output dir, concurrency + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark gpqa + +gym eval run \ + --benchmark gpqa \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/gpqa}/evaluator_rollouts.jsonl" \ + "++overwrite_metrics_conflicts=true" \ + "++policy_model.responses_api_models.vllm_model.chat_template_kwargs={enable_thinking: true}" \ + "++policy_model.responses_api_models.vllm_model.extra_body={skip_special_tokens: false}" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/hle.sh b/nemotron_recipes/lightning-3.5/instruct/gym/hle.sh new file mode 100755 index 00000000000..995ccf1ce89 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/hle.sh @@ -0,0 +1,82 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Humanity's Last Exam (no tools). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY +# and JUDGE_API_KEY). Run from the Gym repo root — the benchmark's dataset and +# prepare script resolve relative to your working directory. Results land in +# ./results/hle. +# +# nemotron_recipes/lightning-3.5/instruct/gym/hle.sh # full benchmark (2158 questions x 1) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/hle.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/hle.sh # output dir, concurrency +# +# Note: the judge overrides below are HLE's grading contract — labels, the +# answer-extraction regex and a strict JSON schema. Changing them changes scores. +# msg_extraction_failure is empty on purpose; Gym's default sentinel would +# otherwise reach the judge as the model's answer. + +# Used judge: gpt-4o (wired as judge_model in env.yaml) + +JUDGE=hle_equivalence_llm_judge_resources_server.resources_servers.equivalence_llm_judge +JPARAMS=equivalence_llm_judge.resources_servers.equivalence_llm_judge.judge_responses_create_params +POLICY=policy_model.responses_api_models.vllm_model + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark hle + +gym eval run \ + --benchmark hle \ + --model-type vllm_model \ + --resources-server labbench2_vlm/judge_model_openai \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/hle}/evaluator_rollouts.jsonl" \ + "++$JUDGE.judge_model_server.name=judge_model" \ + "++$JUDGE.judge_equal_label=HLE_JUDGE_CORRECT" \ + "++$JUDGE.judge_not_equal_label=HLE_JUDGE_INCORRECT" \ + "++$JUDGE.response_extract_regex='(?s)\A(?:(.{1,8192})\Z|(?=.{8193,}\Z))'" \ + "++$JUDGE.msg_extraction_failure=''" \ + "++$JPARAMS.temperature=0.0" \ + "++$JPARAMS.top_p=0.95" \ + "++$JPARAMS.text.format.type=json_schema" \ + "++$JPARAMS.text.format.name=hle_judge" \ + "++$JPARAMS.text.format.strict=true" \ + "++$JPARAMS.text.format.schema.type=object" \ + "++$JPARAMS.text.format.schema.properties.correct.type=string" \ + "++$JPARAMS.text.format.schema.properties.correct.enum=[HLE_JUDGE_CORRECT,HLE_JUDGE_INCORRECT]" \ + "++$JPARAMS.text.format.schema.properties.extracted_final_answer.type=string" \ + "++$JPARAMS.text.format.schema.properties.reasoning.type=string" \ + "++$JPARAMS.text.format.schema.properties.confidence.type=integer" \ + "++$JPARAMS.text.format.schema.required=[correct,extracted_final_answer,reasoning,confidence]" \ + "++$JPARAMS.text.format.schema.additionalProperties=false" \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={seed: 0, skip_special_tokens: false}" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh b/nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh new file mode 100755 index 00000000000..1eb935cdbfd --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# AA-Omniscience (knowledge & hallucination). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY +# and JUDGE_API_KEY). Run from the Gym repo root — the benchmark's dataset and +# prepare script resolve relative to your working directory. Results land in +# ./results/omniscience. +# +# nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh # full benchmark (600 questions x 10) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/omniscience.sh # output dir, concurrency +# +# Note: num_repeats 10 is set through the dataset override below — --num-repeats +# multiplies the config default (8) instead of replacing it. + +# Used judge: Gemini 3 Flash (wired as genrm_model in env.yaml) + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark omniscience + +gym eval run \ + --benchmark omniscience \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/omniscience}/evaluator_rollouts.jsonl" \ + "++omniscience_omniscience_simple_agent.responses_api_agents.simple_agent.datasets=[{name: omniscience, type: benchmark, jsonl_fpath: benchmarks/omniscience/data/omniscience_benchmark.jsonl, prompt_config: benchmarks/omniscience/prompts/generation.yaml, prepare_script: benchmarks/omniscience/prepare.py, num_repeats: 10}]" \ + "++omniscience_omniscience_resources_server.resources_servers.omniscience.judge_responses_create_params.max_output_tokens=32768" \ + "++genrm_model.responses_api_models.openai_model.max_concurrent_requests=16" \ + "++policy_model.responses_api_models.vllm_model.chat_template_kwargs={enable_thinking: true}" \ + "++policy_model.responses_api_models.vllm_model.extra_body={skip_special_tokens: false}" \ + "++policy_model.responses_api_models.vllm_model.sequential_reasoning_allowed=false" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh b/nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh new file mode 100755 index 00000000000..d7fe61992df --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh @@ -0,0 +1,108 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# PinchBench (the model as the brain of an OpenClaw agent). +# +# Needs an active Gym venv, .env loaded into your shell (this recipe uses +# PINCHBENCH_MODEL_BASE_URL, PINCHBENCH_MODEL_API_KEY, PINCHBENCH_MODEL_NAME, +# PINCHBENCH_JUDGE_MODEL, PINCHBENCH_JUDGE_BASE_URL, PINCHBENCH_JUDGE_API_KEY and +# PINCHBENCH_TAVILY_API_KEY) and the per-task sandbox image, pointed at by +# PINCHBENCH_SIF (see instruct/README.md). Run from the Gym repo root — the +# benchmark's dataset and prepare script resolve relative to your working +# directory. Results land in ./results/pinchbench. +# +# nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh # full benchmark (147 tasks x 10) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/pinchbench.sh # output dir, concurrency +# +# Note: OpenClaw calls the policy endpoint itself instead of going through a Gym +# model server, so the endpoint is given here rather than in env.yaml, and the +# sampling settings come from the agent's direct-exec wrapper rather than from +# this recipe. +# +# Note: PARALLEL also caps how many tasks the agent runs at once (16 by default, as +# in the reference run). Lower it if your endpoint slows under load — tasks time out +# rather than queue, so an overloaded endpoint costs score. +# +# Note: num_repeats is 10 below, where the benchmark's own config uses 3. 10 is what +# the published run used; both are deliberate, so don't reconcile them. + +# Used judge: Claude Haiku 4.5 +PINCHBENCH_JUDGE_MODEL="${PINCHBENCH_JUDGE_MODEL:?}" +PINCHBENCH_JUDGE_BASE_URL="${PINCHBENCH_JUDGE_BASE_URL:?}" +PINCHBENCH_JUDGE_API_KEY="${PINCHBENCH_JUDGE_API_KEY:?}" + +PINCHBENCH_SIF="${PINCHBENCH_SIF:?export PINCHBENCH_SIF (image from setup_scripts/build_image.sh)}" +[ -r "$PINCHBENCH_SIF" ] || { echo "sandbox image not readable at $PINCHBENCH_SIF" >&2; exit 1; } + +# Passed straight to OpenClaw, so a wrong name scores a different model. No default. +PINCHBENCH_MODEL_BASE_URL="${PINCHBENCH_MODEL_BASE_URL:?export PINCHBENCH_MODEL_BASE_URL (endpoint serving your model)}" +PINCHBENCH_MODEL_API_KEY="${PINCHBENCH_MODEL_API_KEY:?export PINCHBENCH_MODEL_API_KEY (key for that endpoint)}" +PINCHBENCH_MODEL_NAME="${PINCHBENCH_MODEL_NAME:?export PINCHBENCH_MODEL_NAME (name your endpoint serves the model under)}" + +# One key only — this field is a plain string, so [k1,k2] is taken literally. Kept +# separate from TAVILY_API_KEY for that reason: the other recipes accept a key list. +PINCHBENCH_TAVILY_API_KEY="${PINCHBENCH_TAVILY_API_KEY:?export PINCHBENCH_TAVILY_API_KEY (one key)}" + +# The published run raised Gym's rollout retry budget from its default of 3. An +# exhausted rollout scores 0, so on a flaky endpoint this moves the result. +export NEMO_GYM_MAX_ROLLOUT_ATTEMPTS="${NEMO_GYM_MAX_ROLLOUT_ATTEMPTS:-10}" + +PB=pinchbench_agent.responses_api_agents.pinchbench +BENCH=pinchbench_benchmark_agent.responses_api_agents.pinchbench + +# Absolute, and under the run's output dir: both default to /tmp/pinchbench_gym, which +# strands the per-task transcripts away from the results and collides between runs. +OUTDIR="$(realpath -m "${OUT:-./results/pinchbench}")" + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-86290ee8fdc191f2d27b48bd2e957a25dcbd7fd7}" +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark pinchbench + +gym eval run \ + --benchmark pinchbench \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/pinchbench}/evaluator_rollouts.jsonl" \ + "+sandbox_image=$PINCHBENCH_SIF" \ + "+model_base_url=$PINCHBENCH_MODEL_BASE_URL" \ + "+model_api_key=$PINCHBENCH_MODEL_API_KEY" \ + "+model_name=$PINCHBENCH_MODEL_NAME" \ + "+judge_model=$PINCHBENCH_JUDGE_MODEL" \ + "+judge_base_url=$PINCHBENCH_JUDGE_BASE_URL" \ + "+judge_api_key=$PINCHBENCH_JUDGE_API_KEY" \ + "+tavily_api_key=$PINCHBENCH_TAVILY_API_KEY" \ + "++$BENCH.datasets=[{name: pinchbench, type: benchmark, jsonl_fpath: benchmarks/pinchbench/data/pinchbench_benchmark.jsonl, prompt_config: null, prepare_script: benchmarks/pinchbench/prepare.py, num_repeats: 10, license: MIT}]" \ + "++$PB.web_search_provider=tavily" \ + "++$PB.sandbox_provider.apptainer.direct_exec=true" \ + "++$PB.context_window=262143" \ + "++$PB.max_tokens=65536" \ + "++$PB.max_concurrent=${PARALLEL:-16}" \ + "++$PB.work_root=$OUTDIR/pinchbench_work" \ + "++$PB.transcripts_dir=$OUTDIR/pinchbench_transcripts" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + --concurrency "${PARALLEL:-16}" diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh b/nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh new file mode 100755 index 00000000000..77b21c5de84 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SciCode (scientific coding). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN and +# NVIDIA_API_KEY). Also needs SciCode's ~1 GB test_data.h5, pointed at by +# TEST_DATA (see instruct/README.md). Run from the Gym repo root — the +# benchmark's dataset and prepare script resolve relative to your working +# directory. Results land in ./results/scicode. +# +# nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh # full benchmark (65 problems x 8) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/scicode.sh # output dir, concurrency +# +# Note: num_repeats 8 is set through the dataset override below — --num-repeats +# multiplies the config default (3) instead of replacing it. + +TEST_DATA="${TEST_DATA:?export TEST_DATA (path to SciCode test_data.h5)}" +[ -r "$TEST_DATA" ] || { echo "test_data.h5 not readable at $TEST_DATA" >&2; exit 1; } + +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e446e4f415b9cde0e95bb813c85e9e3e23f5d893}" # 0.5.0rc0 +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +gym eval prepare --benchmark scicode + +gym eval run \ + --benchmark scicode \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/scicode}/evaluator_rollouts.jsonl" \ + "++scicode_benchmark_agent.responses_api_agents.scicode_agent.datasets=[{name: scicode, type: benchmark, jsonl_fpath: benchmarks/scicode/data/scicode_benchmark.jsonl, prepare_script: benchmarks/scicode/prepare.py, prompt_config: null, num_repeats: 8}]" \ + "++scicode_benchmark_resources_server.resources_servers.scicode.test_data_fpath=$TEST_DATA" \ + "++policy_model.responses_api_models.vllm_model.chat_template_kwargs={enable_thinking: true}" \ + "++policy_model.responses_api_models.vllm_model.extra_body={skip_special_tokens: false}" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh b/nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh new file mode 100755 index 00000000000..ddc20c6bf8f --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Tau3-Banking (BM25 + grep knowledge tools). +# +# Needs an active Gym venv, ./env.yaml (copy env.yaml.example) and .env loaded +# into your shell (copy .env.example; this recipe uses HF_TOKEN, NVIDIA_API_KEY +# and SIM_KEY). Run from the Gym repo root — the benchmark's dataset and prepare +# script resolve relative to your working directory. Results land in +# ./results/tau3. +# +# nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh # full benchmark (97 tasks x 5) +# LIMIT=3 nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh # quick smoke +# OUT= PARALLEL= nemotron_recipes/lightning-3.5/instruct/gym/tau3.sh # output dir, concurrency +# +# Note: Gym installs tau2-bench from a branch, so what you get moves over time. +# This recipe pins the commit instead. + +# Used simulator: gpt-5.4-mini plays the customer (wired in env.yaml) + +export NEMO_GYM_TAU2_BENCH_DATA_REF="${NEMO_GYM_TAU2_BENCH_DATA_REF:-bxyu/nemo_gym_data}" + +BENCH=tau2/configs/banking_bm25_grep_artificial_analysis +GYM_ROOT="${GYM_ROOT:-$PWD}" +TAU2_VENV="${TAU2_VENV:-$GYM_ROOT/responses_api_agents/tau2/.venv}" + +# Gym builds the tau2 agent venv on first use, so pre-build it here to give the +# pin below somewhere to install. +# Pin Gym to the commit the tech report numbers were produced with. Set PIN_GYM=0 to +# run against your current checkout instead. `nemotron_recipes` is excluded, so this +# never touches the recipe that is running, and HEAD does not move. Undo the pin with +# `git restore .` from the repo root. +GYM_PIN="${GYM_PIN:-e590c4fadd36a89ab4b826497a7414b4a9de281c}" # 0.5.0rc0, first commit with `gym env prefetch` +if [ "${PIN_GYM:-1}" != 0 ]; then + git rev-parse --verify -q "$GYM_PIN^{commit}" >/dev/null 2>&1 || git fetch origin "$GYM_PIN" + git restore --source="$GYM_PIN" -- . ':(exclude)nemotron_recipes' || exit 1 + echo "pinned Gym to $GYM_PIN (recipes untouched; PIN_GYM=0 to skip; git restore . to undo)" +fi + +[ -d "$TAU2_VENV" ] || gym env prefetch --benchmark "$BENCH" --model-type vllm_model +[ -d "$TAU2_VENV" ] || { echo "tau2 agent venv still missing at $TAU2_VENV" >&2; exit 1; } + +uv pip install --python "$TAU2_VENV/bin/python" --force-reinstall --no-deps \ + "tau2[knowledge] @ git+https://github.com/bxyu-nvidia/tau2-bench@60c2a0dbf974ea7533456a4706f837c3a6d14afc" +uv pip install --python "$TAU2_VENV/bin/python" --quiet rank-bm25 + +SIM=gpt-5_4-mini-2026-03-17.responses_api_models.openai_model +POLICY=policy_model.responses_api_models.vllm_model + +gym eval prepare --benchmark "$BENCH" + +gym eval run \ + --benchmark "$BENCH" \ + --model-type vllm_model \ + --split benchmark \ + ${RESUME:+--resume} \ + --output "${OUT:-./results/tau3}/evaluator_rollouts.jsonl" \ + "++$SIM.extra_body._delete_key=max_output_tokens" \ + "++$POLICY.sequential_reasoning_allowed=false" \ + "++$POLICY.chat_template_kwargs={enable_thinking: true}" \ + "++$POLICY.extra_body={skip_special_tokens: false}" \ + "++skip_venv_if_present=true" \ + "++overwrite_metrics_conflicts=true" \ + ${LIMIT:+--limit "$LIMIT"} \ + ${PARALLEL:+--concurrency "$PARALLEL"} diff --git a/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/_openhands_system.yaml b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/_openhands_system.yaml new file mode 100644 index 00000000000..565e17d1343 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/_openhands_system.yaml @@ -0,0 +1,93 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# OpenHands agent system prompt, shared by the SWE-bench configs. Included with +# `defaults: [{_base_: prompts/_openhands_system.yaml}]` and referenced as +# ${._openhands_system}; the underscore key is stripped before validation. + +"_openhands_system": | + You are OpenHands agent, a helpful AI assistant that can interact with a computer to solve tasks. + + + Your primary role is to assist users by executing commands, modifying code, and solving technical problems effectively. You should be thorough, methodical, and prioritize quality over speed. + * If the user asks a question, like "why is X happening", don't try to fix the problem. Just give an answer to the question. + + + + * Each action you take is somewhat expensive. Wherever possible, combine multiple actions into a single action, e.g. combine multiple bash commands into one, using sed and grep to edit/view multiple files at once. + * When exploring the codebase, use efficient tools like find, grep, and git commands with appropriate filters to minimize unnecessary operations. + + + + * If you've made repeated attempts to solve a problem but tests still fail or the user reports it's still broken: + 1. Step back and reflect on 5-7 different possible sources of the problem + 2. Assess the likelihood of each possible cause + 3. Methodically address the most likely causes, starting with the highest probability + 4. Document your reasoning process + * When you run into any major issue while executing a plan from the user, please don't try to directly work around it. Instead, propose a new plan and confirm with the user before proceeding. + + + + * When terminating processes: + - Do NOT use general keywords with commands like `pkill -f server` or `pkill -f python` as this might accidentally kill other important servers or processes + - Always use specific keywords that uniquely identify the target process + - Prefer using `ps aux` to find the exact process ID (PID) first, then kill that specific PID + - When possible, use more targeted approaches like finding the PID from a pidfile or using application-specific shutdown commands + + + + * You have access to the `task_tracker` tool to help you organize and monitor development work. Use this tool REGULARLY to maintain task visibility and provide users with clear progress updates. This tool is ESSENTIAL for systematic planning and decomposing complex development work into manageable components. Failing to use this tool for planning may result in overlooked requirements - which is unacceptable. + * It is crucial that you update task status to "done" immediately upon completion of each work item. Do not accumulate multiple finished tasks before updating their status. + * For complex, multi-phase development work, use `task_tracker` to establish a comprehensive plan with well-defined steps: + 1. Begin by decomposing the overall objective into primary phases using `task_tracker` + 2. Include detailed work items as necessary to break complex activities into actionable units + 3. Update tasks to "in_progress" status when commencing work on them + 4. Update tasks to "done" status immediately after completing each item + 5. For each primary phase, incorporate additional work items as you identify new requirements + 6. If you determine the plan requires substantial modifications, suggest revisions and obtain user confirmation before proceeding + * Example workflow for debugging and resolution: + ``` + User: "Execute the test suite and resolve any validation failures" + Assistant: I'm going to use the task_tracker tool to organize the following work items: + - Execute the test suite + - Resolve any validation failures + I'm now going to run the test suite using the terminal. + [After running tests and discovering 8 validation failures] + I found 8 validation failures that need attention. I'm going to use the task_tracker tool to add 8 specific items to the task list. + [Updating first task to in_progress] + Let me begin addressing the first validation issue... + [After resolving first failure] + The first validation issue has been resolved, let me mark that task as done and proceed to the second item... + ``` + * Example workflow for component development: + ``` + User: "Build a dashboard component that displays analytics data with interactive charts and filtering options" + Assistant: I'll help you create an analytics dashboard with interactive charts and filtering. Let me first use the task_tracker tool to organize this development work. + Adding the following tasks to the tracker: + 1. Analyze existing analytics data structure and requirements + 2. Design dashboard layout and component architecture + 3. Implement data visualization charts with interactivity + 4. Create filtering and search functionality + 5. Integrate components and perform testing + Let me start by examining the current analytics data structure to understand what we're working with... + [Assistant proceeds with implementation step by step, updating tasks to in_progress and done as work progresses] + ``` + + + + * IMPORTANT: If you were using the task_tracker tool before a condensation event, continue using it after condensation + * Check condensation summaries for TASK_TRACKING sections to maintain continuity + * If you see a condensation event with TASK_TRACKING, immediately use task_tracker to view and continue managing them + diff --git a/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/swebench-instruction.md b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/swebench-instruction.md new file mode 100644 index 00000000000..55b032f0ded --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/swebench-instruction.md @@ -0,0 +1,68 @@ +Implement the necessary changes to the repository ( {{ workspace_path }} ) so that the requirements specified in the are satisfied. +The environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages. + +I've uploaded a {{ instance.repo_language ~ ' ' if instance.repo_language else '' }}code repository in the directory {{ workspace_path }} + + +{{ instance.problem_statement }} + + + +{{ workspace_path }} + + +Can you help me implement the necessary changes to the repository so that the requirements specified in the are met? +I've already taken care of all changes to any of the test files described in the . This means you DON'T have to modify the testing logic or any of the tests in any way! +Also the development environment is already set up for you (i.e., all dependencies already installed), so you don't need to install other packages. +Your task is to make the minimal changes to non-test files in the {{ workspace_path }} directory to ensure the is satisfied. + +When solving bugs, follow these steps: +## 1. Explore the codebase and understand the relevant code + + - Use efficient search commands to identify key files and functions + - Err on the side of caution and inspect all relevant files to build an understanding of: + * how the code works, + * what the expected behaviors and edge cases are, + * what the potential root causes are for the reported issue. + +## 2. Assess whether you can reproduce the issue + + - Create a script that reliably reproduces the error, and execute this script to confirm the erroneous behavior. + - You should reproduce the issue before attempting to fix it. + - Your reproduction script should also assert the expected behavior for the fixed code. + +## 3. Analyze the root cause + + - Identify the underlying problem based on your code exploration and reproduction results. + - Critically analyze different potential approaches to fix the issue. + - You must explicitly reason about multiple possible solutions, then select the most elegant and effective one, considering trade-offs such as correctness, generality, side effects, maintainability, and performance. + - Carefully reason about execution paths, edge cases, and other potential pitfalls. Review the existing unit tests to understand the expected behavior of the relevant code. + +## 4. Implement your solution + + - Once you have determined the root cause, make targeted changes to the necessary files, following idiomatic patterns and style for the codebase. + - Work thoroughly and methodically. + +## 5. Verify your solution + + - Rerun your reproduction script to confirm that the error is fixed. + - If verification fails, iterate on your solution until it passes. If you discover that the reproduction script itself is incorrect, fix the script as needed. + +## 6. Run unit tests + + - Identify and run the unit tests that are relevant to the fix. + - You must run the unit tests to ensure your solution is correct and does not introduce regressions. + - **NOTE: You should not modify any existing tests.** + +## 7. Test edge cases + + - Identify potential edge cases that may challenge your solution. + - Create additional test cases and run them to verify the robustness of your fix. + - If edge case testing reveals issues, refine your solution accordingly. + +## 8. Check the working directory and ensure it only contains fix-related changes + + - Use `git status` to verify that the modifications are exactly what you intend to submit. + - For any unrelated changes (for example, `Cargo.lock` or similar file being modified automatically, or you accidentally modifying any test files), you must run `git restore ` before completing the task to ensure such changes are not included. + - For any unrelated newly created files (for example, temporary debug files created while reproducing the issue), delete them before completing the task to ensure they are not included. + - Make sure that in the final state of the repository, only the clean, intentional fix changes are present, with no unrelated file modifications and no changes to any test files. diff --git a/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml new file mode 100644 index 00000000000..02d98602427 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SWE-bench Multilingual (agentic coding tasks across 9 languages). +# +# Runs through nemo-evaluator, not Gym. Install it and apply the ECS Fargate Terraform +# stack first (see instruct/README.md), and have .env loaded into your shell (this +# recipe uses POLICY_API_KEY, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY). Results land +# in ./results/swebench-multilingual. +# +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml # full benchmark (300 tasks x 5) +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml -O benchmarks.0.max_problems=3 # quick smoke +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-multilingual.yaml -O output.dir= # output dir +# +# Note: serve the model with --reasoning-parser=nemotron_v3 plus +# --enable-auto-tool-choice --tool-call-parser=qwen3_coder. Without the first, +# stays inline and reasoning_replay feeds back a different context each turn; without +# the other two the agent gets no parsed tool calls and the run fails. + +defaults: + - {_base_: prompts/_openhands_system.yaml} + - _self_ + +services: + nemotron: + type: api + url: https:///v1/chat/completions + protocol: chat_completions + model: + api_key: ${POLICY_API_KEY} + max_input_tokens: 262144 + max_output_tokens: 262144 + generation: + temperature: 1.0 + top_p: 0.95 + proxy: + request_timeout: 3600 + extra_body: + chat_template_kwargs: {enable_thinking: true} + skip_special_tokens: false + interceptors: + - name: system_message + config: + strategy: replace + system_message: ${._openhands_system} + - name: turn_counter + config: + max_turns: 200 + position: system_message + - name: drop_params + config: + params: [max_tokens, max_completion_tokens, max_input_tokens_per_task, no_rebuild] + - name: consolidate_system + - name: reasoning + - name: reasoning_replay + config: + mode: think_tags + +output: + dir: ./results/swebench-multilingual + +benchmarks: +- playbook: swebench_multilingual + repeats: 5 + # Throughput only, no effect on score. Raise together with sandbox.concurrency + # below — the lower of the two binds. + max_concurrent: 15 + instruction_template: nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/swebench-instruction.md + params: + keep_swebench_multilingual_hints: false + solver: + service: nemotron + timeout_strategy: max + run_timeout: 10800 + cmd_timeout: 1800 + agent_kwargs: + max_iterations: 200 + version: "1.17.0" + llm_kwargs: + timeout: 3600 + sandbox: + region: ${HARBOR_ECS_REGION:-us-east-2} + ecr_repository: ".dkr.ecr.us-west-2.amazonaws.com/harbor-swebench" # set to your harbor ECR account + concurrency: 15 # keep in sync with max_concurrent above + scrub_git_history: true + log_stream_prefix: swebench-multilingual-nemotron-local + +cluster: + type: local diff --git a/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml new file mode 100644 index 00000000000..85909734719 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml @@ -0,0 +1,99 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SWE-bench Verified (agentic coding tasks). +# +# Runs through nemo-evaluator, not Gym. Install it and apply the ECS Fargate Terraform +# stack first (see instruct/README.md), and have .env loaded into your shell (this +# recipe uses POLICY_API_KEY, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY). Results land +# in ./results/swebench-verified. +# +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml # full benchmark (500 tasks x 5) +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml -O benchmarks.0.max_problems=3 # quick smoke +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/swebench-verified.yaml -O output.dir= # output dir +# +# Note: serve the model with --reasoning-parser=nemotron_v3 plus +# --enable-auto-tool-choice --tool-call-parser=qwen3_coder. Without the first, +# stays inline and reasoning_replay feeds back a different context each turn; without +# the other two the agent gets no parsed tool calls and the run fails. + +defaults: + - {_base_: prompts/_openhands_system.yaml} + - _self_ + +services: + nemotron: + type: api + url: https:///v1/chat/completions + protocol: chat_completions + model: + api_key: ${POLICY_API_KEY} + max_input_tokens: 262144 + max_output_tokens: 262144 + generation: + temperature: 1.0 + top_p: 0.95 + proxy: + request_timeout: 3600 + extra_body: + chat_template_kwargs: {enable_thinking: true} + skip_special_tokens: false + interceptors: + # OpenHands' own system prompt; turn_counter matches agent_kwargs.max_iterations. + - name: system_message + config: + strategy: replace + system_message: ${._openhands_system} + - name: turn_counter + config: + max_turns: 200 + position: system_message + - name: drop_params + config: + params: [max_tokens, max_completion_tokens, max_input_tokens_per_task, no_rebuild] + - name: consolidate_system + - name: reasoning + - name: reasoning_replay + config: + mode: think_tags + +output: + dir: ./results/swebench-verified + +benchmarks: +- playbook: swebench_verified + repeats: 5 + # Throughput only, no effect on score. Raise together with sandbox.concurrency + # below — the lower of the two binds. + max_concurrent: 15 + instruction_template: nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/prompts/swebench-instruction.md + solver: + service: nemotron + timeout_strategy: max + run_timeout: 10800 + cmd_timeout: 1800 + agent_kwargs: + max_iterations: 200 + version: "1.17.0" + llm_kwargs: + timeout: 3600 + sandbox: + region: ${HARBOR_ECS_REGION:-us-east-2} + ecr_repository: ".dkr.ecr.us-west-2.amazonaws.com/harbor-swebench" # set to your harbor ECR account + concurrency: 15 # keep in sync with max_concurrent above + log_stream_prefix: swebench-verified-nemotron-local + +cluster: + type: local diff --git a/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml new file mode 100644 index 00000000000..de4ddba4217 --- /dev/null +++ b/nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Terminal-Bench 2.1 (agentic terminal tasks). +# +# Runs through nemo-evaluator, not Gym. Install it and apply the ECS Fargate Terraform +# stack first (see instruct/README.md), and have .env loaded into your shell (this +# recipe uses POLICY_API_KEY, AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY). Results land +# in ./results/terminal-bench-2.1. +# +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml # full benchmark (89 tasks x 8) +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml -O benchmarks.0.max_problems=3 # quick smoke +# nel eval run nemotron_recipes/lightning-3.5/instruct/nemo-evaluator/terminal-bench-2.1.yaml -O output.dir= # output dir +# +# Note: serve the model with --reasoning-parser=nemotron_v3, or stays inline and +# reasoning_replay feeds back a different context each turn. + +services: + nemotron: + type: api + url: https:///v1/chat/completions + protocol: chat_completions + model: + api_key: ${POLICY_API_KEY} + max_input_tokens: 262144 + max_output_tokens: 262144 + generation: + temperature: 1.0 + top_p: 0.95 + proxy: + request_timeout: 3600 + extra_body: + chat_template_kwargs: {enable_thinking: true} + skip_special_tokens: false + interceptors: + - name: drop_params + config: + params: [max_tokens, max_completion_tokens, max_input_tokens_per_task, no_rebuild] + - name: consolidate_system + - name: reasoning + - name: reasoning_replay + config: + mode: think_tags + +output: + dir: ./results/terminal-bench-2.1 + +benchmarks: +- playbook: terminal_bench_2_1 + repeats: 8 + # Throughput only, no effect on score. Raise together with sandbox.concurrency + # below — the lower of the two binds. + max_concurrent: 15 + solver: + service: nemotron + timeout_strategy: max + run_timeout: 7200 + agent_kwargs: + llm_kwargs: + timeout: 3600 + sandbox: + region: ${HARBOR_ECS_REGION:-us-east-1} + ecr_repository: ".dkr.ecr.${HARBOR_ECS_REGION:-us-east-1}.amazonaws.com/harbor-${HARBOR_ECS_REGION:-us-east-1}" # set to your harbor ECR account + concurrency: 15 # keep in sync with max_concurrent above + log_stream_prefix: terminal-bench-2-1-nemotron-local + +cluster: + type: local diff --git a/nemotron_recipes/lightning-3.5/reproducibility.md b/nemotron_recipes/lightning-3.5/reproducibility.md new file mode 100644 index 00000000000..b7bf30d701e --- /dev/null +++ b/nemotron_recipes/lightning-3.5/reproducibility.md @@ -0,0 +1,80 @@ +# Reproducing the published evaluation results + +This tutorial demonstrates how to reproduce the evaluation results for the NVIDIA +Nemotron 3.5 Lightning 30B A3B model using NeMo Gym. + +The recipes are split by model: + +- **[`instruct/`](./instruct/README.md)** — the instruct model. Most benchmarks are Gym + recipes in [`instruct/gym/`](./instruct/gym/); Terminal-Bench and the two SWE-bench + suites are NeMo Evaluator configs in + [`instruct/nemo-evaluator/`](./instruct/nemo-evaluator/). +- **[`base/`](./base/README.md)** — the base (pretraining) model: + `nvidia/NVIDIA-Nemotron-3.5-Lightning-30B-A3B-Base-BF16`, 21 short-context benchmarks + plus RULER. These are `nemo-evaluator-launcher` configs rather than Gym recipes, and + their prerequisites differ. + +## What you can reproduce + +### Instruct model + +| Benchmark | What it measures | Recipe | +|---|---|---| +| GPQA Diamond | Graduate-level science questions | [`gpqa.sh`](./instruct/gym/gpqa.sh) | +| HLE | Humanity's Last Exam — hard, broad knowledge | [`hle.sh`](./instruct/gym/hle.sh) | +| AA-LCR | Long-context reasoning | [`aa-lcr.sh`](./instruct/gym/aa-lcr.sh) | +| AA-Omniscience | Knowledge and hallucination | [`omniscience.sh`](./instruct/gym/omniscience.sh) | +| SciCode | Scientific coding, graded against numeric ground truth | [`scicode.sh`](./instruct/gym/scicode.sh) † | +| BrowseComp | Web browsing agent | [`browsecomp.sh`](./instruct/gym/browsecomp/browsecomp.sh) | +| Tau3 | Tool use against a simulated customer | [`tau3.sh`](./instruct/gym/tau3.sh) | +| CritPt | Research-level physics | [`critpt.sh`](./instruct/gym/critpt.sh) † | +| GDPval | Real-world work products, judged | [`gdpval.sh`](./instruct/gym/gdpval/gdpval.sh) † | +| PinchBench | The model as the brain of an OpenClaw agent | [`pinchbench.sh`](./instruct/gym/pinchbench.sh) † | +| Terminal-Bench 2.1 | Agentic terminal use | [`terminal-bench-2.1.yaml`](./instruct/nemo-evaluator/terminal-bench-2.1.yaml) † | +| SWE-bench Verified | Agentic coding against a repo's tests | [`swebench-verified.yaml`](./instruct/nemo-evaluator/swebench-verified.yaml) † | +| SWE-bench Multilingual | The same, across nine languages | [`swebench-multilingual.yaml`](./instruct/nemo-evaluator/swebench-multilingual.yaml) † | + +† needs setup beyond installing Gym — see [`instruct/README.md`](./instruct/README.md). +The rest run as they are. + +### Base model + +Run as two configs rather than one recipe per benchmark: the suite below, plus RULER for +long context. Prerequisites differ from the instruct recipes — see +[`base/README.md`](./base/README.md). + +| Group | Benchmarks | +|---|---| +| General knowledge | MMLU, MMLU-Pro, AGIEval, GPQA Diamond | +| Math | GSM8K, Minerva Math, MATH-500 | +| Code | HumanEval (greedy + sampled), MBPP-sanitized (greedy + sampled) | +| Commonsense | CommonsenseQA, ARC-Challenge, HellaSwag, OpenBookQA, PIQA, Social IQa, WinoGrande | +| Reading comprehension | RACE | +| Multilingual | Global-MMLU-Lite, MGSM | +| Long context | RULER † | + +† RULER needs a pre-generated dataset — see [`base/README.md`](./base/README.md). + +## What to expect from your numbers + +**Run the whole benchmark.** Every recipe can be limited for a smoke test — `LIMIT` for +the Gym recipes, `-O benchmarks.0.max_problems=` for the NeMo Evaluator configs, +`limit_samples` for the base suite. A limited run tells you the setup works. It is not a +score, and should not be reported as one. + +**Expect run-to-run variation.** These benchmarks sample at `temperature > 0` and average +over repeats, so two runs of the same recipe against the same endpoint will not agree to +the last digit. Judge a difference against that spread, not against the exact published +value. + +**Serving configuration moves scores as much as the eval configuration does.** vLLM +version, tensor parallelism, cache dtype, prefix caching, and the reasoning and tool-call +parsers all change results. To compare against the published numbers, serve the model as +the recipes describe and point them at that. Running against an endpoint someone else +operates works fine, but then a gap is ambiguous: you cannot tell whether it came from +their serving stack or from the model. That is a sound way to measure a deployment you +already have — it is not a basis for confirming published numbers. + +## License + +Apache 2.0 — see the repository `LICENSE`.