main
py 121 lines 3.93 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """CPU-only tests for Qwen3.5 MTP speculative decoding config overrides."""
4
5 from typing import Any
6
7 import pytest
8 from transformers import AutoConfig, PreTrainedConfig
9
10 from vllm.config.speculative import SpeculativeConfig
11
12 _CHECKPOINTS = {
13 "qwen3_5": "Qwen/Qwen3.8-27B",
14 "qwen3_5_moe": "Qwen/Qwen3.6-35B-A3B",
15 }
16
17
18 def _mtp_config(model_type: str) -> PreTrainedConfig:
19 """Create a top-level MTP configuration with mtp_num_hidden_layers."""
20 kwargs: dict[str, Any] = {
21 "model_type": model_type,
22 "architectures": ["SomeArch"],
23 "mtp_num_hidden_layers": 1,
24 }
25 return PreTrainedConfig(**kwargs)
26
27
28 def _multimodal_wrapper_mtp_config(
29 model_type: str, mtp_layers: int = 1
30 ) -> PreTrainedConfig:
31 """Download a multimodal wrapper config via AutoConfig.from_pretrained
32 and configure mtp_num_hidden_layers in text_config.
33
34 Uses real-world Hugging Face Hub checkpoints:
35 - Dense (qwen3_5): Qwen/Qwen3.8-27B
36 - MoE (qwen3_5_moe): Qwen/Qwen3.6-35B-A3B
37 """
38 repo = _CHECKPOINTS[model_type]
39 config: PreTrainedConfig = AutoConfig.from_pretrained(repo)
40 text_config = config.get_text_config()
41 text_config.mtp_num_hidden_layers = mtp_layers
42 return config
43
44
45 @pytest.mark.parametrize(
46 "model_type,expected_arch",
47 [
48 ("qwen3_5", "Qwen3_5MTP"),
49 ("qwen3_5_moe", "Qwen3_5MoeMTP"),
50 # Text-only config variants must map to the same MTP architectures.
51 ("qwen3_5_text", "Qwen3_5MTP"),
52 ("qwen3_5_moe_text", "Qwen3_5MoeMTP"),
53 ],
54 )
55 def test_mtp_override_recognizes_text_only_types(
56 model_type: str, expected_arch: str
57 ) -> None:
58 """Verify that text-only config variants map to the expected MTP architectures."""
59 cfg = SpeculativeConfig.hf_config_override(_mtp_config(model_type))
60 assert cfg.model_type == "qwen3_5_mtp"
61 assert cfg.architectures == [expected_arch]
62 assert cfg.n_predict == 1
63
64
65 @pytest.mark.parametrize(
66 "model_type,expected_arch",
67 [
68 ("qwen3_5", "Qwen3_5MTP"),
69 ("qwen3_5_moe", "Qwen3_5MoeMTP"),
70 ],
71 )
72 def test_mtp_override_extracts_n_predict_from_multimodal_wrapper(
73 model_type: str, expected_arch: str
74 ) -> None:
75 """Verify that multimodal wrapper checkpoints with mtp_num_hidden_layers
76 in text_config resolve n_predict and architecture correctly."""
77 cfg = SpeculativeConfig.hf_config_override(
78 _multimodal_wrapper_mtp_config(model_type, mtp_layers=2)
79 )
80 assert cfg.model_type == "qwen3_5_mtp"
81 assert cfg.architectures == [expected_arch]
82 assert cfg.n_predict == 2
83
84
85 @pytest.mark.parametrize(
86 "model_type,expected_arch",
87 [
88 ("qwen3_5", "Qwen3_5MTP"),
89 ("qwen3_5_moe", "Qwen3_5MoeMTP"),
90 ],
91 )
92 def test_mtp_override_top_level_precedence_over_nested_text_config(
93 model_type: str, expected_arch: str
94 ) -> None:
95 """Verify that an explicit top-level mtp_num_hidden_layers takes precedence
96 over a nested text_config value."""
97 cfg = _multimodal_wrapper_mtp_config(model_type, mtp_layers=2)
98 cfg.mtp_num_hidden_layers = 3
99 overridden = SpeculativeConfig.hf_config_override(cfg)
100 assert overridden.model_type == "qwen3_5_mtp"
101 assert overridden.architectures == [expected_arch]
102 assert overridden.n_predict == 3
103
104
105 @pytest.mark.parametrize(
106 "model_id,expected_arch",
107 [
108 ("Qwen/Qwen3.8-27B", "Qwen3_5MTP"),
109 ("Qwen/Qwen3.6-35B-A3B", "Qwen3_5MoeMTP"),
110 ],
111 )
112 def test_mtp_override_downloads_real_hf_hub_configs(
113 model_id: str, expected_arch: str
114 ) -> None:
115 """Verify that unmodified real-world checkpoints downloaded via
116 AutoConfig.from_pretrained resolve n_predict=1 from text_config."""
117 hf_config = AutoConfig.from_pretrained(model_id)
118 cfg = SpeculativeConfig.hf_config_override(hf_config)
119 assert cfg.model_type == "qwen3_5_mtp"
120 assert cfg.architectures == [expected_arch]
121 assert cfg.n_predict == 1