diff --git a/docs_new/docs/advanced_features/server_arguments.mdx b/docs_new/docs/advanced_features/server_arguments.mdx index 42954c153..e54afdd31 100644 --- a/docs_new/docs/advanced_features/server_arguments.mdx +++ b/docs_new/docs/advanced_features/server_arguments.mdx @@ -2410,16 +2410,16 @@ Please consult the documentation below and [server_args.py](https://github.com/s bool flag (set to enable) - --enable-dsa-prefill-context-parallel - Enable context parallelism used in the long sequence prefill phase of DeepSeek v3.2. + --enable-prefill-cp + Enable context parallelism for the prefill phase. Select the layout with --cp-strategy. False bool flag (set to enable) - --dsa-prefill-cp-mode - Token splitting mode for the prefill phase of DeepSeek v3.2 under context parallelism. Optional values: round-robin-split(default),in-seq-split. round-robin-split distributes tokens across ranks based on token_idx % cp_size. It supports multi-batch prefill, fused MoE, and FP8 KV cache. - in-seq-split - in-seq-split, round-robin-split + --cp-strategy + Sharding strategy for prefill CP. zigzag is the former in-seq-split mode; interleave is the former round-robin-split mode. + None + zigzag, interleave --enable-fused-qk-norm-rope diff --git a/python/sglang/srt/arg_groups/deepseek_v4_hook.py b/python/sglang/srt/arg_groups/deepseek_v4_hook.py index 3dd5d31c2..4bdb6e007 100644 --- a/python/sglang/srt/arg_groups/deepseek_v4_hook.py +++ b/python/sglang/srt/arg_groups/deepseek_v4_hook.py @@ -54,15 +54,17 @@ def apply_deepseek_v4_defaults(server_args: "ServerArgs", model_arch: str) -> No def validate_deepseek_v4_cp(server_args: "ServerArgs") -> None: """Validate DeepSeek V4 context-parallel configuration.""" - if not server_args.enable_dsa_prefill_context_parallel: + if not server_args.enable_prefill_cp: return - if server_args.dsa_prefill_cp_mode != "round-robin-split": + if server_args.cp_strategy != "interleave": raise ValueError( - f"DeepSeekV4 only supports round-robin-split CP mode, " - f"got {server_args.dsa_prefill_cp_mode}" + "DeepSeekV4 only supports interleave CP strategy, " + f"got {server_args.cp_strategy}" ) + server_args.enable_dsa_prefill_context_parallel = True + server_args.dsa_prefill_cp_mode = "round-robin-split" server_args.enable_dp_attention = True server_args.moe_dense_tp_size = 1 server_args.attn_cp_size = server_args.tp_size // server_args.dp_size diff --git a/python/sglang/srt/server_args.py b/python/sglang/srt/server_args.py index bc723a240..8e1a5cc7f 100644 --- a/python/sglang/srt/server_args.py +++ b/python/sglang/srt/server_args.py @@ -831,14 +831,18 @@ class ServerArgs: kv_canary: str = "none" kv_canary_real_data: str = "none" kv_canary_sweep_interval: int = 0 - # Context parallelism used in the long sequence prefill phase of DeepSeek v3.2 - enable_dsa_prefill_context_parallel: bool = False - dsa_prefill_cp_mode: str = "round-robin-split" enable_fused_qk_norm_rope: bool = False enable_precise_embedding_interpolation: bool = False enable_fused_moe_sum_all_reduce: bool = False - # Context parallelism + # Context parallelism (unified API) + enable_prefill_cp: bool = False + # "zigzag" is former in-seq-split; "interleave" is former round-robin-split. + cp_strategy: Optional[str] = None + + # Context parallelism (deprecated aliases) + enable_dsa_prefill_context_parallel: bool = False + dsa_prefill_cp_mode: str = "round-robin-split" enable_prefill_context_parallel: bool = False prefill_cp_mode: str = "in-seq-split" @@ -944,8 +948,9 @@ class ServerArgs: handle_pd_disaggregation(self) - # Validate --prefill-only-disable-kv-cache args early (before dummy-model - # short-circuit). The backend check is run later after backends settle. + # Normalize deprecated CP aliases before validations or model-specific + # defaults inspect enable_prefill_cp/cp_strategy. + self._handle_legacy_cp_arguments() self._validate_prefill_only_disable_kv_cache_args() if self.model_path.lower() in ["none", "dummy"]: @@ -1029,6 +1034,10 @@ class ServerArgs: # Handle data parallelism. self._handle_data_parallelism() + # Re-apply after model-specific defaults resolve attention_backend so + # canonical CP mirrors to the right legacy runtime aliases. + self._handle_legacy_cp_arguments() + # Handle context parallelism. self._handle_context_parallelism() @@ -2022,25 +2031,23 @@ class ServerArgs: ) if not is_npu() and not is_xpu(): # CUDA or ROCm GPU - if self.enable_dsa_prefill_context_parallel: + if self.enable_prefill_cp: logger.warning( "Context parallel feature is still under experiment. It has only been verified on Hopper platform." ) - if self.dsa_prefill_cp_mode == "in-seq-split": - # TODO Supports moe_dense_tp_size != 1, kv cache dtype = "fp8",moe_a2a_backend non-deepep and cross-machine operation . - self.enable_dp_attention = True - self.moe_dense_tp_size = 1 + self.enable_dp_attention = True + self.moe_dense_tp_size = 1 + if self.cp_strategy == "zigzag": self.moe_a2a_backend = "deepep" self.ep_size = self.tp_size logger.warning( - "For in-seq split mode, we have the following restrictions: moe_dense_tp_size == 1, moe_a2a_backend == deepep, ep_size == tp_size, batch_size == 1" + "zigzag DSA CP requires moe_dense_tp_size=1, " + "moe_a2a_backend=deepep, ep_size=tp_size, batch_size=1." ) else: - self.enable_dp_attention = True - self.moe_dense_tp_size = 1 assert ( self.dp_size == 1 - ), "For round-robin split mode, dp attention is not supported." + ), "interleave DSA CP does not support DP attention." assert ( self.tp_size <= 8 ), "Context parallel only supports single machine (tp_size <= 8). Cross-machine CP has precision issues." @@ -2050,13 +2057,13 @@ class ServerArgs: self.attn_cp_size = self.tp_size // self.dp_size self.cuda_graph_config.prefill.backend = Backend.DISABLED logger.warning( - f"Enable DSA Context Parallel opt, " - f"Setting dp_size == {self.dp_size} and " - f"moe_dense_tp_size == {self.moe_dense_tp_size}, " - f"ep_size == {self.ep_size}, " - f"tp_size == {self.tp_size}, " - f"kv_cache_dtype == {self.kv_cache_dtype}, " - f"moe_a2a_backend {self.moe_a2a_backend}, " + "Enabled DSA context parallel: " + f"strategy={self.cp_strategy}, dp_size={self.dp_size}, " + f"moe_dense_tp_size={self.moe_dense_tp_size}, " + f"ep_size={self.ep_size}, tp_size={self.tp_size}, " + f"attn_cp_size={self.attn_cp_size}, " + f"kv_cache_dtype={self.kv_cache_dtype}, " + f"moe_a2a_backend={self.moe_a2a_backend}, " f"cuda_graph_config[prefill].backend=disabled" ) else: @@ -2093,10 +2100,10 @@ class ServerArgs: self._set_default_dsa_kv_cache_dtype(major, self.quantization) self._set_default_dsa_backends(self.kv_cache_dtype, major) - if self.enable_dsa_prefill_context_parallel: + if self.enable_prefill_cp: assert ( self.disaggregation_mode != "decode" - ), "CP is only supported for prefill when PD disaggregation, please remove --enable-dsa-prefill-context-parallel." + ), "CP is only supported for prefill when PD disaggregation, please remove --enable-prefill-cp." else: # DeepSeek V3/R1/V3.1 @@ -2116,7 +2123,7 @@ class ServerArgs: # MLA prefill CP auto-config. Mirrors the NSA CP block above # (minus the in-seq/round-robin mode split, which MLA CP does not support) - if self.enable_prefill_context_parallel and self.use_mla_backend(): + if self.enable_prefill_cp and self.use_mla_backend(): logger.warning( "MLA prefill context parallel is still experimental. " "Verified on Hopper with the fa3 backend." @@ -3415,7 +3422,54 @@ class ServerArgs: f"got CUDA {cuda_version or 'unknown'}" ) + def _handle_legacy_cp_arguments(self): + legacy_mode_to_strategy = { + "in-seq-split": "zigzag", + "round-robin-split": "interleave", + } + strategy_to_legacy_mode = { + "zigzag": "in-seq-split", + "interleave": "round-robin-split", + } + + if ( + self.enable_prefill_context_parallel + or self.enable_dsa_prefill_context_parallel + ): + self.enable_prefill_cp = True + + if self.enable_prefill_context_parallel and self.cp_strategy is None: + self.cp_strategy = legacy_mode_to_strategy[self.prefill_cp_mode] + if self.enable_dsa_prefill_context_parallel and self.cp_strategy is None: + self.cp_strategy = legacy_mode_to_strategy[self.dsa_prefill_cp_mode] + + if ( + self.enable_prefill_context_parallel + and self.enable_dsa_prefill_context_parallel + ): + return + + if not self.enable_prefill_cp or self.cp_strategy is None: + return + + mode = strategy_to_legacy_mode[self.cp_strategy] + use_dsa_legacy_aliases = self.enable_dsa_prefill_context_parallel or getattr( + self, "attention_backend", None + ) in ("dsa", "dsv4") + if use_dsa_legacy_aliases: + self.enable_dsa_prefill_context_parallel = True + self.enable_prefill_context_parallel = False + else: + self.enable_prefill_context_parallel = True + self.dsa_prefill_cp_mode = mode + self.prefill_cp_mode = mode + def _handle_context_parallelism(self): + if self.enable_prefill_cp and self.cp_strategy is None: + raise ValueError( + "--cp-strategy must be set when --enable-prefill-cp is enabled." + ) + if ( self.enable_prefill_context_parallel and self.enable_dsa_prefill_context_parallel @@ -3901,10 +3955,10 @@ class ServerArgs: "the context-parallel attention path writes K/V to the pool via set_kv_buffer, " "which the no-op pool intentionally rejects." ) - if self.enable_prefill_context_parallel: + if self.enable_prefill_cp: raise ValueError( "--prefill-only-disable-kv-cache is incompatible with " - "--enable-prefill-context-parallel: the prefill-CP path stages K/V through " + "--enable-prefill-cp: the prefill-CP path stages K/V through " "the paged cache, which the no-op pool does not support." ) @@ -7154,6 +7208,27 @@ class ServerArgs: action="store_true", help="Allow input of attention to be scattered when only using tensor parallelism, to reduce the computational load of operations such as qkv latent.", ) + parser.add_argument( + "--enable-prefill-cp", + dest="enable_prefill_cp", + action="store_true", + help=( + "Enable context parallelism for the prefill phase. Select the " + "layout with --cp-strategy." + ), + ) + parser.add_argument( + "--cp-strategy", + dest="cp_strategy", + type=str, + default=ServerArgs.cp_strategy, + choices=("zigzag", "interleave"), + help=( + "Sharding strategy for prefill CP. 'zigzag' is the former " + "in-seq-split mode; 'interleave' is the former " + "round-robin-split mode." + ), + ) parser.add_argument( "--disable-attn-tp-gather", action="store_true", @@ -7169,46 +7244,60 @@ class ServerArgs: parser.add_argument( "--enable-dsa-prefill-context-parallel", dest="enable_dsa_prefill_context_parallel", - action="store_true", - help="Enable context parallelism used in the long sequence prefill phase of DeepSeek v3.2.", + action=DeprecatedStoreTrueAction, + new_flag="--enable-prefill-cp", + help="[Deprecated] Use --enable-prefill-cp instead.", ) parser.add_argument( "--enable-nsa-prefill-context-parallel", dest="enable_dsa_prefill_context_parallel", action=DeprecatedStoreTrueAction, - new_flag="--enable-dsa-prefill-context-parallel", - help="[Deprecated] Use --enable-dsa-prefill-context-parallel instead.", + new_flag="--enable-prefill-cp", + help="[Deprecated] Use --enable-prefill-cp instead.", + ) + parser.add_argument( + "--enable-prefill-context-parallel", + dest="enable_prefill_context_parallel", + action=DeprecatedStoreTrueAction, + new_flag="--enable-prefill-cp", + help="[Deprecated] Use --enable-prefill-cp instead.", ) parser.add_argument( "--dsa-prefill-cp-mode", dest="dsa_prefill_cp_mode", + action=DeprecatedAliasStoreAction, + new_flag="--cp-strategy", type=str, default=ServerArgs.dsa_prefill_cp_mode, choices=DSA_PREFILL_CP_SPLIT_CHOICES, - help="Token splitting mode for the prefill phase of DeepSeek v3.2 under context parallelism.", + help=( + "[Deprecated] Use --cp-strategy {zigzag,interleave} instead. " + "'in-seq-split' maps to 'zigzag'; 'round-robin-split' maps to " + "'interleave'." + ), ) parser.add_argument( "--nsa-prefill-cp-mode", dest="dsa_prefill_cp_mode", action=DeprecatedAliasStoreAction, - new_flag="--dsa-prefill-cp-mode", - default=argparse.SUPPRESS, + new_flag="--cp-strategy", type=str, + default=argparse.SUPPRESS, choices=DSA_PREFILL_CP_SPLIT_CHOICES, - help="Token splitting mode for the prefill phase of DeepSeek v3.2 under context parallelism. Optional values: 'round-robin-split'(default), 'in-seq-split' " - "'round-robin-split' distributes tokens across ranks based on token_idx %% cp_size. It supports multi-batch prefill, fused MoE, and FP8 KV cache.", - ) - parser.add_argument( - "--enable-prefill-context-parallel", - action="store_true", - help="Enable context parallelism used in the prefill phase", + help="[Deprecated] Use --cp-strategy instead.", ) parser.add_argument( "--prefill-cp-mode", + dest="prefill_cp_mode", + action=DeprecatedAliasStoreAction, + new_flag="--cp-strategy", type=str, default=ServerArgs.prefill_cp_mode, choices=PREFILL_CP_SPLIT_CHOICES, - help="Token splitting mode for the prefill phase under context parallelism. Optional values: 'in-seq-split' (default)", + help=( + "[Deprecated] Use --cp-strategy {zigzag,interleave} instead. " + "'in-seq-split' maps to 'zigzag'." + ), ) parser.add_argument( "--enable-fused-qk-norm-rope", diff --git a/test/manual/test_dsa_alias_cli_registry_env.py b/test/manual/test_dsa_alias_cli_registry_env.py index 859b0a82f..c9221cd2b 100644 --- a/test/manual/test_dsa_alias_cli_registry_env.py +++ b/test/manual/test_dsa_alias_cli_registry_env.py @@ -2,11 +2,10 @@ Manual test for step 01: NSA → DSA user-facing alias layer. Tests: - 1. CLI: --dsa-* canonical flags write to dsa_* attrs - 2. CLI: --nsa-* deprecated flags write to dsa_* attrs + log deprecation warning - 3. Registry: "dsa" key creates the backend; "nsa" key triggers DeprecationWarning - 4. Env: SGLANG_DSA_* canonical vars work - 5. Env: SGLANG_NSA_* deprecated vars fall back to SGLANG_DSA_* with DeprecationWarning + 1. CLI: --dsa-* non-CP canonical flags write to dsa_* attrs + 2. Registry: "dsa" key creates the backend; "nsa" key triggers DeprecationWarning + 3. Env: SGLANG_DSA_* canonical vars work + 4. Env: SGLANG_NSA_* deprecated vars fall back to SGLANG_DSA_* with DeprecationWarning Run: python test/manual/test_dsa_alias_cli_registry_env.py @@ -27,17 +26,13 @@ class TestDSAChoicesAndFields(unittest.TestCase): def setUp(self): from sglang.srt.server_args import ( DSA_CHOICES, - DSA_PREFILL_CP_SPLIT_CHOICES, NSA_CHOICES, - NSA_PREFILL_CP_SPLIT_CHOICES, ServerArgs, ) self.ServerArgs = ServerArgs self.DSA_CHOICES = DSA_CHOICES self.NSA_CHOICES = NSA_CHOICES - self.DSA_PREFILL_CP_SPLIT_CHOICES = DSA_PREFILL_CP_SPLIT_CHOICES - self.NSA_PREFILL_CP_SPLIT_CHOICES = NSA_PREFILL_CP_SPLIT_CHOICES def test_dsa_choices_is_canonical(self): self.assertIn("fa3", self.DSA_CHOICES) @@ -50,18 +45,10 @@ class TestDSAChoicesAndFields(unittest.TestCase): "NSA_CHOICES must be the same object as DSA_CHOICES", ) - def test_nsa_cp_split_choices_is_alias(self): - self.assertIs( - self.NSA_PREFILL_CP_SPLIT_CHOICES, - self.DSA_PREFILL_CP_SPLIT_CHOICES, - ) - def test_serverargs_has_dsa_fields(self): sa = self.ServerArgs self.assertTrue(hasattr(sa, "dsa_prefill_backend")) self.assertTrue(hasattr(sa, "dsa_decode_backend")) - self.assertTrue(hasattr(sa, "enable_dsa_prefill_context_parallel")) - self.assertTrue(hasattr(sa, "dsa_prefill_cp_mode")) def test_serverargs_no_nsa_fields(self): """The nsa_* attributes should no longer exist on ServerArgs.""" @@ -74,12 +61,10 @@ class TestDSAChoicesAndFields(unittest.TestCase): hasattr(sa, "nsa_decode_backend"), "nsa_decode_backend should have been renamed", ) - self.assertFalse(hasattr(sa, "enable_nsa_prefill_context_parallel")) - self.assertFalse(hasattr(sa, "nsa_prefill_cp_mode")) class TestCLICanonicalFlags(unittest.TestCase): - """--dsa-* canonical flags write to dsa_* attributes with no warning.""" + """Canonical flags write to canonical attributes with no warning.""" def setUp(self): from sglang.srt.server_args import ServerArgs @@ -98,20 +83,10 @@ class TestCLICanonicalFlags(unittest.TestCase): args = self._parse(["--dsa-decode-backend", "tilelang"]) self.assertEqual(args.dsa_decode_backend, "tilelang") - def test_enable_dsa_prefill_cp_canonical(self): - args = self._parse(["--enable-dsa-prefill-context-parallel"]) - self.assertTrue(args.enable_dsa_prefill_context_parallel) - - def test_dsa_prefill_cp_mode_canonical(self): - args = self._parse(["--dsa-prefill-cp-mode", "in-seq-split"]) - self.assertEqual(args.dsa_prefill_cp_mode, "in-seq-split") - def test_defaults_are_none_or_false(self): args = self._parse([]) self.assertIsNone(args.dsa_prefill_backend) self.assertIsNone(args.dsa_decode_backend) - self.assertFalse(args.enable_dsa_prefill_context_parallel) - self.assertEqual(args.dsa_prefill_cp_mode, "round-robin-split") def test_attention_backend_dsa_key_in_choices(self): args = self._parse(["--attention-backend", "dsa"]) @@ -119,7 +94,7 @@ class TestCLICanonicalFlags(unittest.TestCase): class TestCLIDeprecatedFlags(unittest.TestCase): - """--nsa-* deprecated flags write to dsa_* attributes and emit logger warning.""" + """Deprecated flags write to canonical attributes and emit logger warning.""" def setUp(self): import logging @@ -174,20 +149,6 @@ class TestCLIDeprecatedFlags(unittest.TestCase): self.assertEqual(args.dsa_decode_backend, "tilelang") self.assertIn("deprecated", log_output.lower()) - def test_enable_nsa_prefill_cp_deprecated(self): - args, log_output = self._parse_capture_warnings( - ["--enable-nsa-prefill-context-parallel"] - ) - self.assertTrue(args.enable_dsa_prefill_context_parallel) - self.assertIn("deprecated", log_output.lower()) - - def test_nsa_prefill_cp_mode_deprecated(self): - args, log_output = self._parse_capture_warnings( - ["--nsa-prefill-cp-mode", "in-seq-split"] - ) - self.assertEqual(args.dsa_prefill_cp_mode, "in-seq-split") - self.assertIn("deprecated", log_output.lower()) - def test_attention_backend_nsa_still_accepted(self): """attention_backend='nsa' still parses without error (registry handles the deprecation).""" args = self._parse(["--attention-backend", "nsa"]) diff --git a/test/registered/unit/server_args/test_server_args.py b/test/registered/unit/server_args/test_server_args.py index 4e8c6e4eb..b6e1175d1 100644 --- a/test/registered/unit/server_args/test_server_args.py +++ b/test/registered/unit/server_args/test_server_args.py @@ -97,6 +97,174 @@ class TestLoadBalanceMethod(unittest.TestCase): self.assertIn("'fake'", str(context.exception)) +class TestContextParallelServerArgs(CustomTestCase): + def setUp(self): + self.parser = server_args_module.argparse.ArgumentParser() + ServerArgs.add_cli_args(self.parser) + + def _new_cp_args(self, **overrides): + server_args = object.__new__(ServerArgs) + defaults = dict( + enable_prefill_context_parallel=False, + enable_dsa_prefill_context_parallel=False, + enable_prefill_cp=False, + cp_strategy=None, + dsa_prefill_cp_mode="round-robin-split", + prefill_cp_mode="in-seq-split", + attn_cp_size=1, + tp_size=1, + dp_size=1, + moe_dp_size=1, + ep_size=1, + pp_size=1, + enable_aiter_allreduce_fusion=False, + ) + defaults.update(overrides) + for key, value in defaults.items(): + setattr(server_args, key, value) + return server_args + + def test_canonical_prefill_cp_cli_sets_unified_fields(self): + args = self.parser.parse_args( + ["--model", "dummy", "--enable-prefill-cp", "--cp-strategy", "interleave"] + ) + + self.assertTrue(args.enable_prefill_cp) + self.assertEqual(args.cp_strategy, "interleave") + + def test_canonical_prefill_cp_requires_strategy(self): + args = self.parser.parse_args(["--model", "dummy", "--enable-prefill-cp"]) + + self.assertTrue(args.enable_prefill_cp) + self.assertIsNone(args.cp_strategy) + + server_args = self._new_cp_args( + enable_prefill_cp=args.enable_prefill_cp, + cp_strategy=args.cp_strategy, + ) + with self.assertRaisesRegex(ValueError, "--cp-strategy"): + server_args._handle_context_parallelism() + + def test_deprecated_dsa_cp_mode_maps_to_unified_strategy(self): + args = self.parser.parse_args( + [ + "--model", + "dummy", + "--enable-dsa-prefill-context-parallel", + "--dsa-prefill-cp-mode", + "round-robin-split", + ] + ) + server_args = self._new_cp_args( + enable_dsa_prefill_context_parallel=( + args.enable_dsa_prefill_context_parallel + ), + dsa_prefill_cp_mode=args.dsa_prefill_cp_mode, + ) + + server_args._handle_legacy_cp_arguments() + + self.assertTrue(server_args.enable_prefill_cp) + self.assertEqual(server_args.cp_strategy, "interleave") + self.assertEqual(server_args.dsa_prefill_cp_mode, "round-robin-split") + + def test_canonical_interleave_cp_mirrors_to_dsa_runtime_aliases(self): + server_args = self._new_cp_args( + enable_prefill_cp=True, + cp_strategy="interleave", + attention_backend="dsa", + ) + + server_args._handle_legacy_cp_arguments() + server_args._handle_context_parallelism() + + self.assertTrue(server_args.enable_dsa_prefill_context_parallel) + self.assertFalse(server_args.enable_prefill_context_parallel) + self.assertEqual(server_args.dsa_prefill_cp_mode, "round-robin-split") + self.assertEqual(server_args.prefill_cp_mode, "round-robin-split") + + def test_registered_cp_legacy_args_map_to_unified_strategy(self): + cases = [ + ( + "deepseek_v3_mla_cp", + dict(enable_prefill_context_parallel=True), + "zigzag", + "in-seq-split", + False, + True, + ), + ( + "qwen3_gqa_cp", + dict( + enable_prefill_context_parallel=True, + tp_size=4, + attn_cp_size=2, + ), + "zigzag", + "in-seq-split", + False, + True, + ), + ( + "deepseek_v32_dsa_in_seq_split", + dict( + enable_dsa_prefill_context_parallel=True, + dsa_prefill_cp_mode="in-seq-split", + tp_size=8, + dp_size=2, + attn_cp_size=4, + ), + "zigzag", + "in-seq-split", + True, + False, + ), + ( + "deepseek_v32_dsa_round_robin_split", + dict( + enable_dsa_prefill_context_parallel=True, + tp_size=8, + attn_cp_size=8, + ), + "interleave", + "round-robin-split", + True, + False, + ), + ( + "deepseek_v4_flash_fp4_b200_dsa_round_robin_split", + dict( + enable_dsa_prefill_context_parallel=True, + dsa_prefill_cp_mode="round-robin-split", + tp_size=4, + attn_cp_size=4, + ), + "interleave", + "round-robin-split", + True, + False, + ), + ] + + for name, overrides, strategy, mode, expect_dsa, expect_generic in cases: + with self.subTest(name=name): + server_args = self._new_cp_args(**overrides) + + server_args._handle_legacy_cp_arguments() + server_args._handle_context_parallelism() + + self.assertTrue(server_args.enable_prefill_cp) + self.assertEqual(server_args.cp_strategy, strategy) + self.assertEqual(server_args.dsa_prefill_cp_mode, mode) + self.assertEqual(server_args.prefill_cp_mode, mode) + self.assertEqual( + server_args.enable_dsa_prefill_context_parallel, expect_dsa + ) + self.assertEqual( + server_args.enable_prefill_context_parallel, expect_generic + ) + + class TestPortArgs(unittest.TestCase): @patch("sglang.srt.server_args.get_free_port") @patch("sglang.srt.server_args.tempfile.NamedTemporaryFile") @@ -647,7 +815,7 @@ class TestPrefillOnlyDisableKvCache(unittest.TestCase): ServerArgs(**self._base_kwargs(attn_cp_size=2, tp_size=2)) def test_rejects_prefill_context_parallel(self): - with self.assertRaisesRegex(ValueError, "--enable-prefill-context-parallel"): + with self.assertRaisesRegex(ValueError, "--enable-prefill-cp"): ServerArgs(**self._base_kwargs(enable_prefill_context_parallel=True)) def test_rejects_hisparse(self):