chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,943 @@
|
||||
# Copied and adapted from: https://github.com/hao-ai-lab/FastVideo
|
||||
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# Adapted from SGLang: https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/hf_transformers_utils.py
|
||||
|
||||
# Copyright 2023-2024 SGLang Team
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# ==============================================================================
|
||||
"""Utilities for Huggingface Transformers."""
|
||||
|
||||
import contextlib
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
from functools import reduce
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional, Union, cast
|
||||
|
||||
from diffusers.loaders.lora_base import (
|
||||
_best_guess_weight_name, # watch out for potetential removal from diffusers
|
||||
)
|
||||
from huggingface_hub.errors import (
|
||||
LocalEntryNotFoundError,
|
||||
RepositoryNotFoundError,
|
||||
RevisionNotFoundError,
|
||||
)
|
||||
from requests.exceptions import ConnectionError as RequestsConnectionError
|
||||
from requests.exceptions import RequestException
|
||||
from transformers import AutoConfig, PretrainedConfig
|
||||
|
||||
from sglang.multimodal_gen.runtime.loader.utils import _clean_hf_config_inplace
|
||||
from sglang.multimodal_gen.runtime.loader.weight_utils import get_lock
|
||||
from sglang.multimodal_gen.runtime.platforms import current_platform
|
||||
from sglang.multimodal_gen.runtime.utils.logging_utils import init_logger
|
||||
from sglang.multimodal_gen.runtime.utils.model_overlay import (
|
||||
maybe_load_overlay_model_index,
|
||||
maybe_resolve_overlay_model_path,
|
||||
)
|
||||
from sglang.multimodal_gen.runtime.utils.quantization_utils import (
|
||||
normalize_flat_modelopt_quant_config,
|
||||
)
|
||||
from sglang.srt.environ import envs
|
||||
from sglang.utils import is_in_ci
|
||||
|
||||
logger = init_logger(__name__)
|
||||
|
||||
|
||||
def _check_index_files_for_missing_shards(
|
||||
model_path: str,
|
||||
) -> tuple[bool, list[str], list[str]]:
|
||||
"""
|
||||
Check all subdirectories for missing shards based on index files.
|
||||
|
||||
This catches cases where a model download was interrupted, leaving
|
||||
some safetensors shards missing while the index file exists.
|
||||
|
||||
Args:
|
||||
model_path: Path to the model directory
|
||||
|
||||
Returns:
|
||||
Tuple of (all_valid, missing_files, checked_subdirs)
|
||||
"""
|
||||
missing_files = []
|
||||
checked_subdirs = []
|
||||
|
||||
# Add common subdirectories for diffusers models
|
||||
try:
|
||||
subdirs = os.listdir(model_path)
|
||||
except OSError as e:
|
||||
logger.warning("Failed to list model directory %s: %s", model_path, e)
|
||||
return True, [], [] # Assume valid if we can't check
|
||||
|
||||
# Check the root directory and all subdirectories that might contain model weights
|
||||
dirs_to_check = [model_path]
|
||||
|
||||
for subdir in subdirs:
|
||||
subdir_path = os.path.join(model_path, subdir)
|
||||
if os.path.isdir(subdir_path):
|
||||
dirs_to_check.append(subdir_path)
|
||||
|
||||
for dir_path in dirs_to_check:
|
||||
# Find all safetensors index files
|
||||
index_files = glob.glob(os.path.join(dir_path, "*.safetensors.index.json"))
|
||||
|
||||
for index_file in index_files:
|
||||
checked_subdirs.append(os.path.basename(dir_path))
|
||||
try:
|
||||
with open(index_file) as f:
|
||||
index_data = json.load(f)
|
||||
|
||||
weight_map = index_data.get("weight_map", {})
|
||||
if not weight_map:
|
||||
continue
|
||||
|
||||
# Get unique files referenced in weight_map
|
||||
required_files = set(weight_map.values())
|
||||
|
||||
for file_name in required_files:
|
||||
file_path = os.path.join(dir_path, file_name)
|
||||
if not os.path.exists(file_path):
|
||||
relative_path = os.path.relpath(file_path, model_path)
|
||||
missing_files.append(relative_path)
|
||||
|
||||
except Exception as e:
|
||||
logger.warning("Failed to read index file %s: %s", index_file, e)
|
||||
continue
|
||||
|
||||
return len(missing_files) == 0, missing_files, checked_subdirs
|
||||
|
||||
|
||||
def _cleanup_model_cache(model_path: str, reason: str) -> bool:
|
||||
"""
|
||||
Remove the model cache directory to force a clean re-download.
|
||||
|
||||
Args:
|
||||
model_path: Path to the model directory (snapshot path)
|
||||
reason: Reason for cleanup (for logging)
|
||||
|
||||
Returns:
|
||||
True if cleanup was performed, False otherwise
|
||||
"""
|
||||
# Navigate up to the model root directory: snapshots/hash -> snapshots -> model_root
|
||||
# HF cache structure: models--org--name/snapshots/hash/
|
||||
try:
|
||||
snapshot_dir = os.path.abspath(model_path)
|
||||
snapshots_dir = os.path.dirname(snapshot_dir)
|
||||
repo_folder = os.path.dirname(snapshots_dir)
|
||||
|
||||
# Verify this looks like an HF cache structure
|
||||
if os.path.basename(snapshots_dir) != "snapshots":
|
||||
logger.warning(
|
||||
"Model path %s doesn't appear to be in HF cache structure, skipping cleanup",
|
||||
model_path,
|
||||
)
|
||||
return False
|
||||
|
||||
logger.warning(
|
||||
"Removing model cache at %s. Reason: %s",
|
||||
repo_folder,
|
||||
reason,
|
||||
)
|
||||
shutil.rmtree(repo_folder)
|
||||
logger.info("Successfully removed corrupted cache directory")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
"Failed to remove corrupted cache directory %s: %s. "
|
||||
"Manual cleanup may be required.",
|
||||
model_path,
|
||||
e,
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _ci_validate_diffusers_model(model_path: str) -> tuple[bool, bool]:
|
||||
"""
|
||||
CI-specific validation for diffusers models.
|
||||
|
||||
Checks all subdirectories (transformer, transformer_2, vae, etc.) for
|
||||
missing shards based on their index files. If issues are found in CI,
|
||||
cleans up the cache to force re-download.
|
||||
|
||||
Args:
|
||||
model_path: Path to the model directory
|
||||
|
||||
Returns:
|
||||
Tuple of (is_valid, cleanup_performed)
|
||||
- is_valid: True if the model is valid
|
||||
- cleanup_performed: True if cleanup was performed (only relevant when is_valid=False)
|
||||
"""
|
||||
if not is_in_ci():
|
||||
return True, False
|
||||
is_valid, missing_files, checked_subdirs = _check_index_files_for_missing_shards(
|
||||
model_path
|
||||
)
|
||||
|
||||
if not is_valid:
|
||||
logger.error(
|
||||
"CI validation failed for %s. Missing %d file(s): %s. "
|
||||
"Checked subdirectories: %s",
|
||||
model_path,
|
||||
len(missing_files),
|
||||
missing_files[:5] if len(missing_files) > 5 else missing_files,
|
||||
checked_subdirs,
|
||||
)
|
||||
cleanup_performed = _cleanup_model_cache(
|
||||
model_path,
|
||||
f"Missing {len(missing_files)} shard file(s): {missing_files[:3]}",
|
||||
)
|
||||
return False, cleanup_performed
|
||||
|
||||
if checked_subdirs:
|
||||
logger.info(
|
||||
"CI validation passed for %s. Checked subdirectories: %s",
|
||||
model_path,
|
||||
checked_subdirs,
|
||||
)
|
||||
|
||||
return True, False
|
||||
|
||||
|
||||
def _verify_diffusers_model_complete(path: str) -> bool:
|
||||
"""Check if a diffusers model directory has all required component subdirectories."""
|
||||
config_path = os.path.join(path, "model_index.json")
|
||||
if not os.path.exists(config_path):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(config_path) as config_file:
|
||||
model_index = json.load(config_file)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to read model_index.json at %s: %s", config_path, exc)
|
||||
return False
|
||||
|
||||
component_keys = [
|
||||
key
|
||||
for key, value in model_index.items()
|
||||
if isinstance(value, (list, tuple))
|
||||
and len(value) == 2
|
||||
and all(isinstance(item, str) for item in value)
|
||||
]
|
||||
if component_keys:
|
||||
return all(os.path.exists(os.path.join(path, key)) for key in component_keys)
|
||||
|
||||
return os.path.exists(os.path.join(path, "transformer")) and os.path.exists(
|
||||
os.path.join(path, "vae")
|
||||
)
|
||||
|
||||
|
||||
_CONFIG_REGISTRY: dict[str, type[PretrainedConfig]] = {
|
||||
# ChatGLMConfig.model_type: ChatGLMConfig,
|
||||
# DbrxConfig.model_type: DbrxConfig,
|
||||
# ExaoneConfig.model_type: ExaoneConfig,
|
||||
# Qwen2_5_VLConfig.model_type: Qwen2_5_VLConfig,
|
||||
}
|
||||
|
||||
for name, cls in _CONFIG_REGISTRY.items():
|
||||
with contextlib.suppress(ValueError):
|
||||
AutoConfig.register(name, cls)
|
||||
|
||||
|
||||
def download_from_hf(model_path: str):
|
||||
if os.path.exists(model_path):
|
||||
return model_path
|
||||
|
||||
return snapshot_download(model_path, allow_patterns=["*.json", "*.bin", "*.model"])
|
||||
|
||||
|
||||
def get_hf_config(
|
||||
component_model_path: str,
|
||||
trust_remote_code: bool,
|
||||
revision: str | None = None,
|
||||
model_override_args: dict | None = None,
|
||||
**kwargs,
|
||||
) -> PretrainedConfig:
|
||||
if check_gguf_file(component_model_path):
|
||||
raise NotImplementedError("GGUF models are not supported.")
|
||||
|
||||
config = AutoConfig.from_pretrained(
|
||||
component_model_path,
|
||||
trust_remote_code=trust_remote_code,
|
||||
revision=revision,
|
||||
**kwargs,
|
||||
)
|
||||
if config.model_type in _CONFIG_REGISTRY:
|
||||
config_class = _CONFIG_REGISTRY[config.model_type]
|
||||
config = config_class.from_pretrained(component_model_path, revision=revision)
|
||||
# NOTE(HandH1998): Qwen2VL requires `_name_or_path` attribute in `config`.
|
||||
config._name_or_path = component_model_path
|
||||
if model_override_args:
|
||||
config.update(model_override_args)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def get_config(
|
||||
model: str,
|
||||
trust_remote_code: bool,
|
||||
revision: Optional[str] = None,
|
||||
model_override_args: Optional[dict] = None,
|
||||
**kwargs,
|
||||
):
|
||||
return AutoConfig.from_pretrained(
|
||||
model, trust_remote_code=trust_remote_code, revision=revision, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def load_dict(file_path):
|
||||
if not os.path.exists(file_path):
|
||||
return {}
|
||||
try:
|
||||
# Load the config directly from the file
|
||||
with open(file_path) as f:
|
||||
config_dict: dict[str, Any] = json.load(f)
|
||||
if "_diffusers_version" in config_dict:
|
||||
config_dict.pop("_diffusers_version")
|
||||
# TODO(will): apply any overrides from inference args
|
||||
return config_dict
|
||||
except Exception as e:
|
||||
raise RuntimeError(
|
||||
f"Failed to load diffusers config from {file_path}: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
def prepare_diffusers_component_path_for_loading(component_path: str) -> str:
|
||||
"""Download component repos if needed and patch legacy flat ModelOpt configs."""
|
||||
local_component_path = (
|
||||
maybe_download_model(component_path)
|
||||
if not os.path.exists(component_path)
|
||||
else component_path
|
||||
)
|
||||
config_path = os.path.join(local_component_path, "config.json")
|
||||
if not os.path.exists(config_path):
|
||||
return local_component_path
|
||||
|
||||
with get_lock(config_path):
|
||||
try:
|
||||
with open(config_path, encoding="utf-8") as f:
|
||||
config = cast(dict[str, Any], json.load(f))
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to read component config %s: %s", config_path, exc)
|
||||
return local_component_path
|
||||
|
||||
quant_config = config.get("quantization_config")
|
||||
normalized_quant_config = normalize_flat_modelopt_quant_config(quant_config)
|
||||
if normalized_quant_config == quant_config:
|
||||
return local_component_path
|
||||
|
||||
config["quantization_config"] = normalized_quant_config
|
||||
try:
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(config, f, indent=2, sort_keys=True)
|
||||
f.write("\n")
|
||||
except OSError as exc:
|
||||
logger.warning(
|
||||
"Could not persist normalized ModelOpt config at %s (%s); "
|
||||
"normalization will be applied in memory at load time.",
|
||||
config_path,
|
||||
exc,
|
||||
)
|
||||
else:
|
||||
logger.warning(
|
||||
"Patched legacy flat ModelOpt quantization_config at %s with quant_type=%s "
|
||||
"for diffusers compatibility.",
|
||||
config_path,
|
||||
normalized_quant_config.get("quant_type"),
|
||||
)
|
||||
|
||||
return local_component_path
|
||||
|
||||
|
||||
def get_diffusers_component_config(
|
||||
component_path: str,
|
||||
) -> dict[str, Any]:
|
||||
"""Gets a configuration of a submodule for the given diffusers model."""
|
||||
# Download from HuggingFace Hub if path doesn't exist locally
|
||||
component_path = prepare_diffusers_component_path_for_loading(component_path)
|
||||
|
||||
config_names = ["generation_config.json"]
|
||||
# By default, we load config.json, but scheduler_config.json for scheduler
|
||||
if "scheduler" in component_path:
|
||||
config_names.append("scheduler_config.json")
|
||||
else:
|
||||
config_names.append("config.json")
|
||||
|
||||
config_file_paths = [
|
||||
os.path.join(component_path, config_name) for config_name in config_names
|
||||
]
|
||||
|
||||
combined_config = reduce(
|
||||
lambda acc, path: acc | load_dict(path), config_file_paths, {}
|
||||
)
|
||||
|
||||
quant_config = combined_config.get("quantization_config")
|
||||
if quant_config is not None:
|
||||
combined_config["quantization_config"] = normalize_flat_modelopt_quant_config(
|
||||
quant_config
|
||||
)
|
||||
|
||||
_clean_hf_config_inplace(combined_config)
|
||||
|
||||
logger.debug("HF model config: %s", combined_config)
|
||||
|
||||
return combined_config
|
||||
|
||||
|
||||
# Models don't use the same configuration key for determining the maximum
|
||||
# context length. Store them here so we can sanely check them.
|
||||
# NOTE: The ordering here is important. Some models have two of these and we
|
||||
# have a preference for which value gets used.
|
||||
CONTEXT_LENGTH_KEYS = [
|
||||
"max_sequence_length",
|
||||
"seq_length",
|
||||
"max_seq_len",
|
||||
"model_max_length",
|
||||
"max_position_embeddings",
|
||||
]
|
||||
|
||||
|
||||
def attach_additional_stop_token_ids(tokenizer):
|
||||
# Special handling for stop token <|eom_id|> generated by llama 3 tool use.
|
||||
if "<|eom_id|>" in tokenizer.get_added_vocab():
|
||||
tokenizer.additional_stop_token_ids = {
|
||||
tokenizer.get_added_vocab()["<|eom_id|>"]
|
||||
}
|
||||
else:
|
||||
tokenizer.additional_stop_token_ids = None
|
||||
|
||||
|
||||
def check_gguf_file(model: str | os.PathLike) -> bool:
|
||||
"""Check if the file is a GGUF model."""
|
||||
model = Path(model)
|
||||
if not model.is_file():
|
||||
return False
|
||||
elif model.suffix == ".gguf":
|
||||
return True
|
||||
|
||||
with open(model, "rb") as f:
|
||||
header = f.read(4)
|
||||
return header == b"GGUF"
|
||||
|
||||
|
||||
def maybe_download_lora(
|
||||
model_name_or_path: str,
|
||||
local_dir: str | None = None,
|
||||
download: bool = True,
|
||||
weight_name: str | None = None,
|
||||
) -> str:
|
||||
"""
|
||||
Check if the model path is a Hugging Face Hub model ID and download it if needed.
|
||||
Args:
|
||||
model_name_or_path: Local path or Hugging Face Hub model ID
|
||||
local_dir: Local directory to save the model
|
||||
download: Whether to download the model from Hugging Face Hub
|
||||
weight_name: Specific safetensors filename to load (pins deterministic selection
|
||||
for repos with multiple weight files)
|
||||
|
||||
Returns:
|
||||
Local path to the model
|
||||
"""
|
||||
allow_patterns = ["*.json", "*.safetensors", "*.bin"]
|
||||
|
||||
local_path = maybe_download_model(
|
||||
model_name_or_path,
|
||||
local_dir,
|
||||
download,
|
||||
is_lora=True,
|
||||
allow_patterns=allow_patterns,
|
||||
)
|
||||
# return directly if local_path is a file
|
||||
if os.path.isfile(local_path):
|
||||
return local_path
|
||||
|
||||
if weight_name is not None:
|
||||
target = os.path.join(local_path, weight_name)
|
||||
if not os.path.isfile(target):
|
||||
raise FileNotFoundError(
|
||||
f"Specified lora_weight_name '{weight_name}' not found in {local_path}"
|
||||
)
|
||||
return target
|
||||
|
||||
guessed = _best_guess_weight_name(local_path, file_extension=".safetensors")
|
||||
# AMD workaround: PR 15813 changed from model_name_or_path to local_path,
|
||||
# which can return None. Fall back to original behavior on ROCm.
|
||||
if guessed is None and current_platform.is_rocm():
|
||||
guessed = _best_guess_weight_name(
|
||||
model_name_or_path, file_extension=".safetensors"
|
||||
)
|
||||
return os.path.join(local_path, guessed)
|
||||
|
||||
|
||||
def verify_model_config_and_directory(model_path: str) -> dict[str, Any]:
|
||||
"""
|
||||
Verify that the model directory contains a valid diffusers configuration.
|
||||
|
||||
Args:
|
||||
model_path: Path to the model directory
|
||||
|
||||
Returns:
|
||||
The loaded model configuration as a dictionary
|
||||
"""
|
||||
|
||||
# Check for model_index.json which is required for diffusers models
|
||||
config_path = os.path.join(model_path, "model_index.json")
|
||||
if not os.path.exists(config_path):
|
||||
raise ValueError(
|
||||
f"Model directory {model_path} does not contain model_index.json. "
|
||||
"Only HuggingFace diffusers format is supported."
|
||||
)
|
||||
|
||||
# Load the config
|
||||
with open(config_path) as f:
|
||||
config = json.load(f)
|
||||
|
||||
# Verify diffusers version exists
|
||||
if "_diffusers_version" not in config:
|
||||
raise ValueError("model_index.json does not contain _diffusers_version")
|
||||
|
||||
logger.info("Diffusers version: %s", config["_diffusers_version"])
|
||||
|
||||
component_keys = [
|
||||
key
|
||||
for key, value in config.items()
|
||||
if isinstance(value, (list, tuple))
|
||||
and len(value) == 2
|
||||
and all(isinstance(item, str) for item in value)
|
||||
]
|
||||
if component_keys:
|
||||
missing_components = [
|
||||
component_key
|
||||
for component_key in component_keys
|
||||
if not os.path.exists(os.path.join(model_path, component_key))
|
||||
]
|
||||
if missing_components:
|
||||
missing_str = ", ".join(missing_components)
|
||||
raise ValueError(
|
||||
f"Model directory {model_path} is missing required component "
|
||||
f"directories: {missing_str}."
|
||||
)
|
||||
else:
|
||||
transformer_dir = os.path.join(model_path, "transformer")
|
||||
vae_dir = os.path.join(model_path, "vae")
|
||||
if not os.path.exists(transformer_dir):
|
||||
raise ValueError(
|
||||
f"Model directory {model_path} does not contain a transformer/ directory."
|
||||
)
|
||||
if not os.path.exists(vae_dir):
|
||||
raise ValueError(
|
||||
f"Model directory {model_path} does not contain a vae/ directory."
|
||||
)
|
||||
return cast(dict[str, Any], config)
|
||||
|
||||
|
||||
def _resolve_remote_repo_model_index_path(model_name_or_path: str) -> str:
|
||||
"""Return a local path to a remote repo's ``model_index.json``"""
|
||||
from huggingface_hub.errors import EntryNotFoundError
|
||||
|
||||
try:
|
||||
# Cache-aware: no local_dir, so HF reuses the cache and revalidates the
|
||||
# ETag against the Hub, re-downloading only when the remote changed.
|
||||
return hf_hub_download(repo_id=model_name_or_path, filename="model_index.json")
|
||||
except EntryNotFoundError:
|
||||
# Repo exists but has no model_index.json (single-model repo); let the
|
||||
# caller fall through to the single-model path.
|
||||
raise
|
||||
except Exception as online_err:
|
||||
cached_path = None
|
||||
if not envs.SGLANG_USE_MODELSCOPE.get():
|
||||
from huggingface_hub import try_to_load_from_cache
|
||||
|
||||
cached = try_to_load_from_cache(
|
||||
repo_id=model_name_or_path, filename="model_index.json"
|
||||
)
|
||||
if isinstance(cached, str) and os.path.exists(cached):
|
||||
cached_path = cached
|
||||
if cached_path is not None:
|
||||
logger.warning(
|
||||
"Could not fetch model_index.json for '%s' from the Hugging Face "
|
||||
"Hub (%s); using the locally cached copy at '%s'. The cached copy "
|
||||
"may be out of date — provide an HF token or clear the cache to "
|
||||
"force a refresh.",
|
||||
model_name_or_path,
|
||||
online_err,
|
||||
cached_path,
|
||||
)
|
||||
return cached_path
|
||||
raise
|
||||
|
||||
|
||||
def maybe_download_model_index(model_name_or_path: str) -> dict[str, Any]:
|
||||
"""
|
||||
Download and extract just the model_index.json for a Hugging Face model.
|
||||
|
||||
Args:
|
||||
model_name_or_path: Path or HF Hub model ID
|
||||
|
||||
Returns:
|
||||
The parsed model_index.json as a dictionary
|
||||
"""
|
||||
from huggingface_hub.errors import EntryNotFoundError
|
||||
|
||||
overlay_config = maybe_load_overlay_model_index(
|
||||
model_name_or_path,
|
||||
snapshot_download_fn=snapshot_download,
|
||||
hf_hub_download_fn=hf_hub_download,
|
||||
)
|
||||
if overlay_config is not None:
|
||||
return overlay_config
|
||||
|
||||
# If it's a local path, verify it directly.
|
||||
if os.path.exists(model_name_or_path):
|
||||
try:
|
||||
return verify_model_config_and_directory(model_name_or_path)
|
||||
except ValueError:
|
||||
# Not a pipeline, maybe a single model.
|
||||
config_path = os.path.join(model_name_or_path, "config.json")
|
||||
if os.path.exists(config_path):
|
||||
with open(config_path) as f:
|
||||
config = json.load(f)
|
||||
return config
|
||||
raise
|
||||
|
||||
# For remote models, resolve model_index.json (Hub-first, cache fallback).
|
||||
try:
|
||||
model_index_path = _resolve_remote_repo_model_index_path(model_name_or_path)
|
||||
|
||||
# Load the model_index.json
|
||||
with open(model_index_path) as f:
|
||||
config: dict[str, Any] = json.load(f)
|
||||
|
||||
# Verify it has the required fields
|
||||
if "_class_name" not in config:
|
||||
raise ValueError(
|
||||
f"model_index.json for {model_name_or_path} does not contain _class_name field"
|
||||
)
|
||||
|
||||
if "_diffusers_version" not in config:
|
||||
raise ValueError(
|
||||
f"model_index.json for {model_name_or_path} does not contain _diffusers_version field"
|
||||
)
|
||||
|
||||
# Add the pipeline name for downstream use
|
||||
config["pipeline_name"] = config["_class_name"]
|
||||
|
||||
logger.debug(
|
||||
"Resolved model_index.json for %s, pipeline: %s",
|
||||
model_name_or_path,
|
||||
config["_class_name"],
|
||||
)
|
||||
return config
|
||||
except EntryNotFoundError:
|
||||
logger.debug(
|
||||
"model_index.json not found for %s. Assuming it is a single model and downloading it.",
|
||||
model_name_or_path,
|
||||
)
|
||||
local_path = maybe_download_model(model_name_or_path)
|
||||
config_path = os.path.join(local_path, "config.json")
|
||||
if not os.path.exists(config_path):
|
||||
raise ValueError(
|
||||
f"Failed to find config.json for {model_name_or_path} after failing to find model_index.json"
|
||||
f"You might be looking for models ending with '-Diffusers'"
|
||||
)
|
||||
with open(config_path) as f:
|
||||
config = json.load(f)
|
||||
return config
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
f"Failed to download or parse model_index.json for {model_name_or_path}: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
def maybe_download_model(
|
||||
model_name_or_path: str,
|
||||
local_dir: str | None = None,
|
||||
download: bool = True,
|
||||
is_lora: bool = False,
|
||||
allow_patterns: list[str] | None = None,
|
||||
force_diffusers_model: bool = False,
|
||||
skip_overlay_resolution: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Check if the model path is a Hugging Face Hub model ID and download it if needed.
|
||||
|
||||
Args:
|
||||
model_name_or_path: Local path or Hugging Face Hub model ID
|
||||
local_dir: Local directory to save the model
|
||||
download: Whether to download the model from Hugging Face Hub
|
||||
is_lora: If True, skip model completeness verification (LoRA models don't have transformer/vae directories)
|
||||
force_diffusers_model: If True, apply diffusers model check. Otherwise it should be a component model
|
||||
Returns:
|
||||
Local path to the model
|
||||
"""
|
||||
if force_diffusers_model and not skip_overlay_resolution:
|
||||
# return overlay model path if applicable
|
||||
overlay_model_path = maybe_resolve_overlay_model_path(
|
||||
model_name_or_path,
|
||||
local_dir=local_dir,
|
||||
download=download,
|
||||
allow_patterns=allow_patterns,
|
||||
snapshot_download_fn=snapshot_download,
|
||||
hf_hub_download_fn=hf_hub_download,
|
||||
verify_diffusers_model_complete_fn=_verify_diffusers_model_complete,
|
||||
base_model_download_fn=maybe_download_model,
|
||||
)
|
||||
if overlay_model_path is not None:
|
||||
return overlay_model_path
|
||||
|
||||
# 1. Local path check: if path exists locally, verify it's complete (skip for LoRA)
|
||||
if os.path.exists(model_name_or_path):
|
||||
if not force_diffusers_model:
|
||||
return model_name_or_path
|
||||
if is_lora or _verify_diffusers_model_complete(model_name_or_path):
|
||||
if not is_lora:
|
||||
is_valid, cleanup_performed = _ci_validate_diffusers_model(
|
||||
model_name_or_path
|
||||
)
|
||||
if not is_valid:
|
||||
if cleanup_performed:
|
||||
logger.warning(
|
||||
"CI validation failed for local model at %s, "
|
||||
"cache has been cleaned up, will re-download",
|
||||
model_name_or_path,
|
||||
)
|
||||
# Fall through to download
|
||||
else:
|
||||
raise ValueError(
|
||||
f"CI validation failed for local model at {model_name_or_path}. "
|
||||
"Some safetensors shards are missing. "
|
||||
"Please manually delete the model directory and retry."
|
||||
)
|
||||
else:
|
||||
logger.info("Model already exists locally and is complete")
|
||||
return model_name_or_path
|
||||
else:
|
||||
logger.info("Model already exists locally and is complete")
|
||||
return model_name_or_path
|
||||
else:
|
||||
logger.warning(
|
||||
"Local model at %s appears incomplete (missing required components), "
|
||||
"will attempt re-download",
|
||||
model_name_or_path,
|
||||
)
|
||||
|
||||
# 2. Cache-first strategy (Fast Path)
|
||||
# Try to read from HF cache without network access
|
||||
try:
|
||||
logger.info(
|
||||
"Checking for cached model in HF Hub cache for %s...", model_name_or_path
|
||||
)
|
||||
local_path = snapshot_download(
|
||||
repo_id=model_name_or_path,
|
||||
ignore_patterns=["*.onnx", "*.msgpack"],
|
||||
local_dir=local_dir,
|
||||
local_files_only=True,
|
||||
max_workers=8,
|
||||
)
|
||||
if not force_diffusers_model:
|
||||
return str(local_path)
|
||||
if is_lora or _verify_diffusers_model_complete(local_path):
|
||||
if not is_lora:
|
||||
is_valid, cleanup_performed = _ci_validate_diffusers_model(local_path)
|
||||
if not is_valid:
|
||||
logger.warning(
|
||||
"CI validation failed for cached model at %s, "
|
||||
"%s, will re-download",
|
||||
local_path,
|
||||
(
|
||||
"cache has been cleaned up"
|
||||
if cleanup_performed
|
||||
else "cleanup was not performed"
|
||||
),
|
||||
)
|
||||
# Fall through to download
|
||||
else:
|
||||
logger.info("Found complete model in cache at %s", local_path)
|
||||
return str(local_path)
|
||||
else:
|
||||
logger.info("Found complete model in cache at %s", local_path)
|
||||
return str(local_path)
|
||||
else:
|
||||
if not download:
|
||||
raise ValueError(
|
||||
f"Model {model_name_or_path} found in cache but is incomplete and download=False."
|
||||
)
|
||||
logger.info(
|
||||
"Model found in cache but incomplete, will download from HF Hub"
|
||||
)
|
||||
except LocalEntryNotFoundError:
|
||||
if not download:
|
||||
raise ValueError(
|
||||
f"Model {model_name_or_path} not found in local cache and download=False."
|
||||
)
|
||||
logger.info("Model not found in cache, will download from HF Hub")
|
||||
except Exception as e:
|
||||
logger.warning(
|
||||
"Unexpected error while checking cache for %s: %s, will attempt download",
|
||||
model_name_or_path,
|
||||
e,
|
||||
)
|
||||
if not download:
|
||||
raise ValueError(
|
||||
f"Error checking cache for {model_name_or_path} and download=False: {e}"
|
||||
) from e
|
||||
|
||||
# 3. Download strategy (with retry mechanism)
|
||||
MAX_RETRIES = 5
|
||||
for attempt in range(MAX_RETRIES):
|
||||
try:
|
||||
logger.info(
|
||||
"Downloading model snapshot from HF Hub for %s (attempt %d/%d)...",
|
||||
model_name_or_path,
|
||||
attempt + 1,
|
||||
MAX_RETRIES,
|
||||
)
|
||||
with get_lock(model_name_or_path).acquire(poll_interval=2):
|
||||
local_path = snapshot_download(
|
||||
repo_id=model_name_or_path,
|
||||
ignore_patterns=["*.onnx", "*.msgpack"],
|
||||
allow_patterns=allow_patterns,
|
||||
local_dir=local_dir,
|
||||
max_workers=8,
|
||||
)
|
||||
|
||||
if not force_diffusers_model:
|
||||
return str(local_path)
|
||||
# Verify downloaded model is complete (skip for LoRA)
|
||||
elif not is_lora and not _verify_diffusers_model_complete(local_path):
|
||||
logger.warning(
|
||||
"Downloaded model at %s is incomplete, retrying with force_download=True",
|
||||
local_path,
|
||||
)
|
||||
with get_lock(model_name_or_path).acquire(poll_interval=2):
|
||||
local_path = snapshot_download(
|
||||
repo_id=model_name_or_path,
|
||||
ignore_patterns=["*.onnx", "*.msgpack"],
|
||||
local_dir=local_dir,
|
||||
max_workers=8,
|
||||
force_download=True,
|
||||
)
|
||||
if not _verify_diffusers_model_complete(local_path):
|
||||
raise ValueError(
|
||||
f"Downloaded model at {local_path} is still incomplete after forced re-download. "
|
||||
"The model repository may be missing required components (model_index.json, transformer/, or vae/)."
|
||||
)
|
||||
|
||||
# CI validation: check all subdirectories for missing shards after download
|
||||
if not is_lora:
|
||||
is_valid, cleanup_performed = _ci_validate_diffusers_model(local_path)
|
||||
if not is_valid:
|
||||
# In CI, if validation fails after download, we have a serious issue
|
||||
# If cleanup was performed, the next retry should get a fresh download
|
||||
raise ValueError(
|
||||
f"CI validation failed for downloaded model at {local_path}. "
|
||||
f"Some safetensors shards are missing. Cleanup performed: {cleanup_performed}."
|
||||
)
|
||||
|
||||
logger.info("Downloaded model to %s", local_path)
|
||||
return str(local_path)
|
||||
|
||||
except (RepositoryNotFoundError, RevisionNotFoundError) as e:
|
||||
raise ValueError(
|
||||
f"Model or revision not found at {model_name_or_path}. "
|
||||
f"Please check the model ID or ensure you have access to the repository. Error: {e}"
|
||||
) from e
|
||||
except (RequestException, RequestsConnectionError) as e:
|
||||
if attempt == MAX_RETRIES - 1:
|
||||
raise ValueError(
|
||||
f"Could not find model at {model_name_or_path} and failed to download from HF Hub "
|
||||
f"after {MAX_RETRIES} attempts due to network error: {e}"
|
||||
) from e
|
||||
wait_time = 2**attempt
|
||||
logger.warning(
|
||||
"Download failed (attempt %d/%d) due to network error: %s. "
|
||||
"Retrying in %d seconds...",
|
||||
attempt + 1,
|
||||
MAX_RETRIES,
|
||||
e,
|
||||
wait_time,
|
||||
)
|
||||
time.sleep(wait_time)
|
||||
except Exception as e:
|
||||
raise ValueError(
|
||||
f"Could not find model at {model_name_or_path} and failed to download from HF Hub: {e}"
|
||||
) from e
|
||||
|
||||
|
||||
# Unified download functions with Hugging Face-compatible names
|
||||
def hf_hub_download(
|
||||
repo_id: str,
|
||||
filename: str,
|
||||
local_dir: Optional[Union[str, Path]] = None,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""Unified hf_hub_download that supports both Hugging Face Hub and ModelScope."""
|
||||
if envs.SGLANG_USE_MODELSCOPE.get():
|
||||
from modelscope import model_file_download
|
||||
|
||||
return model_file_download(
|
||||
model_id=repo_id,
|
||||
file_path=filename,
|
||||
cache_dir=local_dir,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
from huggingface_hub import hf_hub_download as _hf_hub_download
|
||||
|
||||
return _hf_hub_download(
|
||||
repo_id=repo_id,
|
||||
filename=filename,
|
||||
local_dir=local_dir,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
def snapshot_download(
|
||||
repo_id: str,
|
||||
local_dir: Optional[Union[str, Path]] = None,
|
||||
ignore_patterns: Optional[Union[list[str], str]] = None,
|
||||
allow_patterns: Optional[Union[list[str], str]] = None,
|
||||
local_files_only: bool = False,
|
||||
max_workers: int = 8,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
"""Unified snapshot_download that supports both Hugging Face Hub and ModelScope."""
|
||||
if envs.SGLANG_USE_MODELSCOPE.get():
|
||||
from modelscope import snapshot_download as _ms_snapshot_download
|
||||
|
||||
ms_kwargs = {
|
||||
"model_id": repo_id,
|
||||
"local_dir": local_dir,
|
||||
"ignore_patterns": ignore_patterns,
|
||||
"allow_patterns": allow_patterns,
|
||||
"local_files_only": local_files_only,
|
||||
"max_workers": max_workers,
|
||||
}
|
||||
ms_kwargs.update(kwargs)
|
||||
return _ms_snapshot_download(**ms_kwargs)
|
||||
else:
|
||||
from huggingface_hub import snapshot_download as _hf_snapshot_download
|
||||
|
||||
hf_kwargs = {
|
||||
"repo_id": repo_id,
|
||||
"local_dir": local_dir,
|
||||
"ignore_patterns": ignore_patterns,
|
||||
"allow_patterns": allow_patterns,
|
||||
"local_files_only": local_files_only,
|
||||
"max_workers": max_workers,
|
||||
"etag_timeout": 60,
|
||||
}
|
||||
hf_kwargs.update(kwargs)
|
||||
return _hf_snapshot_download(**hf_kwargs)
|
||||
Reference in New Issue
Block a user