main
py 357 lines 10.6 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import tempfile
5 from collections import OrderedDict
6 from importlib import reload
7 from unittest.mock import MagicMock
8
9 import pytest
10 import torch
11 import torch.nn as nn
12
13 from vllm.distributed import (
14 cleanup_dist_env_and_memory,
15 init_distributed_environment,
16 initialize_model_parallel,
17 )
18 from vllm.model_executor.layers.linear import (
19 ColumnParallelLinear,
20 MergedColumnParallelLinear,
21 RowParallelLinear,
22 )
23 from vllm.model_executor.layers.logits_processor import LogitsProcessor
24 from vllm.model_executor.layers.vocab_parallel_embedding import ParallelLMHead
25 from vllm.model_executor.models.interfaces import SupportsLoRA
26 from vllm.platforms import current_platform
27 from vllm.transformers_utils.repo_utils import hf_api
28
29
30 @pytest.fixture()
31 def should_do_global_cleanup_after_test(request) -> bool:
32 """Allow subdirectories to skip global cleanup by overriding this fixture.
33 This can provide a ~10x speedup for non-GPU unit tests since they don't need
34 to initialize torch.
35 """
36 return not request.node.get_closest_marker("skip_global_cleanup")
37
38
39 @pytest.fixture(autouse=True)
40 def cleanup_fixture(should_do_global_cleanup_after_test: bool):
41 yield
42 if should_do_global_cleanup_after_test:
43 cleanup_dist_env_and_memory(shutdown_ray=True)
44
45
46 @pytest.fixture
47 def maybe_enable_lora_dual_stream(monkeypatch: pytest.MonkeyPatch):
48 if current_platform.is_cuda():
49 monkeypatch.setenv("VLLM_LORA_ENABLE_DUAL_STREAM", "1")
50 import vllm.lora.layers.base_linear
51
52 if not hasattr(vllm.lora.layers.base_linear, "lora_linear_async"):
53 # Reload the module to ensure the environment variable takes effect.
54 reload(vllm.lora.layers.base_linear)
55 yield
56
57
58 @pytest.fixture
59 def dist_init():
60 from tests.utils import ensure_current_vllm_config
61
62 temp_file = tempfile.mkstemp()[1]
63
64 backend = "gloo" if current_platform.is_tpu() else current_platform.dist_backend
65
66 with ensure_current_vllm_config():
67 init_distributed_environment(
68 world_size=1,
69 rank=0,
70 distributed_init_method=f"file://{temp_file}",
71 local_rank=0,
72 backend=backend,
73 )
74 initialize_model_parallel(1, 1)
75 yield
76 cleanup_dist_env_and_memory(shutdown_ray=True)
77
78
79 class DummyLoRAModel(nn.Sequential, SupportsLoRA):
80 pass
81
82
83 @pytest.fixture
84 def dummy_model(default_vllm_config) -> nn.Module:
85 model = DummyLoRAModel(
86 OrderedDict(
87 [
88 ("dense1", ColumnParallelLinear(764, 100)),
89 ("dense2", RowParallelLinear(100, 50)),
90 (
91 "layer1",
92 nn.Sequential(
93 OrderedDict(
94 [
95 ("dense1", ColumnParallelLinear(100, 10)),
96 ("dense2", RowParallelLinear(10, 50)),
97 ]
98 )
99 ),
100 ),
101 ("act2", nn.ReLU()),
102 ("output", ColumnParallelLinear(50, 10)),
103 ("outact", nn.Sigmoid()),
104 # Special handling for lm_head & sampler
105 ("lm_head", ParallelLMHead(32064, 10)),
106 ("logits_processor", LogitsProcessor(32064)),
107 ]
108 )
109 )
110 model.config = MagicMock()
111 model.embedding_modules = {"lm_head": "lm_head"}
112 model.unpadded_vocab_size = 32064
113 return model
114
115
116 @pytest.fixture
117 def dummy_model_gate_up(default_vllm_config) -> nn.Module:
118 model = DummyLoRAModel(
119 OrderedDict(
120 [
121 ("dense1", ColumnParallelLinear(764, 100)),
122 ("dense2", RowParallelLinear(100, 50)),
123 (
124 "layer1",
125 nn.Sequential(
126 OrderedDict(
127 [
128 ("dense1", ColumnParallelLinear(100, 10)),
129 ("dense2", RowParallelLinear(10, 50)),
130 ]
131 )
132 ),
133 ),
134 ("act2", nn.ReLU()),
135 ("gate_up_proj", MergedColumnParallelLinear(50, [5, 5])),
136 ("outact", nn.Sigmoid()),
137 # Special handling for lm_head & sampler
138 ("lm_head", ParallelLMHead(32064, 10)),
139 ("logits_processor", LogitsProcessor(32064)),
140 ]
141 )
142 )
143 model.config = MagicMock()
144 model.packed_modules_mapping = {
145 "gate_up_proj": [
146 "gate_proj",
147 "up_proj",
148 ],
149 }
150 model.embedding_modules = {"lm_head": "lm_head"}
151 model.unpadded_vocab_size = 32064
152
153 return model
154
155
156 @pytest.fixture(scope="session")
157 def mixtral_lora_files():
158 # Note: this module has incorrect adapter_config.json to test
159 # https://github.com/vllm-project/vllm/pull/5909/files.
160 return hf_api().snapshot_download(repo_id="SangBinCho/mixtral-lora")
161
162
163 @pytest.fixture(scope="session")
164 def chatglm3_lora_files():
165 return hf_api().snapshot_download(repo_id="jeeejeee/chatglm3-text2sql-spider")
166
167
168 @pytest.fixture(scope="session")
169 def baichuan_lora_files():
170 return hf_api().snapshot_download(repo_id="jeeejeee/baichuan7b-text2sql-spider")
171
172
173 @pytest.fixture(scope="session")
174 def baichuan_zero_lora_files():
175 # all the lora_B weights are initialized to zero.
176 return hf_api().snapshot_download(repo_id="jeeejeee/baichuan7b-zero-init")
177
178
179 @pytest.fixture(scope="session")
180 def baichuan_regex_lora_files():
181 return hf_api().snapshot_download(repo_id="jeeejeee/baichuan-7b-lora-zero-regex")
182
183
184 @pytest.fixture(scope="session")
185 def ilama_lora_files():
186 return hf_api().snapshot_download(repo_id="jeeejeee/ilama-text2sql-spider")
187
188
189 @pytest.fixture(scope="session")
190 def minicpmv_lora_files():
191 return hf_api().snapshot_download(repo_id="jeeejeee/minicpmv25-lora-pokemon")
192
193
194 @pytest.fixture(scope="session")
195 def qwen2vl_lora_files():
196 return hf_api().snapshot_download(repo_id="jeeejeee/qwen2-vl-lora-pokemon")
197
198
199 @pytest.fixture(scope="session")
200 def qwen25vl_lora_files():
201 return hf_api().snapshot_download(repo_id="jeeejeee/qwen25-vl-lora-pokemon")
202
203
204 @pytest.fixture(scope="session")
205 def qwen2vl_language_lora_files():
206 return hf_api().snapshot_download(
207 repo_id="prashanth058/qwen2vl-flickr-lora-language"
208 )
209
210
211 @pytest.fixture(scope="session")
212 def qwen2vl_vision_tower_connector_lora_files():
213 return hf_api().snapshot_download(
214 repo_id="prashanth058/qwen2vl-flickr-lora-tower-connector"
215 )
216
217
218 @pytest.fixture(scope="session")
219 def qwen2vl_vision_tower_lora_files():
220 return hf_api().snapshot_download(repo_id="prashanth058/qwen2vl-flickr-lora-tower")
221
222
223 @pytest.fixture(scope="session")
224 def qwen25vl_vision_lora_files():
225 return hf_api().snapshot_download(
226 repo_id="EpochEcho/qwen2.5-3b-vl-lora-vision-connector"
227 )
228
229
230 @pytest.fixture(scope="session")
231 def qwen3vl_vision_lora_files():
232 return hf_api().snapshot_download(
233 repo_id="EpochEcho/qwen3-4b-vl-lora-vision-connector"
234 )
235
236
237 @pytest.fixture(scope="session")
238 def gemma4_vision_lora_files():
239 return hf_api().snapshot_download(repo_id="EpochEcho/gemma4-e2b-it-lora-pokemon")
240
241
242 @pytest.fixture(scope="session")
243 def qwen3_meowing_lora_files():
244 """Download Qwen3 Meow LoRA files once per test session."""
245 return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Meow-LoRA")
246
247
248 @pytest.fixture(scope="session")
249 def qwen3_woofing_lora_files():
250 """Download Qwen3 Woof LoRA files once per test session."""
251 return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Woof-LoRA")
252
253
254 @pytest.fixture(scope="session")
255 def qwen3_guard_star_trek_lora_files():
256 return hf_api().snapshot_download(
257 repo_id="geoffmunn/Qwen3Guard-StarTrek-Classification-0.6B",
258 allow_patterns=["adapter_config.json", "adapter_model.safetensors"],
259 )
260
261
262 @pytest.fixture(scope="session")
263 def qwen3_guard_new_zealand_lora_files():
264 return hf_api().snapshot_download(
265 repo_id="geoffmunn/Qwen3Guard-NewZealand-Classification-0.6B",
266 allow_patterns=["adapter_config.json", "adapter_model.safetensors"],
267 )
268
269
270 @pytest.fixture(scope="session")
271 def skywork_qwen3_reward_lora_files():
272 return hf_api().snapshot_download(
273 repo_id="AmirMohseni/skywork-qwen3-0.6b-reward-lora",
274 allow_patterns=["adapter_config.json", "adapter_model.safetensors"],
275 )
276
277
278 @pytest.fixture(scope="session")
279 def deepseekv2_lora_files():
280 return hf_api().snapshot_download(repo_id="wuchen01/DeepSeek-V2-Lite-Chat-All-LoRA")
281
282
283 @pytest.fixture(scope="session")
284 def gptoss20b_lora_files():
285 return hf_api().snapshot_download(
286 repo_id="jeeejeee/gpt-oss-20b-lora-adapter-text2sql"
287 )
288
289
290 @pytest.fixture(scope="session")
291 def qwen3moe_lora_files():
292 return hf_api().snapshot_download(repo_id="jeeejeee/qwen3-moe-text2sql-spider")
293
294
295 @pytest.fixture(scope="session")
296 def olmoe_lora_files():
297 return hf_api().snapshot_download(repo_id="jeeejeee/olmoe-instruct-text2sql-spider")
298
299
300 @pytest.fixture(scope="session")
301 def qwen3_lora_files():
302 return hf_api().snapshot_download(repo_id="charent/self_cognition_Alice")
303
304
305 @pytest.fixture(scope="session")
306 def llama32_lora_huggingface_id():
307 # huggingface repo id is used to test lora runtime downloading.
308 return "jeeejeee/llama32-3b-text2sql-spider"
309
310
311 @pytest.fixture(scope="session")
312 def llama32_lora_files(llama32_lora_huggingface_id):
313 return hf_api().snapshot_download(repo_id=llama32_lora_huggingface_id)
314
315
316 @pytest.fixture(scope="session")
317 def whisper_lora_files():
318 return hf_api().snapshot_download(
319 repo_id="chengyili2005/whisper-small-mandarin-lora"
320 )
321
322
323 @pytest.fixture(scope="session")
324 def qwen35_text_lora_files():
325 return hf_api().snapshot_download(repo_id="jeeejeee/qwen35-4b-text-only-sql-lora")
326
327
328 @pytest.fixture(scope="session")
329 def qwen35_vl_lora_files():
330 return hf_api().snapshot_download(
331 repo_id="jeeejeee/qwen35-4b-all-linear-pokemon-lora"
332 )
333
334
335 @pytest.fixture(scope="session")
336 def qwen36_moe_2d_lora_files():
337 return hf_api().snapshot_download(
338 repo_id="jeeejeee/qwen36-35ba3b-2d-weights-poken-lora"
339 )
340
341
342 @pytest.fixture(scope="session")
343 def qwen36_moe_3d_lora_files():
344 return hf_api().snapshot_download(
345 repo_id="jeeejeee/qwen36-35ba3b-moe-all-linear-poken-lora"
346 )
347
348
349 @pytest.fixture
350 def reset_default_device():
351 """Some tests, such as `test_punica_ops.py`, explicitly set the
352 default device, which can affect subsequent tests. Adding this fixture
353 helps avoid this problem.
354 """
355 original_device = torch.get_default_device()
356 yield
357 torch.set_default_device(original_device)