main
py 153 lines 4.89 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import json
5
6 import pytest
7
8 from vllm.config import ModelConfig, ParallelConfig, SpeculativeConfig
9 from vllm.transformers_utils.config import get_config
10 from vllm.transformers_utils.configs.k3_dspark import K3DSparkConfig
11
12
13 def _write_dspark_config(path, **overrides):
14 path.mkdir()
15 config = {
16 "architectures": ["K3DSparkModel"],
17 "model_type": "k3_dspark",
18 "hidden_size": 7168,
19 "intermediate_size": 14336,
20 "num_hidden_layers": 5,
21 "num_attention_heads": 96,
22 "num_key_value_heads": 96,
23 "q_lora_rank": 1536,
24 "kv_lora_rank": 512,
25 "qk_nope_head_dim": 128,
26 "qk_rope_head_dim": 64,
27 "v_head_dim": 128,
28 "vocab_size": 163840,
29 "rms_norm_eps": 1e-5,
30 "max_position_embeddings": 32768,
31 "rope_theta": 50000.0,
32 "num_target_layers": 5,
33 "target_hidden_size": 7168,
34 "target_num_hidden_layers": 93,
35 "target_layer_ids": [2, 23, 47, 71, 89],
36 "markov_rank": 256,
37 "draft_vocab_size": 163840,
38 "torch_dtype": "bfloat16",
39 }
40 config.update(overrides)
41 (path / "config.json").write_text(json.dumps(config))
42
43
44 def _write_target_config(path):
45 path.mkdir()
46 config = {
47 "architectures": ["LlamaForCausalLM"],
48 "model_type": "llama",
49 "hidden_size": 7168,
50 "intermediate_size": 14336,
51 "num_hidden_layers": 93,
52 "num_attention_heads": 56,
53 "num_key_value_heads": 8,
54 "vocab_size": 163840,
55 "max_position_embeddings": 32768,
56 "torch_dtype": "bfloat16",
57 }
58 (path / "config.json").write_text(json.dumps(config))
59
60
61 def test_dspark_mla_config_loads_from_local_json(tmp_path):
62 draft_path = tmp_path / "draft"
63 _write_dspark_config(draft_path)
64
65 config = get_config(draft_path, trust_remote_code=False)
66
67 assert isinstance(config, K3DSparkConfig)
68 assert config.model_type == "k3_dspark"
69 assert config.architectures == ["K3DSparkModel"]
70 assert config.hidden_act == "silu"
71 assert config.rope_parameters == {
72 "rope_type": "default",
73 "rope_theta": 50000.0,
74 }
75 assert config.n_routed_experts == 0
76 assert config.draft_vocab_size == config.vocab_size
77
78
79 @pytest.mark.parametrize(
80 "overrides",
81 [
82 {"mla_use_nope": True},
83 {"mla_use_output_gate": True},
84 {"mla_use_qk_norm": True},
85 {"dspark_bonus_anchor": True},
86 {"q_lora_rank": None},
87 {"draft_vocab_size": 8192},
88 {"target_layer_ids": []},
89 {"num_target_layers": 4},
90 ],
91 )
92 def test_dspark_mla_rejects_unsupported_checkpoint_options(tmp_path, overrides):
93 draft_path = tmp_path / "draft"
94 _write_dspark_config(draft_path, **overrides)
95
96 with pytest.raises(ValueError, match="MLA DSpark"):
97 get_config(draft_path, trust_remote_code=False)
98
99
100 @pytest.mark.parametrize(
101 ("num_attention_heads", "expected_local_heads"),
102 [(64, 8), (96, 12)],
103 ids=["released-64-head", "block5-96-head"],
104 )
105 def test_dspark_mla_uses_latent_kv_geometry(
106 tmp_path, num_attention_heads, expected_local_heads
107 ):
108 draft_path = tmp_path / "draft"
109 _write_dspark_config(
110 draft_path,
111 num_attention_heads=num_attention_heads,
112 num_key_value_heads=num_attention_heads,
113 )
114 model_config = ModelConfig(
115 model=str(draft_path),
116 tokenizer_mode="skip",
117 runner="draft",
118 max_model_len=32768,
119 )
120
121 assert model_config.is_deepseek_mla
122 assert model_config.use_mla
123 assert model_config.get_head_size() == 576
124 # external_launcher skips ParallelConfig's local-GPU-count check so the
125 # config logic can be exercised at TP8 on a single-GPU test node.
126 parallel_config = ParallelConfig(
127 tensor_parallel_size=8, distributed_executor_backend="external_launcher"
128 )
129 assert model_config.get_num_kv_heads(parallel_config) == 1
130 assert model_config.get_num_attention_heads(parallel_config) == expected_local_heads
131 assert model_config.get_num_experts() == 0
132
133
134 def test_dspark_mla_speculative_config_preserves_architecture(tmp_path):
135 target_path = tmp_path / "target"
136 draft_path = tmp_path / "draft"
137 _write_target_config(target_path)
138 _write_dspark_config(draft_path)
139 target_config = ModelConfig(
140 model=str(target_path), tokenizer_mode="skip", max_model_len=32768
141 )
142 speculative_config = SpeculativeConfig(
143 model=str(draft_path),
144 method="dspark",
145 num_speculative_tokens=8,
146 target_model_config=target_config,
147 target_parallel_config=ParallelConfig(),
148 )
149
150 assert speculative_config.parallel_drafting
151 assert speculative_config.draft_model_config.architectures == ["K3DSparkModel"]
152 assert speculative_config.draft_model_config.hf_config.model_type == "k3_dspark"
153 assert speculative_config.draft_model_config.use_mla