[Attention] Make sliding-window support an explicit backend capability (#48011)

Signed-off-by: NickLucche <nicolo.lucchesi@mistral.ai> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Andreas Karatzas <akaratza@amd.com>

Nicolò Lucchesi committed Jul 13, 2026 at 10:07 UTC bea70c7cfc10303f843cb4bda0e990e88f525f76
10 files changed +41
vllm/model_executor/layers/attention/attention.py
+1
@@ -355,6 +355,7 @@ class Attention(nn.Module, AttentionLayerBase):
355 use_mm_prefix=self.use_mm_prefix,
356 use_per_head_quant_scales=use_per_head_quant_scales,
357 attn_type=attn_type,
358 + has_sliding_window=sliding_window is not None,
359 )
360 else:
361 self.attn_backend = attn_backend
vllm/v1/attention/backend.py
+7
@@ -258,6 +258,10 @@ class AttentionBackend(ABC):
258 def supports_per_head_quant_scales(cls) -> bool:
259 return False
260
261 + @classmethod
262 + def supports_sliding_window(cls) -> bool:
263 + return False
264 +
265 @classmethod
266 def supports_non_causal(cls) -> bool:
267 """Check if backend supports non-causal (bidirectional) attention
@@ -319,6 +323,7 @@ class AttentionBackend(ABC):
323 use_per_head_quant_scales: bool,
324 device_capability: "DeviceCapability",
325 attn_type: str,
326 + has_sliding_window: bool = False,
327 use_non_causal: bool = False,
328 use_batch_invariant: bool = False,
329 use_kv_connector: bool = False,
@@ -354,6 +359,8 @@ class AttentionBackend(ABC):
359 invalid_reasons.append("compute capability not supported")
360 if not cls.supports_attn_type(attn_type):
361 invalid_reasons.append(f"attention type {attn_type} not supported")
362 + if has_sliding_window and not cls.supports_sliding_window():
363 + invalid_reasons.append("sliding window not supported")
364 if use_non_causal and not cls.supports_non_causal():
365 invalid_reasons.append("non-causal attention not supported")
366 if use_batch_invariant and not cls.supports_batch_invariance():
vllm/v1/attention/backends/cpu_attn.py
+4
@@ -67,6 +67,10 @@ class CPUAttentionBackend(AttentionBackend):
67 def supports_non_causal(cls) -> bool:
68 return True
69
70 + @classmethod
71 + def supports_sliding_window(cls) -> bool:
72 + return True
73 +
74 @classmethod
75 def supports_attn_type(cls, attn_type: str) -> bool:
76 """CPU attention supports decoder,
vllm/v1/attention/backends/flash_attn.py
+4
@@ -92,6 +92,10 @@ class FlashAttentionBackend(AttentionBackend):
92 def get_name() -> str:
93 return "FLASH_ATTN"
94
95 + @classmethod
96 + def supports_sliding_window(cls) -> bool:
97 + return True
98 +
99 @classmethod
100 def supports_batch_invariance(cls) -> bool:
101 return True
vllm/v1/attention/backends/flashinfer.py
+4
@@ -380,6 +380,10 @@ class FlashInferBackend(AttentionBackend):
380 def supports_non_causal(cls) -> bool:
381 return True
382
383 + @classmethod
384 + def supports_sliding_window(cls) -> bool:
385 + return True
386 +
387 @staticmethod
388 def get_impl_cls() -> type["FlashInferImpl"]:
389 return FlashInferImpl
vllm/v1/attention/backends/flex_attention.py
+4
@@ -100,6 +100,10 @@ class FlexAttentionBackend(AttentionBackend):
100 def get_name() -> str:
101 return "FLEX_ATTENTION"
102
103 + @classmethod
104 + def supports_sliding_window(cls) -> bool:
105 + return True
106 +
107 @classmethod
108 def supports_non_causal(cls) -> bool:
109 return True
vllm/v1/attention/backends/rocm_aiter_fa.py
+4
@@ -750,6 +750,10 @@ class AiterFlashAttentionBackend(AttentionBackend):
750 def get_name() -> str:
751 return "FLASH_ATTN"
752
753 + @classmethod
754 + def supports_sliding_window(cls) -> bool:
755 + return True
756 +
757 @staticmethod
758 def get_impl_cls() -> type["AiterFlashAttentionImpl"]:
759 return AiterFlashAttentionImpl
vllm/v1/attention/backends/rocm_attn.py
+4
@@ -220,6 +220,10 @@ class RocmAttentionBackend(AttentionBackend):
220 def get_name() -> str:
221 return "ROCM_ATTN"
222
223 + @classmethod
224 + def supports_sliding_window(cls) -> bool:
225 + return True
226 +
227 @staticmethod
228 def get_impl_cls() -> type["RocmAttentionImpl"]:
229 return RocmAttentionImpl
vllm/v1/attention/backends/triton_attn.py
+4
@@ -306,6 +306,10 @@ class TritonAttentionBackend(AttentionBackend):
306 def get_name() -> str:
307 return "TRITON_ATTN"
308
309 + @classmethod
310 + def supports_sliding_window(cls) -> bool:
311 + return True
312 +
313 @classmethod
314 def supports_batch_invariance(cls) -> bool:
315 return True
vllm/v1/attention/selector.py
+5
@@ -29,6 +29,7 @@ class AttentionSelectorConfig(NamedTuple):
29 use_mm_prefix: bool = False
30 use_per_head_quant_scales: bool = False
31 attn_type: str = AttentionType.DECODER
32 + has_sliding_window: bool = False
33 use_non_causal: bool = False
34 use_batch_invariant: bool = False
35 use_kv_connector: bool = False
@@ -45,6 +46,7 @@ class AttentionSelectorConfig(NamedTuple):
46 f"use_mm_prefix={self.use_mm_prefix}, "
47 f"use_per_head_quant_scales={self.use_per_head_quant_scales}, "
48 f"attn_type={self.attn_type}, "
49 + f"has_sliding_window={self.has_sliding_window}, "
50 f"use_non_causal={self.use_non_causal}, "
51 f"use_batch_invariant={self.use_batch_invariant}, "
52 f"use_kv_connector={self.use_kv_connector})"
@@ -62,6 +64,7 @@ def get_attn_backend(
64 use_per_head_quant_scales: bool = False,
65 attn_type: str | None = None,
66 num_heads: int | None = None,
67 + has_sliding_window: bool = False,
68 ) -> type[AttentionBackend]:
69 """Selects which attention backend to use and lazily imports it."""
70
@@ -77,6 +80,7 @@ def get_attn_backend(
80 vllm_config = get_current_vllm_config()
81
82 cache_config = vllm_config.cache_config
83 + block_size: int | None
84 if cache_config is not None and cache_config.user_specified_block_size:
85 block_size = cache_config.block_size
86 else:
@@ -98,6 +102,7 @@ def get_attn_backend(
102 use_mm_prefix=use_mm_prefix,
103 use_per_head_quant_scales=use_per_head_quant_scales,
104 attn_type=attn_type or AttentionType.DECODER,
105 + has_sliding_window=has_sliding_window,
106 use_non_causal=vllm_config.attention_config.use_non_causal,
107 use_batch_invariant=envs.VLLM_BATCH_INVARIANT,
108 use_kv_connector=use_kv_connector,