[AMD] aiter: fail loudly on cross-layer KV sharing in target_verify (#38755)
This commit is contained in:
@@ -2285,6 +2285,23 @@ class AiterAttnBackend(AttentionBackend):
|
|||||||
)[:2]
|
)[:2]
|
||||||
return output, lse.transpose(0, 1).contiguous()
|
return output, lse.transpose(0, 1).contiguous()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _reject_target_verify_cross_layer_kv(k, v):
|
||||||
|
"""Reject cross-layer KV sharing on the legacy ragged target-verify path.
|
||||||
|
|
||||||
|
Cross-layer KV sharing (e.g. Gemma4) passes ``k=v=None`` so the kernel
|
||||||
|
reads K/V from the pool. The legacy ``extend_attention_fwd`` path takes
|
||||||
|
ragged K/V and has no pool-reading fallback, so it would raise an opaque
|
||||||
|
``AttributeError`` on ``.contiguous``. Fail loudly instead. Inert when
|
||||||
|
real K/V is passed.
|
||||||
|
"""
|
||||||
|
if k is None or v is None:
|
||||||
|
raise ValueError(
|
||||||
|
"aiter target_verify does not support cross-layer KV "
|
||||||
|
"sharing (k/v are None). Use the unified verify path "
|
||||||
|
"(speculative_eagle_topk=1 and SGLANG_AITER_UNIFIED_VERIFY=1)."
|
||||||
|
)
|
||||||
|
|
||||||
def forward_extend(
|
def forward_extend(
|
||||||
self,
|
self,
|
||||||
q: torch.Tensor,
|
q: torch.Tensor,
|
||||||
@@ -2797,6 +2814,8 @@ class AiterAttnBackend(AttentionBackend):
|
|||||||
)
|
)
|
||||||
return o.view(-1, layer.tp_q_head_num * layer.v_head_dim)
|
return o.view(-1, layer.tp_q_head_num * layer.v_head_dim)
|
||||||
|
|
||||||
|
self._reject_target_verify_cross_layer_kv(k, v)
|
||||||
|
|
||||||
self.extend_attention_fwd(
|
self.extend_attention_fwd(
|
||||||
q.view(-1, layer.tp_q_head_num, layer.qk_head_dim),
|
q.view(-1, layer.tp_q_head_num, layer.qk_head_dim),
|
||||||
k.contiguous(),
|
k.contiguous(),
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"""PR2 guard: aiter target-verify rejects cross-layer KV sharing (k/v is None).
|
||||||
|
|
||||||
|
Cross-layer KV sharing (Gemma4) passes ``k=v=None``. The legacy ragged
|
||||||
|
``extend_attention_fwd`` target-verify path has no pool-reading fallback, so it
|
||||||
|
must fail loudly with a ``ValueError`` naming the unified verify path rather
|
||||||
|
than an opaque ``AttributeError`` on ``.contiguous``. The guard is inert when
|
||||||
|
real K/V is passed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from sglang.srt.layers.attention.aiter_backend import AiterAttnBackend
|
||||||
|
from sglang.test.ci.ci_register import register_amd_ci
|
||||||
|
from sglang.test.test_utils import CustomTestCase
|
||||||
|
|
||||||
|
register_amd_ci(est_time=10, suite="stage-b-test-1-gpu-small-amd-mi35x")
|
||||||
|
|
||||||
|
|
||||||
|
class TestAiterCrossLayerKVGuard(CustomTestCase):
|
||||||
|
def test_raises_when_k_is_none(self):
|
||||||
|
with self.assertRaises(ValueError) as ctx:
|
||||||
|
AiterAttnBackend._reject_target_verify_cross_layer_kv(None, torch.empty(0))
|
||||||
|
self.assertIn("cross-layer KV", str(ctx.exception))
|
||||||
|
self.assertIn("unified verify path", str(ctx.exception))
|
||||||
|
|
||||||
|
def test_raises_when_v_is_none(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
AiterAttnBackend._reject_target_verify_cross_layer_kv(torch.empty(0), None)
|
||||||
|
|
||||||
|
def test_raises_when_both_none(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
AiterAttnBackend._reject_target_verify_cross_layer_kv(None, None)
|
||||||
|
|
||||||
|
def test_inert_when_kv_present(self):
|
||||||
|
# Real K/V present -> no raise (the guard must not fire for standard
|
||||||
|
# models that pass ragged K/V into the legacy target-verify path).
|
||||||
|
AiterAttnBackend._reject_target_verify_cross_layer_kv(
|
||||||
|
torch.empty(0), torch.empty(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Reference in New Issue
Block a user