Repository navigation
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
Contributor
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Comment |
This was referenced Oct 22, 2026
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 22, 2026 18:00
b311267 to
160f9ed
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
2 times, most recently
from
October 22, 2026 18:00
a9fce93 to
a85b1b8
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 22, 2026 22:09
160f9ed to
3fe37d2
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 22, 2026 22:09
a85b1b8 to
2b31c86
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 23, 2026 00:09
3fe37d2 to
c87329b
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 23, 2026 00:09
2b31c86 to
c5e24c0
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 23, 2026 04:51
c87329b to
1990db8
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 23, 2026 04:51
43ed4e5 to
00bff0b
Compare
Zhehao-Hu
added a commit
that referenced
this pull request
Oct 23, 2026
…strict (#2839) > **[1/8] Day-0 speculative-decoding readiness flow.** Merge in order 1 → 8. Base: `main`. > Series: [1/8] #2839 (this) · [2/8] #2930 · [3/8] #2931 · [4/8] #2932 · [5/8] #2933 · [6/8] #2934 · [7/8] #2935 · [8/8] #2936 ### What does this PR do? Type of change: Bug fix [common/check_regression.py](https://github.com/NVIDIA/Model-Optimizer/blob/main/tools/launcher/common/check_regression.py) gates DFlash-family training runs on final loss and accuracy. Four things stopped that gate from catching a bad run: 1. **It read the wrong checkpoint on long runs.** `find_trainer_state()` sorted `checkpoint-*` as strings, so `checkpoint-8000` ranked after `checkpoint-12000` and an older checkpoint's metrics were checked. Any run whose saved steps differ in digit count was affected; the ~92K-step full DFlash2 recipe in `hf_online_dflash2.yaml` saves every 4000 steps. It now sorts by step number. 2. **An unreadable run passed.** With thresholds set, a missing `trainer_state.json`, or one with no training logs, only warned and exited 0. An empty threshold variable (`MAX_FINAL_LOSS=""`) crashed on `float('')`. 3. **A regression never failed the job.** `dflash_online_training.sh` runs the check as `check_regression.py … || true`, so Slurm reports `COMPLETED` even when the log says `REGRESSION`. The verdict also existed only as log text. 4. **A crashed run was still graded.** The script's `ERR` trap only records a failure, so after a failed `accelerate launch` it went on to export and run the check, and a checkpoint that survived the crash could pass. Changes: - `check_regression.py`: - Sorts checkpoints by step number. - Fails when thresholds are set but there are no metrics to check, including a `trainer_state.json` of the wrong shape. An empty threshold variable counts as unset. A threshold that isn't a finite number (`7,0`, `nan`) fails as `METRICS_MISSING`. - A NaN or infinite value for a metric that has a threshold fails as `TRAIN_REGRESSION`. - Takes `--json <path>` and writes `{pass, failure_class, trainer_state, metrics, thresholds, messages}` as strict JSON (non-finite metrics as `null`). `failure_class` is `null`, `TRAIN_REGRESSION` or `METRICS_MISSING`. The verdict is serialized before the file is opened, so a failure can't leave a truncated file. - Its existing log lines (`=== Regression Check (…) ===`, `PASS:` / `REGRESSION:`, `Regression check PASSED`) are unchanged, so existing log readers keep working. A failure now also prints `Regression check FAILED`. - `dflash_online_training.sh` writes the verdict to `${OUTPUT_DIR}/regression_check.json`, next to the exports. `REGRESSION_STRICT=1` drops the `|| true`, so a regression fails the task through the existing `ERR` trap and `exit_handler`. **The default (`0` or unset) is unchanged (warn only)**, so the other examples with possibly uncalibrated thresholds don't start failing. Any other value fails the task at startup, before installing or training. - `dflash_online_training.sh` also skips export and the check when training fails, still reporting the failure like any `ERR`-trap failure, and removes an old `regression_check.json` first thing, so no early exit can leave an earlier run's verdict behind. - `examples/Qwen/Qwen3-8B/hf_online_dflash2.yaml`: - Sets `REGRESSION_STRICT: "1"`. - Recalibrates `MAX_FINAL_LOSS` from 5.0 to 7.0. The 5.0 was inherited from `hf_online_dflash.yaml`, whose own reference curve only drops below 5.0 at step ~2500 of a 12.5K-step run. DFlash2 adds a selector loss term and this example stops at 2000 steps, so 5.0 fails every healthy run. `MIN_FINAL_ACC` stays at 0.15. - The `speculative-decoding` skill's `dflash.md` and `dflash2.md` describe the new gate: judge a run by `regression_check.json`, the two failure classes, strict mode, and DFlash2's 7.0 threshold. ### Usage ```bash MAX_FINAL_LOSS=7.0 MIN_FINAL_ACC=0.15 \ python3 common/check_regression.py /scratchspace/dflash2_bs16 --json /scratchspace/dflash2_bs16/regression_check.json echo $? # 0 pass, 1 regression or nothing to check ``` ```yaml environment: - MAX_FINAL_LOSS: "7.0" - REGRESSION_STRICT: "1" # fail the training task on a regression ``` ### Testing - `tools/launcher/tests/test_check_regression.py`: 4 new test functions (13 cases). Run against `main`'s `check_regression.py`, 17 of the file's 18 cases fail: - `test_latest_checkpoint_is_chosen_by_step_number` guards the string-sort bug. - `test_empty_threshold_var_is_unset` guards the `float('')` crash. - `test_cli_exit_code_and_json_verdict` is parametrized over a missing `trainer_state.json`, a truncated one, two of the wrong shape, a NaN loss, an unchecked infinite metric, a regression and a pass. Each case checks the exit code and parses the verdict as strict JSON, so it covers the missing-metrics hole and the `--json` contract. - `test_invalid_threshold_writes_failed_verdict` covers `7,0`, `nan` and `inf`. - `tools/launcher/tests/test_dflash_online_training.py` (5 cases) runs the real script in a sandbox: stubbed `pip`, `nvidia-smi`, `accelerate`, exporter and `torch`, a checkpoint, and an old passing verdict. Each case asserts the exact exit code. Against `main`'s script, all 5 fail: - A failing launch leaves no verdict and exits 1; a succeeding one grades the checkpoint and exits 0 with no `ERR`-trap error. - With a checkpoint over the threshold, `REGRESSION_STRICT=1` exits 1 and `0` exits 0, both writing `TRAIN_REGRESSION`. - `REGRESSION_STRICT=true` exits 1 without launching training and leaves no verdict. - Full launcher suite on this commit: `uv run --with pytest python3 -m pytest tests/`, 183 passed. - `dflash_online_training.sh`: `bash -n` passes. - **GPU (OCI-JHB, GB300, global batch 8):** five 2000-step runs of this example with this change passed the strict gate. Final loss was 6.37–6.71 and accuracy 0.167–0.177, against 7.0 / 0.15. The calibration run that motivated 7.0 (before this change) finished at 6.51 / 0.181. The seed-to-seed spread puts the loss margin at about 4%; see the variance note under Additional Information. - Not run: the strict-failure path and a real training crash on GPU (covered by the sandbox test above). ### Before your PR is "*Ready for review*" Make sure you read and follow [Contributor guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md) and your commits are signed (`git commit -s -S`). Make sure you read and follow the [Security Best Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors) (e.g. avoiding hardcoded `trust_remote_code=True`, `torch.load(..., weights_only=False)`, `pickle`, etc.). - Is this change backward compatible?: ✅ (strict mode is opt-in; log output unchanged) - If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: N/A - Did you write any new necessary tests?: ✅ - Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: N/A - Did you get Claude approval on this PR?: ❌ - not yet run ### Additional Information - Part of the Day-0 speculative-decoding workflow: the `specdec-release` skill ([8/8]) gates training on `regression_check.json`, so it needs a verdict it can read and a job state that reflects it. - **Why not reuse `day0-release/scripts/gate_run.py`?** The two sit at different layers and check different things, so their schemas stay independent on purpose: - `gate_run.py` is an agent-side gate for an *evaluation* run. It reads a run summary the agent builds from NEL output, and checks per-task status, sample counts and judge errors. None of that exists for a training run. - `check_regression.py` runs *inside* the Slurm training task, on rank 0, and its verdict decides the task's exit status in strict mode. It reads `trainer_state.json` against thresholds from the launcher YAML, and has to work for launcher users who never load a skill. A launcher script importing a skill script would also invert the dependency: skills drive the launcher, not the other way round. - The skill-side counterpart of `gate_run.py` for training is `specdec-release/scripts/gate_train.py` ([6/8] #2934). It reads `regression_check.json` and returns the day-0 gate shape (`{pass, failure_class, detail, …}`), with `failure_class` from the shared triage vocabulary. That is the adapter layer, so `check_regression.py` doesn't need to match `gate_run.py`. - What the two share is the convention: a boolean `pass`, and a `failure_class` that is `null` on a pass. The launcher emits only `METRICS_MISSING` and `TRAIN_REGRESSION`, and `gate_train.py` treats any other value as `TRAIN_REGRESSION`, so the launcher can add detail fields without breaking the gate. - Other Qwen3-8B examples share the inherited `MAX_FINAL_LOSS: "5.0"` (`hf_online_domino.yaml`, `hf_online_lilicorr.yaml`). They are left unchanged and non-strict because there is no calibration run for them. - Variance: a seed-1 retrain landed at loss 6.705, 4.2% under the 7.0 floor. Accuracy has more headroom (11%). Recalibrating from more seeds is planned for the release gates, not this PR. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **New Features** * Training runs with an output directory now produce a regression-check verdict file reporting whether configured quality thresholds passed. * Regression checks account for missing or invalid metrics and select checkpoints by numeric step. * **Bug Fixes** * Failed training no longer proceeds to checkpoint export or regression checks. * Strict mode fails the task when regression checks fail; invalid strict-mode values stop the run before training starts. * **Documentation** * Updated DFlash2 guidance and example settings for the final-loss threshold and strict-mode behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Zhehao Hu <zhehaoh@nvidia.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 23, 2026 21:24
00bff0b to
f5f9786
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
2 times, most recently
from
October 23, 2026 21:35
1146ba9 to
6d934c1
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 23, 2026 21:35
f5f9786 to
34d9fd4
Compare
Zhehao-Hu
added a commit
that referenced
this pull request
Oct 23, 2026
…ction (#2930) > **[2/8] Day-0 speculative-decoding readiness flow.** Merge in order 1 → 8. Base: `main` ([1/8] has merged). > Series: [1/8] #2839 (merged) · [2/8] #2930 (this) · [3/8] #2931 · [4/8] #2932 · [5/8] #2933 · [6/8] #2934 · [7/8] #2935 · [8/8] #2936 ### What does this PR do? Type of change: Bug fix The vLLM smoke test (`common/specdec/vllm_smoke_test.sh`) is the serving check after draft training. It had six problems: 1. **It could serve the wrong checkpoint.** `ar_eval_mtbench.sh` and `vllm_smoke_test.sh` picked the newest checkpoint by sorting on a dash-separated field of the **whole path**, so a dash in a parent directory (e.g. `/data/my-drafts/...`) picked the wrong one. `quantize_drafter.sh` had a third, different sort. The smoke test also ignored `exported-checkpoint-final`. 2. **Every throughput figure printed `?`.** Timing used `bc`, which the vLLM image doesn't have. 3. **A drafter that never speculated passed.** With `MIN_ACCEPTANCE_LENGTH` set, a vLLM log with no acceptance length only warned. 4. **The acceptance length covered only the last few seconds.** It came from the log's "Mean acceptance length" line, which covers only vLLM's last logging interval. 5. **The verdict existed only as log text.** 6. **The server wait was fixed at 180 s.** Qwen3.8-27B with a DFlash2 drafter takes longer to start on GB300 (torch.compile, kernel JIT, CUDA graph capture, GDN warmups), so every smoke attempt stopped at `server_timeout`. The script's exit then tore the container down mid-warmup, and the engine's next exec failed with a ptxas ENOENT that read like the cause. Changes: - `service_utils.sh`: new `latest_checkpoint <dir> <prefix>`. It returns `<dir>/<prefix>final` if present, else the highest numeric step, keyed on the directory's own name. It replaces the three ad hoc sorts in `ar_eval_mtbench.sh`, `vllm_smoke_test.sh` and `quantize_drafter.sh`. - `vllm_smoke_test.sh`: - Prefers `exported-checkpoint-final`, as `quantize_drafter.sh` already did. - Times prompts with shell arithmetic and `python3` instead of `bc`. - Computes the mean acceptance length over the whole run from vLLM's cumulative `/metrics` counters: 1 + `spec_decode_num_accepted_tokens_total` / `spec_decode_num_drafts_total`. - With `MIN_ACCEPTANCE_LENGTH` set, fails when no speculative drafts were recorded. - Fails loudly if `service_utils.sh` is missing (it used to be optional, so the failure surfaced as a misleading `draft_missing`). Only a missing file is fatal: `source` returns the status of the file's last command, a rank-0 append that fails outside a launcher working directory. - `SERVER_TIMEOUT` sets the server wait in seconds (default 180) and appears in the timeout reason. - Checks `MIN_ACCEPTANCE_LENGTH` (a finite number), and `NUM_SPEC_TOKENS` and `SERVER_TIMEOUT` (positive integers without leading zeros, since `NUM_SPEC_TOKENS` goes into vLLM's JSON config), before any work, and fails with `stage: invalid_config` and a reason naming the value. Both checks run after the `EXIT` trap is set, so even these failures replace any earlier `SMOKE_JSON`. - Optional `SMOKE_JSON=<path>` writes the verdict on every exit: `{stage, pass, reason, method, target, draft, num_speculative_tokens, prompts passed/failed, tokens, average tok/s, mean_acceptance_length, min_acceptance_length}`. `stage` says how far it got (`invalid_config`, `starting`, `draft_missing`, `server_died`, `server_timeout`, `serving`, `completed`), so a downstream gate can tell "not servable" from "below the floor". The file's parent directory is created if needed, and it is strict JSON (a non-numeric value is written as `null`). The mean acceptance length is checked against the floor and written at full precision; only the log rounds it. ### Usage ```yaml task_2: script: common/specdec/vllm_smoke_test.sh environment: - HF_MODEL_CKPT: <<global_vars.hf_model>> - DRAFT_CKPT_DIR: <<global_vars.output_dir>> # newest export, final preferred - SPEC_METHOD: "dflash" - NUM_SPEC_TOKENS: "7" - MIN_ACCEPTANCE_LENGTH: "1.5" - SMOKE_JSON: <<global_vars.output_dir>>/smoke_check.json - SERVER_TIMEOUT: "900" # optional; default 180 s ``` ### Testing - `tools/launcher/tests/test_service_utils.py`: `latest_checkpoint` with a dash in a parent directory, `final` winning over steps, numeric rather than string order, and no checkpoint at all (3 cases). - `tools/launcher/tests/test_vllm_smoke_test.py` runs the script as rank 0 from a directory with no `modules/` (stubbed `pip` and `nvidia-smi`), up to the paths that stop before vLLM starts (6 cases). Against the script without these fixes, all 6 fail: - It reaches `draft_missing` rather than stopping at `source`. - `MIN_ACCEPTANCE_LENGTH` of `1,5` or `nan`, `NUM_SPEC_TOKENS` of `x` or `08` and `SERVER_TIMEOUT=15m` each fail as `invalid_config` with a strict-JSON `SMOKE_JSON`. Before, `NUM_SPEC_TOKENS=x` crashed in the `EXIT` trap and wrote no JSON. - `bash -n` on all four scripts. Launcher unit suite on this commit: 192 passed. - **GPU (OCI-JHB, GB300, vLLM v0.31.0)**, through `hf_online_dflash2.yaml`'s smoke task ([4/8]) and one smoke-only submission, six runs in all: - `latest_checkpoint` picked `exported-checkpoint-final` in every training run. - Per-prompt tok/s printed (e.g. 335 tok/s average), 8/8 prompts passed. - Mean acceptance length 1.68–1.79 against the 1.5 floor; `smoke_check.json` written each time. - **GPU, Qwen3.8-27B + DFlash2 (same cluster and image):** four smoke runs at the old fixed 180 s each wrote `smoke_check.json` with `stage: server_timeout`. A fifth with `SERVER_TIMEOUT=900` passed at AL 1.76. - Not run on GPU: - the `server_died` and no-drafts failure paths. They exit through the same `write_smoke_json` trap as the passing path; - creating `SMOKE_JSON`'s parent directory. A smoke-only run hit the missing directory before this was added; the rerun used an existing directory. ### Before your PR is "*Ready for review*" Make sure you read and follow [Contributor guidelines](https://github.com/NVIDIA/Model-Optimizer/blob/main/CONTRIBUTING.md) and your commits are signed (`git commit -s -S`). Make sure you read and follow the [Security Best Practices](https://github.com/NVIDIA/Model-Optimizer/blob/main/SECURITY.md#security-coding-practices-for-contributors) (e.g. avoiding hardcoded `trust_remote_code=True`, `torch.load(..., weights_only=False)`, `pickle`, etc.). - Is this change backward compatible?: ✅ (`SMOKE_JSON` is optional. With `MIN_ACCEPTANCE_LENGTH` set, a run with no speculative drafts now fails instead of passing; that was the bug.) - If you copied code from any other sources or added a new PIP dependency, did you follow guidance in `CONTRIBUTING.md`: N/A - Did you write any new necessary tests?: ✅ (`latest_checkpoint`; the smoke script itself is validated on GPU as above) - Did you update [Changelog](https://github.com/NVIDIA/Model-Optimizer/blob/main/CHANGELOG.rst)?: N/A - Did you get Claude approval on this PR?: ❌ - not yet run ### Additional Information - The `specdec-release` skill's `gate_smoke.py` ([6/8]) reads `SMOKE_JSON`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> <!-- coderabbit-ai-generated:v1 --> ## Summary by CodeRabbit * **Improvements** * Checkpoint selection now prefers a `final` checkpoint, or otherwise chooses the highest-numbered checkpoint. * Smoke tests validate numeric settings, use a configurable server timeout, and report run results, failure reasons, and performance metrics in an optional JSON file. * Acceptance-length checks now use cumulative draft and accepted-token counts; prompt failures and unmet acceptance thresholds result in a failed test. * **Tests** * Added coverage for checkpoint selection and smoke-test configuration failures. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Zhehao Hu <zhehaoh@nvidia.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 28, 2026 00:21
6d934c1 to
828fea9
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 28, 2026 00:21
34d9fd4 to
643578c
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 29, 2026 00:35
828fea9 to
6ed607e
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 29, 2026 00:35
643578c to
351897c
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 29, 2026 03:44
6ed607e to
723bdd1
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 29, 2026 03:44
351897c to
b941f72
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 29, 2026 04:53
723bdd1 to
d3c9f5b
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 29, 2026 04:53
b941f72 to
b281093
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 29, 2026 04:59
d3c9f5b to
dbce061
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 29, 2026 04:59
b281093 to
264fb27
Compare
Turn the specdec-release skill into a conductor that takes a target and an algorithm from no drafter to a readiness verdict (DRAFTER_READY, INFEASIBLE or ANOMALOUS) in a fixed sequence: setup, train pipeline (data, train + export, smoke) gated by gate_train and gate_smoke, freeze, then a matched A|B bench gated by gate_bench (gate_correctness reports only). It submits the existing launcher pipelines and never advances past a failed gate. Scope: DFlash and DFlash2, whose training script writes the regression verdict; other algorithms are refused before anything is submitted. - SKILL.md: inputs (existing_drafter, approve_submissions each|none), the stage sequence, how to run and read each gate, cluster fit, watching jobs by result files, specdec_state.json and resume rules, work_log.md with ACTION / HUMAN_INTERVENTION lines, triage, and SPECDEC_REPORT.md. - freeze_drafter.py: copy the export through a .partial sibling, re-hash before the rename, write SHA256SUMS, make it read-only, return a manifest_sha256; --verify re-checks it on resume. - gate_train.py --export-only: check an existing drafter's export without a training verdict. - tests: freeze_drafter and --export-only cases; evals.json trigger and non-trigger prompts. - speculative-decoding: route the end-to-end flow to specdec-release, and add fitting every task to the cluster (whole-node GPU counts, keeping the global batch via gradient_accumulation_steps) to the configure stage. Signed-off-by: Zhehao Hu <zhehaoh@nvidia.com>
gate_smoke ([6/8]) now returns USER_CONFIG_ERROR when vllm_smoke_test.sh stops at stage invalid_config. The triage row for that class said to re-run the gate only, which cannot help: the bad MIN_ACCEPTANCE_LENGTH or NUM_SPEC_TOKENS is in the YAML. The row now says to fix it there and resubmit as a smoke-only submission, since training already passed its gate. The exit-code note says the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Zhehao Hu <zhehaoh@nvidia.com>
…ge weights From the first multi-model run (Qwen3.8-27B + DFlash2): - Triage: CHECKPOINT_NOT_SERVABLE acts on the smoke JSON's stage, not the log's last traceback. The run's server_timeout ended in a ptxas ENOENT from the container teardown, and chasing it cost three smoke attempts and two probe jobs. server_timeout now resubmits with a larger SERVER_TIMEOUT. - approve_submissions: each covers every cluster job (launcher, sbatch, weight staging) and sub-agents. One OK covers one exact job; a retry or a changed partition, image or command needs a new one. A delegated download had made three submissions on one approval. - Step 1 stages a target the job host lacks: reuse a readable hf_local copy, else one download job into hf_local at a pinned revision, then verify the shards. It lives here until it moves to the common skill for ptq and day0-release. - The closeout reports the greedy match next to its target-alone control. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Zhehao Hu <zhehaoh@nvidia.com>
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-release-skill
branch
from
October 29, 2026 21:43
264fb27 to
ae419c1
Compare
Zhehao-Hu
force-pushed
the
zhehaoh/specdec-gates-bench
branch
from
October 29, 2026 21:43
dbce061 to
d3c8a87
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Type of change: New feature
Completes the
specdec-releaseagent skill. Asked "train a DFlash2 drafter for Qwen3-8B and tell me whether it's ready", the agent goes from no drafter to a readiness verdict in a fixed sequence, with a gate after every stage. It is a conductor: it submits the existing launcher pipelines and enforces the gates from [6/8] and [7/8]. It doesn't re-implement training, serving or benchmarking.Scope: DFlash and DFlash2. Only
dflash_online_training.shwrites theregression_check.jsonthatgate_trainreads, so EAGLE3 and DSpark (train_eagle*.sh) are refused before anything is submitted, as are Domino (no vLLM path) and LiLiCorr (SGLang only). Extending to EAGLE3 means making its training and serving scripts write the same verdict files.Verdicts:
DRAFTER_READY: every gate passed. Ready for release review; it is not a publish decision, and the gates set no per-model performance bar.INFEASIBLE: training, export or serving failed, and triage found no fix within the run.ANOMALOUS: the measurement is invalid after the allowed retries, or freeze failed.SYSTEMIC(cluster or credentials down) aborts the run, as inday0-release; it is not a verdict.plugins/modelopt/skills/specdec-release/:SKILL.md: inputs, the stage sequence, how to run each gate, state and resume, the triage table, and the report.existing_drafter, which skips training but not its gates, andapprove_submissions: each | none.eachis the default: the agent shows every cluster job (launcher,sbatch, weight staging, including a sub-agent's) and waits for an OK. One OK covers one exact job; a retry or a changed partition, image or command needs a new one.hf_localcopy, else one download job intohf_localat a pinned revision, then check the shards. A 401/403 on a gated model isUSER_CONFIG_ERROR. It lives here until it moves to the common skill forptqandday0-release.specdec_state.jsonrecords each stage's status, experiment and job ids, overrides and gate verdicts. On resume the agent re-runs nothing that passed, re-watches instead of resubmitting, and re-verifies the frozen drafter before benchmarking.work_log.mdasACTIONorHUMAN_INTERVENTION, so interventions can be counted.SPECDEC_REPORT.md, with the setup, gate verdicts, the frozen drafter's checksums and the A|B table.CHECKPOINT_NOT_SERVABLEacts on the smoke JSON'sstage, not the log's last traceback:server_timeoutresubmits with a largerSERVER_TIMEOUT([2/8]).scripts/freeze_drafter.py: copies the export to/cicd/drafts/<target>/<algo>/<run_id>through a.partialsibling, re-hashes before the rename, writesSHA256SUMS, and makes the files read-only. It returns amanifest_sha256, and--verify --manifest-sha256re-checks it on resume. The bench serves the frozen copy, which freeze proves byte-identical to the export at freeze time.scripts/gate_train.py --export-only: checks an existing drafter's export without a training verdict.tests/:freeze_drafterand--export-onlycases, plusevals.jsonwith 6 trigger and non-trigger prompts.plugins/modelopt/skills/speculative-decoding/:SKILL.mdroutes the end-to-end flow tospecdec-release.references/stages/configure.mdgains "fit every task to the cluster":gpus_per_nodeto the node size on whole-node clusters;gradient_accumulation_stepsto keep the global batch.Usage
Testing
Skills suite on this commit: 163 passed, 1 skipped. Launcher suite: 192 passed.
End-to-end on OCI-JHB (GB300) with Claude Code, unattended apart from submission approvals:
existing_drafter(M0 drafter) and full train (seed 1)DRAFTER_READYDRAFTER_READY, 0 interventions. Exposed a fresh-venv launcher bug, fixed in Pin "leptonai<0.47" to fix tools/launcher CI job #2892 and [launcher] Add build-system so uv run installs modelopt_launcher #2921DRAFTER_READY; the resume re-verified the freeze and re-ran nothing. 3 unlogged nudges, fixed here (watch after every submission, load the cluster in every shell, run dir)DRAFTER_READY, 0 interventions and no nudges. Watched and re-armed its own watchers, ran every remote command throughremote_run, and caught two SSH failures at submit by checkingsqueue(nothing queued), retrying with approvalResults across the variance pass and rehearsals:
In the rehearsals, the bench and smoke jobs exited 7–17 s after writing their results, with no hangs.
First new target, Qwen3.8-27B + DFlash2 (no prior example):
DRAFTER_READY(loss 2.93, smoke AL 1.76, speedup 1.40/1.29/1.06 at C=1/8/32), with 6 interventions beyond approvals. They led to the staging step, the approval scope and stage-first triage above, and [2/8]'sSERVER_TIMEOUT.Not run: the
INFEASIBLEandANOMALOUStriage paths on GPU (their gates' failure classes are unit-tested), local Docker job hosts, and DFlash end to end (in scope, but only DFlash2 was run).Before your PR is "Ready for review"
Make sure you read and follow Contributor guidelines and your commits are signed (
git commit -s -S).Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded
trust_remote_code=True,torch.load(..., weights_only=False),pickle, etc.).speculative-decoding)CONTRIBUTING.md: N/A (stdlib only)Additional Information
plugins/modelopt/skills/day0-release: the same gate contract, verdict style and use of the common skill'sremote_exec.sh.SKILL.md, which has to describe the whole flow in one place for the agent.approve_submissions: noneonce the flow has a clean track record.