main
py 86 lines 3.12 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from dataclasses import replace
4
5 import torch
6
7 from vllm.config import CacheConfig, VllmConfig
8 from vllm.model_executor.layers.attention import Attention
9 from vllm.model_executor.layers.attention.encoder_only_attention import (
10 create_encoder_only_attention_backend,
11 )
12 from vllm.v1.attention.backend import AttentionType
13 from vllm.v1.attention.selector import get_attn_backend
14 from vllm.v1.kv_cache_interface import FullAttentionSpec, KVCacheSpec
15
16
17 class PrefillPrefixLMAttention(Attention):
18 """Decoder attention that runs non-causally (Prefix LM).
19
20 This reuses the encoder-only backend wrapper, which forces
21 ``causal=False`` on *every* metadata build (prefill and decode alike),
22 while keeping ``attn_type=DECODER`` so a KV cache is still allocated.
23
24 Effect by phase:
25 - Prefill: query tokens attend to each other bidirectionally -- this is
26 where the Prefix LM (non-causal) behavior actually takes effect.
27 - Single-token decode: ``causal=False`` is a no-op. The one new query
28 attends to the whole (frozen) KV cache exactly as a causal decode
29 would, and cached tokens cannot attend back to it, so the output is
30 identical to a causal decoder.
31 """
32
33 def __init__(
34 self,
35 num_heads: int,
36 head_size: int,
37 scale: float,
38 cache_config: CacheConfig | None = None,
39 attn_type: str | None = None,
40 **kwargs,
41 ):
42 dtype = torch.get_default_dtype()
43
44 if cache_config is not None:
45 kv_cache_dtype = cache_config.cache_dtype
46 else:
47 kv_cache_dtype = "auto"
48
49 underlying_attn_backend = get_attn_backend(
50 head_size,
51 dtype,
52 kv_cache_dtype,
53 attn_type=AttentionType.DECODER,
54 )
55
56 attn_backend = create_encoder_only_attention_backend(underlying_attn_backend)
57
58 if attn_type is not None:
59 assert attn_type == AttentionType.DECODER, (
60 "PrefillPrefixLMAttention only supports AttentionType.DECODER"
61 )
62
63 super().__init__(
64 num_heads=num_heads,
65 head_size=head_size,
66 scale=scale,
67 cache_config=cache_config,
68 attn_backend=attn_backend,
69 attn_type=AttentionType.DECODER,
70 **kwargs,
71 )
72
73 def get_kv_cache_spec(self, vllm_config: VllmConfig) -> KVCacheSpec | None:
74 """Tag the KV cache spec as non-causal.
75
76 The layout is identical to a regular decoder full-attention layer, so
77 we reuse the base spec and only flip ``non_causal=True``. The engine
78 core reads this flag (across the worker/engine process boundary, via
79 the pickled spec) to disable scheduling features that assume causal
80 attention -- chunked prefill and prefix caching -- which would
81 otherwise corrupt the bidirectional prefill of a Prefix LM.
82 """
83 spec = super().get_kv_cache_spec(vllm_config)
84 if isinstance(spec, FullAttentionSpec):
85 return replace(spec, non_causal=True)
86 return spec