Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions tests/transformers_utils/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ def test_is_remote_gguf_extended_quant_types(self):
assert not is_remote_gguf("repo/model:INVALID_M")
assert not is_remote_gguf("repo/model:Q9_K_M")

def test_is_remote_gguf_nonstandard_quant_type(self):
"""Test is_remote_gguf with non-standard quant types containing
a known GGML type."""
# Non-standard quant types with known GGML type after prefix
assert is_remote_gguf("unsloth/Qwen3.5-35B-A3B-GGUF:UD-Q4_K_XL")
assert is_remote_gguf("user/Model:UD-Q4_K_M")
assert is_remote_gguf("user/SomeModel:Custom-Q8_0")

# Exact GGML type after prefix (no suffix stripping needed)
assert is_remote_gguf("user/Model-GGUF:UD-IQ4_NL")
assert is_remote_gguf("user/Model-GGUF:UD-Q8_0")

# Completely unknown quant types should still fail
assert not is_remote_gguf("repo/model:TOTALLY-RANDOM")
assert not is_remote_gguf("user/Model:UD-INVALID")

# No dash separator → not recognized as prefixed
assert not is_remote_gguf("repo/model:UDIQ4NL")

def test_is_remote_gguf_without_colon(self):
"""Test is_remote_gguf without colon."""
assert not is_remote_gguf("repo/model")
Expand Down Expand Up @@ -143,6 +162,14 @@ def test_split_remote_gguf_extended_quant_types(self):
assert repo_id == "repo/model"
assert quant_type == "Q3_K_S"

def test_split_remote_gguf_nonstandard_quant_type(self):
"""Test split_remote_gguf with non-standard quant types in GGUF repos."""
repo_id, quant_type = split_remote_gguf(
"unsloth/Qwen3.5-35B-A3B-GGUF:UD-Q4_K_XL"
)
assert repo_id == "unsloth/Qwen3.5-35B-A3B-GGUF"
assert quant_type == "UD-Q4_K_XL"

def test_split_remote_gguf_with_path_object(self):
"""Test split_remote_gguf with Path object."""
repo_id, quant_type = split_remote_gguf(Path("unsloth/Qwen3-0.6B-GGUF:IQ1_S"))
Expand Down
41 changes: 38 additions & 3 deletions vllm/transformers_utils/gguf_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,48 @@ def check_gguf_file(model: str | PathLike) -> bool:

@cache
def is_remote_gguf(model: str | Path) -> bool:
"""Check if the model is a remote GGUF model."""
"""Check if the model is a remote GGUF model.

Recognizes two forms:
1. Standard: ``repo_id:quant_type`` where *quant_type* is a known
GGML quantization type (e.g. ``Q4_K_M``).
2. Non-standard: ``repo_id:quant_type`` where *quant_type* contains
a known GGML type with extra prefixes (e.g. ``UD-Q4_K_XL``).
A warning is logged and actual file existence is validated later
during download.
"""
pattern = r"^[a-zA-Z0-9][a-zA-Z0-9._-]*/[a-zA-Z0-9][a-zA-Z0-9._-]*:[A-Za-z0-9_+-]+$"
model = str(model)
if re.fullmatch(pattern, model):
_, quant_type = model.rsplit(":", 1)
return is_valid_gguf_quant_type(quant_type)
if is_valid_gguf_quant_type(quant_type):
return True
if is_nonstandard_gguf_quant_type(quant_type):
logger.warning(
"Non-standard GGUF quant type '%s' detected.",
quant_type,
)
return True
return False


def is_nonstandard_gguf_quant_type(quant_type: str) -> bool:
"""Check if a non-standard quant type contains a known GGML type.

Splits the quant type by the last ``-`` and checks whether the
trailing part is a standard GGML type. For example::

UD-Q4_K_XL → rsplit → ["UD", "Q4_K_XL"] → Q4_K_XL valid ✓
UD-IQ4_NL → rsplit → ["UD", "IQ4_NL"] → IQ4_NL valid ✓
Custom-UD-Q4_K → rsplit → ["Custom-UD", "Q4_K"] → Q4_K valid ✓
RANDOM → no "-" → False
"""
if "-" not in quant_type:
return False
_, remainder = quant_type.rsplit("-", 1)
return is_valid_gguf_quant_type(remainder)


# Common suffixes used in GGUF file naming conventions
# e.g., Q4_K_M, Q3_K_S, Q5_K_L, Q2_K_XL
_GGUF_QUANT_SUFFIXES = ("_M", "_S", "_L", "_XL", "_XS", "_XXS")
Expand Down Expand Up @@ -84,7 +117,9 @@ def split_remote_gguf(model: str | Path) -> tuple[str, str]:
f"Wrong GGUF model or invalid GGUF quant type: {model}.\n"
"- It should be in repo_id:quant_type format.\n"
f"- Valid base quant types: {GGMLQuantizationType._member_names_}\n"
f"- Extended suffixes also supported: {_GGUF_QUANT_SUFFIXES}",
f"- Extended suffixes also supported: {_GGUF_QUANT_SUFFIXES}\n"
"- Non-standard GGUF quant types also supported: "
"dash-separated prefixes (e.g. UD-Q4_K_XL, Custom-Q8_0)",
)


Expand Down
Loading