While reviewing the Qwen3.5/3.8 text-only arms (#490), one remaining hardcoded VL-prefix site turned up outside that row's scope.
SharedHeadSource::LoadInto (src/vllm/entrypoints/model_loader.cpp:305-312) loads the speculative draft's shared target embedding by literal name on the safetensors arm:
*embed = LoadNamedBf16(
*shards_, "model.language_model.embed_tokens.weight", false);
*head = LoadNamedBf16(*shards_, "lm_head.weight", true);
lm_head.weight is top-level in both namespaces and is fine. model.language_model.embed_tokens.weight is the VL-wrapper spelling. A text-only Qwen3.5 target (Qwen3_5ForCausalLM / Qwen3_5MoeForCausalLM, whose checkpoints publish model.embed_tokens.weight) therefore cannot supply the shared embedding to a DFlash/DSpark draft.
Severity is limited: it fails loudly, not silently. The surrounding guard throws naming the source when either tensor is absent, so this is a refusal rather than a wrong-weights load.
Not fixed in #490 deliberately. That row's spec explicitly defers spec-decode arms for 3.8 and refuses them with a message naming the missing piece rather than implementing on speculation, so repairing this there would have been out-of-scope scope creep. #490 introduces ResolveQwen3_5BackbonePrefix, which resolves the backbone namespace once per checkpoint by voting on structural spellings and refuses a mixed index — the natural fix here is to route this site through that same resolver rather than adding a second fallback.
Owed work: route SharedHeadSource::LoadInto through the shared resolver, with a test covering both namespaces. Depends on #490 landing first.
While reviewing the Qwen3.5/3.8 text-only arms (#490), one remaining hardcoded VL-prefix site turned up outside that row's scope.
SharedHeadSource::LoadInto(src/vllm/entrypoints/model_loader.cpp:305-312) loads the speculative draft's shared target embedding by literal name on the safetensors arm:*embed = LoadNamedBf16( *shards_, "model.language_model.embed_tokens.weight", false); *head = LoadNamedBf16(*shards_, "lm_head.weight", true);lm_head.weightis top-level in both namespaces and is fine.model.language_model.embed_tokens.weightis the VL-wrapper spelling. A text-only Qwen3.5 target (Qwen3_5ForCausalLM/Qwen3_5MoeForCausalLM, whose checkpoints publishmodel.embed_tokens.weight) therefore cannot supply the shared embedding to a DFlash/DSpark draft.Severity is limited: it fails loudly, not silently. The surrounding guard throws naming the source when either tensor is absent, so this is a refusal rather than a wrong-weights load.
Not fixed in #490 deliberately. That row's spec explicitly defers spec-decode arms for 3.8 and refuses them with a message naming the missing piece rather than implementing on speculation, so repairing this there would have been out-of-scope scope creep. #490 introduces
ResolveQwen3_5BackbonePrefix, which resolves the backbone namespace once per checkpoint by voting on structural spellings and refuses a mixed index — the natural fix here is to route this site through that same resolver rather than adding a second fallback.Owed work: route
SharedHeadSource::LoadIntothrough the shared resolver, with a test covering both namespaces. Depends on #490 landing first.