diff --git a/fastvideo/platforms/cuda.py b/fastvideo/platforms/cuda.py index ec257d6d1c..10b321b5c5 100644 --- a/fastvideo/platforms/cuda.py +++ b/fastvideo/platforms/cuda.py @@ -146,9 +146,12 @@ def get_attn_backend_cls(cls, selected_backend: AttentionBackendEnum | None, hea if is_attn_qat_infer_available(): logger.info("Using Attn-QAT inference backend (%s).", attn_qat_infer_receipt()) return "fastvideo.attention.backends.attn_qat_infer.AttnQatInferBackend" - # Keep the trailing sentence stable: downstream receipts grep for it. - logger.info("Attn-QAT inference kernel is not built (%s). Fall back to Flash Attention.", - attn_qat_infer_receipt()) + raise ImportError( + f"ATTN_QAT_INFER selected but the inference kernel is not usable ({attn_qat_infer_receipt()}). " + "Silent fallback would run plain FlashAttention while the caller believes it is measuring " + "FP4-QAT attention — an A/B comparison would silently benchmark bf16 against bf16; " + "refusing to proceed. Build the fastvideo-kernel attn_qat_infer target for this arch " + "or pick a different FASTVIDEO_ATTENTION_BACKEND.") elif selected_backend == AttentionBackendEnum.ATTN_QAT_TRAIN: from fastvideo.attention.backends.attn_qat_train import ( # noqa: F401 AttnQatTrainBackend, is_attn_qat_train_available) diff --git a/fastvideo/tests/attention/test_attn_qat_infer_arch_gate.py b/fastvideo/tests/attention/test_attn_qat_infer_arch_gate.py index c13c9a55de..880c5dce18 100644 --- a/fastvideo/tests/attention/test_attn_qat_infer_arch_gate.py +++ b/fastvideo/tests/attention/test_attn_qat_infer_arch_gate.py @@ -4,7 +4,9 @@ The capability sets route: * sm_120a/sm_121a -> fastvideo-kernel CUTLASS extension, * sm_100a/sm_103a -> FP4 FA4 (flash-attention-fp4, #1221 plumbing), - * anything else -> unavailable (honest FlashAttention fallback upstream). + * anything else -> unavailable (hard ImportError upstream: an explicitly + selected QAT-inference backend must never silently degrade to plain + FlashAttention, or A/B benchmarks measure bf16 against bf16). All device/import probes are monkeypatched; no GPU or kernel install needed. """ @@ -71,6 +73,22 @@ def test_unsupported_arch_receipt_lists_support_matrix(monkeypatch) -> None: assert "sm_100a/sm_103a" in receipt +def test_selecting_unavailable_backend_raises_instead_of_falling_back(monkeypatch) -> None: + """Explicit ATTN_QAT_INFER on an arch without the kernel must hard-fail. + + The old behavior logged "Fall back to Flash Attention" and continued, + which let FP4 A/B benchmarks silently measure bf16 against bf16. + """ + import torch + + from fastvideo.platforms.cuda import CudaPlatform + from fastvideo.platforms.interface import AttentionBackendEnum + + _patch(monkeypatch, cap=(9, 0), cutlass=False, fa4=False) + with pytest.raises(ImportError, match="ATTN_QAT_INFER selected but"): + CudaPlatform.get_attn_backend_cls(AttentionBackendEnum.ATTN_QAT_INFER, 128, torch.bfloat16) + + def test_unsupported_arch_forward_never_calls_bundled_extension(monkeypatch) -> None: """An unsupported GPU (e.g. sm_90) carrying an importable sm_12x wheel must fail cleanly at forward — never dispatch into the wrong binary."""