evaluation
Evaluates accuracy of quantized or unquantized LLMs using NeMo Evaluator Launcher (NEL). Triggers on "evaluate model", "benchmark accuracy", "run MMLU",…
npx skills add https://github.com/nvidia/model-optimizer --skill evaluationNeMo Evaluator Launcher Assistant
Guide the user through creating NEL YAML configs, running evaluations, and monitoring progress.
Workspace integration
If MODELOPT_WORKSPACE_ROOT is set, read skills/common/workspace-management.md and reuse existing workspaces (this skill is usually the final stage of PTQ → Deploy → Eval; carry any deployment-time patches into deployment.command).
Workflow
- [ ] Step 0: Check workspace (if MODELOPT_WORKSPACE_ROOT set)
- [ ] Step 1: Check `nel` install + existing config; set up `.env` (+ `modelopttools:eval-config` for judge-scored runs)
- [ ] Step 2: Build base config (5-question flow OR shortcut)
- [ ] Step 3: Configure deployment (model path, params, cross-check)
- [ ] Step 4: Fill remaining ??? values
- [ ] Step 5: Confirm tasks (iterative)
- [ ] Step 6: Multi-node (if needed)
- [ ] Step 7: Interceptors (if needed)
- [ ] Step 7.5: Container auth (SLURM private images)
- [ ] Step 8: Dry-run → canary → full run
- [ ] Step 9: Verify completed run
nel-next path (Terminal-Bench 2.x, SWE-bench, …) — branch here FIRST
A few agentic AA benchmarks do not run on the default
nemo-evaluator-launcher 0.2.6 (Steps 1–9 don't apply). They run on nel-next
(nemo-evaluator[harbor] 0.3.x) — a separate package, CLI (nel eval run), -O
overrides, and services/benchmarks/cluster/output schema. If the user asks
for one, do not add it to a 0.2.6 evaluation.tasks list — instead:
- Read
references/nel-next.md(shared: venv, schema, AWS creds, architecture, timeout strategy, MLflow, run flow) + the per-benchmark reciperecipes/tasks/aa_next/{terminal_bench_2_1,swebench_verified}.md; start fromrecipes/examples/example_eval_next.yaml. - Isolated 0.3.x venv:
.agents/scripts/nel-next.sh --setup-only(keeps 0.2.6neluntouched). - Run
modelopttools:eval-config(Step 3b) to write the AWS-sandbox creds + harbor infra rows (${NEL_NEXT_EVAL_IMAGE},${HARBOR_*_ECR_REPOSITORY}) into.env; always include theoutput.export_config.mlflowblock. - Dry-run → canary → full (
nel-next.sh eval run), then push to MLflow — SLURM doesn't auto-export, so runnel-next.sh mlflow-push -r <run_id> -c <cfg>after (config-driven; seereferences/nel-next.md).
Steps 1–9 below are the 0.2.6 path — use them for everything else.
Step 1 — Prerequisites
Run nel --version; if missing, instruct pip install nemo-evaluator-launcher. If user has an existing config, skip to Step 8 (optionally review for ??? and quantization flags first).
Set up .env now (not Step 8). The working .env lives at the workspace root — the directory you run nel from — matching modelopttools:eval-config's convention; do not create it under the skill dir. (NEL does not discover .env by path: it reads secrets from the shell env via the host: prefix after you source, so the location is purely which file you source before nel run. Keeping the single .env at the workspace root avoids a stale duplicate under the symlinked, shared .agents/ skill tree.) For judge-scored / user-sim tasks (HLE, AA-LCR, Tau2), seed it from the template if absent — the template ships under the skill dir, the working .env does not: [ -f .env ] || cp .agents/skills/evaluation/recipes/env.example .env. Then try modelopttools:eval-config (if available) to fill the judge model_id/url rows (user adds the secret key). Needed before Step 5, which substitutes those values into task <VAR> placeholders.
Secret safety — never open .env with Read/Write/Edit. The harness mirrors later edits of any agent-opened file into the transcript, so touching .env leaks the keys the user adds afterward. Use shell only (cp to create, source to load — neither echoes); edit env.example, never .env; leave value entry to the user / modelopttools:eval-config.
Task recipes (always read before editing the relevant task in the config):
- AA Index v2 suite (default for quantized-checkpoint validation, see
references/quantization-benchmarks.md):recipes/tasks/aa/{gpqa_diamond,hle,lcr,scicode,ifbench,mmmu_pro,tau2_bench_telecom,omniscience}.md - Optional:
recipes/tasks/mmlu_pro.md,recipes/tasks/aime_2025.md,recipes/tasks/livecodebench.md - nel-next only (different evaluator — see the nel-next section below, NOT the 0.2.6 steps): shared reference
references/nel-next.md+ per-benchmark recipesrecipes/tasks/aa_next/{terminal_bench_2_1,swebench_verified}.md(agentic). Theaa_next/dir holds tasks that require nemo-evaluator-next (0.3.x);aa/is the 0.2.6 suite.
AA rule: If the user mentions "AA" / "Artificial Analysis", generate only tasks under recipes/tasks/aa/. Do not add MMLU-Pro, AIME 2025, or LiveCodeBench unless explicitly asked.
Shortcut path (when task list is known up front, e.g. "run AA"):
- Read the task reference file(s).
- Use
recipes/examples/example_eval.yamlas the base. - Copy the YAML fragment(s) into
evaluation.tasks, applying any per-task notes. - MLflow auto-export is on by default — it needs two pieces, both in
example_eval.yaml: (a) the triggerexecution.auto_export.destinations: [mlflow](without it the run is not uploaded), and (b) theexport.mlflowblock that configures it. In theexport.mlflowblock use literal values forexperiment_name/description/tags— substitute the actualserved_model_nameand sampling params. Do not use${deployment.*}/${evaluation.*}cross-references: with auto-export on, NEL resolves the export block at submit time in a scope without those nodes and fails withInterpolation key '...' not found(${oc.env:USER}and${oc.env:MLFLOW_TRACKING_URI}are fine — they're env vars). Because these literals can't interpolate, keep thetemperature/top_p/max_new_tokenstags equal to the top-levelparamsand update both in the same edit — they're the only queryable record of sampling in MLflow (NEL doesn't log them as run params), so a stale tag silently misreports the run.tracking_uri=${oc.env:MLFLOW_TRACKING_URI}frommodelopttools:eval-config(not hand-filled), and auto-export needsexecution.cpu_partition(e.g. gcp-nrtcpu) — it's a separate CPU-only sbatch that GPU-only partitions reject (Cannot find GPU specification), silently dropping the link. - Proceed to Step 3, then Step 4, then Step 7.5/8. Skip Step 2's 5-question flow.
Step 2 — Build base config (when not using shortcut)
Ask the 5 questions via AskUserQuestion (categories must match nel skills build-config --help — run that first to confirm the current option names; CLI options override this list).
- Execution: Local / SLURM
- Deployment: None (External) / vLLM / SGLang / NIM / TRT-LLM. Prefer vLLM unless the user/card says otherwise.
- Auto-export: None / MLflow / wandb
- Model type: Base / Chat / Reasoning
- Benchmarks (multi-select): standard / code / math_reasoning / safety / multilingual
Build the base:
nel skills build-config --execution <...> --deployment <...> --model_type <...> --benchmarks <...> [--export <...>] [--output <...>]
(--output omitted = cwd auto-named; directory = dir + auto-name; *.yaml = exact path. Never overwrites.)
Step 3 — Configure deployment
Model path. Checkpoint path (/, ./, ../, ~, or exists on disk) → set deployment.checkpoint_path, leave hf_model_handle: null. Else HF handle (one /, not on disk) → set deployment.hf_model_handle, leave checkpoint_path: null.
Prefer
checkpoint_pathoverhf_model_handleon SLURM —hf_model_handleisn't reliably mounted at/checkpoint, so the deploy dies withHFValidationError. To eval an un-staged HF model, stage it first (huggingface_hub.snapshot_download) and pointcheckpoint_pathat it. Seeexample_eval.yamlfor why.
Auto-detect ModelOpt quantization (checkpoint paths). Check config.json for quantization_config (or legacy hf_quant_config.json):
- vLLM: no
--quantizationflag by default — vLLM auto-detects fromquantization_config/hf_quant_config.json. Add only when the card, vLLM version, or dry-run error requires it. - SGLang: may need
--quantization modelopt_fp8/modelopt_fp4/modelopt— verify against installed version.
Some models need extra vLLM backend env vars (model-card research) — e.g. VLLM_NVFP4_GEMM_BACKEND=marlin (Nemotron Super), or VLLM_USE_FLASHINFER_MOE_FP4=1 + VLLM_FLASHINFER_MOE_BACKEND=throughput (NVFP4 MoE, e.g. NVIDIA-Nemotron-3-Nano-30B-A3B-NVFP4). Put them in deployment.env_vars (not command) with the lit: prefix (VLLM_USE_FLASHINFER_MOE_FP4: lit:1); see example_eval.yaml and Step 5's prefix rule.
Auto-detect from config.json:
| Field | Flag |
|---|---|
max_position_embeddings | --max-model-len <value> |
auto_map exists | --trust-remote-code |
Cross-check both sources for vLLM (mandatory, neither replaces the other)
Source 1 — recipes.vllm.ai/<org>/<model> (curated vLLM recipes; authoritative for parallelism, family-specific flags like --reasoning-parser / --tool-call-parser / --mm-encoder-tp-mode, vLLM version, spec-decoding, GPU count). Fetch the page for the EXACT model id, not a base/sibling — variant minimums differ (e.g. MiniMax-M2 ≥0.11.0 vs M2.7 ≥0.20.0). Pin variants via query params (e.g. ?variant=fp8&strategy=single_node_tep).
WebFetch caveat — triage the summary:
- "No
vllm servecommands found" / "page is a usage guide": JS-rendering miss. recipes.vllm.ai pages always have ≥1 command. Ask the user to paste it or share the variant URL.- Single recipe returned for a model with known multiple variants → retry with variant-pinned URL. Axis names differ per model (Qwen:
?variant=&strategy=; Kimi:?advanced=; others vary — no fixed pattern).- Variant label contradicts the command (e.g. label "TEP" but command shows DP+EP) → summarizer conflated variants; ask user.
For non-trivial deployments (≥120B, multi-node, novel arch), ask the user which variant before fetching.
Source 2 — HF model card + config.json (authoritative for):
| Signal | Flag |
|---|---|
max_position_embeddings | --max-model-len <value> |
auto_map | --trust-remote-code |
| Reasoning/CoT documented | --reasoning-parser (and --reasoning-parser-plugin if custom) |
| Tool-calling documented | --enable-auto-tool-choice --tool-call-parser <parser> |
| Custom flags in card | Add as specified (e.g. --mamba_ssm_cache_dtype float32) |
Cross-check rules:
- Read both sources before composing the command.
- Agree → use with confidence.
- Disagree → do not silently pick one. Surface both values to the user. Common conflicts: stale cards, parser rename between generations (Qwen2.5
hermes→ Qwen3qwen3_coder), recipe-only flags like--language-model-only, ARM64-specific card notes. - Resolve in Step 3 — don't defer to dry-run.
vLLM deployment command structure — single command: field
Rewrite the build-config output into one command: field. Move all parallelism (--tensor-parallel-size, --data-parallel-size, --pipeline-parallel-size) into the command; do not keep separate tensor_parallel_size / data_parallel_size / extra_args YAML fields.
deployment:
command: >-
vllm serve /checkpoint
--host 0.0.0.0
--port ${deployment.port}
--tensor-parallel-size <N>
--data-parallel-size <M>
--max-model-len <value>
<... rest of cross-checked flags ...>
Conventions: always start vllm serve /checkpoint (NEL mounts here); always --served-model-name ${deployment.served_model_name} (required; see example_eval.yaml for why); always --host 0.0.0.0 --port ${deployment.port}; use folded scalar (>-) for one flag per line. Example fallback --max-model-len 131072 covers AA-LCR (~120K + 16K gen) and SciCode (≥ 65536) — prefer config.json / recipe value.
For how to choose --tensor-parallel-size / --data-parallel-size / --pipeline-parallel-size (and EP) from the model size and your GPU count, read references/parallelism.md — cross-check the layout against recipes.vllm.ai, then adapt to the GPUs you actually have via the fit math there.
Image / vLLM version. Treat default image: vllm/vllm-openai:v0.19.1 as a floor to verify: bump to the exact model's recipes.vllm.ai minimum if higher (e.g. v0.20.0). Running below minimum is a trap — the server starts, then a worker dies mid-inference with CUDA error: an illegal memory access (MiniMax-M2.7 NVFP4 needed ≥0.20.0), easy to misread as a kernel bug. Never :latest (breaks reproducibility). Surface version bumps to the user.
NVFP4 on Blackwell B300/GB300 (sm_103): append
-cu130to the image tag (e.g.vllm/vllm-openai:v0.19.1-cu130— release tags are multi-arch). The default cu12 build has no sm_103 FP4 kernel, so engine init dies withCUDA error: no kernel image is available. If a pinned release predates the model's arch, usecu130-nightly-<arch>(Qwen3.5-9B'sqwen3_5needed it, vLLM 0.19.2rc1.dev134). Multimodal on sm_103 may also need--mm-encoder-attn-backend TRITON_ATTN. Full note inrecipes/examples/example_eval.yaml.
vLLM-backend defaults — always include unless the recipe contradicts
Silence is not contradiction. Drop/override only when the recipe sets a different value for the same setting (e.g. recipe pins --max-num-batched-tokens 16384 → use 16384).
-
--model-loader-extra-config '{"enable_multithread_load": true, "num_threads": 128}'— parallelizes checkpoint load, the single biggest deploy-time cost for large checkpoints. A big MoE otherwise loads shards ~sequentially (~1 min/shard → e.g. ~40 min for a ~450 GB / 45-shard checkpoint); on a preemptible queue that long load window is exactly where jobs get killed before they ever serve.num_threadsdefaults to 128; scale it to the checkpoint (smaller for small models, bounded by the shared-FS read bandwidth — too high yields no gain). Safe to always include. -
--max-num-batched-tokens 8192— caps per-step batched tokens; prevents long-prefill stalls. -
--enable-chunked-prefill— interleaves long prefills with decode steps (required for AA-LCR's ~120K input). Modern vLLM defaults this on for many models; set explicitly to avoid drift. -
--enable-expert-parallel— MoE-only default. Detect MoE from handle suffix (-A10B,-A3B, etc.),num_experts/num_local_experts/n_routed_expertsinconfig.json, or card. No-op when TP=DP=1, safe to always include for MoE. Do not add for dense models. Seereferences/parallelism.mdfor what EP does and the DP-attention + EP-MoE throughput pattern. -
--max-num-seqs N— omit at generation time (top-levelparallelismis???). Add this comment abovecommand::# After filling in `parallelism` values (top-level + per-task overrides), # append `--max-num-seqs N` where N = ceil(max_parallelism / data_parallel_size).In Step 4 compute and append. Example: top-level=16, Tau2=128, DP=8 →
ceil(128/8)=16. Too small → request queuing; too large → wasted KV reservation. For how to choose theparallelismit derives from, readreferences/parallelism.md.
Evaluation params template (top-level params)
The top-level nemo_evaluator_config.config.params must contain exactly these six fields — no top_k / presence_penalty / repetition_penalty / min_p:
nemo_evaluator_config:
config:
params:
parallelism: ??? # Required — size per references/parallelism.md (bounded by total request count vs GPU serving capacity); ask user in Step 4 if still unclear
request_timeout: 3600
max_retries: 10
max_new_tokens: 65536 # see rule below
temperature: 1.0 # from model card (reasoning); adjust
top_p: 0.95 # from model card (reasoning); adjust
Per-task max_new_tokens overrides are forbidden — set one top-level ceiling everywhere.
max_new_tokens — mandatory model-card lookup
- Fetch the HF model card before writing the value. Not optional.
- Scan for any
max_tokens/max_new_tokens/ "output length" recommendation. Pick the highest value the card mentions (Qwen3.6: 32768 general + 81920 math-coding → use 81920). Annotate with a citing comment. - If the card is genuinely silent after a thorough read, fall back to: 65536 (reasoning), 16384 (non-reasoning); surface the silence to the user.
- Forbidden: writing
max_new_tokens: <generic_default>with a "card not yet checked" comment. Either fetch and apply, or fetch and confirm silence. - A higher cap doesn't fix runaway reasoning. On hard tasks (e.g. HLE) a non-terminating model just rambles to the larger cap (~80% length-capped at 131072), and the cap only helps if deployment
--max-model-len > prompt + max_new_tokens(else generation is silently clipped — AA-LCR's ~120K input leaves little room). Treat such tasks as low-confidence.
Quantization-aware benchmark defaults
For quantized checkpoints, read references/quantization-benchmarks.md for sensitivity rankings and recommended sets; present and ask which to include. Read references/model-card-research.md for the full extraction checklist (sampling, reasoning config, ARM64, pre_cmd, output length — see the dedicated bullet there).
Reasoning models: prefer reasoning mode (highest scores). For lower variance / cost / apples-to-apples vs non-reasoning baselines, also consider a non-reasoning companion run.
Reasoning adapter config (use_reasoning)
The adapter_config block in example_eval.yaml controls request/response
logging and reasoning handling. use_reasoning: true strips the model's
reasoning/CoT trace before scoring (grade only the final answer). Set per type:
- Instruct →
use_reasoning: falseand drop thechat_template_kwargsthinking block (no trace to strip; can mangle plain responses). - Reasoning →
use_reasoning: true, especially when the deployment sets--reasoning-parser(vLLM emits a separate reasoning channel to strip). - Hybrid (reasoning on or off) → turn it ON (
use_reasoning: true+ force the thinking flag inchat_template_kwargs). For the exact toggle key (it drifts across generations) and the reasoning-effort policy, seereferences/model-card-research.md→ "Reasoning config".
Step 4 — Fill remaining ??? values
Predefined per-cluster execution config (check FIRST). Some installs ship internal/slurm/<cluster> execution groups (optional nemo_evaluator_launcher_internal pkg) that pre-fill hostname/partition/gres — leaving only account/output_dir/walltime. Discover at runtime (nothing cluster-specific hardcoded):
python3 -c 'import nemo_evaluator_launcher_internal' 2>/dev/null && \
PKG=$(python3 -c 'import nemo_evaluator_launcher_internal as m,os;print(os.path.dirname(m.__file__))') && \
for f in "$PKG"/configs/execution/internal/slurm/*.yaml; do \
echo "$(basename "$f" .yaml) -> $(grep -E '^hostname:' "$f" | awk '{print $2}')"; done
Hostname match → set defaults: - execution: internal/slurm/<cluster>, drop the redundant execution.hostname (keep account/output_dir/walltime), verify with --dry-run. Else keep slurm/default and fill hostname/account/output_dir manually.
On SLURM, several deploy/eval failures are invisible to --dry-run and only surface at canary (mount_home, HF cache, cpu_partition, top-level vs per-stage env_vars) — read references/slurm.md.
- Find every
???left. Ask the user only for what can't be inferred (SLURM hostname/account/output_dir, thecpu_partitionfor auto-export, etc.). Don't propose defaults; let them give plain text. (tracking_uriis not one of these — it's${oc.env:MLFLOW_TRACKING_URI}frommodelopttools:eval-config.) parallelism— size it yourself from the run shape (total requests =dataset_size × repeatsvs GPU serving capacity), and set--max-num-seqsto match. Readreferences/parallelism.mdfor the decision rule and worked examples; only ask the user if a non-GPU cap (e.g. judge rate limit) is unknown.- Ask about other defaults they may want to change (partition, walltime, MLflow tags).
execution.gres— auto-set if you used a predefinedinternal/slurm/<cluster>config (above). On theslurm/defaultfallback it'sgpu:8, so set it to the node's GPU count (and match--data-parallel-size/--tensor-parallel-size) orsbatchrejects the job with "Requested node configuration is not available" (e.g. 4-GPU GB300 →gres: gpu:4; check withsinfo -o '%P %G').
Walltime cap: 4 hours. Always execution.walltime: "04:00:00". The cluster does not schedule jobs longer than 4h — this is a hard limit, not a preference.
Evals that exceed 4h of wall-clock time are handled by NEL's built-in dependency-chain resume, not by shrinking the eval. NEL submits the first SLURM job; if it hits walltime, a dependent follow-on job resumes from the response/result caches the first job wrote, then queues another follow-on. Long evals continue across walltime windows automatically. See references/run-validation.md#nel-timeout-and-resume-behavior for the full mechanism.
Preemption / external kill — resume manually with sbatch run.sub. On a preemptible account (common on busy internal clusters) the scheduler can CANCEL a run mid-eval for a higher-priority job — sacct -j <id> shows CANCELLED by <uid> (a svc-* service account) with Elapsed well under the 4h walltime. NEL does not auto-resume this (its dependency chain only fires on a genuine walltime timeout). But the run.sub that NEL generated for the job (in its run dir) is re-submittable and resumes from the same output_dir + response cache (skip_filled), continuing from the partial output rather than restarting:
ssh <host> "cd <output_dir>/<timestamp>-<invocation>/<task>/ && sbatch run.sub"
Re-submit again if it's preempted again — each resume re-deploys, then skips already-generated samples, so progress is cumulative across attempts until it completes. Always confirm via sacct -j <id> that the prior job was CANCELLED (not a real failure) before resuming.
Implications for the agent:
- Do not lower
num_repeats, split heavy tasks (AA-LCR, SciCode) into separate configs, or otherwise carve up the eval to fit inside 4h. Let NEL chain. - Do not treat a walltime timeout as a failed run. Check
nel status/nel infoand the dependent job's logs before declaring failure.references/run-validation.mdcovers what a real failure looks like vs an expected resume event. - Bumping
data_parallel_size/parallelismto finish faster is fine when the goal is wall-clock latency, not a walltime workaround — but it's optional, not required, for runs longer than 4h.
Step 5 — Confirm tasks (iterative)
-
Tell user: "Run
nel ls tasksfor the full task list." -
For any task with a
recipes/tasks/reference, read it and prefer its YAML fragment + repeat counts. -
Ask about add/remove/modify. Per-task overrides under task's
nemo_evaluator_config.config.params:tasks: - name: <task> nemo_evaluator_config: config: params: temperature: <value> ... -
Apply, show updated list, ask "Final, or more changes?" Loop until confirmed.
Tasks that call an external judge / user-simulator / scoring endpoint. Treat this as a general pattern, not a fixed list — HLE, AA-LCR, and Tau2 need one today, but other benchmarks may too (check each task's recipe). Their model_id / url are config, not secrets: substitute the literal values the user keeps in .env (keys per the task's recipe + recipes/env.example) into the task's <VAR> placeholders. Do not emit ${oc.env:...} for these (it silently fails unless the var was exported with set -a). Only api_key stays an env-var name (e.g. INFERENCE_API_KEY), exported and read by the harness. All judges + user-sims (HLE, AA-LCR, Tau2, AIME) use one INFERENCE_API_KEY against one OpenAI-compatible host (AIME's simple-evals default judge endpoint is overridden to it — see its recipe). Get the *_MODEL_ID/*_URL values via modelopttools:eval-config (see Step 1) rather than guessing a host; only fill them by hand if it's unavailable. .env should already exist (Step 1) — if not, set it up now (don't defer to Step 8) before substituting.
Known issue — nemo-skills self-deployment: If using nemo_skills.* tasks (ns_*) with self-deployment (vLLM/SGLang/NIM), you need both of these:
evaluation:
env_vars:
DUMMY_API_KEY: lit:dummy # MUST be set here — see below
nemo_evaluator_config:
target:
api_endpoint:
api_key_name: DUMMY_API_KEY
api_key_name only names the env var; the nemo-skills client hard-fails if that var has no value inside the eval container (ValueError: api_key_env_var=DUMMY_API_KEY but the value is not set). On SLURM, a shell export DUMMY_API_KEY=dummy (Step 8) does NOT propagate into the container — NEL only injects vars declared in env_vars. So declare DUMMY_API_KEY: lit:dummy under evaluation.env_vars (note the lit: prefix — see below). The shell export only helps for local/Docker runs. External-deployment configs already define api_key_name.
NEL env-var value prefixes (required): every value in an env_vars map needs an explicit prefix — host:VAR (read from the submitting shell's env at submit time), lit:value (literal string), or runtime:VAR (read in the job at run time). A bare value (e.g. DUMMY_API_KEY: dummy) hard-errors: "Env var value '…' must have an explicit prefix." Use lit: for constants like DUMMY_API_KEY and VLLM_* backend selectors, host: for secrets like HF_TOKEN / INFERENCE_API_KEY.
Step 6 — Multi-node
For models > ~120B or higher throughput needs, read references/multi-node.md for HAProxy multi-instance / Ray TP/PP / combined patterns.
Step 7 — Interceptors
Direct user to https://docs.nvidia.com/nemo/evaluator/latest/libraries/nemo-evaluator/interceptors/index.html. Do not provide generic interceptor info — read the specific interceptor's page if asked, then configure via evaluation.nemo_evaluator_config.target.api_endpoint.adapter_config (target is a sibling of config, not nested under it). Use the per-field syntax from the CLI Configuration section, not a full interceptors: list (that overrides the default chain).
Errata: Logging field names are max_logged_requests / max_logged_responses (NOT max_saved_* / max_* as some docs show).
Step 7.5 — Container registry auth (SLURM private images only)
Default images:
| Framework | Image | Registry |
|---|---|---|
| vLLM | vllm/vllm-openai:v0.19.1 (bump per recipe; never :latest) | DockerHub |
| vLLM (NVFP4 on B300/GB300) | vllm/vllm-openai:v0.19.1-cu130 (bump to cu130-nightly-<arch> for new archs) | DockerHub |
| SGLang | lmsysorg/sglang:latest | DockerHub |
| TRT-LLM | nvcr.io/nvidia/tensorrt-llm/release:... | NGC |
| Eval tasks | nvcr.io/nvidia/eval-factory/*:26.03 | NGC |
NVFP4 checkpoints on B300/GB300 (sm_103) need the
cu130image — cu129/v0.19.1 lack sm_103 FP4 kernels (see the "NVFP4 on Blackwell" note in Step 3).
Public images → submit without preflight. Private/restricted → check credentials:
ssh <host> "grep -E '^\s*machine\s+' ~/.config/enroot/.credentials 2>/dev/null"
Add credentials per skills/common/slurm-setup.md §6 if missing. If you can't add, switch to a compatible public image (e.g. nvcr.io/nvidia/vllm:<YY.MM>-py3 — check catalog.ngc.nvidia.com). Do not retry more than once after an auth failure.
Step 8 — Run evaluation (gated dry-run → canary → full)
Run directly when the user asked to launch; otherwise ask before submitting.
Env setup: .env is normally already created and filled back in Step 1 (via modelopttools:eval-config), at the workspace root — the dir you run nel from, not under the skill dir. Ensure it exists and source it — do not clobber an existing .env:
# .env lives at the workspace root (where you run nel); the template ships under the skill dir
[ -f .env ] || cp .agents/skills/evaluation/recipes/env.example .env # create only if Step 1 didn't
set -a && source .env && set +a
# If pre_cmd/post_cmd in config (review pre_cmd first — runs arbitrary commands):
export NEMO_EVALUATOR_TRUST_PRE_CMD=1
# If nemo_skills.* + self-deployment, for LOCAL/Docker runs only:
export DUMMY_API_KEY=dummy
# On SLURM this shell export does NOT reach the container — instead declare
# `DUMMY_API_KEY: lit:dummy` under evaluation.env_vars (see Step 5).
Step 8.1 — Dry-run (config validation):
nel run --config <path> --dry-run
Fix unresolved ???, bad Hydra overrides, missing env vars, invalid mounts, image issues, sbatch errors, obvious deployment errors before proceeding.
Dry-run does NOT validate the image/vLLM version (image pulled only at deploy). Confirm
image:≥ the exact model'srecipes.vllm.aiminimum (Step 3) before submitting — too-old passes dry-run, then crashes mid-inference.
Non-fatal noise: "Failed to get manifest"/
401/404, "Could not extract frame definition file", "proceeding with minimal task definition", "Found N unlisted task(s)" — expected forns_*/recipe tasks and private (gitlab) containers; the task still runs in-container. SetNEMO_EVALUATOR_TRUST_UNLISTED_TASKS=1. Real blockers: unresolved???, interpolation errors, bad mounts, sbatch rejections.
Step 8.2 — Canary (limited-samples, validates everything dry-run can't):
nel run --config <path> -o ++evaluation.nemo_evaluator_config.config.params.limit_samples=10
Catches judge auth/rate-limits, container failures, sandbox issues, OOM, bad request formatting, low evaluated counts. Always inspect logs:
nel status <id>
nel info <id> --logs
ssh <user>@<host> "grep -i 'traceback\|exception\|error\|failed\|oom\|killed\|timeout\|unauthorized\|rate limit\|sandbox\|container\|judge\|parse\|scoring' <log_path>/*.log"
Canary each risky task class separately (judge-scored, code-execution, model-only). Start parallelism conservatively; raise only after judge/sandbox logs are clean — they bottleneck before the model. For capacity-bound runs, tune parallelism/--max-num-seqs here against vLLM's reported max concurrency + preemption — see references/parallelism.md.
Single-task rerun: nel run --config <path> -t <task_name> (combine with -o ++...limit_samples=10 for canary).
Step 8.3 — Full run (after canary passes):
nel run --config <path>
Remove limit_samples overrides; keep canary-validated parallelism. If the canary fails, fix and rerun the canary — don't skip to full.
Monitoring: Register the job per the monitor skill for cross-session tracking. One-off live status / debugging → launching-evals skill. Past-run MLflow queries → accessing-mlflow skill. NEL timeout/resume → read references/run-validation.md before treating the run as failed.
Step 9 — Verify completed run
Before pulling/reporting scores, validate the run. Read references/run-validation.md for NEL timeout/resume behavior, completed-run validation, diagnostics, and score harvesting. For a baseline that will be compared with a candidate, also perform its External Baseline Sanity Check before a success verdict, then hand the validated runs to compare-results for baseline-vs-candidate deltas.
Issues: https://github.com/NVIDIA-NeMo/Evaluator/issues · https://github.com/NVIDIA-NeMo/Evaluator/discussions