Make bare `hugging_face` imports forbidden (#49726)

Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com>

Harry Mellor committed Jul 25, 2026 at 12:18 UTC ca0defa3438832bb3fc5826d8bf2d4e572c57e3d
29 files changed +199 -106
tests/conftest.py
+4 -4
@@ -31,7 +31,7 @@ import pytest
31 import torch
32 import torch.nn as nn
33 import torch.nn.functional as F
34 -from huggingface_hub import snapshot_download
34 +from vllm.transformers_utils.repo_utils import hf_api
35 from PIL import Image
36 from transformers import (
37 AutoConfig,
@@ -1496,7 +1496,7 @@ _dummy_gemma2_embedding_path = os.path.join(temp_dir, "dummy_gemma2_embedding")
1496 def dummy_opt_path():
1497 json_path = os.path.join(_dummy_opt_path, "config.json")
1498 if not os.path.exists(_dummy_opt_path):
1499 - snapshot_download(
1499 + hf_api().snapshot_download(
1500 repo_id="facebook/opt-125m",
1501 local_dir=_dummy_opt_path,
1502 ignore_patterns=["*.bin", "*.bin.index.json", "*.pt", "*.h5", "*.msgpack"],
@@ -1514,7 +1514,7 @@ def dummy_opt_path():
1514 def dummy_llava_path():
1515 json_path = os.path.join(_dummy_llava_path, "config.json")
1516 if not os.path.exists(_dummy_llava_path):
1517 - snapshot_download(
1517 + hf_api().snapshot_download(
1518 repo_id="llava-hf/llava-1.5-7b-hf",
1519 local_dir=_dummy_llava_path,
1520 ignore_patterns=[
@@ -1539,7 +1539,7 @@ def dummy_llava_path():
1539 def dummy_gemma2_embedding_path():
1540 json_path = os.path.join(_dummy_gemma2_embedding_path, "config.json")
1541 if not os.path.exists(_dummy_gemma2_embedding_path):
1542 - snapshot_download(
1542 + hf_api().snapshot_download(
1543 repo_id="BAAI/bge-multilingual-gemma2",
1544 local_dir=_dummy_gemma2_embedding_path,
1545 ignore_patterns=[
tests/distributed/test_rocm_quick_reduce.py
+3 -3
@@ -15,12 +15,12 @@ from unittest.mock import patch
15 import pytest
16 import torch
17 import torch.distributed as dist
18 -from huggingface_hub import snapshot_download
18
19 import vllm.envs as envs
20 from vllm import LLM, SamplingParams
21 from vllm.distributed import cleanup_dist_env_and_memory
22 from vllm.platforms import current_platform
23 +from vllm.transformers_utils.repo_utils import hf_api
24 from vllm.utils.network_utils import get_open_port
25
26 pytestmark = pytest.mark.skipif(
@@ -210,11 +210,11 @@ def _log_prompt_summaries() -> None:
210 @lru_cache(maxsize=1)
211 def _get_model_path() -> str:
212 try:
213 - path = snapshot_download(repo_id=MODEL_NAME, local_files_only=True)
213 + path = hf_api().snapshot_download(repo_id=MODEL_NAME, local_files_only=True)
214 _log(f"using cached model snapshot: {path}")
215 return path
216 except Exception:
217 - path = snapshot_download(repo_id=MODEL_NAME)
217 + path = hf_api().snapshot_download(repo_id=MODEL_NAME)
218 _log(f"downloaded model snapshot: {path}")
219 return path
220
tests/entrypoints/conftest.py
+10 -8
@@ -190,30 +190,32 @@ number: "1" | "2"
190 @pytest.fixture(scope="session")
191 def qwen3_lora_files():
192 """Download Qwen3 LoRA files once per test session."""
193 - from huggingface_hub import snapshot_download
193 + from vllm.transformers_utils.repo_utils import hf_api
194
195 - return snapshot_download(repo_id="charent/self_cognition_Alice")
195 + return hf_api().snapshot_download(repo_id="charent/self_cognition_Alice")
196
197
198 @pytest.fixture(scope="session")
199 def qwen3_meowing_lora_files():
200 """Download Qwen3 LoRA files once per test session."""
201 - from huggingface_hub import snapshot_download
201 + from vllm.transformers_utils.repo_utils import hf_api
202
203 - return snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Meow-LoRA")
203 + return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Meow-LoRA")
204
205
206 @pytest.fixture(scope="session")
207 def qwen3_woofing_lora_files():
208 """Download Qwen3 LoRA files once per test session."""
209 - from huggingface_hub import snapshot_download
209 + from vllm.transformers_utils.repo_utils import hf_api
210
211 - return snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Woof-LoRA")
211 + return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Woof-LoRA")
212
213
214 @pytest.fixture(scope="session")
215 def opt125_lora_files() -> str:
216 """Download opt-125m LoRA files once per test session."""
217 - from huggingface_hub import snapshot_download
217 + from vllm.transformers_utils.repo_utils import hf_api
218
219 - return snapshot_download(repo_id="peft-internal-testing/opt-125m-dummy-lora")
219 + return hf_api().snapshot_download(
220 + repo_id="peft-internal-testing/opt-125m-dummy-lora"
221 + )
tests/entrypoints/multimodal/openai/chat_completion/test_chat_completion_with_mixed_audio_embeds.py
+5 -3
@@ -12,10 +12,10 @@ import pytest_asyncio
12 import safetensors
13 import torch
14 import torch.nn as nn
15 -from huggingface_hub import hf_hub_download
15 from transformers import AutoConfig, AutoTokenizer
16
17 from tests.utils import RemoteOpenAIServer
18 +from vllm.transformers_utils.repo_utils import hf_api
19 from vllm.utils.serial_utils import tensor2base64
20 from vllm.utils.torch_utils import is_torch_equal_or_newer
21
@@ -126,11 +126,13 @@ def qwen2audio_aligned_content_and_embeds_b64() -> tuple[str, str]:
126 content = "Describe this audio."
127 tokenizer = AutoTokenizer.from_pretrained(QWEN2AUDIO_MODEL, trust_remote_code=True)
128
129 - index_path = hf_hub_download(QWEN2AUDIO_MODEL, "model.safetensors.index.json")
129 + index_path = hf_api().hf_hub_download(
130 + QWEN2AUDIO_MODEL, "model.safetensors.index.json"
131 + )
132 with open(index_path) as f:
133 weight_map = json.load(f)["weight_map"]
134 embed_key = next(k for k in weight_map if k.endswith("embed_tokens.weight"))
133 - shard_path = hf_hub_download(QWEN2AUDIO_MODEL, weight_map[embed_key])
135 + shard_path = hf_api().hf_hub_download(QWEN2AUDIO_MODEL, weight_map[embed_key])
136 with safetensors.safe_open(shard_path, framework="pt", device="cpu") as f:
137 embed_weight = f.get_tensor(embed_key)
138 embed_layer = nn.Embedding.from_pretrained(embed_weight.to(QWEN2AUDIO_DTYPE))
tests/entrypoints/multimodal/openai/chat_completion/test_chat_completion_with_mixed_image_embeds.py
+3 -3
@@ -13,12 +13,12 @@ import pytest_asyncio
13 import safetensors
14 import torch
15 import torch.nn as nn
16 -from huggingface_hub import hf_hub_download
16 from transformers import AutoTokenizer
17
18 from tests.utils import RemoteOpenAIServer
19 from vllm.assets.image import ImageAsset
20 from vllm.multimodal.utils import encode_image_url
21 +from vllm.transformers_utils.repo_utils import hf_api
22 from vllm.utils.serial_utils import tensor2base64
23
24 MODEL_NAME = "Qwen/Qwen2-VL-2B-Instruct"
@@ -84,11 +84,11 @@ def aligned_content_and_embeds_b64() -> tuple[str, str]:
84 content = "Describe this image."
85 tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
86
87 - index_path = hf_hub_download(MODEL_NAME, "model.safetensors.index.json")
87 + index_path = hf_api().hf_hub_download(MODEL_NAME, "model.safetensors.index.json")
88 with open(index_path) as f:
89 weight_map = json.load(f)["weight_map"]
90 embed_key = next(k for k in weight_map if k.endswith("embed_tokens.weight"))
91 - shard_path = hf_hub_download(MODEL_NAME, weight_map[embed_key])
91 + shard_path = hf_api().hf_hub_download(MODEL_NAME, weight_map[embed_key])
92 with safetensors.safe_open(shard_path, framework="pt", device="cpu") as f:
93 embed_weight = f.get_tensor(embed_key)
94 embed_layer = nn.Embedding.from_pretrained(embed_weight.to(MODEL_DTYPE))
tests/entrypoints/multimodal/openai/chat_completion/test_default_mm_loras.py
+4 -2
@@ -6,17 +6,19 @@ import os
6 import openai # use the official client for correctness check
7 import pytest
8 import pytest_asyncio
9 -from huggingface_hub import snapshot_download
9
10 from tests.conftest import AudioTestAssets
11 from tests.utils import RemoteOpenAIServer
12 +from vllm.transformers_utils.repo_utils import hf_api
13
14 # NOTE - the tests in this module are currently analogous to test_chat, but are
15 # separated to avoid OOM killing due to module-scoped servers, since we
16 # need a multimodal model for these tests.
17
18 # Contains a modality specific lora alongside the base model
19 -MULTIMODAL_MODEL_NAME = snapshot_download("microsoft/Phi-4-multimodal-instruct")
19 +MULTIMODAL_MODEL_NAME = hf_api().snapshot_download(
20 + "microsoft/Phi-4-multimodal-instruct"
21 +)
22 AUDIO_LORA_PATH = os.path.join(MULTIMODAL_MODEL_NAME, "speech-lora")
23
24 ACTIVE_MM_LORA_RESPONSE = "Spoken text: The first words I spoke in the original chronograph, a little piece of practical poetry. Mary had a little lamb, it slept with quite a snow, and everywhere that Mary went, the lamb was sure to go." # noqa: E501
tests/entrypoints/openai/chat_completion/test_chat.py
+2 -2
@@ -27,9 +27,9 @@ MODEL_NAME = "HuggingFaceH4/zephyr-7b-beta"
27 @pytest.fixture(scope="module")
28 def zephyr_lora_files():
29 """Download zephyr LoRA files once per test session."""
30 - from huggingface_hub import snapshot_download
30 + from vllm.transformers_utils.repo_utils import hf_api
31
32 - return snapshot_download(repo_id="typeof/zephyr-7b-beta-lora")
32 + return hf_api().snapshot_download(repo_id="typeof/zephyr-7b-beta-lora")
33
34
35 @pytest.fixture(scope="module")
tests/entrypoints/pooling/scoring/util.py
+2 -2
@@ -6,7 +6,6 @@ from io import BytesIO
6 import pybase64 as base64
7 import torch
8 import torch.nn.functional as F
9 -from huggingface_hub import hf_hub_download
9 from PIL import Image
10 from safetensors.torch import load_file
11 from transformers import AutoModel, AutoTokenizer
@@ -18,6 +17,7 @@ from vllm.entrypoints.chat_utils import (
17 )
18 from vllm.entrypoints.pooling.scoring.typing import ScoreMultiModalParam
19 from vllm.entrypoints.pooling.scoring.utils import compute_maxsim_score
20 +from vllm.transformers_utils.repo_utils import hf_api
21
22
23 class ColBERTScoringHfRunner(torch.nn.Module):
@@ -38,7 +38,7 @@ class ColBERTScoringHfRunner(torch.nn.Module):
38 ).to(self.device)
39 self.model.eval()
40
41 - path = hf_hub_download(model_name, filename="model.safetensors")
41 + path = hf_api().hf_hub_download(model_name, filename="model.safetensors")
42 weights = load_file(path)
43
44 self.linear_weight = weights[linear_weights_key].to(self.device).float()
tests/entrypoints/serve/sagemaker/conftest.py
+2 -2
@@ -21,9 +21,9 @@ HEADER_SAGEMAKER_NEW_SESSION_ID = "X-Amzn-SageMaker-New-Session-Id"
21 @pytest.fixture(scope="session")
22 def smollm2_lora_files():
23 """Download LoRA files once per test session."""
24 - from huggingface_hub import snapshot_download
24 + from vllm.transformers_utils.repo_utils import hf_api
25
26 - return snapshot_download(repo_id=LORA_ADAPTER_NAME_SMOLLM)
26 + return hf_api().snapshot_download(repo_id=LORA_ADAPTER_NAME_SMOLLM)
27
28
29 @pytest.fixture(scope="module")
tests/entrypoints/tool_parsers/test_hermes_tool_parser.py
+2 -2
@@ -6,13 +6,13 @@ import json
6 import openai
7 import pytest
8 import pytest_asyncio
9 -from huggingface_hub import snapshot_download
9 from typing_extensions import TypedDict
10
11 from tests.utils import RemoteOpenAIServer
12 from vllm.tool_parsers.abstract_tool_parser import ToolParser
13 from vllm.tool_parsers.granite4_tool_parser import Granite4ToolParser
14 from vllm.tool_parsers.hermes_tool_parser import Hermes2ProToolParser
15 +from vllm.transformers_utils.repo_utils import hf_api
16
17 LORA_MODEL = "minpeter/LoRA-Llama-3.2-1B-tool-vllm-ci"
18
@@ -91,7 +91,7 @@ def server_config(request):
91 config = CONFIGS[request.param]
92
93 # download model and tokenizer using transformers
94 - snapshot_download(config["model"])
94 + hf_api().snapshot_download(config["model"])
95 yield CONFIGS[request.param]
96
97
tests/kernels/quantization/test_nvfp4_emulation.py
+2 -2
@@ -2,7 +2,6 @@
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from typing import cast
4
5 -import huggingface_hub
5 import pytest
6 import torch
7 from safetensors import safe_open
@@ -33,6 +32,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
32 kNvfp4Static,
33 )
34 from vllm.platforms import current_platform
35 +from vllm.transformers_utils.repo_utils import hf_api
36 from vllm.triton_utils import triton
37
38 if current_platform.is_rocm():
@@ -56,7 +56,7 @@ MOE_MODEL_CONFIGS = {
56 @pytest.fixture(scope="module")
57 def loaded_model_files():
58 return {
59 - model_id: huggingface_hub.snapshot_download(
59 + model_id: hf_api().snapshot_download(
60 repo_id=model_id, allow_patterns=config["shards"]
61 )
62 for model_id, config in MOE_MODEL_CONFIGS.items()
tests/lora/conftest.py
+47 -29
@@ -9,7 +9,6 @@ from unittest.mock import MagicMock
9 import pytest
10 import torch
11 import torch.nn as nn
12 -from huggingface_hub import snapshot_download
12
13 from vllm.distributed import (
14 cleanup_dist_env_and_memory,
@@ -25,6 +24,7 @@ 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()
@@ -170,43 +170,43 @@ def dummy_model_gate_up(default_vllm_config) -> nn.Module:
170 def mixtral_lora_files():
171 # Note: this module has incorrect adapter_config.json to test
172 # https://github.com/vllm-project/vllm/pull/5909/files.
173 - return snapshot_download(repo_id="SangBinCho/mixtral-lora")
173 + return hf_api().snapshot_download(repo_id="SangBinCho/mixtral-lora")
174
175
176 @pytest.fixture(scope="session")
177 def chatglm3_lora_files():
178 - return snapshot_download(repo_id="jeeejeee/chatglm3-text2sql-spider")
178 + return hf_api().snapshot_download(repo_id="jeeejeee/chatglm3-text2sql-spider")
179
180
181 @pytest.fixture(scope="session")
182 def baichuan_lora_files():
183 - return snapshot_download(repo_id="jeeejeee/baichuan7b-text2sql-spider")
183 + return hf_api().snapshot_download(repo_id="jeeejeee/baichuan7b-text2sql-spider")
184
185
186 @pytest.fixture(scope="session")
187 def baichuan_zero_lora_files():
188 # all the lora_B weights are initialized to zero.
189 - return snapshot_download(repo_id="jeeejeee/baichuan7b-zero-init")
189 + return hf_api().snapshot_download(repo_id="jeeejeee/baichuan7b-zero-init")
190
191
192 @pytest.fixture(scope="session")
193 def baichuan_regex_lora_files():
194 - return snapshot_download(repo_id="jeeejeee/baichuan-7b-lora-zero-regex")
194 + return hf_api().snapshot_download(repo_id="jeeejeee/baichuan-7b-lora-zero-regex")
195
196
197 @pytest.fixture(scope="session")
198 def ilama_lora_files():
199 - return snapshot_download(repo_id="jeeejeee/ilama-text2sql-spider")
199 + return hf_api().snapshot_download(repo_id="jeeejeee/ilama-text2sql-spider")
200
201
202 @pytest.fixture(scope="session")
203 def minicpmv_lora_files():
204 - return snapshot_download(repo_id="jeeejeee/minicpmv25-lora-pokemon")
204 + return hf_api().snapshot_download(repo_id="jeeejeee/minicpmv25-lora-pokemon")
205
206
207 @pytest.fixture(scope="session")
208 def qwen2vl_lora_files():
209 - return snapshot_download(repo_id="jeeejeee/qwen2-vl-lora-pokemon")
209 + return hf_api().snapshot_download(repo_id="jeeejeee/qwen2-vl-lora-pokemon")
210
211
212 @pytest.fixture(scope="session")
@@ -217,74 +217,84 @@ def qwen25vl_base_huggingface_id():
217
218 @pytest.fixture(scope="session")
219 def qwen25vl_lora_files():
220 - return snapshot_download(repo_id="jeeejeee/qwen25-vl-lora-pokemon")
220 + return hf_api().snapshot_download(repo_id="jeeejeee/qwen25-vl-lora-pokemon")
221
222
223 @pytest.fixture(scope="session")
224 def qwen2vl_language_lora_files():
225 - return snapshot_download(repo_id="prashanth058/qwen2vl-flickr-lora-language")
225 + return hf_api().snapshot_download(
226 + repo_id="prashanth058/qwen2vl-flickr-lora-language"
227 + )
228
229
230 @pytest.fixture(scope="session")
231 def qwen2vl_vision_tower_connector_lora_files():
230 - return snapshot_download(repo_id="prashanth058/qwen2vl-flickr-lora-tower-connector")
232 + return hf_api().snapshot_download(
233 + repo_id="prashanth058/qwen2vl-flickr-lora-tower-connector"
234 + )
235
236
237 @pytest.fixture(scope="session")
238 def qwen2vl_vision_tower_lora_files():
235 - return snapshot_download(repo_id="prashanth058/qwen2vl-flickr-lora-tower")
239 + return hf_api().snapshot_download(repo_id="prashanth058/qwen2vl-flickr-lora-tower")
240
241
242 @pytest.fixture(scope="session")
243 def qwen25vl_vision_lora_files():
240 - return snapshot_download(repo_id="EpochEcho/qwen2.5-3b-vl-lora-vision-connector")
244 + return hf_api().snapshot_download(
245 + repo_id="EpochEcho/qwen2.5-3b-vl-lora-vision-connector"
246 + )
247
248
249 @pytest.fixture(scope="session")
250 def qwen3vl_vision_lora_files():
245 - return snapshot_download(repo_id="EpochEcho/qwen3-4b-vl-lora-vision-connector")
251 + return hf_api().snapshot_download(
252 + repo_id="EpochEcho/qwen3-4b-vl-lora-vision-connector"
253 + )
254
255
256 @pytest.fixture(scope="session")
257 def qwen3_meowing_lora_files():
258 """Download Qwen3 Meow LoRA files once per test session."""
251 - return snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Meow-LoRA")
259 + return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Meow-LoRA")
260
261
262 @pytest.fixture(scope="session")
263 def qwen3_woofing_lora_files():
264 """Download Qwen3 Woof LoRA files once per test session."""
257 - return snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Woof-LoRA")
265 + return hf_api().snapshot_download(repo_id="Jackmin108/Qwen3-0.6B-Woof-LoRA")
266
267
268 @pytest.fixture(scope="session")
269 def tinyllama_lora_files():
262 - return snapshot_download(repo_id="jashing/tinyllama-colorist-lora")
270 + return hf_api().snapshot_download(repo_id="jashing/tinyllama-colorist-lora")
271
272
273 @pytest.fixture(scope="session")
274 def deepseekv2_lora_files():
267 - return snapshot_download(repo_id="wuchen01/DeepSeek-V2-Lite-Chat-All-LoRA")
275 + return hf_api().snapshot_download(repo_id="wuchen01/DeepSeek-V2-Lite-Chat-All-LoRA")
276
277
278 @pytest.fixture(scope="session")
279 def gptoss20b_lora_files():
272 - return snapshot_download(repo_id="jeeejeee/gpt-oss-20b-lora-adapter-text2sql")
280 + return hf_api().snapshot_download(
281 + repo_id="jeeejeee/gpt-oss-20b-lora-adapter-text2sql"
282 + )
283
284
285 @pytest.fixture(scope="session")
286 def qwen3moe_lora_files():
277 - return snapshot_download(repo_id="jeeejeee/qwen3-moe-text2sql-spider")
287 + return hf_api().snapshot_download(repo_id="jeeejeee/qwen3-moe-text2sql-spider")
288
289
290 @pytest.fixture(scope="session")
291 def olmoe_lora_files():
282 - return snapshot_download(repo_id="jeeejeee/olmoe-instruct-text2sql-spider")
292 + return hf_api().snapshot_download(repo_id="jeeejeee/olmoe-instruct-text2sql-spider")
293
294
295 @pytest.fixture(scope="session")
296 def qwen3_lora_files():
287 - return snapshot_download(repo_id="charent/self_cognition_Alice")
297 + return hf_api().snapshot_download(repo_id="charent/self_cognition_Alice")
298
299
300 @pytest.fixture(scope="session")
@@ -295,32 +305,40 @@ def llama32_lora_huggingface_id():
305
306 @pytest.fixture(scope="session")
307 def llama32_lora_files(llama32_lora_huggingface_id):
298 - return snapshot_download(repo_id=llama32_lora_huggingface_id)
308 + return hf_api().snapshot_download(repo_id=llama32_lora_huggingface_id)
309
310
311 @pytest.fixture(scope="session")
312 def whisper_lora_files():
303 - return snapshot_download(repo_id="chengyili2005/whisper-small-mandarin-lora")
313 + return hf_api().snapshot_download(
314 + repo_id="chengyili2005/whisper-small-mandarin-lora"
315 + )
316
317
318 @pytest.fixture(scope="session")
319 def qwen35_text_lora_files():
308 - return snapshot_download(repo_id="jeeejeee/qwen35-4b-text-only-sql-lora")
320 + return hf_api().snapshot_download(repo_id="jeeejeee/qwen35-4b-text-only-sql-lora")
321
322
323 @pytest.fixture(scope="session")
324 def qwen35_vl_lora_files():
313 - return snapshot_download(repo_id="jeeejeee/qwen35-4b-all-linear-pokemon-lora")
325 + return hf_api().snapshot_download(
326 + repo_id="jeeejeee/qwen35-4b-all-linear-pokemon-lora"
327 + )
328
329
330 @pytest.fixture(scope="session")
331 def qwen36_moe_2d_lora_files():
318 - return snapshot_download(repo_id="jeeejeee/qwen36-35ba3b-2d-weights-poken-lora")
332 + return hf_api().snapshot_download(
333 + repo_id="jeeejeee/qwen36-35ba3b-2d-weights-poken-lora"
334 + )
335
336
337 @pytest.fixture(scope="session")
338 def qwen36_moe_3d_lora_files():
323 - return snapshot_download(repo_id="jeeejeee/qwen36-35ba3b-moe-all-linear-poken-lora")
339 + return hf_api().snapshot_download(
340 + repo_id="jeeejeee/qwen36-35ba3b-moe-all-linear-poken-lora"
341 + )
342
343
344 @pytest.fixture
tests/lora/test_default_mm_loras.py
+2 -2
@@ -8,15 +8,15 @@ import os
8 import unittest.mock as mock
9
10 import pytest
11 -from huggingface_hub import snapshot_download
11
12 from vllm.lora.request import LoRARequest
13 from vllm.platforms import current_platform
14 +from vllm.transformers_utils.repo_utils import hf_api
15
16 from ..conftest import AudioTestAssets, VllmRunner
17 from ..utils import create_new_process_for_each_test
18
19 -MODEL_PATH = snapshot_download("microsoft/Phi-4-multimodal-instruct")
19 +MODEL_PATH = hf_api().snapshot_download("microsoft/Phi-4-multimodal-instruct")
20 AUDIO_LORA_PATH = os.path.join(MODEL_PATH, "speech-lora")
21 IMAGE_LORA_PATH = os.path.join(MODEL_PATH, "vision-lora")
22
tests/model_executor/model_loader/runai_streamer_loader/test_runai_model_streamer_s3.py
+2 -2
@@ -3,10 +3,10 @@
3
4 from pathlib import Path
5
6 -from huggingface_hub import snapshot_download
6 from runai_model_streamer.safetensors_streamer.streamer_mock import StreamerPatcher
7
8 from vllm.engine.arg_utils import EngineArgs
9 +from vllm.transformers_utils.repo_utils import hf_api
10
11 from .conftest import RunaiDummyExecutor
12
@@ -25,7 +25,7 @@ def test_runai_model_loader_download_files_s3_mocked_with_patch(
25
26 # Download model from HF
27 mock_model_dir = f"{tmp_path}/gpt2"
28 - snapshot_download(repo_id=test_model, local_dir=mock_model_dir)
28 + hf_api().snapshot_download(repo_id=test_model, local_dir=mock_model_dir)
29
30 monkeypatch.setattr(
31 "vllm.transformers_utils.runai_utils.runai_list_safetensors",
tests/model_executor/model_loader/test_sharded_state_loader.py
+2 -2
@@ -9,11 +9,11 @@ from tempfile import TemporaryDirectory
9
10 import pytest
11 import torch
12 -from huggingface_hub import snapshot_download
12
13 from vllm import LLM, SamplingParams
14 from vllm.model_executor.model_loader import ShardedStateLoader
15 from vllm.platforms import current_platform
16 +from vllm.transformers_utils.repo_utils import hf_api
17
18 prompts = [
19 "Hello, my name is",
@@ -52,7 +52,7 @@ def test_filter_subtensors():
52
53 @pytest.fixture(scope="module")
54 def llama_3p2_1b_files():
55 - input_dir = snapshot_download(
55 + input_dir = hf_api().snapshot_download(
56 "meta-llama/Llama-3.2-1B-Instruct", ignore_patterns=["*.bin*", "original/*"]
57 )
58
tests/models/language/pooling/test_colbert.py
+3 -2
@@ -126,10 +126,11 @@ def _load_hf_model(model_name: str, hf_spec: dict, device: torch.device):
126
127 def _load_projection_weight(model_name: str, hf_spec: dict, device: torch.device):
128 """Download and return the ColBERT linear projection weight."""
129 - from huggingface_hub import hf_hub_download
129 from safetensors.torch import load_file
130
132 - path = hf_hub_download(model_name, filename=hf_spec["weights_file"])
131 + from vllm.transformers_utils.repo_utils import hf_api
132 +
133 + path = hf_api().hf_hub_download(model_name, filename=hf_spec["weights_file"])
134 weights = load_file(path)
135 return weights[hf_spec["weights_key"]].to(device)
136
tests/models/multimodal/generation/test_phi4mm.py
+2 -2
@@ -6,7 +6,6 @@ from collections.abc import Sequence
6
7 import pytest
8 import regex as re
9 -from huggingface_hub import snapshot_download
9 from transformers import AutoTokenizer
10
11 from vllm.assets.image import ImageAsset
@@ -14,6 +13,7 @@ from vllm.logprobs import SampleLogprobs
13 from vllm.lora.request import LoRARequest
14 from vllm.multimodal.image import convert_image_mode, rescale_image_size
15 from vllm.multimodal.media.audio import load_audio
16 +from vllm.transformers_utils.repo_utils import hf_api
17
18 from ....conftest import (
19 IMAGE_ASSETS,
@@ -35,7 +35,7 @@ HF_MULTIIMAGE_IMAGE_PROMPT = (
35 "<|user|>\n<|image_1|>\n<|image_2|>\nDescribe these images.<|end|>\n<|assistant|>\n" # noqa: E501
36 )
37
38 -model_path = snapshot_download("microsoft/Phi-4-multimodal-instruct")
38 +model_path = hf_api().snapshot_download("microsoft/Phi-4-multimodal-instruct")
39 # Since the vision-lora and speech-lora co-exist with the base model,
40 # we have to manually specify the path of the lora weights.
41 vision_lora_path = os.path.join(model_path, "vision-lora")
tests/models/multimodal/pooling/test_intern_vit.py
+2 -2
@@ -3,11 +3,11 @@
3 import pytest
4 import torch
5 import torch.nn as nn
6 -from huggingface_hub import snapshot_download
6 from transformers import AutoConfig, AutoModel, CLIPImageProcessor
7
8 from vllm.distributed import cleanup_dist_env_and_memory
9 from vllm.platforms import current_platform
10 +from vllm.transformers_utils.repo_utils import hf_api
11 from vllm.utils.torch_utils import STR_DTYPE_TO_TORCH_DTYPE
12
13 from ....conftest import ImageTestAssets
@@ -31,7 +31,7 @@ def run_intern_vit_test(
31 *,
32 dtype: str,
33 ):
34 - model = snapshot_download(model_id, allow_patterns=DOWNLOAD_PATTERN)
34 + model = hf_api().snapshot_download(model_id, allow_patterns=DOWNLOAD_PATTERN)
35 torch_dtype = STR_DTYPE_TO_TORCH_DTYPE[dtype]
36
37 img_processor = CLIPImageProcessor.from_pretrained(model)
tests/models/multimodal/pooling/test_radio.py
+2 -2
@@ -3,13 +3,13 @@
3 import pytest
4 import torch
5 import torch.nn as nn
6 -from huggingface_hub import snapshot_download
6 from transformers import AutoConfig, AutoModel, CLIPImageProcessor
7
8 from vllm.distributed import cleanup_dist_env_and_memory
9 from vllm.model_executor.models.radio import RadioModel
10 from vllm.platforms import current_platform
11 from vllm.transformers_utils.configs.radio import RadioConfig
12 +from vllm.transformers_utils.repo_utils import hf_api
13 from vllm.utils.torch_utils import STR_DTYPE_TO_TORCH_DTYPE
14
15 from ....conftest import ImageTestAssets
@@ -28,7 +28,7 @@ def run_radio_test(
28 *,
29 dtype: str,
30 ):
31 - model = snapshot_download(model_id, allow_patterns=DOWNLOAD_PATTERN)
31 + model = hf_api().snapshot_download(model_id, allow_patterns=DOWNLOAD_PATTERN)
32 torch_dtype = STR_DTYPE_TO_TORCH_DTYPE[dtype]
33
34 img_processor = CLIPImageProcessor.from_pretrained(model)
tests/models/quantization/test_gpt_oss.py
+2 -1
@@ -22,6 +22,7 @@ import pytest
22 from packaging import version
23
24 from vllm.platforms import current_platform
25 +from vllm.transformers_utils.repo_utils import hf_api
26
27 if current_platform.is_rocm():
28 from vllm.platforms.rocm import on_gfx950
@@ -47,7 +48,7 @@ QUARK_MXFP4_AVAILABLE = importlib.util.find_spec("quark") is not None and versio
48
49 def has_huggingface_access(repo):
50 try:
50 - huggingface_hub.list_repo_refs(repo)
51 + hf_api().list_repo_refs(repo)
52 return True
53 except huggingface_hub.errors.RepositoryNotFoundError:
54 return False
tests/plugins_tests/gguf/test_gguf_plugin_multimodal.py
+3 -3
@@ -8,12 +8,12 @@ os.environ["TOKENIZERS_PARALLELISM"] = "true"
8 from typing import Any, NamedTuple
9
10 import pytest
11 -from huggingface_hub import hf_hub_download
11 from pytest import MarkDecorator
12 from transformers import AutoModelForImageTextToText
13
14 from vllm.assets.image import ImageAsset
15 from vllm.multimodal.image import rescale_image_size
16 +from vllm.transformers_utils.repo_utils import hf_api
17 from vllm.utils.torch_utils import set_default_torch_num_threads
18
19 from ...conftest import IMAGE_ASSETS, HfRunner, VllmRunner
@@ -33,8 +33,8 @@ class GGUFMMTestConfig(NamedTuple):
33
34 @property
35 def gguf_model(self):
36 - hf_hub_download(self.gguf_repo, filename=self.gguf_mmproj)
37 - return hf_hub_download(self.gguf_repo, filename=self.gguf_backbone)
36 + hf_api().hf_hub_download(self.gguf_repo, filename=self.gguf_mmproj)
37 + return hf_api().hf_hub_download(self.gguf_repo, filename=self.gguf_backbone)
38
39
40 # Common prompts aligned with test_common.py "gemma3" entry format
tests/plugins_tests/lora_resolvers/test_filesystem_resolver.py
+3 -3
@@ -4,9 +4,9 @@ import os
4 import shutil
5
6 import pytest
7 -from huggingface_hub import snapshot_download
7
8 from vllm.plugins.lora_resolvers.filesystem_resolver import FilesystemResolver
9 +from vllm.transformers_utils.repo_utils import hf_api
10
11 MODEL_NAME = "Qwen/Qwen3-0.6B"
12 LORA_NAME = "charent/self_cognition_Alice"
@@ -22,12 +22,12 @@ def adapter_cache(request, tmpdir_factory):
22
23 @pytest.fixture(scope="module")
24 def qwen3_lora_files():
25 - return snapshot_download(repo_id=LORA_NAME)
25 + return hf_api().snapshot_download(repo_id=LORA_NAME)
26
27
28 @pytest.fixture(scope="module")
29 def pa_files():
30 - return snapshot_download(repo_id=PA_NAME)
30 + return hf_api().snapshot_download(repo_id=PA_NAME)
31
32
33 @pytest.mark.asyncio
tests/quantization/test_modelopt.py
+2 -2
@@ -42,12 +42,12 @@ def _skip(msg: str) -> NoReturn:
42
43 def _snapshot_download_or_skip(model_id: str) -> str:
44 try:
45 - from huggingface_hub import snapshot_download
45 + from vllm.transformers_utils.repo_utils import hf_api
46 except Exception as e: # pragma: no cover
47 _skip(f"huggingface_hub is required to download {model_id}: {e}")
48
49 try:
50 - return snapshot_download(
50 + return hf_api().snapshot_download(
51 repo_id=model_id,
52 repo_type="model",
53 # These checkpoints are already small; download full repo for simplicity.
tests/quantization/test_quark.py
+2 -1
@@ -30,6 +30,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
30 is_layer_skipped,
31 )
32 from vllm.platforms import current_platform
33 +from vllm.transformers_utils.repo_utils import hf_api
34
35 if current_platform.is_rocm():
36 from vllm.platforms.rocm import on_gfx942, on_gfx950
@@ -59,7 +60,7 @@ if QUARK_MXFP4_AVAILABLE:
60 from quark.torch.quantization.config.config import FP4PerGroupSpec
61
62 try:
62 - huggingface_hub.list_repo_refs(
63 + hf_api().list_repo_refs(
64 "amd/Llama-3.3-70B-Instruct-WMXFP4-AMXFP4-KVFP8-Scale-UINT8-SQ"
65 )
66 HF_HUB_AMD_ORG_ACCESS = True
tests/tool_use/conftest.py
+2 -2
@@ -3,10 +3,10 @@
3
4 import pytest
5 import pytest_asyncio
6 -from huggingface_hub import snapshot_download
6
7 from tests.utils import RemoteOpenAIServer
8 from vllm.platforms import current_platform
9 +from vllm.transformers_utils.repo_utils import hf_api
10
11 from .utils import ARGS, CONFIGS, ServerConfig
12
@@ -47,7 +47,7 @@ def server_config(request):
47 )
48
49 # download model and tokenizer using transformers
50 - snapshot_download(config["model"])
50 + hf_api().snapshot_download(config["model"])
51 yield CONFIGS[request.param]
52
53
tests/tool_use/mistral/conftest.py
+2 -2
@@ -3,10 +3,10 @@
3
4 import pytest
5 import pytest_asyncio
6 -from huggingface_hub import snapshot_download
6
7 from tests.utils import RemoteOpenAIServer
8 from vllm.platforms import current_platform
9 +from vllm.transformers_utils.repo_utils import hf_api
10
11 from .utils import ARGS, CONFIGS, ServerConfig
12
@@ -22,7 +22,7 @@ def server_config(request):
22 )
23
24 # download model and tokenizer using transformers
25 - snapshot_download(config["model"])
25 + hf_api().snapshot_download(config["model"])
26 yield CONFIGS[request.param]
27
28
tests/utils.py
+2 -2
@@ -33,7 +33,6 @@ import pytest
33 import requests
34 import torch
35 import torch.nn.functional as F
36 -from huggingface_hub import hf_hub_download
36 from huggingface_hub.constants import HF_HUB_OFFLINE
37 from openai.types.completion import Completion
38 from typing_extensions import ParamSpec
@@ -57,6 +56,7 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
56 from vllm.model_executor.model_loader import get_model_loader
57 from vllm.platforms import current_platform
58 from vllm.tokenizers import get_tokenizer
59 +from vllm.transformers_utils.repo_utils import hf_api
60 from vllm.utils.argparse_utils import FlexibleArgumentParser
61 from vllm.utils.mem_constants import GB_bytes
62 from vllm.utils.network_utils import get_open_port
@@ -77,7 +77,7 @@ def prewarm_hf_cache(assets: list[tuple[str, str]]) -> None:
77 return
78 for repo_id, filename in assets:
79 try:
80 - hf_hub_download(repo_id=repo_id, filename=filename)
80 + hf_api().hf_hub_download(repo_id=repo_id, filename=filename)
81 except Exception as e:
82 logger.warning(
83 "Failed to prefetch %s/%s: %r. Tests depending on this asset may fail.",
tools/pre_commit/check_forbidden_imports.py
+75 -4
@@ -6,6 +6,13 @@ from dataclasses import dataclass, field
6
7 import regex as re
8
9 +# Hub entry points that must go through the vLLM-tagged repo_utils helpers.
10 +_HF_NAMES = (
11 + r"HfApi|HfFileSystem|hf_hub_download|snapshot_download"
12 + r"|list_repo_files|file_exists|try_to_load_from_cache"
13 + r"|list_repo_refs|repo_exists"
14 +)
15 +
16
17 @dataclass
18 class ForbiddenImport:
@@ -13,6 +20,7 @@ class ForbiddenImport:
20 tip: str
21 allowed_pattern: re.Pattern = re.compile(r"^$") # matches nothing by default
22 allowed_files: set[str] = field(default_factory=set)
23 + allowed_dirs: set[str] = field(default_factory=set)
24
25
26 CHECK_IMPORTS = {
@@ -83,6 +91,23 @@ CHECK_IMPORTS = {
91 ),
92 allowed_files={"vllm/triton_utils/importing.py"},
93 ),
94 + "huggingface_hub repo API": ForbiddenImport(
95 + # Catch `from huggingface_hub import <fn>`, including parenthesized,
96 + # multi-line imports.
97 + pattern=(
98 + r"^\s*from\s+huggingface_hub\s+import\s*\([^)]*\b(?:" + _HF_NAMES + r")\b"
99 + r"|"
100 + r"^\s*from\s+huggingface_hub\s+import\b[^\n]*\b(?:" + _HF_NAMES + r")\b"
101 + ),
102 + tip=(
103 + "Use the shared, vLLM-tagged helpers from "
104 + "vllm.transformers_utils.repo_utils (e.g. hf_api(), hf_fs(), "
105 + "list_repo_files, file_exists) instead of calling "
106 + "huggingface_hub directly."
107 + ),
108 + allowed_files={"vllm/transformers_utils/repo_utils.py"},
109 + allowed_dirs={"examples/"},
110 + ),
111 }
112
113
@@ -95,11 +120,18 @@ def check_file(path: str) -> int:
120 # Skip files that are allowed for this import
121 if path in forbidden_import.allowed_files:
122 continue
123 + # Skip directories that are allowed for this import
124 + if any(path.startswith(prefix) for prefix in forbidden_import.allowed_dirs):
125 + continue
126 # Search for forbidden imports
127 for match in re.finditer(forbidden_import.pattern, content, re.MULTILINE):
128 # Check if it's allowed
129 if forbidden_import.allowed_pattern.match(match.group()):
130 continue
131 + # Skip matches inside a comment
132 + line_start = content.rfind("\n", 0, match.start()) + 1
133 + if "#" in content[line_start : match.start()]:
134 + continue
135 # Calculate line number from match position
136 line_num = content[: match.start() + 1].count("\n") + 1
137 print(
@@ -119,7 +151,10 @@ def main():
151
152
153 def test_regex():
122 - test_cases = [
154 + def matches(rule: str, content: str) -> bool:
155 + return bool(re.search(CHECK_IMPORTS[rule].pattern, content, re.MULTILINE))
156 +
157 + pickle_cases = [
158 # Should match
159 ("import pickle", True),
160 ("import cloudpickle", True),
@@ -139,11 +174,47 @@ def test_regex():
174 ("print('import pickle')", False),
175 ("import pickleas as asdf", False),
176 ]
142 - for i, (line, should_match) in enumerate(test_cases):
143 - result = bool(CHECK_IMPORTS["pickle/cloudpickle"].pattern.match(line))
177 + for i, (content, should_match) in enumerate(pickle_cases):
178 + result = matches("pickle/cloudpickle", content)
179 assert result == should_match, (
145 - f"Test case {i} failed: '{line}' (expected {should_match}, got {result})"
180 + f"pickle case {i} failed: {content!r} "
181 + f"(expected {should_match}, got {result})"
182 )
183 +
184 + hf_cases = [
185 + # Should match
186 + ("from huggingface_hub import snapshot_download", True),
187 + ("from huggingface_hub import hf_hub_download", True),
188 + ("from huggingface_hub import HfApi", True),
189 + ("from huggingface_hub import HfFileSystem", True),
190 + ("from huggingface_hub import list_repo_files", True),
191 + ("from huggingface_hub import try_to_load_from_cache", True),
192 + (" from huggingface_hub import snapshot_download", True),
193 + ("from huggingface_hub import PyTorchModelHubMixin, hf_hub_download", True),
194 + ("from huggingface_hub import (snapshot_download)", True),
195 + # Parenthesized multi-line import must not bypass the hook
196 + ("from huggingface_hub import (\n snapshot_download,\n)", True),
197 + (
198 + "from huggingface_hub import (\n PyTorchModelHubMixin,\n HfApi,\n)",
199 + True,
200 + ),
201 + # Should not match
202 + ("import huggingface_hub", False),
203 + ("import huggingface_hub as hf", False),
204 + ("from huggingface_hub import PyTorchModelHubMixin", False),
205 + ("from huggingface_hub.constants import HF_HUB_CACHE", False),
206 + ("from huggingface_hub.utils import EntryNotFoundError", False),
207 + ("from vllm.transformers_utils.repo_utils import hf_api", False),
208 + ("from huggingface_hub import (\n PyTorchModelHubMixin,\n)", False),
209 + ("# from huggingface_hub import snapshot_download", False),
210 + ]
211 + for i, (content, should_match) in enumerate(hf_cases):
212 + result = matches("huggingface_hub repo API", content)
213 + assert result == should_match, (
214 + f"huggingface_hub case {i} failed: {content!r} "
215 + f"(expected {should_match}, got {result})"
216 + )
217 +
218 print("All regex tests passed.")
219
220
vllm/model_executor/models/llava_onevision2.py
+5 -10
@@ -24,8 +24,6 @@ from __future__ import annotations
24
25 import hashlib
26 import importlib
27 -import json
28 -import os
27 from collections.abc import Callable, Iterable, Mapping, Sequence
28 from functools import lru_cache
29 from typing import (
@@ -39,7 +37,6 @@ import regex as re
37 import torch
38 import torch.nn as nn
39 import torch.nn.functional as F
42 -from huggingface_hub import hf_hub_download
40 from PIL import Image
41 from transformers import AutoProcessor, AutoTokenizer, BatchFeature
42 from transformers.dynamic_module_utils import get_class_from_dynamic_module
@@ -108,6 +105,7 @@ from vllm.multimodal.video import (
105 )
106 from vllm.sequence import IntermediateTensors
107 from vllm.transformers_utils.processor import _merge_mm_kwargs
108 +from vllm.transformers_utils.repo_utils import get_hf_file_to_dict
109 from vllm.transformers_utils.utils import convert_model_repo_to_path
110 from vllm.utils.tensor_schema import TensorSchema, TensorShape
111
@@ -165,13 +163,10 @@ def _load_ov2_processor(
163 # codec video backend keeps its configured defaults.
164 codec_config: dict = {}
165 try:
168 - config_file = os.path.join(path, "preprocessor_config.json")
169 - if not os.path.isfile(config_file):
170 - config_file = hf_hub_download(
171 - path, "preprocessor_config.json", revision=revision
172 - )
173 - with open(config_file, encoding="utf-8") as f:
174 - codec_config = json.load(f).get("codec", {}) or {}
166 + preprocessor_config = get_hf_file_to_dict(
167 + "preprocessor_config.json", path, revision
168 + )
169 + codec_config = (preprocessor_config or {}).get("codec", {}) or {}
170 except Exception:
171 logger.debug("OV2: no codec defaults found in preprocessor_config.json")
172