3737from vllm .config import VllmConfig , get_current_vllm_config
3838from vllm .utils .math_utils import cdiv
3939from vllm .v1 .attention .backend import AttentionCGSupport
40+ from vllm .v1 .attention .backends .registry import AttentionBackendEnum , register_backend
4041from vllm .v1 .attention .backends .utils import CommonAttentionMetadata
4142
4243from vllm_fl .dispatch .backends .vendor .ascend .impl .attention_mask import (
@@ -215,6 +216,7 @@ class AscendAttentionMetadataBuilder:
215216 # ACL graph support - ALWAYS means full graph capture is supported
216217 aclgraph_support : ClassVar [AttentionCGSupport ] = AttentionCGSupport .ALWAYS
217218 reorder_batch_threshold : ClassVar [int ] = 1
219+ supports_update_block_table : bool = False
218220
219221 @staticmethod
220222 def get_cudagraph_support (vllm_config , kv_cache_spec ) -> AttentionCGSupport :
@@ -433,7 +435,7 @@ class AscendAttentionBackend(AttentionBackend):
433435
434436 @staticmethod
435437 def get_name () -> str :
436- return "ASCEND_FL "
438+ return "CUSTOM "
437439
438440 @staticmethod
439441 def get_impl_cls () -> Type ["AscendAttentionBackendImpl" ]:
@@ -443,6 +445,15 @@ def get_impl_cls() -> Type["AscendAttentionBackendImpl"]:
443445 def get_builder_cls () -> Type ["AscendAttentionMetadataBuilder" ]:
444446 return AscendAttentionMetadataBuilder
445447
448+ @staticmethod
449+ def get_supported_kernel_block_sizes () -> list [int ]:
450+ # Ascend fused_infer_attention_score and paged_attention kernels
451+ # are validated for block size 128 in vllm-ascend. Allowing the
452+ # default MultipleOf(1) lets the V1 engine pick unsupported merged
453+ # storage block sizes (e.g. 784 for Qwen3.5 hybrid models), which
454+ # causes aclnnFusedInferAttentionScoreV3 to fail with error 561002.
455+ return [128 ]
456+
446457 @staticmethod
447458 def get_kv_cache_shape (
448459 num_blocks : int ,
@@ -488,6 +499,12 @@ def get_supported_block_size() -> list[int]:
488499 return [128 ]
489500
490501
502+ register_backend (
503+ AttentionBackendEnum .CUSTOM ,
504+ "vllm_fl.dispatch.backends.vendor.ascend.impl.attention.AscendAttentionBackend" ,
505+ )
506+
507+
491508class AscendAttentionBackendImpl (AttentionImpl ):
492509 """
493510 Ascend attention implementation using native torch_npu operators.
@@ -561,20 +578,16 @@ def _get_fia_params(
561578 value = self .value_cache .view (num_block , block_size , - 1 )
562579 actual_seq_lengths_kv = attn_metadata .seq_lens_list
563580 elif attn_metadata .attn_state == AscendAttentionState .DecodeOnly :
564- # num_block, block_size, _, _ = self.key_cache.shape
565- # key = self.key_cache.view(num_block, block_size, -1)
566- # value = self.value_cache.view(num_block, block_size, -1)
567- key = self .key_cache .view (- 1 , block_size , 256 )
568- value = self .value_cache .view (- 1 , block_size , 256 )
581+ num_block , block_size , _ , _ = self .key_cache .shape
582+ key = self .key_cache .view (num_block , block_size , - 1 )
583+ value = self .value_cache .view (num_block , block_size , - 1 )
569584 block_table = attn_metadata .block_tables
570585 actual_seq_lengths_kv = attn_metadata .seq_lens_list
571586 else :
572587 # ChunkedPrefill
573- # num_block, block_size, _, _ = self.key_cache.shape
574- # key = self.key_cache.view(num_block, block_size, -1)
575- # value = self.value_cache.view(num_block, block_size, -1)
576- key = self .key_cache .view (- 1 , block_size , 256 )
577- value = self .value_cache .view (- 1 , block_size , 256 )
588+ num_block , block_size , _ , _ = self .key_cache .shape
589+ key = self .key_cache .view (num_block , block_size , - 1 )
590+ value = self .value_cache .view (num_block , block_size , - 1 )
578591 block_table = attn_metadata .block_tables
579592 actual_seq_lengths_kv = attn_metadata .seq_lens_list
580593
@@ -596,7 +609,6 @@ def reshape_and_cache(
596609 # TODO(yxa): block_table.py: CUDA uses int64, NPU uses int32.
597610 if slots .dtype != torch .int32 :
598611 slots = slots .to (torch .int32 )
599- # Use torch_npu reshape_and_cache
600612 torch_npu ._npu_reshape_and_cache (
601613 key = key [:attn_metadata .num_actual_tokens ],
602614 value = value [:attn_metadata .num_actual_tokens ],
@@ -625,9 +637,9 @@ def forward_fused_infer_attention(
625637 key = key [:num_tokens ]
626638 value = value [:num_tokens ]
627639
628- # Determine sparse_mode based on mask availability
629- # sparse_mode=3 requires attn_mask; sparse_mode=0 does not
630- # sparse_mode = 3 if attn_metadata.attn_mask is not None else 0
640+ # sparse_mode: 3 = causal with mask, 0 = no mask
641+ sparse_mode = 3 if attn_metadata . attn_mask is not None else 0
642+
631643 attn_output , _ = torch_npu .npu_fused_infer_attention_score (
632644 query = query ,
633645 key = key ,
@@ -641,7 +653,7 @@ def forward_fused_infer_attention(
641653 num_key_value_heads = self .num_kv_heads ,
642654 num_heads = self .num_heads ,
643655 scale = self .scale ,
644- sparse_mode = 3 ,
656+ sparse_mode = sparse_mode ,
645657 )
646658
647659 attn_output = attn_output .view (num_tokens , self .num_heads , self .head_size )
@@ -779,8 +791,9 @@ def forward(
779791 return output .fill_ (0 )
780792
781793 # Reshape and cache KV
782- if attn_metadata != AscendAttentionState .DecodeOnly :
783- kv_cache = [i .contiguous () for i in kv_cache ]
794+ # Note: kv_cache[0]/[1] may be non-contiguous views of a
795+ # [2, num_blocks, ...] tensor. _npu_reshape_and_cache handles
796+ # them directly via slot_indices — no contiguous copy needed.
784797 if key is not None and value is not None :
785798 key = key .contiguous ()
786799 value = value .contiguous ()
0 commit comments