diff --git a/tests/kernels/moe/test_unquantized_backend_selection.py b/tests/kernels/moe/test_unquantized_backend_selection.py index d4c037d978..9d0f4d6900 100644 --- a/tests/kernels/moe/test_unquantized_backend_selection.py +++ b/tests/kernels/moe/test_unquantized_backend_selection.py @@ -6,6 +6,7 @@ import pytest import torch from tests.kernels.moe.utils import make_dummy_moe_config +from vllm.model_executor.layers.fused_moe.activation import MoEActivation from vllm.model_executor.layers.fused_moe.config import RoutingMethodType from vllm.model_executor.layers.fused_moe.oracle.unquantized import ( UnquantizedMoeBackend, @@ -228,6 +229,65 @@ def test_select_rocm_aiter_backend(mock_aiter_enabled, mock_has_flashinfer): assert expert_cls is not None +@patch( + "vllm.utils.flashinfer.has_flashinfer", + return_value=False, +) +@patch( + "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops." + "is_fused_moe_enabled", + return_value=True, +) +@patch( + "vllm.model_executor.layers.fused_moe.oracle.unquantized.rocm_aiter_ops." + "is_rdna_aiter_enabled", + return_value=False, +) +@pytest.mark.skipif( + not current_platform.is_rocm(), reason="ROCm-specific backend selection test" +) +def test_select_rocm_aiter_backend_non_gated_activation_falls_back( + mock_rdna_disabled, mock_aiter_enabled, mock_has_flashinfer, monkeypatch +): + """Test ROCm backend selection falls back (not raises) for non-gated MoE.""" + monkeypatch.setenv("VLLM_ROCM_USE_AITER", "1") + monkeypatch.setenv("VLLM_ROCM_USE_AITER_MOE", "1") + + with patch( + "vllm.model_executor.layers.fused_moe.oracle.unquantized.current_platform" + ) as mock_platform: + mock_platform.is_cuda.return_value = False + mock_platform.is_rocm.return_value = True + mock_platform.is_cpu.return_value = False + mock_platform.is_xpu.return_value = False + mock_platform.is_tpu.return_value = False + mock_platform.is_out_of_tree.return_value = False + + moe_config = make_dummy_moe_config(activation=MoEActivation.SILU_NO_MUL) + assert moe_config.is_act_and_mul is False + + selected_backend, expert_cls = select_unquantized_moe_backend( + moe_config=moe_config, + ) + + assert selected_backend != UnquantizedMoeBackend.AITER + assert expert_cls is not None + + +@pytest.mark.skipif( + not current_platform.is_rocm(), reason="ROCm-specific backend selection test" +) +def test_explicit_aiter_backend_non_gated_activation_still_raises(): + """Explicit `--moe-backend aiter` still raises for non-gated MoE; + only the env-var opt-in path falls back.""" + moe_config = make_dummy_moe_config(activation=MoEActivation.SILU_NO_MUL) + moe_config.moe_backend = "aiter" # explicit pin, not "auto" + assert moe_config.is_act_and_mul is False + + with pytest.raises(ValueError): + select_unquantized_moe_backend(moe_config=moe_config) + + @patch( "vllm.model_executor.layers.fused_moe.experts.trtllm_bf16_moe.TrtLlmBf16ExpertsMonolithic.is_supported_config", return_value=(True, None), diff --git a/vllm/model_executor/layers/fused_moe/oracle/unquantized.py b/vllm/model_executor/layers/fused_moe/oracle/unquantized.py index 9101787059..d1437933c3 100644 --- a/vllm/model_executor/layers/fused_moe/oracle/unquantized.py +++ b/vllm/model_executor/layers/fused_moe/oracle/unquantized.py @@ -305,7 +305,33 @@ def select_unquantized_moe_backend( AVAILABLE_BACKENDS.remove(UnquantizedMoeBackend.AITER) else: backend = UnquantizedMoeBackend.AITER - return _return_or_raise(backend, moe_config, activation_format) + reason = None + for k_cls in backend_to_kernel_cls(backend): + supported, reason = k_cls.is_supported_config( + k_cls, moe_config, None, None, activation_format + ) + if supported: + logger.info_once(_make_log_backend(backend)) + return backend, k_cls + # AITER was explicitly requested but does not support this + # deployment configuration (e.g. a non-gated MoE activation, + # which AiterExperts._supports_no_act_and_mul() rejects + # unconditionally). Rather than hard-crashing at engine init, + # warn — explicitly stating that this is falling back, and + # which backends will be tried next — and fall back to the + # remaining backends in priority order below. + logger.warning_once( + "VLLM_ROCM_USE_AITER_MOE=1 was requested, but %s " + "Falling back to try the remaining available MoE backends: %s.", + _make_log_unsupported(backend, reason), + ", ".join( + b.value + for b in AVAILABLE_BACKENDS + if b != UnquantizedMoeBackend.AITER + ), + ) + if UnquantizedMoeBackend.AITER in AVAILABLE_BACKENDS: + AVAILABLE_BACKENDS.remove(UnquantizedMoeBackend.AITER) for backend in AVAILABLE_BACKENDS: for k_cls in backend_to_kernel_cls(backend):