fix: Salt failure is a hard error - #406
Conversation
Signed-off-by: Rashid Kaleem <230885705+arekay-nv@users.noreply.github.com>
|
MLCommons CLA bot All contributors have signed the MLCommons CLA ✍️ ✅ |
There was a problem hiding this comment.
Code Review
This pull request introduces early validation for dataset salting during the benchmark setup phase. It adds a validate_saltable method to ensure that all dataset samples are compatible with salting (i.e., they are dictionaries containing a string prompt and no input_tokens) before any subprocesses are spawned. The review feedback highlights a critical bug in validate_saltable where iterating over self.data directly will fail if it is a pandas.DataFrame (as it would iterate over column names instead of rows), and provides a code suggestion to handle this case.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Review Council — PR #406 (3× Claude, depth: thorough)Three independent Claude reviewers (bugs/correctness, design/edge, testing/docs) plus a code-quality pass, deduped and severity-recalibrated. "R" marks issues flagged independently by multiple reviewers. All line numbers verified against HEAD ( Verdict: Solid, focused bug-fix. Fail-fast placement is correct — validation at 🟡 Should fix
🔵 Consider
🧹 Code quality
Convergence: all three reviewers independently landed on the salt-default- |
- Default warmup.salt to False so pre-tokenized (input_tokens) workloads no longer hard-fail on a plain --warmup; the hard error now fires only when salt is explicitly enabled. - Replace raw-string salt-violation reasons with a typed DatasetValidationError.Reason enum plus optional detail; _salt_violation becomes _can_salt returning the enum. UNSPECIFIED covers not-yet-mapped --dataset parse errors. - validate_saltable: assert on unloaded data (no silent skip); clarify the error index is into the loaded post-transform order; document the intentional whole-dataset (fail-on-any-invalid) strictness and the deliberate pre-spawn check. - Tests: unpatched integration coverage (offline/online raise, accuracy-only skip), agentic messages sample, typed-reason assertions; drop brittle index regex. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: arekay-nv <230885705+arekay-nv@users.noreply.github.com>
Warmup without --warmup-salt issues prompts verbatim, priming the server KV/prefix cache so the measured phase can be served warm and understate latency. Surface it as a warning at dataset-load time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nv-alicheng
left a comment
There was a problem hiding this comment.
Review Council — Multi-AI Code Review
Reviewed by: Codex + Claude + Code-Quality | Depth: standard
Found 3 issues (1 cross-confirmed by two reviewers). Overall a clean, well-tested refactor — typed Reason enum, fail-fast placement, and the collapsed _apply_salt are sound.
| # | File | Line | Severity | Category | Reviewer(s) | Summary |
|---|---|---|---|---|---|---|
| 1 | dataset_manager/dataset.py |
279 | medium | regression/api-contract | Codex + Claude | Multimodal list-form prompts now hard-error at load; list prompts were previously salted (first text part), not silently skipped — tested behavior removed. |
| 2 | commands/benchmark/cli.py |
70 | low | code-quality | Code-Quality | Comment narrates future work ("aren't yet mapped") without a tracking issue. |
| 3 | dataset_manager/dataset.py |
471 | low | documentation | Claude | validate_saltable docstring omits messages (code also rejects it). |
#1 is the decision point: the "silent skip" justification doesn't hold for list-form prompts — confirm the multimodal-salt drop is intended (and give list prompts a distinct Reason), or restore the branch.
nv-alicheng
left a comment
There was a problem hiding this comment.
Approved, but comments need to be addressed.
Address review-council + reviewer feedback on the salt-failure-hard-error change: - Rename _can_salt -> _check_unsaltable (truthy Reason == unsaltable) and fix the validate_saltable walrus so it raises when a sample IS unsaltable - Add DatasetParseError for --dataset string parse failures; remove the vestigial Reason.UNSPECIFIED - Rename the list-prompt reason to PROMPT_LIST_UNSUPPORTED (spec-accurate: an OpenAI list prompt is a batch / token-ID array, not necessarily multimodal) - Drop the duplicate cyclopts help= on WarmupConfig.salt; Field(description=) is the single source for --help and the YAML templates - Make DatasetValidationError copy/pickle-safe via a 3-tuple __reduce__ (restores __dict__/__notes__ state, matching CPython's default reducer) - Propagate typed CLI errors from audit phases (InputValidationError -> exit 2) instead of recasting them to ExecutionError (exit 4) - Raise a clear ValueError on conflicting scalar/nested --dataset keys instead of an uncaught TypeError that escaped to an exit-1 traceback - Document the newly reachable exit 2 in audit.py and compliance_audit_plan.md - Add regression tests for all of the above Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
main #437 refactored _load_datasets to return (dataloader, eval_configs). test_accuracy_only_skips_salt_validation is branch-only, so the merge kept its stale 3-tuple unpack — the sole site the merge missed. Adopt the 2-tuple form; perf_loader is still the first element, so the assertion is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pass (reason, detail) to super().__init__() so args mirror the constructor signature; CPython's default exception reducer then round-trips copy/pickle (including __notes__) with no custom reducer. Add __str__ for the human-readable message and a test pinning its absolute output. Addresses PR #406 review feedback (viraatc). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What does this PR do?
Makes salt failures a hard error instead of a silent skip. Previously, when
--warmup-saltwas on (the default) but a sample couldn't be salted,Dataset._apply_saltlogged a warning (or silently passed the sample through) and the warmup ran with no cache-busting — the exact condition salt exists to prevent, failing quietly.Now
salt=Truerequires every sample to be adictwith a text (str)promptand noinput_tokens; anything else raisesDatasetValidationErrorat dataset-load time, before any load is issued.Key changes:
Dataset.validate_saltable()— rejects non-dictsamples,input_tokens(pre-tokenized; adapters send these verbatim so a saltedpromptnever reaches the server), missingprompt, and non-strprompt. Error names the offending sample index + remediation._load_datasetswhenwarmup.enabled and warmup.salt, so an unsaltable dataset fails before worker/aggregator subprocesses spawn. Also called fromwith_salt()so the salting mechanism stays self-protecting._apply_saltcollapses to a single dict-merge ({**data, "prompt": f"[{salt}] {data['prompt']}"}) — the multimodal-list handling,input_tokenswarnings, and silent passthroughs are deleted; the contract is guaranteed upstream.promptthat is a list (image/video workloads) or a dataset withinput_tokens(e.g. gpt-oss-120b, DeepSeek-R1 via/v1/completions) will fail warmup instead of silently skipping salt. Fix: set--warmup-salt=false/warmup.salt: false.n_requestssubset would never issue. Stricter than the old per-sample skip; intended by the hard-error design.Type of change
Related issues
Testing
Checklist