Skip to content

Commit 929e461

Browse files
authored
[Bugfix] Fix GLM-5.1 IndexCache weight loading (vllm-project#11363)
## What this PR does / why we need it? The existing patch treated `skip_topk=True` as proof that the checkpoint omitted the layer's `Indexer`. This is valid for GLM-5.2 shared-indexer layers, but not for GLM-5.1 when IndexCache is enabled through runtime HF overrides. GLM-5.1 checkpoints still contain `indexer.*` weights for every layer, so removing the module makes `AutoWeightsLoader` reject those weights. This change separates top-k reuse from checkpoint structure: `skip_topk` controls computation, while `indexer_types[layer_id] == "shared"` controls whether the module is omitted. - Keep per-layer `Indexer` modules for GLM-5.1 when IndexCache is enabled through `use_index_cache` and `index_topk_freq` overrides. - Skip `Indexer` initialization only for GLM-5.2 layers explicitly marked as `shared` in `indexer_types`. - Keep complete `Indexer` modules for MTP layers. - Add focused unit coverage for GLM-5.1, GLM-5.2 shared-indexer, and MTP behavior. ## How was this patch tested? - vLLM version: v0.23.0 - vLLM main: vllm-project/vllm@a30addc Signed-off-by: ZYang6263 <50876451+ZYang6263@users.noreply.github.com>
1 parent c56b0d8 commit 929e461

3 files changed

Lines changed: 69 additions & 23 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
from types import SimpleNamespace
4+
5+
from vllm_ascend.patch.worker.patch_deepseek_v2 import _should_skip_indexer_init
6+
7+
8+
def _config(**overrides) -> SimpleNamespace:
9+
values = {"num_hidden_layers": 80}
10+
values.update(overrides)
11+
return SimpleNamespace(**values)
12+
13+
14+
def test_glm51_skip_topk_keeps_per_layer_indexer():
15+
assert not _should_skip_indexer_init(
16+
_config(),
17+
"model.layers.2.self_attn",
18+
skip_topk=True,
19+
)
20+
21+
22+
def test_glm52_shared_layer_skips_indexer_init():
23+
assert _should_skip_indexer_init(
24+
_config(indexer_types=["full", "full", "shared"]),
25+
"model.layers.2.self_attn",
26+
skip_topk=True,
27+
)
28+
29+
30+
def test_mtp_layer_keeps_indexer():
31+
indexer_types = ["full"] * 80 + ["shared"]
32+
assert not _should_skip_indexer_init(
33+
_config(indexer_types=indexer_types),
34+
"model.layers.80.self_attn",
35+
skip_topk=True,
36+
)

vllm_ascend/patch/__init__.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -760,14 +760,16 @@
760760
# Remove this patch when vllm supports rotary quant or pluggable `MultiTokenPredictorLayer`.
761761
# ** 19a. File: worker/patch_deepseek_v2.py**
762762
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
763-
# 1. `vllm.model_executor.models.deepseek_v2.DeepseekV2Attention.__init__`
763+
# 1. `vllm.model_executor.models.deepseek_v2.DeepseekV2MLAAttention.__init__`
764764
# Why:
765-
# GLM/DeepSeek DSA models can skip topk on selected layers. Those layers
766-
# should not initialize `Indexer`, while MTP layers still need full indexer
767-
# initialization.
765+
# GLM-5.2 checkpoints omit `Indexer` weights on shared-indexer layers,
766+
# while GLM-5.1 IndexCache overrides only skip top-k computation and keep
767+
# per-layer `Indexer` weights. Treating both layouts alike breaks GLM-5.1
768+
# weight loading.
768769
# How:
769-
# Wrap `DeepseekV2Attention.__init__` and skip `Indexer` construction on
770-
# backbone layers whose config marks topk as skipped.
770+
# Skip `Indexer` construction only when the layer both skips top-k and is
771+
# explicitly marked `shared` in `indexer_types`. MTP layers always retain
772+
# a complete `Indexer`.
771773
# Related PR (if no, explain why):
772774
# https://github.com/vllm-project/vllm/pull/45895
773775
# Future Plan:

vllm_ascend/patch/worker/patch_deepseek_v2.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,27 @@
2424
from vllm.model_executor.models.utils import extract_layer_index
2525

2626

27+
def _should_skip_indexer_init(
28+
config: DeepseekV2Config | DeepseekV3Config,
29+
prefix: str,
30+
skip_topk: bool,
31+
) -> bool:
32+
if not skip_topk:
33+
return False
34+
35+
layer_id = extract_layer_index(prefix)
36+
num_hidden_layers = getattr(config, "num_hidden_layers", None)
37+
if num_hidden_layers is not None and layer_id >= num_hidden_layers:
38+
return False
39+
40+
# GLM-5.2 describes checkpoint-level shared indexers explicitly. Runtime
41+
# IndexCache overrides on GLM-5.1 only skip top-k computation; its
42+
# checkpoint still contains an Indexer for every layer.
43+
indexer_types = getattr(config, "indexer_types", None)
44+
indexer_type = indexer_types[layer_id] if indexer_types is not None and layer_id < len(indexer_types) else None
45+
return isinstance(indexer_type, str) and indexer_type.lower() == "shared"
46+
47+
2748
def _deepseek_v2_mla_attention_init(
2849
self,
2950
vllm_config: VllmConfig,
@@ -161,10 +182,8 @@ def _deepseek_v2_mla_attention_init(
161182

162183
# IndexCache config.
163184
#
164-
# PR #45895 的关键修改是:
165-
# 1. 在创建 Indexer 前先计算当前层是否 skip_topk;
166-
# 2. skip_topk 的 backbone 层不创建 Indexer;
167-
# 3. MTP/nextn 层即使命中 skip pattern,也必须创建完整 Indexer。
185+
# skip_topk controls top-k reuse. Indexer initialization is skipped only
186+
# when the checkpoint marks this layer as sharing another layer's Indexer.
168187
_skip_topk = False
169188
_index_topk_freq = getattr(
170189
config,
@@ -196,19 +215,8 @@ def _deepseek_v2_mla_attention_init(
196215
elif 0 <= layer_id < len(_index_topk_pattern):
197216
_skip_topk = _index_topk_pattern[layer_id] == "S"
198217

199-
# Skip pattern only governs backbone layers.
200-
#
201-
# MTP/nextn layers must always build a complete Indexer. The MTP
202-
# implementation computes top-k indices at draft step 0, then changes
203-
# skip_topk dynamically during the remaining speculative iterations.
204-
_num_hidden_layers = getattr(
205-
config,
206-
"num_hidden_layers",
207-
None,
208-
)
209-
is_mtp_layer = _num_hidden_layers is not None and layer_id >= _num_hidden_layers
210-
211-
if self.is_v32 and (not _skip_topk or is_mtp_layer):
218+
skip_indexer_init = _should_skip_indexer_init(config, prefix, _skip_topk)
219+
if self.is_v32 and not skip_indexer_init:
212220
self.indexer_rope_emb = get_rope(
213221
qk_rope_head_dim,
214222
max_position=max_position_embeddings,

0 commit comments

Comments
 (0)