fix: route GGUF-only Gemma models safely
This commit is contained in:
@@ -366,6 +366,46 @@
|
||||
"defaults": {
|
||||
"context_length": 131072
|
||||
}
|
||||
},
|
||||
"Gemma-4-E4B-Uncensored-Aggressive-GGUF [Q2:4.4GB|Q3:4.7-4.9GB|Q4:5.1-5.4GB|Q5:5.8GB|Q6:6.3GB|Q8:8.1GB|VRAM:~7GB]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"model_files": [
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q3_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072
|
||||
}
|
||||
},
|
||||
"Gemma-4-E2B-Uncensored-Aggressive-GGUF [Q2:3GB|Q3:3.1GB|Q4:3.4GB|Q5:3.7GB|Q6:3.9GB|Q8:5GB|VRAM:~5GB]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"model_files": [
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072
|
||||
}
|
||||
}
|
||||
},
|
||||
"qwenVL_model": {
|
||||
@@ -611,6 +651,58 @@
|
||||
"pool_size": 4194304
|
||||
}
|
||||
},
|
||||
"Gemma-4-E4B-Uncensored-Aggressive-GGUF [Q2:4.4GB|Q3:4.7-4.9GB|Q4:5.1-5.4GB|Q5:5.8GB|Q6:6.3GB|Q8:8.1GB|VRAM:~7GB]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"mmproj_file": "mmproj-Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-f16.gguf",
|
||||
"model_files": [
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-IQ4_XS.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q3_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_M.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072,
|
||||
"image_max_tokens": 4096,
|
||||
"n_batch": 512,
|
||||
"gpu_layers": -1,
|
||||
"top_k": 64,
|
||||
"pool_size": 4194304
|
||||
}
|
||||
},
|
||||
"Gemma-4-E2B-Uncensored-Aggressive-GGUF [Q2:3GB|Q3:3.1GB|Q4:3.4GB|Q5:3.7GB|Q6:3.9GB|Q8:5GB|VRAM:~5GB]": {
|
||||
"author": "HauhauCS",
|
||||
"repo_name": "Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"repo_id": "HauhauCS/Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"mmproj_file": "mmproj-Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-f16.gguf",
|
||||
"model_files": [
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-IQ3_M.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q2_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q3_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q5_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q6_K_P.gguf",
|
||||
"Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q8_K_P.gguf"
|
||||
],
|
||||
"defaults": {
|
||||
"context_length": 131072,
|
||||
"image_max_tokens": 4096,
|
||||
"n_batch": 512,
|
||||
"gpu_layers": -1,
|
||||
"top_k": 64,
|
||||
"pool_size": 4194304
|
||||
}
|
||||
},
|
||||
"Qwen3-VL-4B-Instruct-c_abliterated-v2-GGUF [Q4:2.4GB|Q5:2.8GB|Q6:3.2GB|Q8:4.2GB|F16:8GB|VRAM:~4GB]": {
|
||||
"author": "mradermacher",
|
||||
"repo_name": "Qwen3-VL-4B-Instruct-c_abliterated-v2-GGUF",
|
||||
|
||||
@@ -209,28 +209,6 @@
|
||||
"8bit": 22.0,
|
||||
"4bit": 14.0
|
||||
}
|
||||
},
|
||||
"Gemma-4-E4B-Uncensored-Aggressive [DL: 16GB, VRAM: 7.0GB]": {
|
||||
"repo_id": "HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"default": false,
|
||||
"quantized": false,
|
||||
"vram_requirement": {
|
||||
"full": 7.0,
|
||||
"8bit": 4.5,
|
||||
"4bit": 2.5
|
||||
}
|
||||
},
|
||||
"Gemma-4-E2B-Uncensored-Aggressive [DL: 10.3GB, VRAM: 4.0GB]": {
|
||||
"repo_id": "HauhauCS/Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"default": false,
|
||||
"quantized": false,
|
||||
"vram_requirement": {
|
||||
"full": 4.0,
|
||||
"8bit": 2.5,
|
||||
"4bit": 1.5
|
||||
}
|
||||
}
|
||||
},
|
||||
"hf_text_models": {
|
||||
@@ -397,28 +375,6 @@
|
||||
"8bit": 22.0,
|
||||
"4bit": 14.0
|
||||
}
|
||||
},
|
||||
"Gemma-4-E4B-Uncensored-Aggressive [DL: 16GB, VRAM: 7.0GB]": {
|
||||
"repo_id": "HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"default": false,
|
||||
"quantized": false,
|
||||
"vram_requirement": {
|
||||
"full": 7.0,
|
||||
"8bit": 4.5,
|
||||
"4bit": 2.5
|
||||
}
|
||||
},
|
||||
"Gemma-4-E2B-Uncensored-Aggressive [DL: 10.3GB, VRAM: 4.0GB]": {
|
||||
"repo_id": "HauhauCS/Gemma-4-E2B-Uncensored-HauhauCS-Aggressive",
|
||||
"commercial_status": "unclear",
|
||||
"default": false,
|
||||
"quantized": false,
|
||||
"vram_requirement": {
|
||||
"full": 4.0,
|
||||
"8bit": 2.5,
|
||||
"4bit": 1.5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+132
-1
@@ -106,6 +106,18 @@ def build_loader_test_stubs() -> dict[str, types.ModuleType]:
|
||||
),
|
||||
"huggingface_hub": build_stub_module(
|
||||
"huggingface_hub",
|
||||
HfApi=type(
|
||||
"HfApi",
|
||||
(),
|
||||
{
|
||||
"__init__": lambda self, *args, **kwargs: None,
|
||||
"list_repo_files": lambda self, *args, **kwargs: [
|
||||
"config.json",
|
||||
"model.safetensors",
|
||||
"processor_config.json",
|
||||
],
|
||||
},
|
||||
),
|
||||
hf_hub_download=lambda *args, **kwargs: "",
|
||||
snapshot_download=lambda *args, **kwargs: "",
|
||||
),
|
||||
@@ -759,7 +771,11 @@ class TestModelRecommendations(unittest.TestCase):
|
||||
|
||||
def test_gemma4_e2b_text_catalog_uses_current_hugging_face_files(self):
|
||||
payload = json.loads((PKG / "gguf_models.json").read_text(encoding="utf-8"))
|
||||
entries = [entry for key, entry in (payload.get("Qwen_model") or {}).items() if "Gemma-4-E2B" in key]
|
||||
entries = [
|
||||
entry
|
||||
for key, entry in (payload.get("Qwen_model") or {}).items()
|
||||
if "Gemma-4-E2B" in key and "Aggressive" not in key
|
||||
]
|
||||
|
||||
self.assertTrue(entries)
|
||||
for entry in entries:
|
||||
@@ -770,6 +786,33 @@ class TestModelRecommendations(unittest.TestCase):
|
||||
"google_gemma-4-E2B-it-bf16.gguf",
|
||||
])
|
||||
|
||||
def test_hauhaucs_gemma4_gguf_only_models_are_not_in_transformers_catalogs(self):
|
||||
hf_payload = json.loads((PKG / "hf_models.json").read_text(encoding="utf-8"))
|
||||
gguf_payload = json.loads((PKG / "gguf_models.json").read_text(encoding="utf-8"))
|
||||
expected = {
|
||||
"HauhauCS/Gemma-4-E4B-Uncensored-HauhauCS-Aggressive": {
|
||||
"model": "Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-Q4_K_M.gguf",
|
||||
"mmproj": "mmproj-Gemma-4-E4B-Uncensored-HauhauCS-Aggressive-f16.gguf",
|
||||
},
|
||||
"HauhauCS/Gemma-4-E2B-Uncensored-HauhauCS-Aggressive": {
|
||||
"model": "Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-Q4_K_P.gguf",
|
||||
"mmproj": "mmproj-Gemma-4-E2B-Uncensored-HauhauCS-Aggressive-f16.gguf",
|
||||
},
|
||||
}
|
||||
|
||||
for hf_section in ("hf_vl_models", "hf_text_models"):
|
||||
repo_ids = {entry.get("repo_id") for entry in (hf_payload.get(hf_section) or {}).values()}
|
||||
self.assertTrue(expected.keys().isdisjoint(repo_ids))
|
||||
|
||||
for repo_id, filenames in expected.items():
|
||||
text_entries = [entry for entry in (gguf_payload.get("Qwen_model") or {}).values() if entry.get("repo_id") == repo_id]
|
||||
vision_entries = [entry for entry in (gguf_payload.get("qwenVL_model") or {}).values() if entry.get("repo_id") == repo_id]
|
||||
self.assertEqual(1, len(text_entries))
|
||||
self.assertEqual(1, len(vision_entries))
|
||||
self.assertIn(filenames["model"], text_entries[0]["model_files"])
|
||||
self.assertIn(filenames["model"], vision_entries[0]["model_files"])
|
||||
self.assertEqual(filenames["mmproj"], vision_entries[0]["mmproj_file"])
|
||||
|
||||
def test_qwen3_asr_gguf_catalog_is_removed(self):
|
||||
gguf_payload = json.loads((PKG / "gguf_models.json").read_text(encoding="utf-8"))
|
||||
vision_catalog = gguf_payload.get("qwenVL_model") or {}
|
||||
@@ -2103,6 +2146,94 @@ class TestHuggingFaceTokenSupport(unittest.TestCase):
|
||||
self.assertIn("<redacted HF token>", message)
|
||||
self.assertIn("access was rejected", message)
|
||||
|
||||
def test_hf_snapshot_rejects_gguf_only_repo_before_download_and_preserves_target(self):
|
||||
module = self._load_qwenvl_with_stubs()
|
||||
access_globals = module._download_model_snapshot.__globals__
|
||||
snapshot_download_mock = mock.Mock(side_effect=AssertionError("GGUF-only repository must not be downloaded as Transformers"))
|
||||
|
||||
class FakeHfApi:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def list_repo_files(self, **kwargs):
|
||||
return ["README.md", "model-Q4_K_M.gguf", "mmproj-f16.gguf"]
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
target = Path(temp_dir) / "existing-model"
|
||||
target.mkdir()
|
||||
sentinel = target / "user-file.gguf"
|
||||
sentinel.write_bytes(b"preserve me")
|
||||
with mock.patch.dict(access_globals, {"HfApi": FakeHfApi, "snapshot_download": snapshot_download_mock}):
|
||||
with self.assertRaisesRegex(RuntimeError, "ThinkingLLM GGUF node"):
|
||||
module._download_model_snapshot("example/gguf-only", target)
|
||||
|
||||
self.assertEqual(b"preserve me", sentinel.read_bytes())
|
||||
snapshot_download_mock.assert_not_called()
|
||||
|
||||
def test_hf_snapshot_stages_validation_and_preserves_existing_files(self):
|
||||
module = self._load_qwenvl_with_stubs()
|
||||
access_globals = module._download_model_snapshot.__globals__
|
||||
download_targets = []
|
||||
|
||||
class FakeHfApi:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def list_repo_files(self, **kwargs):
|
||||
return ["config.json", "model.safetensors", "processor_config.json"]
|
||||
|
||||
def fake_snapshot_download(**kwargs):
|
||||
staging = Path(kwargs["local_dir"])
|
||||
download_targets.append(staging)
|
||||
staging.mkdir(parents=True)
|
||||
(staging / "config.json").write_text("{}", encoding="utf-8")
|
||||
(staging / "processor_config.json").write_text("{}", encoding="utf-8")
|
||||
(staging / "model.safetensors").write_bytes(b"weights")
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
target = Path(temp_dir) / "existing-model"
|
||||
target.mkdir()
|
||||
sentinel = target / "user-note.txt"
|
||||
sentinel.write_text("preserve me", encoding="utf-8")
|
||||
with mock.patch.dict(access_globals, {"HfApi": FakeHfApi, "snapshot_download": mock.Mock(side_effect=fake_snapshot_download)}):
|
||||
module._download_model_snapshot("example/transformers", target, require_processor=True)
|
||||
|
||||
self.assertEqual("preserve me", sentinel.read_text(encoding="utf-8"))
|
||||
self.assertEqual(b"weights", (target / "model.safetensors").read_bytes())
|
||||
self.assertEqual(1, len(download_targets))
|
||||
self.assertNotEqual(target, download_targets[0])
|
||||
|
||||
def test_hf_snapshot_validation_failure_does_not_modify_existing_target(self):
|
||||
module = self._load_qwenvl_with_stubs()
|
||||
access_globals = module._download_model_snapshot.__globals__
|
||||
|
||||
class FakeHfApi:
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def list_repo_files(self, **kwargs):
|
||||
return ["config.json", "model.safetensors"]
|
||||
|
||||
def fake_incomplete_download(**kwargs):
|
||||
staging = Path(kwargs["local_dir"])
|
||||
staging.mkdir(parents=True)
|
||||
(staging / "config.json").write_text("{}", encoding="utf-8")
|
||||
|
||||
with tempfile.TemporaryDirectory() as temp_dir:
|
||||
target = Path(temp_dir) / "existing-model"
|
||||
target.mkdir()
|
||||
sentinel = target / "user-note.txt"
|
||||
sentinel.write_text("preserve me", encoding="utf-8")
|
||||
with mock.patch.dict(
|
||||
access_globals,
|
||||
{"HfApi": FakeHfApi, "snapshot_download": mock.Mock(side_effect=fake_incomplete_download)},
|
||||
):
|
||||
with self.assertRaisesRegex(RuntimeError, "existing target was preserved"):
|
||||
module._download_model_snapshot("example/incomplete", target)
|
||||
|
||||
self.assertEqual([sentinel], list(target.iterdir()))
|
||||
self.assertEqual("preserve me", sentinel.read_text(encoding="utf-8"))
|
||||
|
||||
def test_hf_file_download_failure_raises_redacted_message(self):
|
||||
module = self._load_qwenvl_with_stubs()
|
||||
access_globals = module.download_hf_file_to_path.__globals__
|
||||
|
||||
@@ -689,22 +689,12 @@ def ensure_model(model_name, require_processor=False, node_id=None, progress_lab
|
||||
_download_model_snapshot(
|
||||
repo_id,
|
||||
target,
|
||||
require_processor=require_processor,
|
||||
node_id=node_id,
|
||||
progress_label=progress_label or f"QwenVL HF Download: {model_name}",
|
||||
hf_token=hf_token,
|
||||
)
|
||||
|
||||
if not _model_snapshot_has_required_files(target, require_processor=require_processor):
|
||||
print(f"[QwenVL] Refreshed snapshot is still incomplete, retrying clean download: {target}")
|
||||
_download_model_snapshot(
|
||||
repo_id,
|
||||
target,
|
||||
force_clean_target=True,
|
||||
node_id=node_id,
|
||||
progress_label=progress_label or f"QwenVL HF Download: {model_name}",
|
||||
hf_token=hf_token,
|
||||
)
|
||||
|
||||
if not _model_snapshot_has_required_files(target, require_processor=require_processor):
|
||||
missing = []
|
||||
if not (target / "config.json").exists():
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
import inspect
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from huggingface_hub import hf_hub_download, snapshot_download
|
||||
from huggingface_hub import HfApi, hf_hub_download, snapshot_download
|
||||
except ImportError:
|
||||
from huggingface_hub import snapshot_download
|
||||
HfApi = None
|
||||
hf_hub_download = None
|
||||
try:
|
||||
from tqdm.auto import tqdm
|
||||
@@ -37,6 +39,12 @@ MODEL_STATUS_PRESENTATION = {
|
||||
}
|
||||
MODEL_INSTALLED_SUFFIX = " [installed]"
|
||||
MODEL_LOCAL_PREFIX = "[local] "
|
||||
_PROCESSOR_CONFIG_FILENAMES = (
|
||||
"preprocessor_config.json",
|
||||
"processor_config.json",
|
||||
"image_processor_config.json",
|
||||
"video_preprocessor_config.json",
|
||||
)
|
||||
|
||||
|
||||
def _format_download_size(num_bytes) -> str:
|
||||
@@ -403,43 +411,73 @@ def _model_snapshot_has_required_files(target: Path, require_processor: bool = F
|
||||
return False
|
||||
if not (target / "config.json").exists():
|
||||
return False
|
||||
if require_processor:
|
||||
processor_files = (
|
||||
"preprocessor_config.json",
|
||||
"processor_config.json",
|
||||
"image_processor_config.json",
|
||||
"video_preprocessor_config.json",
|
||||
)
|
||||
if not any((target / name).exists() for name in processor_files):
|
||||
return False
|
||||
if require_processor and not any((target / name).exists() for name in _PROCESSOR_CONFIG_FILENAMES):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _download_model_snapshot(repo_id: str, target: Path, *, force_clean_target: bool = False, node_id=None, progress_label: str | None = None, hf_token: str | None = None) -> None:
|
||||
def _validate_transformers_repo_files(repo_id: str, repo_files: list[str], *, require_processor: bool = False) -> None:
|
||||
root_files = {Path(str(filename)).as_posix() for filename in repo_files if filename}
|
||||
missing = []
|
||||
if "config.json" not in root_files:
|
||||
missing.append("config.json")
|
||||
if not any("/" not in filename and filename.endswith((".safetensors", ".bin")) for filename in root_files):
|
||||
missing.append("Transformers model weights")
|
||||
if require_processor and not any(filename in root_files for filename in _PROCESSOR_CONFIG_FILENAMES):
|
||||
missing.append("processor metadata")
|
||||
if not missing:
|
||||
return
|
||||
|
||||
gguf_hint = " The repository contains GGUF files; select it through a ThinkingLLM GGUF node instead." if any(
|
||||
filename.lower().endswith(".gguf") for filename in root_files
|
||||
) else ""
|
||||
raise ValueError(
|
||||
f"[QwenVL] {repo_id} is not a compatible Transformers snapshot: missing {', '.join(missing)}.{gguf_hint}"
|
||||
)
|
||||
|
||||
|
||||
def _download_model_snapshot(repo_id: str, target: Path, *, require_processor: bool = False, node_id=None, progress_label: str | None = None, hf_token: str | None = None) -> None:
|
||||
if COMMERCIAL_RELEASE:
|
||||
raise RuntimeError("[QwenVL] Commercial release mode only permits preinstalled, hash-locked models.")
|
||||
reporter = _DownloadProgressReporter(node_id=node_id, label=progress_label or "QwenVL HF Download", repo_id=repo_id)
|
||||
token_for_download = _clean_hf_token(hf_token)
|
||||
download_kwargs = {}
|
||||
if force_clean_target and target.exists() and target.is_dir():
|
||||
print(f"[QwenVL] Removing incomplete model snapshot before retry: {target}")
|
||||
reporter.stage("Cleaning incomplete snapshot", detail=target.name)
|
||||
shutil.rmtree(target, ignore_errors=False)
|
||||
reporter.stage("Preparing download", detail=target.name)
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
reporter.stage("Checking repository", detail=target.name)
|
||||
try:
|
||||
download_kwargs = {
|
||||
"repo_id": repo_id,
|
||||
"local_dir": str(target),
|
||||
"ignore_patterns": ["*.md", ".git*"],
|
||||
"force_download": force_clean_target,
|
||||
}
|
||||
if token_for_download:
|
||||
download_kwargs["token"] = token_for_download
|
||||
tqdm_cls = reporter.make_tqdm_class()
|
||||
if tqdm_cls is not None:
|
||||
download_kwargs["tqdm_class"] = tqdm_cls
|
||||
snapshot_download(**download_kwargs)
|
||||
_cleanup_unneeded_snapshot_weights(target)
|
||||
if HfApi is None:
|
||||
raise RuntimeError("huggingface_hub.HfApi is unavailable; update huggingface-hub before downloading models")
|
||||
api = HfApi(token=token_for_download) if token_for_download else HfApi()
|
||||
repo_files = api.list_repo_files(repo_id=repo_id, repo_type="model")
|
||||
_validate_transformers_repo_files(repo_id, repo_files, require_processor=require_processor)
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix=f".{target.name}.download-", dir=target.parent) as temp_dir:
|
||||
staging_target = Path(temp_dir) / "snapshot"
|
||||
reporter.stage("Preparing download", detail=target.name)
|
||||
download_kwargs = {
|
||||
"repo_id": repo_id,
|
||||
"local_dir": str(staging_target),
|
||||
"ignore_patterns": ["*.md", ".git*"],
|
||||
}
|
||||
if token_for_download:
|
||||
download_kwargs["token"] = token_for_download
|
||||
tqdm_cls = reporter.make_tqdm_class()
|
||||
if tqdm_cls is not None:
|
||||
download_kwargs["tqdm_class"] = tqdm_cls
|
||||
snapshot_download(**download_kwargs)
|
||||
_cleanup_unneeded_snapshot_weights(staging_target)
|
||||
if not _model_snapshot_has_required_files(staging_target, require_processor=require_processor):
|
||||
raise FileNotFoundError(
|
||||
f"[QwenVL] Downloaded model snapshot is incomplete for {repo_id}; the existing target was preserved"
|
||||
)
|
||||
|
||||
reporter.stage("Installing validated snapshot", detail=target.name)
|
||||
if target.exists():
|
||||
if not target.is_dir():
|
||||
raise FileExistsError(f"[QwenVL] Model target exists and is not a directory: {target}")
|
||||
shutil.copytree(staging_target, target, dirs_exist_ok=True)
|
||||
else:
|
||||
shutil.move(str(staging_target), str(target))
|
||||
except Exception as exc:
|
||||
reporter.fail(_hf_download_error_message(exc, repo_id=repo_id, hf_token=token_for_download))
|
||||
raise RuntimeError(_hf_download_error_message(exc, repo_id=repo_id, hf_token=token_for_download)) from exc
|
||||
|
||||
Reference in New Issue
Block a user