# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project """Tests for AutoAWQConfig behavior after unification. These tests verify the bug fixes for: 1. CPU platform override conflict (auto_awq should not override on CPU) 2. MoE fallback compatibility (full_config["quant_method"] should be "awq") 3. Config attribute consistency 4. End-to-end quantization method loading (auto_awq loads and runs correctly) Note: Tests that require importing the full auto_awq module (which has GPU-dependent imports) should use subprocess or be run in a GPU environment. """ from __future__ import annotations import pytest import torch from tests.quantization.utils import is_quant_method_supported def _get_auto_awq_config_source() -> str: """Read the AutoAWQConfig class source code for isolated testing.""" import inspect import vllm.model_executor.layers.quantization.auto_awq as auto_awq_module return inspect.getsource(auto_awq_module.AutoAWQConfig) class TestAutoAWQConfigFromConfig: """Tests for AutoAWQConfig.from_config behavior. These tests require GPU environment to import the full module. They are skipped on non-GPU platforms. """ def test_full_config_quant_method_is_awq_for_moe_fallback(self): """full_config should have quant_method='awq' for MoE fallback compatibility. MoeWNA16Config only accepts 'gptq' or 'awq' as linear_quant_method. If full_config has 'auto_awq', the MoE fallback will fail. """ from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig config = { "w_bit": 4, "q_group_size": 128, "zero_point": True, "lm_head": False, } awq_config = AutoAWQConfig.from_config(config) # Verify quant_method is 'awq' for MoE fallback assert awq_config.full_config["quant_method"] == "awq", ( f"Expected quant_method='awq', got {awq_config.full_config['quant_method']}" ) def test_full_config_preserves_other_fields(self): """full_config should preserve all original config fields.""" from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig config = { "w_bit": 4, "q_group_size": 128, "zero_point": True, "lm_head": False, "custom_field": "custom_value", } awq_config = AutoAWQConfig.from_config(config) assert awq_config.full_config["w_bit"] == 4 assert awq_config.full_config["q_group_size"] == 128 assert awq_config.full_config["zero_point"] is True assert awq_config.full_config["lm_head"] is False assert awq_config.full_config["custom_field"] == "custom_value" def test_full_config_is_copy_not_original(self): """full_config should be a copy, not the original dict.""" from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig config = { "w_bit": 4, "q_group_size": 128, "zero_point": True, "lm_head": False, } original_quant_method = config.get("quant_method") AutoAWQConfig.from_config(config) # Original config should not be modified assert config.get("quant_method") == original_quant_method class TestAutoAWQConfigAttributes: """Tests for AutoAWQConfig attribute consistency. These tests require GPU environment to import the full module. They are skipped on non-GPU platforms. """ def test_config_attributes_match_input(self): """Config attributes should match input values.""" from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig awq_config = AutoAWQConfig( weight_bits=4, group_size=128, zero_point=True, lm_head_quantized=False, modules_to_not_convert=["lm_head"], ) assert awq_config.weight_bits == 4 assert awq_config.group_size == 128 assert awq_config.zero_point is True assert awq_config.lm_head_quantized is False assert awq_config.modules_to_not_convert == ["lm_head"] def test_pack_factor_for_4bit(self): """Pack factor should be 8 for 4-bit quantization.""" from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig awq_config = AutoAWQConfig( weight_bits=4, group_size=128, zero_point=True, lm_head_quantized=False, ) assert awq_config.pack_factor == 8 # 32 // 4 class TestAutoAWQConfigOverrideLogic: """Tests for override logic by parsing source code (no GPU import required).""" def _get_auto_awq_source(self) -> str: """Read the auto_awq.py source file.""" import inspect import pathlib import vllm.model_executor.layers.quantization.auto_awq as auto_awq_module source_path = inspect.getfile(auto_awq_module) return pathlib.Path(source_path).read_text() def test_cpu_check_in_override_method(self): """override_quantization_method should check current_platform.is_cpu().""" source = self._get_auto_awq_source() # Verify the CPU check exists in override method assert "current_platform.is_cpu()" in source, ( "override_quantization_method should check is_cpu()" ) assert "return None" in source, ( "override_quantization_method should return None on CPU" ) def test_quant_method_normalization_in_from_config(self): """from_config should normalize quant_method to 'awq' for MoE fallback.""" source = self._get_auto_awq_source() # Verify the normalization exists assert ( '"quant_method"] = "awq"' in source or "'quant_method'] = 'awq'" in source ), "from_config should set quant_method='awq' in full_config" # ============================================================================= # End-to-end integration tests (require GPU environment) # ============================================================================= PROMPT = "On the surface of Mars, we found" # Small AWQ model for testing - using Qwen2 1.5B which has official AWQ checkpoint AWQ_MODELS = [ "Qwen/Qwen2-1.5B-Instruct-AWQ", ] @pytest.mark.skipif( not is_quant_method_supported("auto_awq"), reason="auto_awq is not supported on this GPU type.", ) @pytest.mark.parametrize("model_id", AWQ_MODELS) def test_auto_awq_quantization_method(vllm_runner, model_id: str, monkeypatch): """Test that quantization='auto_awq' loads and runs correctly.""" monkeypatch.setenv("VLLM_ALLOW_INSECURE_SERIALIZATION", "1") with vllm_runner( model_id, dtype=torch.float16, quantization="auto_awq", max_model_len=2048, enforce_eager=True, ) as llm: def check_model(model): from vllm.model_executor.layers.quantization.auto_awq import ( AutoAWQLinearMethod, AutoAWQMarlinLinearMethod, ) for name, submodule in model.named_modules(): if name == "model.layers.0.self_attn.qkv_proj": # Should use either AutoAWQLinearMethod (Triton) or # AutoAWQMarlinLinearMethod (Marlin) depending on hardware assert isinstance( submodule.quant_method, (AutoAWQLinearMethod, AutoAWQMarlinLinearMethod), ), ( f"Expected AutoAWQLinearMethod or AutoAWQMarlinLinearMethod " f"for {name}, got {type(submodule.quant_method)}" ) break llm.apply_model(check_model) outputs = llm.generate_greedy([PROMPT], max_tokens=8) assert outputs assert len(outputs[0][1]) > 0 def test_auto_awq_config_get_name(): """Test that AutoAWQConfig.get_name() returns 'auto_awq'.""" from vllm.model_executor.layers.quantization.auto_awq import AutoAWQConfig assert AutoAWQConfig.get_name() == "auto_awq"