Repository navigation
Conversation
added 2 commits
September 30, 2026 15:52
install_frspec_draft_head made the full-vocabulary wrapper live only through the Qwen4 native MTP bind hook. On the generic mtp_patch draft forward (dense Qwen3.5 / Qwen3.8), which projects every draft step through _mtplx_draft_lm_head, the pruned head was built and never used unless MTPLX_FRSPEC_LEGACY=1 was also set. The install now puts the wrapper in that slot itself (the configured head is kept at _mtplx_frspec_saved_head); the wrapper's output stays full-vocabulary, so draft readers and acceptance see real token ids. The native route and MTPLX_FRSPEC_LEGACY keep their meaning. Also: a rotated packed head (Bonsai) is refused instead of being pruned without its activation transform, and the session-bank draft-head identity hashes the wrapper's pruned head instead of an empty attribute set.
jvmenen
pushed a commit
to jvmenen/MTPLX
that referenced
this pull request
Sep 30, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner
|
Not in 2.12.1, which was a stability release. I'll review this for the next release. |
jvmenen
pushed a commit
to jvmenen/MTPLX
that referenced
this pull request
Oct 8, 2026
…#578 not used by prod Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ly for Flash-Next packs
jvmenen
pushed a commit
to jvmenen/MTPLX
that referenced
this pull request
Oct 9, 2026
…N 125) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
jvmenen
pushed a commit
to jvmenen/MTPLX
that referenced
this pull request
Oct 14, 2026
…al#578, float32 lse output)
jvmenen
force-pushed
the
fix/frspec-configured-route
branch
from
October 14, 2026 11:56
3acd262 to
8fe3dad
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.
Why
FR-Spec is meant to make the draft step cheaper. On packs whose draft step runs through the generic MTP route, such as the dense Qwen3.8-27B,
MTPLX_FRSPEC_DRAFT=1builds the smaller draft head and reportsinstalled: True, but the draft step keeps using the original full head, so the switch has no effect. The only working alternative was the older legacy lane (MTPLX_FRSPEC_LEGACY=1), which in turn rules out two other draft speedups: the K20 prescatter and the sampled draft chain.How it works, in plain terms
In speculative decoding a small draft head guesses the next tokens and the full model checks them. Scoring all of the roughly 248,000 tokens in the vocabulary is a large part of each draft step. FR-Spec gives the draft head a reduced output layer with only the 65,536 most frequent tokens. The full model still checks every guess against the whole vocabulary, so the output stays exactly the same; only how often a guess is accepted can change. MTPLX already built that reduced layer, but on this route it never reached the place the draft step reads from. This change puts it there (
_mtplx_draft_lm_head). The tokens that were left out get a score so low that they are never picked, so everything after the draft step still sees ordinary token ids. Defaults do not change: outside Flash-Next, FR-Spec stays opt-in.What changes
install_frspec_draft_head(mtplx/frspec_draft.py) now makes the full-vocabulary wrapper live on the configured draft-head route too. A new helper_bind_full_headkeeps the Qwen4 native route as it was (bind hook,_mtplx_draft_lm_headuntouched) and, when the model has no bind hook, puts the wrapper in_mtplx_draft_lm_head, which is the head the genericmtp_patchdraft forward projects through on every step. The configured head is kept at_mtplx_frspec_saved_head, and the install report gainsbinding. Two small guards come with it: a rotated packed head (Bonsai'sHadamardQuantizedLinear) is refused instead of pruned without its activation transform, and_draft_head_identityhashes the wrapper's pruned head instead of an empty attribute set.Results
M5 Pro 64 GB,
Youssofal/Qwen3.8-27B-MTPLX-Optimized-Speed, profile turbo, depth 3,capture_commit, draft head 4-bit g64, draft temperature 0.6, fans default, 2026-09-30. Arm A: FR-Spec off; arm B:MTPLX_FRSPEC_DRAFT=1with the builtinqwen38-code-64ktable, bound by this change. Fresh server per run, runs alternated.By text type (10 prompts, 800 tokens each, pooled over both runs per arm):
With the builtin table, code and English gain 7-9% while Dutch loses, because the table is ranked on code and too many likely Dutch tokens fall outside the kept 65,536 rows. That is why this PR keeps FR-Spec opt-in. The binding works with an operator table too (
MTPLX_FRSPEC_VOCAB=<file.npy>): a 65,536-row table ranked on an operator's own, mostly Dutch, agent traffic, on the same model and settings (two boots, compared with three FR-Spec-off boots): code +14% (29.3 -> 33.5 tok/s), English prose +9% (27.1 -> 29.4), Dutch with reasoning +11% (27.6 -> 30.7), Dutch without thinking +10% (24.1 -> 26.4), with Dutch acceptance unchanged (D1 0.82 -> 0.81). So the binding itself carries the gain; which table fits depends on the workload.An agent tool-calling task (13 requests, 18 tool calls) completed with tests green with FR-Spec on.
Tests
tests/test_frspec_configured_route.py: the wrapper is live in_mtplx_draft_lm_headwith a full-vocabulary output equal to the configured head on kept rows; the native route is unchanged; a rotated head is refused; the draft-head identity follows the pruned head. All fail onmain.