Support original z-lab Qwen3 DFlash checkpoints; require checkpoint target_layer_ids - #4991
Open
harshal-96 wants to merge 3 commits into
Open
harshal-96 wants to merge 3 commits into
harshal-96 wants to merge 3 commits into
Conversation
Validated dense Qwen3 end-to-end on the DFlash stack (RTX 4070 8 GB, target thewimo/Qwen3-4B-AWQ float16, draft z-lab/Qwen3-4B-DFlash-b16 bfloat16, greedy, 256 new tokens): 33.6-36.8 tok/s vs 25.1 baseline, mean 4.19 of 15 drafts accepted per block, coherent lossless output. Two gaps had to be fixed to get there: - parse_dflash_config required dflash_config['block_size'], but the original z-lab Qwen3 checkpoints (Qwen3-4B/8B-DFlash-b16) store block_size at the top level of config.json. Fall back to the top-level attribute (same resolution order as the reference implementation), for mask_token_id as well, with a regression test. - The draft ran the shared target embedding output and the target aux states in the target dtype through bfloat16 draft weights, which fails with a dtype mismatch for float16 targets (e.g. AWQ). Casting the whole engine to float16 instead is not an option: Qwen3 hidden state outliers overflow float16 during feature fusion and acceptance collapses to zero (measured 0.06 of 15). Cast to the draft dtype at the two ingestion boundaries; logits already cast via get_logits. Signed-off-by: harshal-96 <harshal.dhandrut@gmail.com>
…er_ids The evenly spaced fallback cannot be correct: the tap layers are a training-time choice baked into the checkpoint (the fc input width and which target layers the draft was trained against), so any load-time guess reads the wrong features. For 14 of the 20 published DFlash checkpoints the guess differs from the trained layers, and when the counts collide the load succeeds silently with wrong layers (z-lab/dflash#156). Every published DFlash checkpoint sets dflash_config.target_layer_ids, so a missing key now fails loudly. Matches the same removal in the reference repo (z-lab/dflash#157) and sglang (sgl-project/sglang#37476). Signed-off-by: harshal-96 <harshal.dhandrut@gmail.com>
RunningLeon
requested review from
RunningLeon
and
a lite review from Copilot
September 20, 2026 03:06
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Add mixed-dtype regression coverage and address the two identified test/diagnostic gaps.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (3)
What changed in this PR
This PR adds support for original Qwen3 DFlash checkpoints and enforces checkpoint-defined target layers.
Changes:
- Supports top-level checkpoint metadata.
- Adds mixed-dtype boundary casts.
- Requires explicit
target_layer_idsand updates tests.
| File | Summary |
|---|---|
tests/pytorch/spec_decode/test_dflash_utils.py |
Adds checkpoint-layout and required-layer tests; top-level mask_token_id coverage remains missing. |
lmdeploy/pytorch/spec_decode/dflash_utils.py |
Resolves metadata and removes inferred target layers; one diagnostic still references the nested block-size key. |
lmdeploy/pytorch/models/qwen3_dflash.py |
Adds target/draft dtype casts; mixed-dtype paths lack regression tests. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+355
to
+357
| # the shared target embedding may run in a different dtype than the | ||
| # draft (e.g. float16 AWQ target with a bfloat16 draft checkpoint) | ||
| return embeds.to(self.dtype) |
Comment on lines
127
to
128
| raise ValueError('DFlash query length (1 + speculative_num_draft_tokens) must not exceed checkpoint ' | ||
| 'dflash_config.block_size. ' |
Comment on lines
+73
to
+81
| def test_parse_dflash_config_top_level_checkpoint_layout(): | ||
| """Original z-lab DFlash checkpoints (e.g. Qwen3-4B-DFlash-b16) keep | ||
| block_size at the top level of config.json and nest only mask_token_id and | ||
| target_layer_ids inside dflash_config.""" | ||
| config = _draft_config(block_size=4, | ||
| dflash_config=dict( | ||
| mask_token_id=32001, | ||
| target_layer_ids=[1, 5, 9, 13], | ||
| )) |
…rer block_size error - Unit-test the two draft-dtype ingestion boundaries (shared target embeddings and projected target hidden states) with a float16 source and a bfloat16 draft; both fail on the uncast code. - Cover the top-level mask_token_id fallback, matching the block_size fallback coverage. - Mention both supported block_size locations in the query-length error. Signed-off-by: harshal-96 <harshal.dhandrut@gmail.com>
harshal-96
force-pushed
the
feat/dflash-qwen3-dense-rebased
branch
from
September 20, 2026 18:41
63c2073 to
57883c2
Compare
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.


Motivation
Follow-up to #4789, as discussed with @RunningLeon in #4789 (comment): enable dense Qwen3 DFlash on the merged infrastructure. The original z-lab checkpoints (z-lab/Qwen3-4B-DFlash-b16, Qwen3-8B-DFlash-b16) do not load on main for two reasons, and main also carries the incorrect target-layer fallback that the reference repo removed.
block_size(andmask_token_idon some) at the top level of config.json rather than insidedflash_config, soparse_dflash_configrejects them.build_target_layer_idsguesses evenly spaced tap layers whendflash_config.target_layer_idsis missing. The tap layers are a training-time choice baked into the checkpoint (the fc input width and which target layers the draft was trained against), so a guess reads the wrong features; for 14 of the 20 published DFlash checkpoints the guess differs from the trained layers, and when only the counts match the load succeeds silently with wrong layers. See bug: build_target_layer_ids never runs, and its guess would be wrong for 14 of 20 checkpoints z-lab/dflash#156; the reference repo removed the fallback in bug: require dflash_config.target_layer_ids instead of guessing it z-lab/dflash#157 and sglang did the same in spec: require dflash_config.target_layer_ids instead of guessing tap layers sgl-project/sglang#37476. Every published DFlash checkpoint sets the key.Modification
parse_dflash_config: acceptblock_size/mask_token_idat the top level of the draft config as a fallback, mirroring the reference implementation's resolution order (dflash_configfirst).qwen3_dflash.py: cast to the draft dtype at the two boundaries where target-side tensors enter the draft (input embeddings and projected target hidden states), so a bf16 draft runs against an fp16 target.dflash_utils.py: removebuild_target_layer_ids; a missingdflash_config.target_layer_idsnow raises with a message naming the key.tests/pytorch/spec_decode/test_dflash_utils.py(top-level layout regression test, explicit target_layer_ids in the block-size tests, missing-key raise test).BC-breaking (Optional)
Checkpoints relying on the guessed target layers would now fail at load with a clear error instead of silently producing near-zero acceptance. No published DFlash checkpoint omits
dflash_config.target_layer_ids.Use cases (Optional)
Verified end to end on the merged main (RTX 4070 Laptop 8GB, WSL2, eager mode, greedy, batch 1, target thewimo/Qwen3-4B-AWQ, draft z-lab/Qwen3-4B-DFlash-b16; one math and one code prompt, 256 new tokens each):
Checklist
pytest tests/pytorch/spec_decode/test_dflash_utils.pypasses 48 tests.