Signed-off-by: wangyu <410167048@qq.com> Signed-off-by: lishunyang12 <lishunyang12@163.com> Signed-off-by: [Your Name] <your.email@example.com> Signed-off-by: wangyu <53896905+yenuo26@users.noreply.github.com> Signed-off-by: Bo Li <22713281+bobboli@users.noreply.github.com> Co-authored-by: lishunyang12 <lishunyang12@163.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Bo Li <22713281+bobboli@users.noreply.github.com>
208 lines
7.8 KiB
Python
208 lines
7.8 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
|
|
|
import importlib
|
|
import sys
|
|
import types
|
|
|
|
import pytest
|
|
import torch
|
|
from vllm.platforms import current_platform
|
|
|
|
from vllm_omni.diffusion.attention.backends.abstract import AttentionMetadata
|
|
|
|
pytestmark = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu]
|
|
|
|
|
|
SAGE_ATTN3_MODULE = "vllm_omni.diffusion.attention.backends.sage_attn3"
|
|
|
|
|
|
def load_sage_attn3_module(monkeypatch: pytest.MonkeyPatch, kernel_impl):
|
|
fake_module = types.ModuleType("sageattn3")
|
|
setattr(fake_module, "sageattn3_blackwell", kernel_impl)
|
|
monkeypatch.setitem(sys.modules, "sageattn3", fake_module)
|
|
sys.modules.pop(SAGE_ATTN3_MODULE, None)
|
|
return importlib.import_module(SAGE_ATTN3_MODULE)
|
|
|
|
|
|
def test_sage_attn3_forward_uses_blackwell_layout(monkeypatch: pytest.MonkeyPatch):
|
|
calls = {}
|
|
|
|
def fake_kernel(query, key, value, is_causal=False):
|
|
calls["query_shape"] = query.shape
|
|
calls["is_causal"] = is_causal
|
|
return query + key + value
|
|
|
|
backend_module = load_sage_attn3_module(monkeypatch, fake_kernel)
|
|
impl = backend_module.SageAttention3Impl(
|
|
num_heads=4,
|
|
head_size=64,
|
|
softmax_scale=1.0 / 8.0,
|
|
causal=False,
|
|
)
|
|
|
|
query = torch.randn(2, 8, 4, 64)
|
|
key = torch.randn(2, 8, 4, 64)
|
|
value = torch.randn(2, 8, 4, 64)
|
|
|
|
output = impl.forward_cuda(query, key, value)
|
|
|
|
assert calls["query_shape"] == (2, 4, 8, 64)
|
|
assert calls["is_causal"] is False
|
|
expected = (query.transpose(1, 2) + key.transpose(1, 2) + value.transpose(1, 2)).transpose(1, 2)
|
|
assert torch.allclose(output, expected)
|
|
|
|
|
|
def test_sage_attn3_rejects_gqa_instead_of_falling_back(monkeypatch: pytest.MonkeyPatch):
|
|
def fake_kernel(*args, **kwargs):
|
|
raise AssertionError("sageattn3_blackwell should not be used for GQA")
|
|
|
|
backend_module = load_sage_attn3_module(monkeypatch, fake_kernel)
|
|
impl = backend_module.SageAttention3Impl(
|
|
num_heads=4,
|
|
head_size=64,
|
|
softmax_scale=1.0 / 8.0,
|
|
causal=False,
|
|
)
|
|
|
|
query = torch.randn(2, 8, 4, 64)
|
|
key = torch.randn(2, 8, 2, 64)
|
|
value = torch.randn(2, 8, 2, 64)
|
|
|
|
with pytest.raises(NotImplementedError, match="does not support GQA/MQA"):
|
|
impl.forward_cuda(query, key, value)
|
|
|
|
|
|
def test_sage_attn3_rejects_mask_instead_of_ignoring_it(monkeypatch: pytest.MonkeyPatch):
|
|
def fake_kernel(*args, **kwargs):
|
|
raise AssertionError("sageattn3_blackwell should not run with an unsupported mask")
|
|
|
|
backend_module = load_sage_attn3_module(monkeypatch, fake_kernel)
|
|
impl = backend_module.SageAttention3Impl(
|
|
num_heads=4,
|
|
head_size=64,
|
|
softmax_scale=1.0 / 8.0,
|
|
causal=False,
|
|
)
|
|
query = torch.randn(2, 8, 4, 64)
|
|
metadata = AttentionMetadata(attn_mask=torch.ones(2, 8, dtype=torch.bool))
|
|
|
|
with pytest.raises(ValueError, match="does not support attn_mask"):
|
|
impl.forward_cuda(query, query, query, metadata)
|
|
|
|
|
|
def test_sage_attn3_rejects_custom_softmax_scale(monkeypatch: pytest.MonkeyPatch):
|
|
backend_module = load_sage_attn3_module(monkeypatch, lambda *args, **kwargs: None)
|
|
|
|
with pytest.raises(ValueError, match="does not expose a custom softmax scale"):
|
|
backend_module.SageAttention3Impl(
|
|
num_heads=4,
|
|
head_size=64,
|
|
softmax_scale=1.0,
|
|
causal=False,
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(not current_platform.is_cuda(), reason="sage_attn3 tests require CUDA platform")
|
|
def test_cuda_platform_selects_sage_attn3_alias(monkeypatch: pytest.MonkeyPatch):
|
|
from vllm.platforms.interface import DeviceCapability
|
|
|
|
from vllm_omni.diffusion.attention.backends.registry import DiffusionAttentionBackendEnum
|
|
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
|
|
from vllm_omni.platforms.cuda import platform as cuda_platform_module
|
|
from vllm_omni.platforms.cuda.platform import CudaOmniPlatform
|
|
|
|
original_import_module = importlib.import_module
|
|
|
|
monkeypatch.setattr(
|
|
CudaOmniPlatform,
|
|
"get_device_capability",
|
|
classmethod(lambda cls, device_id=0: DeviceCapability(10, 0)),
|
|
)
|
|
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": False})
|
|
monkeypatch.setattr(
|
|
cuda_platform_module.importlib,
|
|
"import_module",
|
|
lambda module_name: object() if module_name == "sageattn3" else original_import_module(module_name),
|
|
)
|
|
|
|
backend_path = CudaOmniPlatform.get_diffusion_attn_backend_cls("SAGE_ATTN_3", head_size=64)
|
|
|
|
assert backend_path == DiffusionAttentionBackendEnum.SAGE_ATTN_3.get_path()
|
|
|
|
|
|
@pytest.mark.skipif(not current_platform.is_cuda(), reason="sage_attn3 tests require CUDA platform")
|
|
@pytest.mark.parametrize("head_size", [32, 320])
|
|
def test_cuda_platform_rejects_explicit_sage_attn3_unsupported_head_size(
|
|
monkeypatch: pytest.MonkeyPatch, head_size: int
|
|
):
|
|
from vllm.platforms.interface import DeviceCapability
|
|
|
|
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
|
|
from vllm_omni.platforms.cuda import platform as cuda_platform_module
|
|
from vllm_omni.platforms.cuda.platform import CudaOmniPlatform
|
|
|
|
original_import_module = importlib.import_module
|
|
|
|
monkeypatch.setattr(
|
|
CudaOmniPlatform,
|
|
"get_device_capability",
|
|
classmethod(lambda cls, device_id=0: DeviceCapability(10, 0)),
|
|
)
|
|
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": False})
|
|
monkeypatch.setattr(
|
|
cuda_platform_module.importlib,
|
|
"import_module",
|
|
lambda module_name: object() if module_name == "sageattn3" else original_import_module(module_name),
|
|
)
|
|
load_sage_attn3_module(monkeypatch, lambda *args, **kwargs: None)
|
|
|
|
with pytest.raises(ValueError, match=f"head_size={head_size} is unsupported"):
|
|
CudaOmniPlatform.get_diffusion_attn_backend_cls("SAGE_ATTN_3", head_size=head_size)
|
|
|
|
|
|
@pytest.mark.skipif(not current_platform.is_cuda(), reason="sage_attn3 tests require CUDA platform")
|
|
def test_cuda_platform_rejects_explicit_sage_attn3_on_unsupported_gpu(monkeypatch: pytest.MonkeyPatch):
|
|
from vllm.platforms.interface import DeviceCapability
|
|
|
|
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
|
|
from vllm_omni.platforms.cuda.platform import CudaOmniPlatform
|
|
|
|
monkeypatch.setattr(
|
|
CudaOmniPlatform,
|
|
"get_device_capability",
|
|
classmethod(lambda cls, device_id=0: DeviceCapability(9, 0)),
|
|
)
|
|
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": False})
|
|
|
|
with pytest.raises(ValueError, match="explicitly selected"):
|
|
CudaOmniPlatform.get_diffusion_attn_backend_cls("SAGE_ATTN_3", head_size=64)
|
|
|
|
|
|
@pytest.mark.skipif(not current_platform.is_cuda(), reason="sage_attn3 tests require CUDA platform")
|
|
def test_cuda_platform_rejects_missing_explicit_sage_attn3(monkeypatch: pytest.MonkeyPatch):
|
|
from vllm.platforms.interface import DeviceCapability
|
|
|
|
from vllm_omni.diffusion.envs import PACKAGES_CHECKER
|
|
from vllm_omni.platforms.cuda import platform as cuda_platform_module
|
|
from vllm_omni.platforms.cuda.platform import CudaOmniPlatform
|
|
|
|
original_import_module = importlib.import_module
|
|
|
|
monkeypatch.setattr(
|
|
CudaOmniPlatform,
|
|
"get_device_capability",
|
|
classmethod(lambda cls, device_id=0: DeviceCapability(10, 0)),
|
|
)
|
|
monkeypatch.setattr(PACKAGES_CHECKER, "get_packages_info", lambda: {"has_flash_attn": False})
|
|
|
|
def _missing_sageattn3(module_name):
|
|
if module_name == "sageattn3":
|
|
raise ImportError("sageattn3 not installed")
|
|
return original_import_module(module_name)
|
|
|
|
monkeypatch.setattr(cuda_platform_module.importlib, "import_module", _missing_sageattn3)
|
|
|
|
with pytest.raises(ImportError, match="explicitly selected"):
|
|
CudaOmniPlatform.get_diffusion_attn_backend_cls("SAGE_ATTN_3", head_size=64)
|