Preserve FP8 CP shared-KV page contracts

NSA FP8 CP shared-KV reuse must operate on packed page-slot rows, not bf16 compact rows. The change keeps current-only and partial-current reuse inside the page-aligned materialization contract, fails fast for non-page-aligned CP split inputs, and prevents FP8 FlashMLA-KV prefill from reaching incompatible in-seq CP metadata.

Constraint: NSA FP8 persistent MLA KV rows are packed 656-byte records and CP shared KV cache management is page-granular.\nConstraint: FlashMLA-KV prefill metadata is not CP-local after NSA in-seq splitting.\nRejected: Silently splice bf16 current rows into FP8 materialized cache | corrupts the packed cache layout.\nRejected: Keep FP8 FlashMLA-KV auto prefill under NSA CP | reaches num_splits shape errors after q-row splitting.\nConfidence: medium\nScope-risk: moderate\nDirective: Do not re-enable FP8 FlashMLA-KV prefill for NSA in-seq CP until metadata is rebuilt after CP splitting or made CP-local.\nTested: Local git diff --check and py_compile for touched SGLang files.\nTested: Remote g0034 related unit sweep recorded in docs: test_nsa_cp_utils.py, test_cp_shared_kv_layout.py, test_cp_shared_kv_runtime.py, test_cp_hicache_metadata.py passed.\nNot-tested: Full FP8 ETE startup and performance run after this commit.
This commit is contained in:
laoyao0822
2026-06-01 03:33:44 +08:00
parent 46be97adc0
commit 6ef4face89
7 changed files with 810 additions and 20 deletions
@@ -283,6 +283,62 @@ class TestNSAInSeqCPUtils(unittest.TestCase):
):
self.assertFalse(can_cp_split(256, 8, True, forward_batch))
def test_can_cp_split_skips_current_only_when_page_units_do_not_cover_all_lanes(
self,
):
class Mode:
def is_context_parallel_extend(self):
return True
forward_batch = SimpleNamespace(
uses_cp_shared_kv=True,
extend_seq_lens_cpu=[64],
extend_prefix_lens_cpu=[0],
token_to_kv_pool=SimpleNamespace(page_size=64),
forward_mode=Mode(),
)
with (
patch(
"sglang.srt.layers.attention.nsa.utils.is_nsa_prefill_cp_round_robin_split",
return_value=False,
),
patch(
"sglang.srt.layers.attention.nsa.utils.is_nsa_enable_prefill_cp",
return_value=True,
),
):
self.assertFalse(can_cp_split(64, 8, True, forward_batch))
def test_can_cp_split_fails_on_non_page_aligned_cp_shared_prefix(self):
class Mode:
def is_context_parallel_extend(self):
return True
forward_batch = SimpleNamespace(
uses_cp_shared_kv=True,
extend_seq_lens_cpu=[1024],
extend_prefix_lens_cpu=[65],
token_to_kv_pool=SimpleNamespace(page_size=64),
forward_mode=Mode(),
)
with (
patch(
"sglang.srt.layers.attention.nsa.utils.is_nsa_prefill_cp_round_robin_split",
return_value=False,
),
patch(
"sglang.srt.layers.attention.nsa.utils.is_nsa_enable_prefill_cp",
return_value=True,
),
self.assertRaisesRegex(
RuntimeError,
r"\[CP_SHARED_KV_FAIL_FAST\]\[cp_split_non_page_aligned_prefix\]",
),
):
can_cp_split(1089, 8, True, forward_batch)
def test_can_cp_split_keeps_cp_for_radix_hit_suffix_with_one_page_per_rank(self):
class Mode:
def is_context_parallel_extend(self):
@@ -653,10 +709,18 @@ class TestNSAInSeqCPUtils(unittest.TestCase):
indexer._maybe_materialize_shared_index_buffer = fake_materialize
indexer._get_topk_ragged_with_cp = fake_get_topk
class Mode:
def is_extend_without_speculative(self):
return True
forward_batch = type(
"ForwardBatchStub",
(),
{
"forward_mode": Mode(),
"extend_prefix_lens_cpu": [0],
"extend_seq_lens_cpu": [5],
"seq_lens_cpu": torch.tensor([5], dtype=torch.int64),
"nsa_cp_metadata": NSAContextParallelMetadata(
kv_len_prev=5,
kv_len_next=9,
@@ -741,10 +805,18 @@ class TestNSAInSeqCPUtils(unittest.TestCase):
indexer._maybe_materialize_shared_index_buffer = fake_materialize
indexer._get_topk_ragged_with_cp = fake_get_topk
class Mode:
def is_extend_without_speculative(self):
return True
forward_batch = type(
"ForwardBatchStub",
(),
{
"forward_mode": Mode(),
"extend_prefix_lens_cpu": [0],
"extend_seq_lens_cpu": [5],
"seq_lens_cpu": torch.tensor([5], dtype=torch.int64),
"nsa_cp_metadata": NSAContextParallelMetadata(
kv_len_prev=5,
kv_len_next=9,