From 6d1a988145444e7981da6ba8f2fe9c5bd89df86f Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:22:59 +0100 Subject: [PATCH 1/9] Add `library_name` to all HF API calls from `vllm-omni` Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- .pre-commit-config.yaml | 6 +- .../model_loader/test_diffusers_loader.py | 4 +- .../rlhf_test/test_verl_omni_e2e.py | 4 +- .../test_cosyvoice3_expansion.py | 4 +- .../test_mammoth_moda2_expansion.py | 4 +- .../test_moss_tts_realtime.py | 5 +- .../e2e/offline_inference/test_soulxsinger.py | 8 +- tests/helpers/__init__.py | 5 +- .../omnivoice/test_nonverbal_tags_tokenize.py | 4 +- tools/pre_commit/check_forbidden_imports.py | 160 ++++++++++++++++++ tools/pre_commit/check_pickle_imports.py | 83 --------- .../data_modules/daily_omni_dataset.py | 4 +- .../data_modules/seed_tts_dataset.py | 4 +- .../benchmarks/data_modules/seed_tts_eval.py | 5 +- .../diffusion/model_loader/hub_prefetch.py | 4 +- .../models/cosmos3/sound_tokenizer.py | 4 +- .../models/dreamzero/pipeline_dreamzero.py | 4 +- .../models/helios/pipeline_helios.py | 4 +- .../diffusion/models/ltx2/ltx2_components.py | 8 +- .../models/magi_human/pipeline_magi_human.py | 8 +- .../models/omnivoice/pipeline_omnivoice.py | 4 +- .../sensenova_u1/pipeline_sensenova_u1.py | 4 +- .../diffusion/models/soulx_singer/utils.py | 4 +- vllm_omni/diffusion/models/utils.py | 4 +- .../models/wan2_2/pipeline_wan2_2.py | 8 +- vllm_omni/engine/arg_utils.py | 4 +- vllm_omni/engine/stage_init_utils.py | 4 +- vllm_omni/entrypoints/openai/api_server.py | 4 +- .../entrypoints/openai/serving_speech.py | 4 +- .../model_loader/weight_utils.py | 5 +- .../model_executor/models/bagel/bagel.py | 4 +- .../models/cosyvoice3/cosyvoice3.py | 8 +- .../models/covo_audio/covo_audio_code2wav.py | 4 +- .../models/dynin_omni/dynin_omni_common.py | 14 +- .../dynin_omni/dynin_omni_token2audio.py | 6 +- .../model_executor/models/glm_tts/glm_tts.py | 8 +- .../higgs_audio_v2/higgs_audio_v2_code2wav.py | 4 +- .../higgs_audio_v2_tokenizer.py | 4 +- .../models/indextts2/preprocess_utils.py | 9 +- .../models/indextts2/s2mel/modules/bigvgan.py | 5 +- .../models/ming_tts/speaker_extractor.py | 4 +- .../models/omnivoice/omnivoice.py | 4 +- .../step_audio2/step_audio2_token2wav.py | 4 +- .../models/voxtral_tts/voxtral_tts.py | 4 +- vllm_omni/transformers_utils/repo_utils.py | 29 ++++ 45 files changed, 300 insertions(+), 188 deletions(-) create mode 100644 tools/pre_commit/check_forbidden_imports.py delete mode 100644 tools/pre_commit/check_pickle_imports.py create mode 100644 vllm_omni/transformers_utils/repo_utils.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index be649785d8d..d6b27c629e5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -70,9 +70,9 @@ repos: pass_filenames: false # Insert new entries above the `suggestion` entry - - id: check-pickle-imports - name: Prevent new pickle/cloudpickle imports - entry: python tools/pre_commit/check_pickle_imports.py + - id: check-forbidden-imports + name: Check for forbidden imports + entry: python tools/pre_commit/check_forbidden_imports.py language: python types: [python] additional_dependencies: [regex] diff --git a/tests/diffusion/model_loader/test_diffusers_loader.py b/tests/diffusion/model_loader/test_diffusers_loader.py index f370d5b497b..93ee4e150b8 100644 --- a/tests/diffusion/model_loader/test_diffusers_loader.py +++ b/tests/diffusion/model_loader/test_diffusers_loader.py @@ -10,8 +10,8 @@ import pytest import torch import torch.nn as nn -from huggingface_hub import snapshot_download from vllm.config.load import LoadConfig +from vllm_omni.transformers_utils.repo_utils import hf_api from vllm_omni.diffusion.config import get_current_diffusion_config, get_current_diffusion_config_or_none from vllm_omni.diffusion.data import OmniDiffusionConfig @@ -27,7 +27,7 @@ @pytest.fixture(scope="module") def prefetch_helios_model(): """Downloads the tiny helios model prior to running a test.""" - snapshot_download(model_path) + hf_api().snapshot_download(model_path) @pytest.fixture(scope="function") diff --git a/tests/e2e/offline_inference/rlhf_test/test_verl_omni_e2e.py b/tests/e2e/offline_inference/rlhf_test/test_verl_omni_e2e.py index 420c9bb688a..60ff3c36d95 100644 --- a/tests/e2e/offline_inference/rlhf_test/test_verl_omni_e2e.py +++ b/tests/e2e/offline_inference/rlhf_test/test_verl_omni_e2e.py @@ -43,7 +43,6 @@ import ray import torch import torchvision.transforms as T -from huggingface_hub import snapshot_download from omegaconf import DictConfig, OmegaConf from pydantic import BaseModel, ConfigDict from transformers import AutoTokenizer @@ -57,6 +56,7 @@ from vllm_omni.entrypoints.openai.api_server import omni_init_app_state from vllm_omni.inputs.data import OmniCustomPrompt, OmniDiffusionSamplingParams from vllm_omni.outputs import OmniRequestOutput +from vllm_omni.transformers_utils.repo_utils import hf_api logger = logging.getLogger(__name__) @@ -78,7 +78,7 @@ def _resolve_model_path(repo_id: str) -> str: # Allow overriding with a pre-existing local path (skips download). if os.path.isdir(repo_id): return repo_id - return snapshot_download(repo_id=repo_id) + return hf_api().snapshot_download(repo_id=repo_id) _MIN_PROMPT_TOKENS = 35 diff --git a/tests/e2e/offline_inference/test_cosyvoice3_expansion.py b/tests/e2e/offline_inference/test_cosyvoice3_expansion.py index 49079266d6c..67ad7008703 100644 --- a/tests/e2e/offline_inference/test_cosyvoice3_expansion.py +++ b/tests/e2e/offline_inference/test_cosyvoice3_expansion.py @@ -20,7 +20,6 @@ import numpy as np import pytest import soundfile as sf -from huggingface_hub import snapshot_download from vllm.sampling_params import SamplingParams from tests.helpers.mark import hardware_test @@ -30,6 +29,7 @@ from vllm_omni.model_executor.models.cosyvoice3.tokenizer import get_qwen_tokenizer from vllm_omni.outputs import OmniRequestOutput from vllm_omni.transformers_utils.configs.cosyvoice3 import CosyVoice3Config +from vllm_omni.transformers_utils.repo_utils import hf_api MODEL = "FunAudioLLM/Fun-CosyVoice3-0.5B-2512" MODEL_DIR_ENV = "VLLM_OMNI_COSYVOICE3_MODEL_DIR" @@ -59,7 +59,7 @@ def _resolve_model_dir() -> Path: override = os.environ.get(MODEL_DIR_ENV) if override: return Path(override).expanduser().resolve() - return Path(snapshot_download(MODEL, allow_patterns=["*"])) + return Path(hf_api().snapshot_download(MODEL, allow_patterns=["*"])) def _reference_zero_shot_stage0_sampling(*, text: str) -> SamplingParams: diff --git a/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py b/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py index 04308c4a796..dac1a8d7410 100644 --- a/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py +++ b/tests/e2e/offline_inference/test_mammoth_moda2_expansion.py @@ -19,12 +19,12 @@ import pytest import torch -from huggingface_hub import snapshot_download from vllm.sampling_params import SamplingParams from tests.helpers.mark import hardware_test from tests.helpers.runtime import OmniRunner from tests.helpers.stage_config import get_deploy_config_path +from vllm_omni.transformers_utils.repo_utils import hf_api # --------------------------------------------------------------------------- # Constants @@ -64,7 +64,7 @@ # Helpers # --------------------------------------------------------------------------- def _load_t2i_gen_config(repo_id: str) -> dict: - weights_dir = Path(snapshot_download(repo_id)) + weights_dir = Path(hf_api().snapshot_download(repo_id)) cfg_path = weights_dir / "t2i_generation_config.json" if not cfg_path.exists(): pytest.skip(f"t2i_generation_config.json not found at {cfg_path}") diff --git a/tests/e2e/offline_inference/test_moss_tts_realtime.py b/tests/e2e/offline_inference/test_moss_tts_realtime.py index 892a395ba9f..440baa91785 100644 --- a/tests/e2e/offline_inference/test_moss_tts_realtime.py +++ b/tests/e2e/offline_inference/test_moss_tts_realtime.py @@ -160,12 +160,13 @@ def _build_request(ref_audio_path: str, text: str) -> dict: Frees the codec model before returning to avoid competing with the running vllm engine for GPU/CPU memory. """ - from huggingface_hub import snapshot_download from transformers import AutoModel, AutoTokenizer + from vllm_omni.transformers_utils.repo_utils import hf_api + # Step 1: locate the realtime processor module in the snapshot. try: - snap_dir = Path(snapshot_download(repo_id=MODEL)) + snap_dir = Path(hf_api().snapshot_download(repo_id=MODEL)) except Exception as exc: msg = f"Cannot locate snapshot for {MODEL}: {exc}" if os.environ.get("MOSS_TTS_SKIP_ON_NET_FAIL"): diff --git a/tests/e2e/offline_inference/test_soulxsinger.py b/tests/e2e/offline_inference/test_soulxsinger.py index 465f5547b62..97f5e71c029 100644 --- a/tests/e2e/offline_inference/test_soulxsinger.py +++ b/tests/e2e/offline_inference/test_soulxsinger.py @@ -66,9 +66,9 @@ @functools.lru_cache(maxsize=1) def _resolve_weights() -> tuple[Path, Path, Path]: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - base = Path(snapshot_download("Soul-AILab/SoulX-Singer", allow_patterns=["*"])) + base = Path(hf_api().snapshot_download("Soul-AILab/SoulX-Singer", allow_patterns=["*"])) # phone_set.json is not on HF; best-effort stage it from pinned upstream so # SVS can load. SVS tests skip (not fail) if both this and a manual copy are absent. @@ -93,9 +93,9 @@ def _resolve_weights() -> tuple[Path, Path, Path]: if (pre / "rmvpe" / "rmvpe.pt").is_file(): return base, svc_dir, pre - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - pre = Path(snapshot_download("Soul-AILab/SoulX-Singer-Preprocess", allow_patterns=["*"])) + pre = Path(hf_api().snapshot_download("Soul-AILab/SoulX-Singer-Preprocess", allow_patterns=["*"])) return base, svc_dir, pre diff --git a/tests/helpers/__init__.py b/tests/helpers/__init__.py index 04d51fb90fd..521f689a2f2 100644 --- a/tests/helpers/__init__.py +++ b/tests/helpers/__init__.py @@ -20,12 +20,13 @@ def skip_if_gated_repo_inaccessible(repo_id: str) -> None: and we skip cleanly. """ try: - from huggingface_hub import hf_hub_download from huggingface_hub.errors import GatedRepoError, RepositoryNotFoundError + + from vllm_omni.transformers_utils.repo_utils import hf_api except Exception: return try: - hf_hub_download(repo_id=repo_id, filename="config.json") + hf_api().hf_hub_download(repo_id=repo_id, filename="config.json") except GatedRepoError as exc: pytest.skip( f"Skipping: gated HF repo {repo_id!r} inaccessible to the current " diff --git a/tests/model_executor/models/omnivoice/test_nonverbal_tags_tokenize.py b/tests/model_executor/models/omnivoice/test_nonverbal_tags_tokenize.py index fbf5ac0b010..b3c30c11aa2 100644 --- a/tests/model_executor/models/omnivoice/test_nonverbal_tags_tokenize.py +++ b/tests/model_executor/models/omnivoice/test_nonverbal_tags_tokenize.py @@ -1,12 +1,12 @@ from __future__ import annotations import pytest -from huggingface_hub import hf_hub_download from tokenizers import Tokenizer as HFTokenizer from vllm_omni.diffusion.models.omnivoice.pipeline_omnivoice import ( _tokenize_with_nonverbal_tags, ) +from vllm_omni.transformers_utils.repo_utils import hf_api pytestmark = [pytest.mark.core_model, pytest.mark.cpu] @@ -18,7 +18,7 @@ def _ids(tok: HFTokenizer, s: str) -> list[int]: class TestNonVerbalTags: @classmethod def setup_class(cls): - tokenizer_path = hf_hub_download(repo_id="k2-fsa/OmniVoice", filename="tokenizer.json") + tokenizer_path = hf_api().hf_hub_download(repo_id="k2-fsa/OmniVoice", filename="tokenizer.json") cls.tokenizer = HFTokenizer.from_file(tokenizer_path) def test_plain_text_fallback(self): diff --git a/tools/pre_commit/check_forbidden_imports.py b/tools/pre_commit/check_forbidden_imports.py new file mode 100644 index 00000000000..bd1c2d858a5 --- /dev/null +++ b/tools/pre_commit/check_forbidden_imports.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +import sys +from dataclasses import dataclass, field +from pathlib import PurePath + +import regex as re + + +@dataclass +class ForbiddenImport: + pattern: str + tip: str + allowed_pattern: re.Pattern = re.compile(r"^$") # matches nothing by default + allowed_files: set[str] = field(default_factory=set) + allowed_dirs: set[str] = field(default_factory=set) + + +CHECK_IMPORTS = { + # STOP AND READ BEFORE YOU ADD ANYTHING ELSE TO THIS LIST: + # The pickle and cloudpickle modules are known to be unsafe when + # deserializing data from potentially untrusted parties. They have resulted + # in multiple CVEs for vLLM and numerous vulnerabilities in the Python + # ecosystem more broadly. Before adding new uses of pickle/cloudpickle, + # please consider safer alternatives like msgpack or pydantic that are + # already in use in vLLM. Only add to this list if absolutely necessary and + # after careful security review. + "pickle/cloudpickle": ForbiddenImport( + pattern=( + r"^\s*(import\s+(pickle|cloudpickle)(\s|$|\sas)" + r"|from\s+(pickle|cloudpickle)\s+import\b)" + ), + tip=( + "Avoid using pickle or cloudpickle or add this file to " + "tools/pre_commit/check_forbidden_imports.py." + ), + allowed_files={ + "tests/helpers/process.py", + "vllm_omni/diffusion/distributed/group_coordinator.py", + "tests/diffusion/attention/test_attention_sp.py", + }, + ), + "huggingface_hub": ForbiddenImport( + pattern=( + r"^\s*from\s+huggingface_hub\s+import\b[^\n]*" + r"\b(?:snapshot_download|hf_hub_download|HfApi|HfFileSystem" + r"|get_safetensors_metadata)\b" + ), + tip=( + "Use 'hf_api()' / 'hf_fs()' from " + "'vllm_omni.transformers_utils.repo_utils' instead, so requests are " + "tagged with vLLM-Omni's library info." + ), + allowed_files={"vllm_omni/transformers_utils/repo_utils.py"}, + allowed_dirs={"examples", "benchmarks"}, + ), +} + + +def check_file(path: str) -> int: + with open(path, encoding="utf-8") as f: + content = f.read() + return_code = 0 + # Check all patterns in the whole file + for import_name, forbidden_import in CHECK_IMPORTS.items(): + # Skip files that are allowed for this import + if path in forbidden_import.allowed_files: + continue + # Skip files whose top-level directory is exempt from this import + parts = PurePath(path).parts + if parts and parts[0] in forbidden_import.allowed_dirs: + continue + # Search for forbidden imports + for match in re.finditer(forbidden_import.pattern, content, re.MULTILINE): + # Check if it's allowed + if forbidden_import.allowed_pattern.match(match.group()): + continue + # Calculate line number from match position + line_num = content[: match.start() + 1].count("\n") + 1 + print( + f"{path}:{line_num}: " + "\033[91merror:\033[0m " # red color + f"Found forbidden import: {import_name}. {forbidden_import.tip}" + ) + return_code = 1 + return return_code + + +def main(): + returncode = 0 + for path in sys.argv[1:]: + returncode |= check_file(path) + return returncode + + +def test_regex(): + pickle_cases = [ + # Should match + ("import pickle", True), + ("import cloudpickle", True), + ("import pickle as pkl", True), + ("import cloudpickle as cpkl", True), + ("from pickle import *", True), + ("from cloudpickle import dumps", True), + ("from pickle import dumps, loads", True), + ("from cloudpickle import (dumps, loads)", True), + (" import pickle", True), + ("\timport cloudpickle", True), + ("from pickle import loads", True), + # Should not match + ("import somethingelse", False), + ("from somethingelse import pickle", False), + ("# import pickle", False), + ("print('import pickle')", False), + ("import pickleas as asdf", False), + ] + pickle_pattern = re.compile(CHECK_IMPORTS["pickle/cloudpickle"].pattern) + for i, (line, should_match) in enumerate(pickle_cases): + result = bool(pickle_pattern.match(line)) + assert result == should_match, ( + f"pickle case {i} failed: '{line}' (expected {should_match}, got {result})" + ) + + hf_cases = [ + # Should match + ("from huggingface_hub import snapshot_download", True), + ("from huggingface_hub import hf_hub_download", True), + ("from huggingface_hub import HfApi", True), + ("from huggingface_hub import HfFileSystem", True), + ("from huggingface_hub import get_safetensors_metadata", True), + (" from huggingface_hub import snapshot_download", True), + ("from huggingface_hub import PyTorchModelHubMixin, hf_hub_download", True), + # Should not match + ("import huggingface_hub", False), + ("import huggingface_hub as hf", False), + ("from huggingface_hub import PyTorchModelHubMixin", False), + ("from huggingface_hub import try_to_load_from_cache", False), + ("from huggingface_hub.constants import HF_HUB_CACHE", False), + ("from huggingface_hub.utils import EntryNotFoundError", False), + ("from vllm_omni.transformers_utils.repo_utils import hf_api", False), + ("# resolves via ``huggingface_hub.snapshot_download``", False), + (' """Falls back to snapshot_download for remote repos."""', False), + ] + hf_pattern = re.compile(CHECK_IMPORTS["huggingface_hub"].pattern, re.MULTILINE) + for i, (line, should_match) in enumerate(hf_cases): + result = bool(hf_pattern.search(line)) + assert result == should_match, ( + f"huggingface_hub case {i} failed: '{line}' " + f"(expected {should_match}, got {result})" + ) + + print("All regex tests passed.") + + +if __name__ == "__main__": + if "--test-regex" in sys.argv: + test_regex() + else: + sys.exit(main()) diff --git a/tools/pre_commit/check_pickle_imports.py b/tools/pre_commit/check_pickle_imports.py deleted file mode 100644 index 819a7c8c3dd..00000000000 --- a/tools/pre_commit/check_pickle_imports.py +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/env python3 -# SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project -import sys - -import regex as re - -# List of files (relative to repo root) that are allowed to import pickle or -# cloudpickle -# -# STOP AND READ BEFORE YOU ADD ANYTHING ELSE TO THIS LIST: -# The pickle and cloudpickle modules are known to be unsafe when deserializing -# data from potentially untrusted parties. They have resulted in multiple CVEs -# for vLLM and numerous vulnerabilities in the Python ecosystem more broadly. -# Before adding new uses of pickle/cloudpickle, please consider safer -# alternatives like msgpack or pydantic that are already in use in vLLM. Only -# add to this list if absolutely necessary and after careful security review. -ALLOWED_FILES = { - "tests/helpers/process.py", - "vllm_omni/diffusion/distributed/group_coordinator.py", - "tests/diffusion/attention/test_attention_sp.py", -} - -PICKLE_RE = re.compile( - r"^\s*(import\s+(pickle|cloudpickle)(\s|$|\sas)" - r"|from\s+(pickle|cloudpickle)\s+import\b)" -) - - -def scan_file(path: str) -> int: - with open(path, encoding="utf-8") as f: - for i, line in enumerate(f, 1): - if PICKLE_RE.match(line): - print( - f"{path}:{i}: " - "\033[91merror:\033[0m " # red color - "Found pickle/cloudpickle import" - ) - return 1 - return 0 - - -def main(): - returncode = 0 - for filename in sys.argv[1:]: - if filename in ALLOWED_FILES: - continue - returncode |= scan_file(filename) - return returncode - - -def test_regex(): - test_cases = [ - # Should match - ("import pickle", True), - ("import cloudpickle", True), - ("import pickle as pkl", True), - ("import cloudpickle as cpkl", True), - ("from pickle import *", True), - ("from cloudpickle import dumps", True), - ("from pickle import dumps, loads", True), - ("from cloudpickle import (dumps, loads)", True), - (" import pickle", True), - ("\timport cloudpickle", True), - ("from pickle import loads", True), - # Should not match - ("import somethingelse", False), - ("from somethingelse import pickle", False), - ("# import pickle", False), - ("print('import pickle')", False), - ("import pickleas as asdf", False), - ] - for i, (line, should_match) in enumerate(test_cases): - result = bool(PICKLE_RE.match(line)) - assert result == should_match, f"Test case {i} failed: '{line}' (expected {should_match}, got {result})" - print("All regex tests passed.") - - -if __name__ == "__main__": - if "--test-regex" in sys.argv: - test_regex() - else: - sys.exit(main()) diff --git a/vllm_omni/benchmarks/data_modules/daily_omni_dataset.py b/vllm_omni/benchmarks/data_modules/daily_omni_dataset.py index 65918414f45..8c294072684 100644 --- a/vllm_omni/benchmarks/data_modules/daily_omni_dataset.py +++ b/vllm_omni/benchmarks/data_modules/daily_omni_dataset.py @@ -120,7 +120,7 @@ def ensure_daily_omni_hub_videos_dir(repo_id: str) -> Path: raise ValueError("repo_id is required to download Daily-Omni Videos.tar") try: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api except ImportError as e: raise ImportError( "Daily-Omni Hub media download requires huggingface_hub. " @@ -135,7 +135,7 @@ def ensure_daily_omni_hub_videos_dir(repo_id: str) -> Path: tar_path: Path | None = None for fname in ("Videos.tar", "videos.tar"): try: - tar_path = Path(hf_hub_download(repo_id=rid, filename=fname, repo_type="dataset")) + tar_path = Path(hf_api().hf_hub_download(repo_id=rid, filename=fname, repo_type="dataset")) break except Exception: continue diff --git a/vllm_omni/benchmarks/data_modules/seed_tts_dataset.py b/vllm_omni/benchmarks/data_modules/seed_tts_dataset.py index 447495bffbc..5d1be92d29f 100644 --- a/vllm_omni/benchmarks/data_modules/seed_tts_dataset.py +++ b/vllm_omni/benchmarks/data_modules/seed_tts_dataset.py @@ -111,7 +111,7 @@ def resolve_seed_tts_root(dataset_path: str | None, *, explicit_root: str | None repo_id = dataset_path.strip() try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api except ImportError as e: raise ImportError( "Install huggingface_hub to download Seed-TTS from the Hub, or clone the dataset " @@ -120,7 +120,7 @@ def resolve_seed_tts_root(dataset_path: str | None, *, explicit_root: str | None # Download only the requested locale subtree instead of the whole dataset # repo. This avoids large, flaky nightly downloads when we only need e.g. # ``en/meta.lst`` + ``en/prompt-wavs/**``. - cache = snapshot_download( + cache = hf_api().snapshot_download( repo_id=repo_id, repo_type="dataset", allow_patterns=[f"{locale}/**"], diff --git a/vllm_omni/benchmarks/data_modules/seed_tts_eval.py b/vllm_omni/benchmarks/data_modules/seed_tts_eval.py index 13c1f2c2177..a44000143d3 100644 --- a/vllm_omni/benchmarks/data_modules/seed_tts_eval.py +++ b/vllm_omni/benchmarks/data_modules/seed_tts_eval.py @@ -313,7 +313,8 @@ def _ensure_utmos_jit_model() -> Any | None: return _utmos_jit_model try: import torch - from huggingface_hub import hf_hub_download + + from vllm_omni.transformers_utils.repo_utils import hf_api repo = os.environ.get("SEED_TTS_UTMOS_HF_REPO", "balacoon/utmos").strip() or "balacoon/utmos" fname = os.environ.get("SEED_TTS_UTMOS_JIT_FILE", "utmos.jit").strip() or "utmos.jit" @@ -322,7 +323,7 @@ def _ensure_utmos_jit_model() -> Any | None: repo, fname, ) - path = hf_hub_download(repo_id=repo, filename=fname, repo_type="model") + path = hf_api().hf_hub_download(repo_id=repo, filename=fname, repo_type="model") # TODO The model weights in UTMOS must be loaded in cuda:0; otherwise, the model execution will fail. want = "cuda:0" diff --git a/vllm_omni/diffusion/model_loader/hub_prefetch.py b/vllm_omni/diffusion/model_loader/hub_prefetch.py index 9a9295ab9d1..a308b3b3838 100644 --- a/vllm_omni/diffusion/model_loader/hub_prefetch.py +++ b/vllm_omni/diffusion/model_loader/hub_prefetch.py @@ -295,7 +295,7 @@ def prefetch_subfolders( logger.info("Prefetching %s subfolders: %s", model, subfolders) try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api except ImportError: # pragma: no cover - huggingface_hub is a hard dep logger.debug("huggingface_hub unavailable; skipping prefetch of %s", model) return @@ -332,7 +332,7 @@ def prefetch_subfolders( for attempt in range(1, _PREFETCH_MAX_ATTEMPTS + 1): try: with _repo_prefetch_lock(model): - snapshot_download( + hf_api().snapshot_download( repo_id=model, allow_patterns=allow_patterns, ) diff --git a/vllm_omni/diffusion/models/cosmos3/sound_tokenizer.py b/vllm_omni/diffusion/models/cosmos3/sound_tokenizer.py index 5d6ff8dd968..5b1fa022168 100644 --- a/vllm_omni/diffusion/models/cosmos3/sound_tokenizer.py +++ b/vllm_omni/diffusion/models/cosmos3/sound_tokenizer.py @@ -338,9 +338,9 @@ def from_config(cls, od_config: OmniDiffusionConfig) -> Cosmos3SoundTokenizer: model_root = str(model_path) if model_path and os.path.isdir(model_path) else None if model_root is None and model_path and not explicit_avae_path: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - model_root = snapshot_download( + model_root = hf_api().snapshot_download( repo_id=str(model_path), revision=getattr(od_config, "revision", None), allow_patterns=[ diff --git a/vllm_omni/diffusion/models/dreamzero/pipeline_dreamzero.py b/vllm_omni/diffusion/models/dreamzero/pipeline_dreamzero.py index 711b345d225..0b6cfa9bfb6 100644 --- a/vllm_omni/diffusion/models/dreamzero/pipeline_dreamzero.py +++ b/vllm_omni/diffusion/models/dreamzero/pipeline_dreamzero.py @@ -21,7 +21,6 @@ import numpy as np import torch import torch.nn as nn -from huggingface_hub import hf_hub_download from transformers import AutoTokenizer, UMT5Config, UMT5EncoderModel from vllm.model_executor.model_loader.weight_utils import default_weight_loader @@ -62,6 +61,7 @@ ARDiffusionKVCacheSpec, ) from vllm_omni.inputs.data import OmniDiffusionSamplingParams +from vllm_omni.transformers_utils.repo_utils import hf_api logger = logging.getLogger(__name__) MAX_DREAMZERO_SESSIONS = 64 @@ -527,7 +527,7 @@ def _load_repo_json(model_path: str, relative_path: str, local_files_only: bool) return json.load(f) try: - json_path = hf_hub_download(model_path, relative_path) + json_path = hf_api().hf_hub_download(model_path, relative_path) with open(json_path) as f: return json.load(f) except Exception: diff --git a/vllm_omni/diffusion/models/helios/pipeline_helios.py b/vllm_omni/diffusion/models/helios/pipeline_helios.py index 90ee0ab3f83..013f27d8a4d 100644 --- a/vllm_omni/diffusion/models/helios/pipeline_helios.py +++ b/vllm_omni/diffusion/models/helios/pipeline_helios.py @@ -73,9 +73,9 @@ def load_json_config(model_path: str, subfolder: str, filename: str, local_files return json.load(f) else: try: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - config_path = hf_hub_download( + config_path = hf_api().hf_hub_download( repo_id=model_path, filename=f"{subfolder}/{filename}", ) diff --git a/vllm_omni/diffusion/models/ltx2/ltx2_components.py b/vllm_omni/diffusion/models/ltx2/ltx2_components.py index cfe990a4e09..d0517e78e3f 100644 --- a/vllm_omni/diffusion/models/ltx2/ltx2_components.py +++ b/vllm_omni/diffusion/models/ltx2/ltx2_components.py @@ -16,7 +16,6 @@ from diffusers.pipelines.ltx2 import LTX2TextConnectors from diffusers.pipelines.ltx2.vocoder import LTX2Vocoder from diffusers.video_processor import VideoProcessor -from huggingface_hub import hf_hub_download from transformers import AutoTokenizer, Gemma3ForConditionalGeneration from vllm_omni.diffusion.distributed.autoencoders.autoencoder_kl_ltx2 import DistributedAutoencoderKLLTX2Video @@ -24,6 +23,7 @@ from vllm_omni.diffusion.model_loader.diffusers_loader import DiffusersPipelineLoader from vllm_omni.diffusion.model_loader.hub_prefetch import from_pretrained_with_prefetch, prefetch_subfolders from vllm_omni.diffusion.offloader.module_collector import ModuleDiscovery +from vllm_omni.transformers_utils.repo_utils import hf_api if TYPE_CHECKING: from vllm.model_executor.layers.quantization.base_config import QuantizationConfig @@ -87,7 +87,7 @@ def _detect_vocoder_output_sample_rate(model: str) -> int | None: vocoder_config_path = os.path.join(model, "vocoder", "config.json") if not os.path.exists(vocoder_config_path): try: - vocoder_config_path = hf_hub_download(model, "vocoder/config.json") + vocoder_config_path = hf_api().hf_hub_download(model, "vocoder/config.json") except Exception: return None try: @@ -267,9 +267,7 @@ def load_transformer_config( return json.load(config_file) else: try: - from huggingface_hub import hf_hub_download - - config_path = hf_hub_download( + config_path = hf_api().hf_hub_download( repo_id=model_path, filename=f"{subfolder}/config.json", ) diff --git a/vllm_omni/diffusion/models/magi_human/pipeline_magi_human.py b/vllm_omni/diffusion/models/magi_human/pipeline_magi_human.py index 513d9333547..be9f2b0fb3d 100644 --- a/vllm_omni/diffusion/models/magi_human/pipeline_magi_human.py +++ b/vllm_omni/diffusion/models/magi_human/pipeline_magi_human.py @@ -1649,9 +1649,9 @@ def _load_json(model_path: str, filename: str, local_files_only: bool = True) -> with open(path) as f: return json.load(f) else: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - cached = hf_hub_download(repo_id=model_path, filename=filename) + cached = hf_api().hf_hub_download(repo_id=model_path, filename=filename) with open(cached) as f: return json.load(f) @@ -1669,12 +1669,12 @@ def _resolve_subdir( """ if local_files_only: return os.path.join(model_path, subfolder) - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api files = required_files or ["config.json"] last_cached: str | None = None for fname in files: - last_cached = hf_hub_download(repo_id=model_path, filename=f"{subfolder}/{fname}") + last_cached = hf_api().hf_hub_download(repo_id=model_path, filename=f"{subfolder}/{fname}") return os.path.dirname(last_cached) diff --git a/vllm_omni/diffusion/models/omnivoice/pipeline_omnivoice.py b/vllm_omni/diffusion/models/omnivoice/pipeline_omnivoice.py index 62ac027ca8b..68a35bbd105 100644 --- a/vllm_omni/diffusion/models/omnivoice/pipeline_omnivoice.py +++ b/vllm_omni/diffusion/models/omnivoice/pipeline_omnivoice.py @@ -141,9 +141,9 @@ def __init__(self, *, od_config: OmniDiffusionConfig, prefix: str = ""): # Resolve model path (HF hub ID → local cache) if not os.path.isdir(self.model_path): - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - self.model_path = snapshot_download(self.model_path) + self.model_path = hf_api().snapshot_download(self.model_path) # Load OmniVoice config config_path = os.path.join(self.model_path, "config.json") diff --git a/vllm_omni/diffusion/models/sensenova_u1/pipeline_sensenova_u1.py b/vllm_omni/diffusion/models/sensenova_u1/pipeline_sensenova_u1.py index 67e141e6ae6..98d7d0ffa66 100644 --- a/vllm_omni/diffusion/models/sensenova_u1/pipeline_sensenova_u1.py +++ b/vllm_omni/diffusion/models/sensenova_u1/pipeline_sensenova_u1.py @@ -94,9 +94,9 @@ def _resolve_model_path(model_path: str) -> str: """Resolve a HuggingFace model ID or local path to a local directory.""" if os.path.isdir(model_path): return model_path - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model_path) + return hf_api().snapshot_download(model_path) def _round_by_factor(number: float, factor: int) -> int: diff --git a/vllm_omni/diffusion/models/soulx_singer/utils.py b/vllm_omni/diffusion/models/soulx_singer/utils.py index 4f201c4077f..0ff94049339 100644 --- a/vllm_omni/diffusion/models/soulx_singer/utils.py +++ b/vllm_omni/diffusion/models/soulx_singer/utils.py @@ -5,11 +5,11 @@ import numpy as np import torch import torchaudio -from huggingface_hub import snapshot_download from omegaconf import DictConfig, OmegaConf from vllm.logger import init_logger from vllm_omni.diffusion.data import OmniDiffusionConfig +from vllm_omni.transformers_utils.repo_utils import hf_api logger = init_logger(__name__) @@ -182,7 +182,7 @@ def resolve_preprocess_weights_root(od_config: OmniDiffusionConfig) -> Path: "SoulX preprocess weights not found locally; downloading %s", _PREPROCESS_WEIGHTS_REPO, ) - downloaded = snapshot_download( + downloaded = hf_api().snapshot_download( _PREPROCESS_WEIGHTS_REPO, allow_patterns=["*"], local_dir=local_dir, diff --git a/vllm_omni/diffusion/models/utils.py b/vllm_omni/diffusion/models/utils.py index 122646219ff..10b525002bd 100644 --- a/vllm_omni/diffusion/models/utils.py +++ b/vllm_omni/diffusion/models/utils.py @@ -140,8 +140,8 @@ def _load_json(model_path: str, filename: str, local_files_only: bool = True) -> with open(path) as f: return json.load(f) else: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - cached = hf_hub_download(repo_id=model_path, filename=filename) + cached = hf_api().hf_hub_download(repo_id=model_path, filename=filename) with open(cached) as f: return json.load(f) diff --git a/vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2.py b/vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2.py index d3726e08760..ce792673c1b 100644 --- a/vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2.py +++ b/vllm_omni/diffusion/models/wan2_2/pipeline_wan2_2.py @@ -110,9 +110,9 @@ def load_transformer_config(model_path: str, subfolder: str = "transformer", loc else: # Try to download config from HF Hub try: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - config_path = hf_hub_download( + config_path = hf_api().hf_hub_download( repo_id=model_path, filename=f"{subfolder}/config.json", ) @@ -301,9 +301,9 @@ def __init__( else: # For remote models, download and read model_index.json try: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - model_index_path = hf_hub_download(repo_id=model, filename="model_index.json") + model_index_path = hf_api().hf_hub_download(repo_id=model, filename="model_index.json") with open(model_index_path) as f: model_index = json.load(f) self.expand_timesteps = model_index.get("expand_timesteps", False) diff --git a/vllm_omni/engine/arg_utils.py b/vllm_omni/engine/arg_utils.py index 19db55c63fc..926ec65b2a3 100644 --- a/vllm_omni/engine/arg_utils.py +++ b/vllm_omni/engine/arg_utils.py @@ -305,9 +305,9 @@ def create_model_config(self) -> OmniModelConfig: if subfolder: # Download just the tokenizer files from the subfolder try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - local_dir = snapshot_download( + local_dir = hf_api().snapshot_download( model_path, allow_patterns=[ f"{subfolder}/tokenizer*", diff --git a/vllm_omni/engine/stage_init_utils.py b/vllm_omni/engine/stage_init_utils.py index 9861d7b3847..02060a5408c 100644 --- a/vllm_omni/engine/stage_init_utils.py +++ b/vllm_omni/engine/stage_init_utils.py @@ -69,10 +69,10 @@ def _resolve_model_to_local_path(model: str) -> str: return model try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api # Keep init path resolution offline-friendly. - return snapshot_download(model, local_files_only=True) + return hf_api().snapshot_download(model, local_files_only=True) except Exception: logger.warning( "[stage_init] Could not resolve %s to local snapshot; using as-is", diff --git a/vllm_omni/entrypoints/openai/api_server.py b/vllm_omni/entrypoints/openai/api_server.py index 56c7ccf4604..55cf9c240c5 100755 --- a/vllm_omni/entrypoints/openai/api_server.py +++ b/vllm_omni/entrypoints/openai/api_server.py @@ -171,9 +171,9 @@ def _load_model_chat_template_json(model: str) -> str | None: if template_path is None: try: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - template_path = hf_hub_download( + template_path = hf_api().hf_hub_download( repo_id=model, filename="chat_template.json", local_files_only=True, diff --git a/vllm_omni/entrypoints/openai/serving_speech.py b/vllm_omni/entrypoints/openai/serving_speech.py index ca3b8c21fce..fce4a2a6619 100644 --- a/vllm_omni/entrypoints/openai/serving_speech.py +++ b/vllm_omni/entrypoints/openai/serving_speech.py @@ -2827,9 +2827,9 @@ def _apply_cosyvoice3_dynamic_tokens( if tokenizer is None: model_path = self.engine_client.model_config.model if not os.path.isdir(model_path): - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - model_path = snapshot_download(model_path) + model_path = hf_api().snapshot_download(model_path) tokenizer = get_qwen_tokenizer( token_path=os.path.join(model_path, hf_cfg.qwen_pretrain_path), skip_special_tokens=hf_cfg.skip_special_tokens, diff --git a/vllm_omni/model_executor/model_loader/weight_utils.py b/vllm_omni/model_executor/model_loader/weight_utils.py index c5225ec0287..7a0f6ca0d59 100644 --- a/vllm_omni/model_executor/model_loader/weight_utils.py +++ b/vllm_omni/model_executor/model_loader/weight_utils.py @@ -9,7 +9,10 @@ if envs.VLLM_USE_MODELSCOPE: from modelscope.hub.snapshot_download import snapshot_download else: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api + + def snapshot_download(*args, **kwargs): + return hf_api().snapshot_download(*args, **kwargs) logger = init_logger(__name__) diff --git a/vllm_omni/model_executor/models/bagel/bagel.py b/vllm_omni/model_executor/models/bagel/bagel.py index a3dfd7bf151..95a956f1fee 100644 --- a/vllm_omni/model_executor/models/bagel/bagel.py +++ b/vllm_omni/model_executor/models/bagel/bagel.py @@ -122,9 +122,9 @@ def _patch_max_latent_size(self, config): if p.is_dir(): index_path = p / "model.safetensors.index.json" else: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - index_path = Path(hf_hub_download(model_name, "model.safetensors.index.json")) + index_path = Path(hf_api().hf_hub_download(model_name, "model.safetensors.index.json")) if not index_path.exists(): return diff --git a/vllm_omni/model_executor/models/cosyvoice3/cosyvoice3.py b/vllm_omni/model_executor/models/cosyvoice3/cosyvoice3.py index b3e17856526..80ca807552e 100644 --- a/vllm_omni/model_executor/models/cosyvoice3/cosyvoice3.py +++ b/vllm_omni/model_executor/models/cosyvoice3/cosyvoice3.py @@ -10,7 +10,6 @@ import onnxruntime import torch import torch.nn as nn -from huggingface_hub import snapshot_download from scipy.signal import resample_poly from transformers import Qwen2Config from transformers.feature_extraction_utils import BatchFeature @@ -51,6 +50,7 @@ from vllm_omni.model_executor.models.output_templates import OmniOutput from vllm_omni.platforms import current_omni_platform from vllm_omni.transformers_utils.configs.cosyvoice3 import CosyVoice3Config +from vllm_omni.transformers_utils.repo_utils import hf_api from vllm_omni.utils.speaker_cache import get_speaker_cache logger = init_logger(__name__) @@ -123,7 +123,7 @@ def _build_runtime_components(self, model_dir: str, config: CosyVoice3Config) -> """Build the per-model runtime components once (cached process-wide).""" # If model_dir is an HF repo ID (not a local path), resolve to cache. if not os.path.isdir(model_dir): - model_dir = snapshot_download(model_dir) + model_dir = hf_api().snapshot_download(model_dir) tokenizer = get_qwen_tokenizer( token_path=os.path.join(model_dir, config.qwen_pretrain_path), @@ -449,7 +449,7 @@ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): self.model_stage = vllm_config.model_config.model_stage model_dir = vllm_config.model_config.model if not os.path.isdir(model_dir): - model_dir = snapshot_download(model_dir) + model_dir = hf_api().snapshot_download(model_dir) self.model_dir = model_dir self.model = None if self.model_stage == "cosyvoice3_talker": @@ -895,7 +895,7 @@ def _resolve_flow_estimator_onnx(self) -> str | None: repo = getattr(self.config, "flow_estimator_onnx_repo", None) if repo: try: - fetched_dir = snapshot_download(repo, allow_patterns=[fp16_name]) + fetched_dir = hf_api().snapshot_download(repo, allow_patterns=[fp16_name]) fetched = os.path.join(fetched_dir, fp16_name) if os.path.exists(fetched): return fetched diff --git a/vllm_omni/model_executor/models/covo_audio/covo_audio_code2wav.py b/vllm_omni/model_executor/models/covo_audio/covo_audio_code2wav.py index 4b0b2fe4bea..e770c2d643c 100644 --- a/vllm_omni/model_executor/models/covo_audio/covo_audio_code2wav.py +++ b/vllm_omni/model_executor/models/covo_audio/covo_audio_code2wav.py @@ -25,9 +25,9 @@ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): if os.path.isdir(model_name): model_path = model_name else: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - model_path = snapshot_download(model_name) + model_path = hf_api().snapshot_download(model_name) token2wav_path = os.path.join(model_path, "token2wav") code2wav_config = CovoAudioCode2WavConfig() diff --git a/vllm_omni/model_executor/models/dynin_omni/dynin_omni_common.py b/vllm_omni/model_executor/models/dynin_omni/dynin_omni_common.py index 5c67decaa56..d8146d32dcc 100644 --- a/vllm_omni/model_executor/models/dynin_omni/dynin_omni_common.py +++ b/vllm_omni/model_executor/models/dynin_omni/dynin_omni_common.py @@ -20,9 +20,9 @@ logger = init_logger(__name__) try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api except Exception: # pragma: no cover - snapshot_download = None + hf_api = None class DetokTarget(IntEnum): @@ -397,13 +397,13 @@ def _find_dynin_config_under_root(root: Path) -> Path | None: @lru_cache(maxsize=16) def _resolve_dynin_config_from_hf_repo(repo_id: str) -> str | None: - if not _looks_like_hf_repo_id(repo_id) or snapshot_download is None: + if not _looks_like_hf_repo_id(repo_id) or hf_api is None: return None try: snapshot_dir = ( Path( - snapshot_download( + hf_api().snapshot_download( repo_id=repo_id, repo_type="model", allow_patterns=list(_DYNIN_CONFIG_CANDIDATE_RELPATHS), @@ -719,7 +719,7 @@ def _resolve_remote_snapshot_dir( if source_path.is_dir(): return str(source_path.resolve()) - if snapshot_download is None: + if hf_api is None: raise RuntimeError("huggingface_hub is required to load remote code.") kwargs: dict[str, Any] = { @@ -732,10 +732,10 @@ def _resolve_remote_snapshot_dir( kwargs["revision"] = revision try: - return str(snapshot_download(**kwargs)) + return str(hf_api().snapshot_download(**kwargs)) except TypeError: kwargs.pop("local_files_only", None) - return str(snapshot_download(**kwargs)) + return str(hf_api().snapshot_download(**kwargs)) def _ensure_remote_package(snapshot_dir: str) -> str: diff --git a/vllm_omni/model_executor/models/dynin_omni/dynin_omni_token2audio.py b/vllm_omni/model_executor/models/dynin_omni/dynin_omni_token2audio.py index 80bc0a78b47..5be18a8de0c 100644 --- a/vllm_omni/model_executor/models/dynin_omni/dynin_omni_token2audio.py +++ b/vllm_omni/model_executor/models/dynin_omni/dynin_omni_token2audio.py @@ -80,7 +80,7 @@ def _ensure_remote_s2u_vendor_root( return str(existing_path) try: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api except Exception as e: logger.warning("huggingface_hub unavailable; cannot fetch s2u_vendor from %s: %s", repo_id, e) return None @@ -91,7 +91,7 @@ def _ensure_remote_s2u_vendor_root( for revision in revisions: try: - snapshot_dir = snapshot_download( + snapshot_dir = hf_api().snapshot_download( repo_id=repo_id, revision=revision, allow_patterns=["s2u_vendor/**"], @@ -99,7 +99,7 @@ def _ensure_remote_s2u_vendor_root( ) except TypeError: try: - snapshot_dir = snapshot_download( + snapshot_dir = hf_api().snapshot_download( repo_id=repo_id, revision=revision, allow_patterns=["s2u_vendor/**"], diff --git a/vllm_omni/model_executor/models/glm_tts/glm_tts.py b/vllm_omni/model_executor/models/glm_tts/glm_tts.py index 664a794c4c0..b7bd1ccc1ef 100644 --- a/vllm_omni/model_executor/models/glm_tts/glm_tts.py +++ b/vllm_omni/model_executor/models/glm_tts/glm_tts.py @@ -113,9 +113,9 @@ def resolve_glm_tts_tokenizer_path(model_name_or_path: Any) -> str: return candidate return model_path - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - local_dir = snapshot_download( + local_dir = hf_api().snapshot_download( model_path, allow_patterns=[ f"{_GLM_TTS_TOKENIZER_SUBDIR}/tokenizer*", @@ -169,9 +169,9 @@ def _has_files(root: str) -> bool: except Exception: pass - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model_name_or_path) + return hf_api().snapshot_download(model_name_or_path) def _first_glm_tts_value(value: Any) -> Any: diff --git a/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_code2wav.py b/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_code2wav.py index 91609e1a4ee..e53e7e1ce14 100644 --- a/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_code2wav.py +++ b/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_code2wav.py @@ -231,9 +231,9 @@ def _resolve_hf_id_to_local(repo_id: str) -> str: if os.path.isfile(os.path.join(candidate, "config.json")): return candidate - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(repo_id=repo_id) + return hf_api().snapshot_download(repo_id=repo_id) # ------------------------------------------------------ direct decode API @torch.inference_mode() diff --git a/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_tokenizer.py b/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_tokenizer.py index c3f5024b3bc..78326161f3c 100755 --- a/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_tokenizer.py +++ b/vllm_omni/model_executor/models/higgs_audio_v2/higgs_audio_v2_tokenizer.py @@ -248,9 +248,9 @@ def _load_audio_tokenizer(): audio_tokenizer_dir = _resolve_audio_tokenizer_dir() if audio_tokenizer_dir is None: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - repo_path = snapshot_download( + repo_path = hf_api().snapshot_download( _K2_OMNIVOICE_REPO, allow_patterns=[f"{_K2_OMNIVOICE_SUBDIR}/*"], ) diff --git a/vllm_omni/model_executor/models/indextts2/preprocess_utils.py b/vllm_omni/model_executor/models/indextts2/preprocess_utils.py index 452bce9bb2b..d37b6eab4a5 100644 --- a/vllm_omni/model_executor/models/indextts2/preprocess_utils.py +++ b/vllm_omni/model_executor/models/indextts2/preprocess_utils.py @@ -90,9 +90,10 @@ def load_semantic_codec(model_path: str, config: dict, device: torch.device): codec.load_state_dict(state, strict=False) else: import safetensors.torch - from huggingface_hub import hf_hub_download - ckpt_path = hf_hub_download("amphion/MaskGCT", filename="semantic_codec/model.safetensors") + from vllm_omni.transformers_utils.repo_utils import hf_api + + ckpt_path = hf_api().hf_hub_download("amphion/MaskGCT", filename="semantic_codec/model.safetensors") safetensors.torch.load_model(codec, ckpt_path) _freeze(codec.to(device=device, dtype=torch.float32)) _semantic_codec_cache[cache_key] = codec @@ -108,9 +109,9 @@ def load_campplus(model_path: str, device: torch.device): campplus = CAMPPlus(feat_dim=80, embedding_size=192) ckpt_path = resolve_model_file(model_path, "campplus.pth") if ckpt_path is None: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api - ckpt_path = hf_hub_download("funasr/campplus", filename="campplus_cn_common.bin") + ckpt_path = hf_api().hf_hub_download("funasr/campplus", filename="campplus_cn_common.bin") state = torch.load(ckpt_path, map_location="cpu", weights_only=True) campplus.load_state_dict(state, strict=False) _freeze(campplus.to(device=device, dtype=torch.float32)) diff --git a/vllm_omni/model_executor/models/indextts2/s2mel/modules/bigvgan.py b/vllm_omni/model_executor/models/indextts2/s2mel/modules/bigvgan.py index 5876bb14742..271278bd695 100644 --- a/vllm_omni/model_executor/models/indextts2/s2mel/modules/bigvgan.py +++ b/vllm_omni/model_executor/models/indextts2/s2mel/modules/bigvgan.py @@ -9,13 +9,14 @@ import torch import torch.nn as nn -from huggingface_hub import PyTorchModelHubMixin, hf_hub_download +from huggingface_hub import PyTorchModelHubMixin from torch.nn import Conv1d, ConvTranspose1d from torch.nn.utils import remove_weight_norm, weight_norm from vllm_omni.model_executor.models.common.alias_free_activation import AliasFreeActivation1d from vllm_omni.model_executor.models.common.snake_activation import Snake, SnakeBeta from vllm_omni.model_executor.models.indextts2.s2mel.modules.commons import AttrDict +from vllm_omni.transformers_utils.repo_utils import hf_api # --------------------------------------------------------------------------- # Helpers (inlined from env.py / utils.py) @@ -328,7 +329,7 @@ def _from_pretrained(cls, *, model_id: str, map_location: str = "cpu", **kwargs) def _resolve(filename): if os.path.isdir(model_id): return os.path.join(model_id, filename) - return hf_hub_download( + return hf_api().hf_hub_download( repo_id=model_id, filename=filename, **{ diff --git a/vllm_omni/model_executor/models/ming_tts/speaker_extractor.py b/vllm_omni/model_executor/models/ming_tts/speaker_extractor.py index 00523c26fd4..e196b19d754 100644 --- a/vllm_omni/model_executor/models/ming_tts/speaker_extractor.py +++ b/vllm_omni/model_executor/models/ming_tts/speaker_extractor.py @@ -14,9 +14,9 @@ def resolve_model_to_local_path(model): if os.path.isdir(model): return model - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model) + return hf_api().snapshot_download(model) class MingSpeakerEmbeddingExtractor: diff --git a/vllm_omni/model_executor/models/omnivoice/omnivoice.py b/vllm_omni/model_executor/models/omnivoice/omnivoice.py index c167aa1c0a1..c0f6c06dac9 100644 --- a/vllm_omni/model_executor/models/omnivoice/omnivoice.py +++ b/vllm_omni/model_executor/models/omnivoice/omnivoice.py @@ -487,9 +487,9 @@ def _resolve_model_dir(self) -> str: if os.path.isdir(model_dir): return model_dir # HF hub model ID — resolve to local cache - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model_dir) + return hf_api().snapshot_download(model_dir) def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: try: diff --git a/vllm_omni/model_executor/models/step_audio2/step_audio2_token2wav.py b/vllm_omni/model_executor/models/step_audio2/step_audio2_token2wav.py index 026276c52aa..25a2e3c3859 100644 --- a/vllm_omni/model_executor/models/step_audio2/step_audio2_token2wav.py +++ b/vllm_omni/model_executor/models/step_audio2/step_audio2_token2wav.py @@ -391,9 +391,9 @@ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): if model_path is None: # Resolve HF repo names to local cache path if not os.path.isdir(model_name_or_path): - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - model_name_or_path = snapshot_download(model_name_or_path) + model_name_or_path = hf_api().snapshot_download(model_name_or_path) model_path = f"{model_name_or_path}/token2wav" float16 = getattr(self.config, "token2wav_float16", False) diff --git a/vllm_omni/model_executor/models/voxtral_tts/voxtral_tts.py b/vllm_omni/model_executor/models/voxtral_tts/voxtral_tts.py index 40d4ec62069..ab7972b48fc 100644 --- a/vllm_omni/model_executor/models/voxtral_tts/voxtral_tts.py +++ b/vllm_omni/model_executor/models/voxtral_tts/voxtral_tts.py @@ -6,7 +6,6 @@ import regex as re import torch import torch.nn as nn -from huggingface_hub import hf_hub_download from vllm.config import VllmConfig from vllm.logger import init_logger from vllm.model_executor.models.interfaces import SupportsMultiModal @@ -28,6 +27,7 @@ VoxtralTTSMultiModalProcessor, VoxtralTTSProcessingInfo, ) +from vllm_omni.transformers_utils.repo_utils import hf_api logger = init_logger(__name__) @@ -131,7 +131,7 @@ def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): self.voice_to_embedding = {} for sid in speaker_id: if self.is_hf_model: - path = hf_hub_download(repo_id=self.repo_id, filename=f"voice_embedding/{sid}.pt") + path = hf_api().hf_hub_download(repo_id=self.repo_id, filename=f"voice_embedding/{sid}.pt") else: path = Path(self.repo_id) / "voice_embedding" / f"{sid}.pt" if Path(path).exists(): diff --git a/vllm_omni/transformers_utils/repo_utils.py b/vllm_omni/transformers_utils/repo_utils.py new file mode 100644 index 00000000000..ea05c22dc1a --- /dev/null +++ b/vllm_omni/transformers_utils/repo_utils.py @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +"""Utilities for model repo interaction.""" + +import huggingface_hub +from huggingface_hub import HfApi + +from vllm_omni.version import __version__ as VLLM_OMNI_VERSION + +_hf_api: HfApi | None = None + + +def hf_api() -> HfApi: + """Return a shared HfApi instance tagged with vLLM-Omni's library info.""" + global _hf_api + if _hf_api is None: + _hf_api = HfApi( + library_name="vllm-omni", + library_version=VLLM_OMNI_VERSION, + ) + return _hf_api + + +def hf_fs() -> "huggingface_hub.HfFileSystem": + """Return a fresh HfFileSystem tagged with vLLM-Omni's library info.""" + return huggingface_hub.HfFileSystem( + library_name="vllm-omni", + library_version=VLLM_OMNI_VERSION, + ) From 4ca208289cab2f7251298474fdf1786d53fc88dc Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:24:21 +0100 Subject: [PATCH 2/9] pre-commit Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- .../diffusion/model_loader/test_diffusers_loader.py | 2 +- tools/pre_commit/check_forbidden_imports.py | 12 +++--------- .../model_executor/model_loader/weight_utils.py | 1 + 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/tests/diffusion/model_loader/test_diffusers_loader.py b/tests/diffusion/model_loader/test_diffusers_loader.py index 93ee4e150b8..8ca4bed7989 100644 --- a/tests/diffusion/model_loader/test_diffusers_loader.py +++ b/tests/diffusion/model_loader/test_diffusers_loader.py @@ -11,13 +11,13 @@ import torch import torch.nn as nn from vllm.config.load import LoadConfig -from vllm_omni.transformers_utils.repo_utils import hf_api from vllm_omni.diffusion.config import get_current_diffusion_config, get_current_diffusion_config_or_none from vllm_omni.diffusion.data import OmniDiffusionConfig from vllm_omni.diffusion.model_loader.diffusers_loader import DiffusersPipelineLoader from vllm_omni.diffusion.models.helios import HeliosPipeline from vllm_omni.diffusion.registry import initialize_model +from vllm_omni.transformers_utils.repo_utils import hf_api pytestmark = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu] diff --git a/tools/pre_commit/check_forbidden_imports.py b/tools/pre_commit/check_forbidden_imports.py index bd1c2d858a5..24f23c2083c 100644 --- a/tools/pre_commit/check_forbidden_imports.py +++ b/tools/pre_commit/check_forbidden_imports.py @@ -31,10 +31,7 @@ class ForbiddenImport: r"^\s*(import\s+(pickle|cloudpickle)(\s|$|\sas)" r"|from\s+(pickle|cloudpickle)\s+import\b)" ), - tip=( - "Avoid using pickle or cloudpickle or add this file to " - "tools/pre_commit/check_forbidden_imports.py." - ), + tip=("Avoid using pickle or cloudpickle or add this file to tools/pre_commit/check_forbidden_imports.py."), allowed_files={ "tests/helpers/process.py", "vllm_omni/diffusion/distributed/group_coordinator.py", @@ -118,9 +115,7 @@ def test_regex(): pickle_pattern = re.compile(CHECK_IMPORTS["pickle/cloudpickle"].pattern) for i, (line, should_match) in enumerate(pickle_cases): result = bool(pickle_pattern.match(line)) - assert result == should_match, ( - f"pickle case {i} failed: '{line}' (expected {should_match}, got {result})" - ) + assert result == should_match, f"pickle case {i} failed: '{line}' (expected {should_match}, got {result})" hf_cases = [ # Should match @@ -146,8 +141,7 @@ def test_regex(): for i, (line, should_match) in enumerate(hf_cases): result = bool(hf_pattern.search(line)) assert result == should_match, ( - f"huggingface_hub case {i} failed: '{line}' " - f"(expected {should_match}, got {result})" + f"huggingface_hub case {i} failed: '{line}' (expected {should_match}, got {result})" ) print("All regex tests passed.") diff --git a/vllm_omni/model_executor/model_loader/weight_utils.py b/vllm_omni/model_executor/model_loader/weight_utils.py index 7a0f6ca0d59..dd0683517d7 100644 --- a/vllm_omni/model_executor/model_loader/weight_utils.py +++ b/vllm_omni/model_executor/model_loader/weight_utils.py @@ -14,6 +14,7 @@ def snapshot_download(*args, **kwargs): return hf_api().snapshot_download(*args, **kwargs) + logger = init_logger(__name__) From 8717863ff049f1743766311da6b60cdddafb7f9c Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:35:54 +0100 Subject: [PATCH 3/9] review comments Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- tests/benchmarks/test_accuracy_bench_utils.py | 9 +++------ .../models/cosmos3/test_cosmos3_sound_tokenizer.py | 5 ++--- vllm_omni/transformers_utils/repo_utils.py | 9 --------- 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/tests/benchmarks/test_accuracy_bench_utils.py b/tests/benchmarks/test_accuracy_bench_utils.py index 222b8c2c7b3..84628c4e181 100644 --- a/tests/benchmarks/test_accuracy_bench_utils.py +++ b/tests/benchmarks/test_accuracy_bench_utils.py @@ -3,7 +3,6 @@ import math import os import sys -import types from pathlib import Path import pytest @@ -84,11 +83,9 @@ def fake_snapshot_download(*, repo_id, repo_type, allow_patterns): captured["allow_patterns"] = allow_patterns return str(downloaded_root) - monkeypatch.setitem( - sys.modules, - "huggingface_hub", - types.SimpleNamespace(snapshot_download=fake_snapshot_download), - ) + from vllm_omni.transformers_utils import repo_utils + + monkeypatch.setattr(repo_utils.hf_api(), "snapshot_download", fake_snapshot_download) resolved = resolve_seed_tts_root( "zhaochenyang20/seed-tts-eval", diff --git a/tests/diffusion/models/cosmos3/test_cosmos3_sound_tokenizer.py b/tests/diffusion/models/cosmos3/test_cosmos3_sound_tokenizer.py index 7ab04cc212f..6b2db3f250a 100644 --- a/tests/diffusion/models/cosmos3/test_cosmos3_sound_tokenizer.py +++ b/tests/diffusion/models/cosmos3/test_cosmos3_sound_tokenizer.py @@ -81,9 +81,8 @@ def test_from_config_loads_local_diffusers_component(tmp_path, monkeypatch: pyte def test_from_config_downloads_component_from_hf_repo(tmp_path, monkeypatch: pytest.MonkeyPatch) -> None: - import huggingface_hub - from vllm_omni.diffusion.models.cosmos3 import sound_tokenizer + from vllm_omni.transformers_utils import repo_utils cache_dir = tmp_path / "hf" _write_component(cache_dir, checkpoint_name=DIFFUSERS_SOUND_TOKENIZER_CHECKPOINT_NAME) @@ -95,7 +94,7 @@ def fake_snapshot_download(repo_id: str, *, revision: str | None, allow_patterns calls.append((repo_id, revision, allow_patterns)) return str(cache_dir) - monkeypatch.setattr(huggingface_hub, "snapshot_download", fake_snapshot_download) + monkeypatch.setattr(repo_utils.hf_api(), "snapshot_download", fake_snapshot_download) sound_tokenizer.Cosmos3SoundTokenizer.from_config( SimpleNamespace( diff --git a/vllm_omni/transformers_utils/repo_utils.py b/vllm_omni/transformers_utils/repo_utils.py index ea05c22dc1a..fe42a4a0174 100644 --- a/vllm_omni/transformers_utils/repo_utils.py +++ b/vllm_omni/transformers_utils/repo_utils.py @@ -2,7 +2,6 @@ # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Utilities for model repo interaction.""" -import huggingface_hub from huggingface_hub import HfApi from vllm_omni.version import __version__ as VLLM_OMNI_VERSION @@ -19,11 +18,3 @@ def hf_api() -> HfApi: library_version=VLLM_OMNI_VERSION, ) return _hf_api - - -def hf_fs() -> "huggingface_hub.HfFileSystem": - """Return a fresh HfFileSystem tagged with vLLM-Omni's library info.""" - return huggingface_hub.HfFileSystem( - library_name="vllm-omni", - library_version=VLLM_OMNI_VERSION, - ) From a36c4dc59254a8ea63e57b8f459b18fe49a7d5c0 Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Fri, 24 Jul 2026 17:40:28 +0100 Subject: [PATCH 4/9] better regex Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- tools/pre_commit/check_forbidden_imports.py | 56 ++++++++++++--------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/tools/pre_commit/check_forbidden_imports.py b/tools/pre_commit/check_forbidden_imports.py index 24f23c2083c..d6a8e7b4025 100644 --- a/tools/pre_commit/check_forbidden_imports.py +++ b/tools/pre_commit/check_forbidden_imports.py @@ -7,6 +7,9 @@ import regex as re +# Hub entry points that must go through the library-tagged ``hf_api()`` helper. +_HF_NAMES = r"snapshot_download|hf_hub_download|HfApi|HfFileSystem|get_safetensors_metadata" + @dataclass class ForbiddenImport: @@ -40,14 +43,14 @@ class ForbiddenImport: ), "huggingface_hub": ForbiddenImport( pattern=( - r"^\s*from\s+huggingface_hub\s+import\b[^\n]*" - r"\b(?:snapshot_download|hf_hub_download|HfApi|HfFileSystem" - r"|get_safetensors_metadata)\b" + r"^\s*from\s+huggingface_hub\s+import\s*\([^)]*\b(?:" + _HF_NAMES + r")\b" + r"|" + r"^\s*from\s+huggingface_hub\s+import\b[^\n]*\b(?:" + _HF_NAMES + r")\b" ), tip=( - "Use 'hf_api()' / 'hf_fs()' from " - "'vllm_omni.transformers_utils.repo_utils' instead, so requests are " - "tagged with vLLM-Omni's library info." + "Use 'hf_api()' from 'vllm_omni.transformers_utils.repo_utils' (or " + "add a tagged helper there) instead, so requests are tagged with " + "vLLM-Omni's library info." ), allowed_files={"vllm_omni/transformers_utils/repo_utils.py"}, allowed_dirs={"examples", "benchmarks"}, @@ -59,20 +62,22 @@ def check_file(path: str) -> int: with open(path, encoding="utf-8") as f: content = f.read() return_code = 0 + parts = PurePath(path).parts + top_dir = parts[0] if parts else None # Check all patterns in the whole file for import_name, forbidden_import in CHECK_IMPORTS.items(): - # Skip files that are allowed for this import - if path in forbidden_import.allowed_files: - continue - # Skip files whose top-level directory is exempt from this import - parts = PurePath(path).parts - if parts and parts[0] in forbidden_import.allowed_dirs: + # Skip files/directories that are allowed for this import + if path in forbidden_import.allowed_files or top_dir in forbidden_import.allowed_dirs: continue # Search for forbidden imports for match in re.finditer(forbidden_import.pattern, content, re.MULTILINE): # Check if it's allowed if forbidden_import.allowed_pattern.match(match.group()): continue + # Skip matches inside a comment + line_start = content.rfind("\n", 0, match.start()) + 1 + if "#" in content[line_start : match.start()]: + continue # Calculate line number from match position line_num = content[: match.start() + 1].count("\n") + 1 print( @@ -92,13 +97,16 @@ def main(): def test_regex(): + def matches(rule: str, content: str) -> bool: + return bool(re.search(CHECK_IMPORTS[rule].pattern, content, re.MULTILINE)) + pickle_cases = [ # Should match ("import pickle", True), ("import cloudpickle", True), ("import pickle as pkl", True), ("import cloudpickle as cpkl", True), - ("from pickle import *", True), + ("from pickle import loads", True), ("from cloudpickle import dumps", True), ("from pickle import dumps, loads", True), ("from cloudpickle import (dumps, loads)", True), @@ -112,10 +120,9 @@ def test_regex(): ("print('import pickle')", False), ("import pickleas as asdf", False), ] - pickle_pattern = re.compile(CHECK_IMPORTS["pickle/cloudpickle"].pattern) - for i, (line, should_match) in enumerate(pickle_cases): - result = bool(pickle_pattern.match(line)) - assert result == should_match, f"pickle case {i} failed: '{line}' (expected {should_match}, got {result})" + for i, (content, should_match) in enumerate(pickle_cases): + result = matches("pickle/cloudpickle", content) + assert result == should_match, f"pickle case {i} failed: {content!r} (expected {should_match}, got {result})" hf_cases = [ # Should match @@ -126,6 +133,10 @@ def test_regex(): ("from huggingface_hub import get_safetensors_metadata", True), (" from huggingface_hub import snapshot_download", True), ("from huggingface_hub import PyTorchModelHubMixin, hf_hub_download", True), + ("from huggingface_hub import (snapshot_download)", True), + # Parenthesized multi-line import must not bypass the hook + ("from huggingface_hub import (\n snapshot_download,\n)", True), + ("from huggingface_hub import (\n PyTorchModelHubMixin,\n HfApi,\n)", True), # Should not match ("import huggingface_hub", False), ("import huggingface_hub as hf", False), @@ -134,14 +145,13 @@ def test_regex(): ("from huggingface_hub.constants import HF_HUB_CACHE", False), ("from huggingface_hub.utils import EntryNotFoundError", False), ("from vllm_omni.transformers_utils.repo_utils import hf_api", False), - ("# resolves via ``huggingface_hub.snapshot_download``", False), - (' """Falls back to snapshot_download for remote repos."""', False), + ("from huggingface_hub import (\n PyTorchModelHubMixin,\n)", False), + ("# from huggingface_hub import snapshot_download", False), ] - hf_pattern = re.compile(CHECK_IMPORTS["huggingface_hub"].pattern, re.MULTILINE) - for i, (line, should_match) in enumerate(hf_cases): - result = bool(hf_pattern.search(line)) + for i, (content, should_match) in enumerate(hf_cases): + result = matches("huggingface_hub", content) assert result == should_match, ( - f"huggingface_hub case {i} failed: '{line}' (expected {should_match}, got {result})" + f"huggingface_hub case {i} failed: {content!r} (expected {should_match}, got {result})" ) print("All regex tests passed.") From b4932e12d0fb25a2356a83993dcfadd306086a93 Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:22:50 +0100 Subject: [PATCH 5/9] fix ltx2 Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- vllm_omni/diffusion/models/ltx2/ltx2_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_omni/diffusion/models/ltx2/ltx2_components.py b/vllm_omni/diffusion/models/ltx2/ltx2_components.py index 0818cdd7ebd..18c205115af 100644 --- a/vllm_omni/diffusion/models/ltx2/ltx2_components.py +++ b/vllm_omni/diffusion/models/ltx2/ltx2_components.py @@ -127,7 +127,7 @@ def _load_ltx_metadata_json(model: str, filename: str) -> dict[str, Any]: return {} else: try: - path = hf_hub_download(repo_id=model, filename=filename) + path = hf_api().hf_hub_download(repo_id=model, filename=filename) except Exception: return {} try: From aaaa001155dcd1d01dfaacb552be3546d55a78db Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:23:52 +0100 Subject: [PATCH 6/9] keep suggestion last Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- .pre-commit-config.yaml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6b27c629e5..06960a95071 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -61,6 +61,13 @@ repos: types: [python] files: ^tests/ + - id: check-forbidden-imports + name: Check for forbidden imports + entry: python tools/pre_commit/check_forbidden_imports.py + language: python + types: [python] + additional_dependencies: [regex] + # Keep `suggestion` last - id: suggestion name: Suggestion @@ -69,10 +76,3 @@ repos: verbose: true pass_filenames: false # Insert new entries above the `suggestion` entry - - - id: check-forbidden-imports - name: Check for forbidden imports - entry: python tools/pre_commit/check_forbidden_imports.py - language: python - types: [python] - additional_dependencies: [regex] From d0c4d8e95f34894ee44f419155e12a1ad19bf4c7 Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Tue, 4 Aug 2026 08:31:19 +0100 Subject: [PATCH 7/9] fix new errors Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- .../perf/scripts/run_diffusion_benchmark.py | 4 ++-- .../ltx/test_ltx_official_similarity.py | 8 ++++---- .../test_minimax_h3_t2va_similarity.py | 4 ++-- .../features/rlhf_test/test_verl_omni_e2e.py | 4 ++-- tests/helpers/minicpmo_4_5_duplex.py | 4 ++-- .../model_executor/models/audex/checkpoint.py | 18 +++++++++--------- .../minicpmo_4_5/minicpmo_4_5_code2wav.py | 4 ++-- 7 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/dfx/perf/scripts/run_diffusion_benchmark.py b/tests/dfx/perf/scripts/run_diffusion_benchmark.py index 375d2a440f6..df222f55de7 100644 --- a/tests/dfx/perf/scripts/run_diffusion_benchmark.py +++ b/tests/dfx/perf/scripts/run_diffusion_benchmark.py @@ -361,9 +361,9 @@ def _resolve_offline_model(model: str) -> str: if not model or os.path.isdir(model) or not huggingface_hub.constants.HF_HUB_OFFLINE: return model - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model, local_files_only=True) + return hf_api().snapshot_download(model, local_files_only=True) class DiffusionServer: diff --git a/tests/e2e/accuracy/ltx/test_ltx_official_similarity.py b/tests/e2e/accuracy/ltx/test_ltx_official_similarity.py index 647ece7a74e..5409d331426 100644 --- a/tests/e2e/accuracy/ltx/test_ltx_official_similarity.py +++ b/tests/e2e/accuracy/ltx/test_ltx_official_similarity.py @@ -23,11 +23,11 @@ import numpy as np import pytest import torch -from huggingface_hub import hf_hub_download, snapshot_download from torchmetrics.image import PeakSignalNoiseRatio, StructuralSimilarityIndexMeasure from tests.e2e.accuracy.helpers import reset_artifact_dir from tests.helpers.mark import hardware_test +from vllm_omni.transformers_utils.repo_utils import hf_api OFFICIAL_REPOSITORY = "https://github.com/Lightricks/LTX-2.git" OFFICIAL_REVISION = "9377758131b1ffde4b7f766804590a6617bf2ab9" @@ -202,7 +202,7 @@ def _resolve_model(case: LTXAccuracyCase) -> Path: if revision is None and model_id == case.model_id: revision = case.model_revision return Path( - snapshot_download( + hf_api().snapshot_download( repo_id=model_id, revision=revision, allow_patterns=[ @@ -242,7 +242,7 @@ def _resolve_checkpoint(case: LTXAccuracyCase, model: Path) -> Path: if model_checkpoint.is_file(): return model_checkpoint return Path( - hf_hub_download( + hf_api().hf_hub_download( repo_id=case.checkpoint_repo, filename=case.checkpoint_filename, revision=case.checkpoint_revision, @@ -261,7 +261,7 @@ def _resolve_image(case: LTXAccuracyCase) -> Path | None: assert image.is_file(), f"LTX I2V conditioning image not found: {image}" return image return Path( - hf_hub_download( + hf_api().hf_hub_download( repo_id=case.image_repo, repo_type="dataset", filename=case.image_filename, diff --git a/tests/e2e/accuracy/minimax_h3/test_minimax_h3_t2va_similarity.py b/tests/e2e/accuracy/minimax_h3/test_minimax_h3_t2va_similarity.py index f86e9d5f5d9..251acc66abc 100644 --- a/tests/e2e/accuracy/minimax_h3/test_minimax_h3_t2va_similarity.py +++ b/tests/e2e/accuracy/minimax_h3/test_minimax_h3_t2va_similarity.py @@ -11,7 +11,6 @@ import pytest import requests import torch -from huggingface_hub import snapshot_download from tests.e2e.accuracy.helpers import ( assert_video_metadata, @@ -22,6 +21,7 @@ ) from tests.helpers.mark import hardware_test from tests.helpers.runtime import OmniServer +from vllm_omni.transformers_utils.repo_utils import hf_api pytestmark = [pytest.mark.benchmark, pytest.mark.diffusion, pytest.mark.full_model] @@ -55,7 +55,7 @@ def _model_name() -> str: if configured: return configured - snapshot_root = snapshot_download( + snapshot_root = hf_api().snapshot_download( repo_id=MODEL_REPO_ID, revision=MODEL_REVISION, allow_patterns=["FL2VA/**"], diff --git a/tests/e2e/features/rlhf_test/test_verl_omni_e2e.py b/tests/e2e/features/rlhf_test/test_verl_omni_e2e.py index dab5feef1f8..2c2a91e8a99 100644 --- a/tests/e2e/features/rlhf_test/test_verl_omni_e2e.py +++ b/tests/e2e/features/rlhf_test/test_verl_omni_e2e.py @@ -35,7 +35,6 @@ import pytest import ray import torch -from huggingface_hub import snapshot_download from omegaconf import OmegaConf from transformers import AutoTokenizer @@ -46,6 +45,7 @@ vLLMOmniHttpServerLocal, ) from tests.helpers.mark import hardware_test +from vllm_omni.transformers_utils.repo_utils import hf_api MODEL = "tiny-random/Qwen-Image" TOKENIZER_MODEL = "Qwen/Qwen2-1.5B-Instruct" @@ -55,7 +55,7 @@ def _resolve_model_path(repo_id: str) -> str: if os.path.isdir(repo_id): return repo_id - return snapshot_download(repo_id=repo_id) + return hf_api().snapshot_download(repo_id=repo_id) @lru_cache(maxsize=1) diff --git a/tests/helpers/minicpmo_4_5_duplex.py b/tests/helpers/minicpmo_4_5_duplex.py index e19903e8490..8b906c2dd5d 100644 --- a/tests/helpers/minicpmo_4_5_duplex.py +++ b/tests/helpers/minicpmo_4_5_duplex.py @@ -9,10 +9,10 @@ from types import SimpleNamespace import pytest -from huggingface_hub import snapshot_download from tests.helpers.runtime import OmniServerParams from tests.helpers.stage_config import get_deploy_config_path, modify_stage_config +from vllm_omni.transformers_utils.repo_utils import hf_api MODEL = "openbmb/MiniCPM-o-4_5" DEPLOY_CONFIG = modify_stage_config( @@ -110,7 +110,7 @@ def resolve_ref_audio(model_prefix: str) -> Path: if model_prefix: model_root = Path(model_prefix) / MODEL else: - model_root = Path(snapshot_download(MODEL, local_files_only=True)) + model_root = Path(hf_api().snapshot_download(MODEL, local_files_only=True)) ref_audio = model_root / REF_AUDIO_RELATIVE_PATH if not ref_audio.is_file(): raise FileNotFoundError(f"MiniCPM-o checkpoint ref audio is missing: {ref_audio}") diff --git a/vllm_omni/model_executor/models/audex/checkpoint.py b/vllm_omni/model_executor/models/audex/checkpoint.py index 38b0531e660..3568a064cba 100644 --- a/vllm_omni/model_executor/models/audex/checkpoint.py +++ b/vllm_omni/model_executor/models/audex/checkpoint.py @@ -57,12 +57,12 @@ def _download_audiogen_index(model: str) -> str: metadata round trip per stage-engine boot; a cache miss falls back to the network. """ - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api try: - return hf_hub_download(model, _AUDIOGEN_INDEX_FILE, local_files_only=True) + return hf_api().hf_hub_download(model, _AUDIOGEN_INDEX_FILE, local_files_only=True) except Exception: - return hf_hub_download(model, _AUDIOGEN_INDEX_FILE) + return hf_api().hf_hub_download(model, _AUDIOGEN_INDEX_FILE) def _dedup_shard_patterns(model: str) -> list[str]: @@ -111,13 +111,13 @@ def ensure_audex_snapshot(model: str, profile: str = "tts") -> str: if profile in ("tts", "tta"): patterns += _dedup_shard_patterns(model) - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api try: - return snapshot_download(model, allow_patterns=patterns) + return hf_api().snapshot_download(model, allow_patterns=patterns) except Exception as download_exc: try: - return snapshot_download(model, allow_patterns=patterns, local_files_only=True) + return hf_api().snapshot_download(model, allow_patterns=patterns, local_files_only=True) except Exception: raise RuntimeError( f"Could not resolve the Audex repo {model!r} (profile {profile!r}): the download " @@ -135,13 +135,13 @@ def ensure_xcodec1_snapshot(model: str | None) -> str: if os.path.isdir(model): return model - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api try: - return snapshot_download(model) + return hf_api().snapshot_download(model) except Exception as download_exc: try: - return snapshot_download(model, local_files_only=True) + return hf_api().snapshot_download(model, local_files_only=True) except Exception: raise RuntimeError( f"Could not resolve the XCodec1 checkpoint {model!r}: the download failed and " diff --git a/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_code2wav.py b/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_code2wav.py index 92eccb55277..651aa2dfae1 100644 --- a/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_code2wav.py +++ b/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_code2wav.py @@ -39,9 +39,9 @@ def _resolve_model_dir(model_ref: str, revision: str | None = None) -> str: """ if Path(model_ref).is_dir(): return model_ref - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api - return snapshot_download(model_ref, revision=revision, allow_patterns=["assets/*"]) + return hf_api().snapshot_download(model_ref, revision=revision, allow_patterns=["assets/*"]) def _batch_error(reason: str, **details: Any) -> RuntimeError: From b2d3e1e3de5e0c83d9e1ff38577e583b123dea27 Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Fri, 7 Aug 2026 09:47:10 +0100 Subject: [PATCH 8/9] fix new forbidden imports Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- tests/dfx/perf/scripts/run_diffusion_benchmark.py | 8 +++----- .../minimax_h3/test_minimax_h3_quantization_quality.py | 8 ++++---- .../experimental/fullduplex/personaplex/runtime.py | 10 +++++----- .../fullduplex/personaplex/serving/server.py | 4 ++-- .../models/personaplex/personaplex_mimi.py | 5 +++-- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/tests/dfx/perf/scripts/run_diffusion_benchmark.py b/tests/dfx/perf/scripts/run_diffusion_benchmark.py index bac3b24c4ae..55baa7d6d08 100644 --- a/tests/dfx/perf/scripts/run_diffusion_benchmark.py +++ b/tests/dfx/perf/scripts/run_diffusion_benchmark.py @@ -359,6 +359,8 @@ def _resolve_offline_model(model: str) -> str: """ import huggingface_hub + from vllm_omni.transformers_utils.repo_utils import hf_api + if not model or os.path.isdir(model): return model @@ -383,9 +385,7 @@ def _resolve_offline_model(model: str) -> str: if len(parts) >= 3: repo_id = "/".join(parts[:2]) subfolder = "/".join(parts[2:]) - from huggingface_hub import snapshot_download - - snapshot_root = snapshot_download( + snapshot_root = hf_api().snapshot_download( repo_id, allow_patterns=[f"{subfolder}/**"], local_files_only=huggingface_hub.constants.HF_HUB_OFFLINE, @@ -394,8 +394,6 @@ def _resolve_offline_model(model: str) -> str: if not huggingface_hub.constants.HF_HUB_OFFLINE: return model - from vllm_omni.transformers_utils.repo_utils import hf_api - return hf_api().snapshot_download(model, local_files_only=True) diff --git a/tests/diffusion/models/minimax_h3/test_minimax_h3_quantization_quality.py b/tests/diffusion/models/minimax_h3/test_minimax_h3_quantization_quality.py index 80d99895f3e..73e62f20553 100644 --- a/tests/diffusion/models/minimax_h3/test_minimax_h3_quantization_quality.py +++ b/tests/diffusion/models/minimax_h3/test_minimax_h3_quantization_quality.py @@ -42,10 +42,10 @@ def _resolve_fl2va_model_ref() -> str: - from huggingface_hub import snapshot_download + from vllm_omni.transformers_utils.repo_utils import hf_api repo_root = Path( - snapshot_download( + hf_api().snapshot_download( repo_id=_MINIMAX_H3_REPO, revision=_MINIMAX_H3_REVISION, allow_patterns=["FL2VA/**"], @@ -258,13 +258,13 @@ def test_resolve_fl2va_model_ref(tmp_path, monkeypatch): fl2va_root.mkdir() (fl2va_root / "model_index.json").write_text("{}", encoding="utf-8") - def fake_snapshot_download(*, repo_id, revision, allow_patterns): + def fake_snapshot_download(self, *, repo_id, revision, allow_patterns): assert repo_id == _MINIMAX_H3_REPO assert revision == _MINIMAX_H3_REVISION assert allow_patterns == ["FL2VA/**"] return str(tmp_path) - monkeypatch.setattr("huggingface_hub.snapshot_download", fake_snapshot_download) + monkeypatch.setattr("huggingface_hub.HfApi.snapshot_download", fake_snapshot_download) assert _resolve_fl2va_model_ref() == str(fl2va_root) diff --git a/vllm_omni/experimental/fullduplex/personaplex/runtime.py b/vllm_omni/experimental/fullduplex/personaplex/runtime.py index 885b36aa5c7..5ea591ce10d 100644 --- a/vllm_omni/experimental/fullduplex/personaplex/runtime.py +++ b/vllm_omni/experimental/fullduplex/personaplex/runtime.py @@ -103,7 +103,6 @@ def load(self) -> PersonaPlexEngine: return self import sentencepiece import torch - from huggingface_hub import hf_hub_download from safetensors.torch import load_file from vllm_omni.model_executor.models.personaplex.configuration_personaplex import ( @@ -121,6 +120,7 @@ def load(self) -> PersonaPlexEngine: from vllm_omni.model_executor.models.personaplex.personaplex_temporal import ( PersonaPlexTemporalStreaming, ) + from vllm_omni.transformers_utils.repo_utils import hf_api cfg = self.config B = cfg.batch_size @@ -128,11 +128,11 @@ def load(self) -> PersonaPlexEngine: self._codec = PersonaPlexMimiCodec(device=cfg.device) self._codec.streaming_init(B) - tok_path = hf_hub_download(cfg.hf_repo, "tokenizer_spm_32k_3.model") + tok_path = hf_api().hf_hub_download(cfg.hf_repo, "tokenizer_spm_32k_3.model") self._tokenizer = sentencepiece.SentencePieceProcessor(tok_path) logger.info("PersonaPlex native: loading temporal + depformer + embeddings") - sd = load_file(hf_hub_download(cfg.hf_repo, "model.safetensors"), device=cfg.device) + sd = load_file(hf_api().hf_hub_download(cfg.hf_repo, "model.safetensors"), device=cfg.device) dtype = torch.bfloat16 self._temporal = PersonaPlexTemporalStreaming().to(cfg.device, dtype) self._temporal.load_weights(sd) @@ -181,11 +181,11 @@ def _load_voice(self, voice: str) -> None: self._loaded_voice = voice def _resolve_voice_prompt(self, voice: str) -> str: - from huggingface_hub import hf_hub_download + from vllm_omni.transformers_utils.repo_utils import hf_api if os.path.exists(voice): return voice - tgz = Path(hf_hub_download(self.config.hf_repo, "voices.tgz")) + tgz = Path(hf_api().hf_hub_download(self.config.hf_repo, "voices.tgz")) vdir = tgz.parent / "voices" if not vdir.exists(): with tarfile.open(tgz, "r:gz") as tar: diff --git a/vllm_omni/experimental/fullduplex/personaplex/serving/server.py b/vllm_omni/experimental/fullduplex/personaplex/serving/server.py index fcb85762871..5e464823b48 100644 --- a/vllm_omni/experimental/fullduplex/personaplex/serving/server.py +++ b/vllm_omni/experimental/fullduplex/personaplex/serving/server.py @@ -41,7 +41,6 @@ import numpy as np import sphn from aiohttp import WSMsgType, web -from huggingface_hub import hf_hub_download from vllm_omni.experimental.fullduplex.personaplex.config import PersonaPlexConfig from vllm_omni.experimental.fullduplex.personaplex.engine import PersonaPlexEngine @@ -51,6 +50,7 @@ PersonaPlexSession, offer_realtime, ) +from vllm_omni.transformers_utils.repo_utils import hf_api logger = logging.getLogger(__name__) _HF_REPO = "nvidia/personaplex-7b-v1" @@ -81,7 +81,7 @@ def _opus_decode(reader: sphn.OpusStreamReader, data: bytes) -> np.ndarray | Non def _official_web_dir() -> Path | None: """Download + extract the official PersonaPlex web client (dist.tgz).""" try: - tgz = Path(hf_hub_download(_HF_REPO, "dist.tgz")) + tgz = Path(hf_api().hf_hub_download(_HF_REPO, "dist.tgz")) dist = tgz.parent / "dist" if not dist.exists(): with tarfile.open(tgz, "r:gz") as tar: diff --git a/vllm_omni/model_executor/models/personaplex/personaplex_mimi.py b/vllm_omni/model_executor/models/personaplex/personaplex_mimi.py index 48df20fab1e..0add6ccb80c 100644 --- a/vllm_omni/model_executor/models/personaplex/personaplex_mimi.py +++ b/vllm_omni/model_executor/models/personaplex/personaplex_mimi.py @@ -288,10 +288,11 @@ class PersonaPlexMimiCodec(nn.Module): def __init__(self, hf_repo: str = DEFAULT_HF_REPO, checkpoint: str | None = None, device: str = "cuda") -> None: super().__init__() - from huggingface_hub import hf_hub_download from safetensors.torch import load_file from transformers import MimiConfig, MimiModel + from vllm_omni.transformers_utils.repo_utils import hf_api + self.device = torch.device(device) # The PersonaPlex repo ships the reference mimi checkpoint in the moshi @@ -300,7 +301,7 @@ def __init__(self, hf_repo: str = DEFAULT_HF_REPO, checkpoint: str | None = None # transformer stacks remain absent because the streaming implementations # below replace them with the checkpoint's fused QKV layout. if checkpoint is None: - checkpoint = hf_hub_download( + checkpoint = hf_api().hf_hub_download( "nvidia/personaplex-7b-v1", "tokenizer-e351c8d8-checkpoint125.safetensors", ) From 75480fee1766f83740eed0114dd079c48026892f Mon Sep 17 00:00:00 2001 From: Harry Mellor <19981378+hmellor@users.noreply.github.com> Date: Tue, 8 Sep 2026 10:59:11 +0100 Subject: [PATCH 9/9] fix tests Signed-off-by: Harry Mellor <19981378+hmellor@users.noreply.github.com> --- tests/diffusion/model_loader/test_hub_prefetch.py | 6 +++--- tests/diffusion/models/ltx2/test_ltx2_pipeline.py | 13 ++++++++----- tests/engine/test_arg_utils.py | 14 +++++++++----- .../models/minicpmo_4_5/test_code2wav_model_dir.py | 10 +++++----- 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/tests/diffusion/model_loader/test_hub_prefetch.py b/tests/diffusion/model_loader/test_hub_prefetch.py index 39eb2b27711..94170edcc3f 100644 --- a/tests/diffusion/model_loader/test_hub_prefetch.py +++ b/tests/diffusion/model_loader/test_hub_prefetch.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project import contextlib @@ -14,10 +14,10 @@ def test_prefetch_subfolders_propagates_revision(monkeypatch): calls = [] - def fake_snapshot_download(**kwargs): + def fake_snapshot_download(self, **kwargs): calls.append(kwargs) - monkeypatch.setattr(huggingface_hub, "snapshot_download", fake_snapshot_download) + monkeypatch.setattr(huggingface_hub.HfApi, "snapshot_download", fake_snapshot_download) monkeypatch.setattr(hub_prefetch, "_repo_prefetch_lock", lambda _model: contextlib.nullcontext()) hub_prefetch.prefetch_subfolders( diff --git a/tests/diffusion/models/ltx2/test_ltx2_pipeline.py b/tests/diffusion/models/ltx2/test_ltx2_pipeline.py index 9cafa22b6b2..aeccf7e9a4c 100644 --- a/tests/diffusion/models/ltx2/test_ltx2_pipeline.py +++ b/tests/diffusion/models/ltx2/test_ltx2_pipeline.py @@ -10,6 +10,7 @@ from types import SimpleNamespace from typing import Any +import huggingface_hub import pytest import torch @@ -182,11 +183,11 @@ def test_ltx_artifact_uses_source_revision_and_hub_fallback( filename = "ltx-sidecar.safetensors" calls = [] - def fake_download(**kwargs): + def fake_download(self, **kwargs): calls.append(kwargs) return "/cache/ltx-sidecar.safetensors" - monkeypatch.setattr(ltx2_components, "hf_hub_download", fake_download) + monkeypatch.setattr(huggingface_hub.HfApi, "hf_hub_download", fake_download) assert ( resolve_ltx_artifact( @@ -211,7 +212,9 @@ def test_ltx_artifact_prefers_model_root(tmp_path, monkeypatch): filename = "ltx-sidecar.safetensors" expected = tmp_path / filename expected.write_bytes(b"sidecar") - monkeypatch.setattr(ltx2_components, "hf_hub_download", lambda **_kwargs: pytest.fail("unexpected Hub lookup")) + monkeypatch.setattr( + huggingface_hub.HfApi, "hf_hub_download", lambda *_args, **_kwargs: pytest.fail("unexpected Hub lookup") + ) assert resolve_ltx_artifact( str(tmp_path), @@ -225,11 +228,11 @@ def test_ltx_artifact_prefers_model_root(tmp_path, monkeypatch): def test_ltx_artifact_local_model_missing_sidecar_falls_back_to_hub(tmp_path, monkeypatch): calls = [] - def fake_download(**kwargs): + def fake_download(self, **kwargs): calls.append(kwargs) return "/cache/ltx-sidecar.safetensors" - monkeypatch.setattr(ltx2_components, "hf_hub_download", fake_download) + monkeypatch.setattr(huggingface_hub.HfApi, "hf_hub_download", fake_download) assert ( resolve_ltx_artifact( diff --git a/tests/engine/test_arg_utils.py b/tests/engine/test_arg_utils.py index fb8231a75c6..0e88a00ef88 100644 --- a/tests/engine/test_arg_utils.py +++ b/tests/engine/test_arg_utils.py @@ -1,3 +1,6 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project + """ Tests for Omni config utils. For stability, these tests should largely be invariant to the specific attributes of vLLM config except in cases where we @@ -63,7 +66,7 @@ class ConnectorRunner(OmniConnectorModelRunnerMixin): class ConnectorWorker: model_runner_cls = ConnectorRunner - worker_module.ConnectorWorker = ConnectorWorker + setattr(worker_module, "ConnectorWorker", ConnectorWorker) monkeypatch.setitem(sys.modules, worker_module.__name__, worker_module) monkeypatch.setattr(OmniEngineArgs, "_ensure_omni_models_registered", lambda _self: None) @@ -95,7 +98,7 @@ def test_stage_without_connector_configuration_accepts_plain_runner(monkeypatch) class WorkerWithoutConnector: model_runner_cls = object - worker_module.WorkerWithoutConnector = WorkerWithoutConnector + setattr(worker_module, "WorkerWithoutConnector", WorkerWithoutConnector) monkeypatch.setitem(sys.modules, worker_module.__name__, worker_module) args = OmniEngineArgs( @@ -113,7 +116,7 @@ def test_full_payload_capability_requires_selected_worker_connector(monkeypatch) class WorkerWithoutConnector: model_runner_cls = object - worker_module.WorkerWithoutConnector = WorkerWithoutConnector + setattr(worker_module, "WorkerWithoutConnector", WorkerWithoutConnector) monkeypatch.setitem(sys.modules, worker_module.__name__, worker_module) with pytest.raises(ValueError, match="does not provide an Omni connector model runner"): @@ -130,7 +133,7 @@ def test_full_payload_capability_validates_platform_selected_worker(monkeypatch) class WorkerWithoutConnector: model_runner_cls = object - worker_module.WorkerWithoutConnector = WorkerWithoutConnector + setattr(worker_module, "WorkerWithoutConnector", WorkerWithoutConnector) monkeypatch.setitem(sys.modules, worker_module.__name__, worker_module) monkeypatch.setattr( current_omni_platform, @@ -293,7 +296,7 @@ def test_remote_tokenizer_subfolder_download_does_not_report_failure(tmp_path, m baseline_config = Mock() warning = mocker.patch("vllm_omni.engine.arg_utils.logger.warning") - monkeypatch.setattr("huggingface_hub.snapshot_download", lambda *args, **kwargs: str(tmp_path)) + monkeypatch.setattr("huggingface_hub.HfApi.snapshot_download", lambda *args, **kwargs: str(tmp_path)) monkeypatch.setattr(OmniEngineArgs, "_patch_empty_hf_config", lambda *args, **kwargs: None) monkeypatch.setattr(EngineArgs, "create_model_config", lambda _self: baseline_config) monkeypatch.setattr( @@ -339,6 +342,7 @@ def test_patch_missing_local_hf_config(tmp_path): def test_non_missing_local_hf_config_error_reaches_parent_loader(tmp_path, monkeypatch, config_entry): """Non-missing config errors must reach vLLM's normal loader.""" config_path = tmp_path / "config.json" + loader_error: Exception if config_entry == "malformed": config_path.write_text("{not valid json", encoding="utf-8") loader_error = json.JSONDecodeError("invalid config", "{not valid json", 1) diff --git a/tests/model_executor/models/minicpmo_4_5/test_code2wav_model_dir.py b/tests/model_executor/models/minicpmo_4_5/test_code2wav_model_dir.py index 5aa78262ae8..8d5aeade657 100644 --- a/tests/model_executor/models/minicpmo_4_5/test_code2wav_model_dir.py +++ b/tests/model_executor/models/minicpmo_4_5/test_code2wav_model_dir.py @@ -1,5 +1,5 @@ # SPDX-License-Identifier: Apache-2.0 -# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project """Unit tests for MiniCPM-o 4.5 code2wav model-dir resolution (#5442). In hub/CI deployments ``model_config.model`` is a repo id rather than a local @@ -27,7 +27,7 @@ def _no_hub(monkeypatch): def _fail(*args, **kwargs): # pragma: no cover - must not be reached raise AssertionError("snapshot_download must not be called here") - monkeypatch.setattr(huggingface_hub, "snapshot_download", _fail) + monkeypatch.setattr(huggingface_hub.HfApi, "snapshot_download", _fail) def test_local_directory_is_returned_unchanged(tmp_path, monkeypatch): @@ -38,13 +38,13 @@ def test_local_directory_is_returned_unchanged(tmp_path, monkeypatch): def test_repo_id_resolves_via_snapshot_download(tmp_path, monkeypatch): calls = {} - def _fake_snapshot_download(model_ref, revision=None, allow_patterns=None): + def _fake_snapshot_download(self, model_ref, revision=None, allow_patterns=None): calls["model_ref"] = model_ref calls["revision"] = revision calls["allow_patterns"] = allow_patterns return str(tmp_path / "snapshot") - monkeypatch.setattr(huggingface_hub, "snapshot_download", _fake_snapshot_download) + monkeypatch.setattr(huggingface_hub.HfApi, "snapshot_download", _fake_snapshot_download) resolved = _resolve_model_dir("openbmb/MiniCPM-o-4_5", revision="abc123") assert resolved == str(tmp_path / "snapshot") assert calls["model_ref"] == "openbmb/MiniCPM-o-4_5" @@ -56,7 +56,7 @@ def test_snapshot_download_failure_propagates(monkeypatch): def _raise(*args, **kwargs): raise FileNotFoundError("offline and not cached") - monkeypatch.setattr(huggingface_hub, "snapshot_download", _raise) + monkeypatch.setattr(huggingface_hub.HfApi, "snapshot_download", _raise) with pytest.raises(FileNotFoundError): _resolve_model_dir("openbmb/MiniCPM-o-4_5")