Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions python/sglang/srt/arg_groups/deepseek_v4_hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,24 @@ def validate_deepseek_v41_features(server_args: ServerArgs) -> None:
# Late layers see a per-request tail slice, not the captured prefill shape.
# The breakable backend is admitted: the tail switch runs as eager breaks and
# the tail is a fixed-capacity row block inside the captured segments.
graph_tail = cfg.cuda_graph_config.prefill.backend == Backend.BREAKABLE
incompatible = (
(
"the prefill CUDA graph",
cfg.cuda_graph_config.prefill.backend
not in (Backend.DISABLED, Backend.BREAKABLE),
),
# Only the CUDA backend builds the graph tail block at capture; the HIP
# radix backend would hand the model an eager tail.
(
"the breakable prefill CUDA graph on a platform other than CUDA",
graph_tail and not get_platform().is_cuda,
),
# The tail block's metadata is built for a rank's full rows.
(
"the breakable prefill CUDA graph with prefill context parallelism",
graph_tail and (cfg.enable_prefill_cp or cfg.attn_cp_size > 1),
),
# DP attention + TP MoE re-sizes the MoE gather to every rank's tail
# (deepseek_v4 _late_layer_dp_counts, ROCm only); other DP layouts do not.
(
Expand Down
6 changes: 6 additions & 0 deletions python/sglang/srt/models/deepseek_v4.py
Original file line number Diff line number Diff line change
Expand Up @@ -4215,6 +4215,12 @@ def _forward_layers_hc_pre_from_prev(
and tail.scatter_indices is not None
and is_in_breakable_cuda_graph()
)
# Inside a breakable graph the tail must be the capture-time block; an
# eager tail here would be gathered live and silently miss the replay.
assert tail is None or tail_in_graph or not is_in_breakable_cuda_graph(), (
"decoder SWA bounded replay inside the breakable prefill graph needs the "
"graph tail block (scatter_indices); the backend built an eager tail"
)
tail_sentinel = positions.new_zeros((1,)) if tail_in_graph else None
saved_full = None
# A pending post never meets a residual reader: the HIP boundary's defer_post
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Decoder SWA bounded replay inside the breakable prefill CUDA graph is refused
up front, not at graph capture, off CUDA and with prefill context parallelism.

python -m pytest test/registered/unit/server_args/test_dsv41_bounded_replay_graph_guards.py -v
"""

import unittest
from types import SimpleNamespace

from sglang.srt.arg_groups.deepseek_v4_hook import validate_deepseek_v41_features
from sglang.srt.model_executor.cuda_graph_config import (
Backend,
CudaGraphConfig,
PhaseConfig,
)
from sglang.srt.runtime_context import override_platform
from sglang.srt.server_args import ServerArgs
from sglang.test.ci.ci_register import register_cpu_ci
from sglang.test.test_utils import CustomTestCase

register_cpu_ci(est_time=2, suite="base-a-test-cpu")


def _v41_args(**overrides):
args = ServerArgs(
**{
"model_path": "local-deepseek-v41",
"attention_backend": "dsv4",
"enable_decoder_swa_bounded_replay": True,
"cuda_graph_config": CudaGraphConfig(
prefill=PhaseConfig(backend=Backend.BREAKABLE)
),
**overrides,
}
)
args._model_config = SimpleNamespace(
hf_config=SimpleNamespace(
architectures=["DeepseekV4ForCausalLM"], model_type="deepseek_v41"
),
hf_text_config=SimpleNamespace(model_type="deepseek_v41"),
is_multimodal=False,
)
return args


class TestBoundedReplayGraphGuards(CustomTestCase):
@override_platform(is_cuda=False, is_hip=True, is_npu=False, is_musa=False)
def test_rejects_platforms_other_than_cuda(self):
with self.assertRaisesRegex(ValueError, "platform other than CUDA"):
validate_deepseek_v41_features(_v41_args())

@override_platform(is_cuda=True, is_hip=False, is_npu=False, is_musa=False)
def test_rejects_prefill_context_parallelism(self):
with self.assertRaisesRegex(ValueError, "prefill context parallelism"):
validate_deepseek_v41_features(_v41_args(attn_cp_size=2))
with self.assertRaisesRegex(ValueError, "prefill context parallelism"):
validate_deepseek_v41_features(
_v41_args(enable_prefill_cp=True, cp_strategy="interleave")
)

@override_platform(is_cuda=True, is_hip=False, is_npu=False, is_musa=False)
def test_eager_prefill_keeps_cp_and_hip(self):
# The rejections are specific to the graph tail: eager prefill with
# bounded replay still admits CP, and the breakable graph without
# bounded replay is untouched.
validate_deepseek_v41_features(
_v41_args(
attn_cp_size=2,
cuda_graph_config=CudaGraphConfig(
prefill=PhaseConfig(backend=Backend.DISABLED)
),
)
)
validate_deepseek_v41_features(
_v41_args(enable_decoder_swa_bounded_replay=False)
)


if __name__ == "__main__":
unittest.main()
Loading