From 2f8363c5fc0618bba36246ff77364e99fd6e3bd1 Mon Sep 17 00:00:00 2001 From: Kyle Merritt Date: Thu, 8 Oct 2026 07:00:05 -0400 Subject: [PATCH] fix(quant): resolve hyphenated registry methods and disk-dict from_config in quant factory _normalize_method_name canonicalizes dashes to underscores for _OVERRIDES lookup, but vLLM's QUANTIZATION_METHODS keeps historical spellings ('compressed-tensors'), so both --quantization compressed-tensors and checkpoint auto-detection raised 'Unknown quantization method'. Resolve the normalized name back to the registry spelling, and fall back to from_config for registry configs whose __init__ does not take the serialized schema (compressed-tensors config_groups/format/ignore). --- tests/quantization/test_quant_factory.py | 72 ++++++++++++++++++++++++ vllm_omni/quantization/factory.py | 32 +++++++++-- 2 files changed, 98 insertions(+), 6 deletions(-) create mode 100644 tests/quantization/test_quant_factory.py diff --git a/tests/quantization/test_quant_factory.py b/tests/quantization/test_quant_factory.py new file mode 100644 index 000000000..2978d7b14 --- /dev/null +++ b/tests/quantization/test_quant_factory.py @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: Apache-2.0 +# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project +"""Tests for the quantization config factory's method-name and disk-dict resolution.""" + +from __future__ import annotations + +import pytest +from vllm.model_executor.layers.quantization.compressed_tensors.compressed_tensors import ( + CompressedTensorsConfig, +) + +from vllm_omni.quantization import build_quant_config, resolve_component_quant_config +from vllm_omni.quantization.component_config import ( + ComponentQuantizationConfig, + resolve_encoder_quant_config, +) +from vllm_omni.quantization.factory import SUPPORTED_QUANTIZATION_METHODS, _build_single + + +def _int8_weight_only_disk_config() -> dict: + """Serialized compressed-tensors int8 weight-only metadata as it appears on disk.""" + weights = { + "num_bits": 8, + "type": "int", + "symmetric": True, + "strategy": "channel", + "dynamic": False, + "observer": "minmax", + } + return { + "quant_method": "compressed-tensors", + "format": "pack-quantized", + "config_groups": {"group_0": {"targets": ["Linear"], "weights": weights}}, + "ignore": ["img_in"], + "quantization_status": "frozen", + } + + +def test_hyphenated_registry_method_builds_from_str() -> None: + config = _build_single("compressed-tensors") + assert isinstance(config, CompressedTensorsConfig) + assert config.get_name() == "compressed-tensors" + + +@pytest.mark.parametrize("spelling", ["compressed-tensors", "compressed_tensors"]) +def test_disk_dict_builds_via_from_config(spelling: str) -> None: + disk = _int8_weight_only_disk_config() + disk["quant_method"] = spelling + config = build_quant_config(disk) + assert isinstance(config, CompressedTensorsConfig) + + +def test_per_component_dict_routes_transformer_only() -> None: + disk = {"transformer": _int8_weight_only_disk_config()} + config = build_quant_config(disk) + assert isinstance(config, ComponentQuantizationConfig) + assert isinstance( + resolve_component_quant_config(config, "transformer"), CompressedTensorsConfig + ) + encoder = resolve_encoder_quant_config( + resolve_component_quant_config(config, "text_encoder") + ) + assert encoder is None + + +def test_unknown_method_still_raises() -> None: + with pytest.raises(ValueError, match="Unknown quantization method"): + _build_single("not-a-real-method") + + +def test_supported_list_advertises_hyphenated_registry_names() -> None: + assert "compressed-tensors" in SUPPORTED_QUANTIZATION_METHODS diff --git a/vllm_omni/quantization/factory.py b/vllm_omni/quantization/factory.py index 55a50a090..2fff1100b 100644 --- a/vllm_omni/quantization/factory.py +++ b/vllm_omni/quantization/factory.py @@ -326,20 +326,36 @@ def _build_from_method_and_config(method: str, config: Mapping[str, Any]) -> Qua return _build_single(method, **config) +def _resolve_registry_method_name(method: str) -> str | None: + """Resolve a normalized (underscore) method name to vLLM's registry spelling. + + vLLM's ``QUANTIZATION_METHODS`` keeps canonical names with their historical + spelling — including hyphenated ones such as ``compressed-tensors`` — while + ``_normalize_method_name`` canonicalizes dashes to underscores for the + ``_OVERRIDES`` lookup. Try both spellings so hyphenated registry entries + remain reachable. + """ + for candidate in (method, method.replace("_", "-")): + if candidate in QUANTIZATION_METHODS: + return candidate + return None + + def _build_single(method: str, **kwargs: Any) -> QuantizationConfig: """Build a single QuantizationConfig by method name. Resolution: _OVERRIDES first, then vLLM registry via from_config(). """ - method = _normalize_method_name(method) + normalized = _normalize_method_name(method) - if method in _OVERRIDES: - return _OVERRIDES[method](**kwargs) + if normalized in _OVERRIDES: + return _OVERRIDES[normalized](**kwargs) - if method not in QUANTIZATION_METHODS: + registry_method = _resolve_registry_method_name(normalized) + if registry_method is None: raise ValueError(f"Unknown quantization method: {method!r}. Supported: {SUPPORTED_QUANTIZATION_METHODS}") - config_cls = get_quantization_config(method) + config_cls = get_quantization_config(registry_method) from vllm.config.quantization import resolve_quantization_config from vllm.model_executor.layers.quantization.online.base import OnlineQuantizationConfig @@ -347,7 +363,7 @@ def _build_single(method: str, **kwargs: Any) -> QuantizationConfig: if config_cls is OnlineQuantizationConfig: if "args" in kwargs: return config_cls(**kwargs) - args = resolve_quantization_config(method, kwargs or None) + args = resolve_quantization_config(registry_method, kwargs or None) if args is None: raise ValueError("Online quantization requires quantization config arguments") return config_cls(args) @@ -355,6 +371,10 @@ def _build_single(method: str, **kwargs: Any) -> QuantizationConfig: try: return config_cls(**kwargs) except TypeError: + # Registry configs built from checkpoint metadata (e.g. compressed-tensors) + # take the serialized schema through from_config rather than __init__. + if kwargs and hasattr(config_cls, "from_config"): + return config_cls.from_config(dict(kwargs)) sig = inspect.signature(config_cls.__init__) raise TypeError( f"Cannot instantiate {config_cls.__name__} with kwargs {kwargs}. Expected signature: {sig}"