Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

2058 lines
76 KiB
Python

import json
import os
import sys
import tempfile
import unittest
from contextlib import contextmanager
from unittest.mock import patch
from sglang.multimodal_gen.configs.models.fsdp import (
is_module_list_entry,
is_module_list_entry_in,
is_zimage_layer,
)
from sglang.multimodal_gen.configs.pipeline_configs.base import (
ModelTaskType,
PipelineConfig,
)
from sglang.multimodal_gen.configs.pipeline_configs.hunyuan import FastHunyuanConfig
from sglang.multimodal_gen.configs.pipeline_configs.ltx_2 import (
LTX2PipelineConfig,
LTX23PipelineConfig,
)
from sglang.multimodal_gen.configs.pipeline_configs.mova import MOVAPipelineConfig
from sglang.multimodal_gen.configs.pipeline_configs.qwen_image import (
QwenImagePipelineConfig,
)
from sglang.multimodal_gen.configs.pipeline_configs.sana_wm import (
SanaWMPipelineConfig,
SanaWMRealtimeConfig,
)
from sglang.multimodal_gen.configs.pipeline_configs.wan import (
FastWan2_2_TI2V_5B_Config,
TurboWanT2V480PConfig,
Wan2_2_I2V_A14B_Config,
Wan2_2_T2V_A14B_Config,
WanI2V480PConfig,
WanI2V720PConfig,
WanT2V480PConfig,
WanT2V720PConfig,
)
from sglang.multimodal_gen.configs.pipeline_configs.zimage import ZImagePipelineConfig
from sglang.multimodal_gen.registry import _get_config_info
from sglang.multimodal_gen.runtime.models.dits.qwen_image import (
QwenImageTransformer2DModel,
)
from sglang.multimodal_gen.runtime.server_args import ServerArgs
from sglang.multimodal_gen.utils import FlexibleArgumentParser
@contextmanager
def _mock_cuda_platform(
*,
memory_gb: int = 80,
available_memory_gb: int | dict[int, int] | None = None,
):
def get_available_gpu_memory(device_id=0, **_kwargs):
if isinstance(available_memory_gb, dict):
return available_memory_gb[device_id]
if available_memory_gb is not None:
return available_memory_gb
return memory_gb
with (
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_mps",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=memory_gb * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
side_effect=get_available_gpu_memory,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.enable_dit_layerwise_offload_for_wan_by_default",
return_value=True,
),
):
yield
def _from_dict_without_model_resolution(
kwargs, pipeline_config: PipelineConfig | None = None
):
pipeline_config = pipeline_config or QwenImagePipelineConfig()
with (
patch.object(PipelineConfig, "from_kwargs", return_value=pipeline_config),
_mock_cuda_platform(),
):
return ServerArgs.from_dict(kwargs)
class TestServerArgsPathExpansion(unittest.TestCase):
def _from_dict_without_model_resolution(self, kwargs):
return _from_dict_without_model_resolution(kwargs)
def test_tilde_model_path_is_expanded(self):
args = self._from_dict_without_model_resolution(
{"model_path": "~/fake/local/model"}
)
expected = os.path.expanduser("~/fake/local/model")
self.assertEqual(args.model_path, expected)
self.assertFalse(args.model_path.startswith("~"))
def test_absolute_path_is_unchanged(self):
args = self._from_dict_without_model_resolution(
{"model_path": "/data/my-model"}
)
self.assertEqual(args.model_path, "/data/my-model")
def test_component_paths_are_expanded_before_pipeline_resolution(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_paths": {"vae": "~/fake/local/vae"},
}
)
self.assertEqual(
args.component_paths["vae"], os.path.expanduser("~/fake/local/vae")
)
def test_component_attention_backends_are_normalized(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_attention_backends": "text-encoder=torch_sdpa,transformer=fa3",
}
)
self.assertEqual(
args.component_attention_backends,
{"text_encoder": "torch_sdpa", "transformer": "fa"},
)
def test_component_attention_backend_lookup(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_attention_backends": {"text_encoder": "torch_sdpa"},
}
)
backend, matched_key = args.resolve_component_attention_backend(
"text_encoder", "transformer"
)
self.assertEqual(backend.name, "TORCH_SDPA")
self.assertEqual(matched_key, "text_encoder")
def test_invalid_component_attention_backend_raises(self):
with self.assertRaises(ValueError):
self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_attention_backends": {"text_encoder": "bad_backend"},
}
)
with self.assertRaises(ValueError):
self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"component_attention_backends": "text_encoder",
}
)
def test_dynamic_component_attention_backend_cli_args(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--component-attention-backends.text-encoder",
"torch_sdpa",
]
with (
patch.object(sys, "argv", ["sglang"] + argv),
patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_mps",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=80 * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
return_value=80,
),
):
args, unknown_args = parser.parse_known_args(argv)
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertEqual(
server_args.component_attention_backends, {"text_encoder": "torch_sdpa"}
)
def test_layerwise_offload_components_imply_layerwise(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"performance_mode": "manual",
}
)
args.layerwise_offload_components = ["text_encoder", "transformer"]
args._adjust_layerwise_offload_components()
self.assertTrue(args.layerwise_offload_components)
self.assertEqual(
args.layerwise_offload_components, ["text_encoder", "transformer"]
)
def test_dit_layerwise_offload_selects_dit_group(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"performance_mode": "manual",
"dit_layerwise_offload": True,
}
)
self.assertTrue(args.layerwise_offload_components)
self.assertEqual(args.layerwise_offload_components, ["dit"])
def test_dit_layerwise_offload_from_kwargs(self):
with patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
):
args = ServerArgs.from_kwargs(
model_path="/data/my-model",
performance_mode="manual",
dit_layerwise_offload=True,
)
self.assertTrue(args.layerwise_offload_components)
self.assertEqual(args.layerwise_offload_components, ["dit"])
def test_layerwise_offload_components_normalize_commas(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"performance_mode": "manual",
}
)
args.layerwise_offload_components = ["text-encoder,transformer"]
args._adjust_layerwise_offload_components()
self.assertEqual(
args.layerwise_offload_components, ["text_encoder", "transformer"]
)
def test_layerwise_offload_components_normalize_default_group(self):
args = self._from_dict_without_model_resolution(
{
"model_path": "/data/my-model",
"performance_mode": "manual",
}
)
args.layerwise_offload_components = ["default", "text_encoder"]
args._adjust_layerwise_offload_components()
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_dit_layerwise_offload_cli_arg(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--performance-mode",
"manual",
"--dit-layerwise-offload",
"true",
]
with patch.object(sys, "argv", ["sglang"] + argv):
args, unknown_args = parser.parse_known_args(argv)
with patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
):
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertTrue(server_args.layerwise_offload_components)
self.assertEqual(server_args.layerwise_offload_components, ["dit"])
def test_layerwise_offload_components_cli_args(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--performance-mode",
"manual",
"--layerwise-offload-components",
"transformer",
"text_encoder",
]
with patch.object(sys, "argv", ["sglang"] + argv):
args, unknown_args = parser.parse_known_args(argv)
with patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
):
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertEqual(
server_args.layerwise_offload_components, ["transformer", "text_encoder"]
)
def test_serve_cli_preserves_config_and_dynamic_unknown_args(self):
from sglang.multimodal_gen.runtime.entrypoints.cli.serve import (
add_multimodal_gen_serve_args,
)
with tempfile.NamedTemporaryFile("w", suffix=".json") as config_file:
json.dump({"model_path": "/from/config", "num_gpus": 2}, config_file)
config_file.flush()
parser = FlexibleArgumentParser()
add_multimodal_gen_serve_args(parser)
argv = [
"--config",
config_file.name,
"--model-path",
"/from/cli",
"--vae-path",
"/custom/vae",
"--component-attention-backends.transformer",
"fa3",
]
with patch.object(sys, "argv", ["sglang", "serve"] + argv):
args, unknown_args = parser.parse_known_args(argv)
with (
patch.object(
PipelineConfig,
"from_kwargs",
return_value=QwenImagePipelineConfig(),
),
patch(
"sglang.multimodal_gen.registry.get_model_info",
return_value=None,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=80 * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
return_value=80,
),
):
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertEqual("/from/cli", server_args.model_path)
self.assertEqual(2, server_args.num_gpus)
self.assertEqual("/custom/vae", server_args.component_paths["vae"])
self.assertEqual(
{"transformer": "fa"},
server_args.component_attention_backends,
)
def test_serve_cli_defaults_warmup_on(self):
from sglang.multimodal_gen.runtime.entrypoints.cli.serve import (
add_multimodal_gen_serve_args,
execute_serve_cmd,
)
parser = FlexibleArgumentParser()
add_multimodal_gen_serve_args(parser)
argv = [
"--model-path",
"/fake",
]
with (
patch.object(sys, "argv", ["sglang", "serve"] + argv),
patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
),
patch(
"sglang.multimodal_gen.runtime.entrypoints.cli.serve.dispatch_launch"
) as dispatch_launch,
):
args, unknown_args = parser.parse_known_args(argv)
execute_serve_cmd(args, unknown_args)
server_args = dispatch_launch.call_args.args[0]
self.assertTrue(server_args.warmup)
self.assertTrue(server_args.server_warmup)
self.assertFalse(server_args.is_arg_explicitly_set("warmup"))
self.assertFalse(server_args.is_arg_explicitly_set("server_warmup"))
def test_serve_cli_preserves_explicit_warmup_false(self):
from sglang.multimodal_gen.runtime.entrypoints.cli.serve import (
add_multimodal_gen_serve_args,
execute_serve_cmd,
)
parser = FlexibleArgumentParser()
add_multimodal_gen_serve_args(parser)
argv = [
"--model-path",
"/fake",
"--warmup",
"false",
]
with (
patch.object(sys, "argv", ["sglang", "serve"] + argv),
patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
),
patch(
"sglang.multimodal_gen.runtime.entrypoints.cli.serve.dispatch_launch"
) as dispatch_launch,
):
args, unknown_args = parser.parse_known_args(argv)
execute_serve_cmd(args, unknown_args)
server_args = dispatch_launch.call_args.args[0]
self.assertFalse(server_args.warmup)
self.assertFalse(server_args.server_warmup)
self.assertTrue(server_args.is_arg_explicitly_set("warmup"))
def test_serve_cli_preserves_config_warmup_false(self):
from sglang.multimodal_gen.runtime.entrypoints.cli.serve import (
add_multimodal_gen_serve_args,
execute_serve_cmd,
)
with tempfile.NamedTemporaryFile("w", suffix=".json") as config_file:
json.dump({"model_path": "/fake", "warmup": False}, config_file)
config_file.flush()
parser = FlexibleArgumentParser()
add_multimodal_gen_serve_args(parser)
argv = [
"--config",
config_file.name,
]
with (
patch.object(sys, "argv", ["sglang", "serve"] + argv),
patch.object(
PipelineConfig,
"from_kwargs",
return_value=QwenImagePipelineConfig(),
),
patch(
"sglang.multimodal_gen.runtime.entrypoints.cli.serve.dispatch_launch"
) as dispatch_launch,
):
args, unknown_args = parser.parse_known_args(argv)
execute_serve_cmd(args, unknown_args)
server_args = dispatch_launch.call_args.args[0]
self.assertFalse(server_args.warmup)
self.assertFalse(server_args.server_warmup)
self.assertTrue(server_args.is_arg_explicitly_set("warmup"))
def test_disagg_role_disables_server_warmup(self):
with patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
):
server_args = ServerArgs.from_dict(
{
"model_path": "/fake",
"warmup": True,
"server_warmup": True,
"disagg_role": "server",
}
)
self.assertTrue(server_args.warmup)
self.assertFalse(server_args.server_warmup)
class TestWarmupModeNormalization(unittest.TestCase):
"""`_adjust_warmup` resolves the canonical warmup_mode and its derived booleans."""
def _resolve(
self,
*,
warmup_mode=None,
warmup=False,
server_warmup=False,
warmup_resolutions=None,
enable_torch_compile=False,
disagg_role=None,
explicit=(),
):
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
sa = ServerArgs.__new__(ServerArgs)
sa.warmup_mode = warmup_mode
sa.warmup = warmup
sa.server_warmup = server_warmup
sa.warmup_resolutions = warmup_resolutions
sa.enable_torch_compile = enable_torch_compile
sa.disagg_role = RoleType.MONOLITHIC if disagg_role is None else disagg_role
sa._explicit_arg_names = set(explicit)
sa._adjust_warmup()
return sa
def test_explicit_mode_off_disables_all(self):
sa = self._resolve(warmup_mode="off", explicit=("warmup_mode",))
self.assertEqual(sa.warmup_mode, "off")
self.assertFalse(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_explicit_mode_request(self):
sa = self._resolve(warmup_mode="request", explicit=("warmup_mode",))
self.assertEqual(sa.warmup_mode, "request")
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_explicit_mode_server(self):
sa = self._resolve(warmup_mode="server", explicit=("warmup_mode",))
self.assertEqual(sa.warmup_mode, "server")
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
def test_explicit_mode_overrides_explicit_legacy(self):
sa = self._resolve(
warmup_mode="request",
warmup=True,
server_warmup=True,
explicit=("warmup_mode", "warmup", "server_warmup"),
)
self.assertEqual(sa.warmup_mode, "request")
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_explicit_legacy_false_beats_defaulted_mode(self):
# serve defaults warmup_mode="server" (not explicit); `--warmup false` wins.
sa = self._resolve(
warmup_mode="server",
warmup=False,
server_warmup=False,
explicit=("warmup",),
)
self.assertEqual(sa.warmup_mode, "off")
self.assertFalse(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_defaulted_mode_applies_without_legacy_flags(self):
# bare `sglang serve`: warmup_mode="server" defaulted, no legacy override.
sa = self._resolve(warmup_mode="server")
self.assertEqual(sa.warmup_mode, "server")
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
def test_legacy_only_maps_to_request(self):
sa = self._resolve(warmup_mode=None, warmup=True, explicit=("warmup",))
self.assertEqual(sa.warmup_mode, "request")
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_resolutions_force_warmup_on(self):
sa = self._resolve(
warmup_mode="off",
warmup_resolutions=["512x512"],
explicit=("warmup_mode",),
)
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
self.assertEqual(sa.warmup_mode, "request")
def test_torch_compile_defaults_to_server_warmup(self):
sa = self._resolve(enable_torch_compile=True)
self.assertEqual(sa.warmup_mode, "server")
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
def test_legacy_warmup_on_uses_defaulted_server_mode(self):
# `serve --warmup` (legacy ON, mode defaulted to "server" but not
# explicit) must resolve to server-based warmup, not silently downgrade
# to request mode.
sa = self._resolve(warmup_mode="server", warmup=True, explicit=("warmup",))
self.assertEqual(sa.warmup_mode, "server")
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
def test_torch_compile_respects_explicit_warmup_off(self):
sa = self._resolve(
warmup_mode="off",
enable_torch_compile=True,
explicit=("warmup_mode",),
)
self.assertEqual(sa.warmup_mode, "off")
self.assertFalse(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_torch_compile_uses_server_warmup_for_explicit_resolutions(self):
sa = self._resolve(
warmup_resolutions=["1024x1024"],
enable_torch_compile=True,
explicit=("warmup_resolutions",),
)
self.assertEqual(sa.warmup_mode, "server")
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
def test_legacy_warmup_with_resolutions_runs_server_warmup(self):
# Dead-zone regression: `serve --warmup --warmup-resolutions X` must run
# server-based (synthetic) warmup, not end up with no warmup at all
# (request-based warmup bails out when warmup_resolutions is set).
sa = self._resolve(
warmup_mode="server",
warmup=True,
warmup_resolutions=["1024x1024"],
explicit=("warmup",),
)
self.assertTrue(sa.warmup)
self.assertTrue(sa.server_warmup)
self.assertEqual(sa.warmup_mode, "server")
def test_disagg_role_disables_server_warmup(self):
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
sa = self._resolve(
warmup_mode="server",
disagg_role=RoleType.DENOISER,
explicit=("warmup_mode",),
)
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
self.assertEqual(sa.warmup_mode, "request")
def test_torch_compile_server_warmup_disabled_for_disagg_role(self):
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
sa = self._resolve(enable_torch_compile=True, disagg_role=RoleType.DENOISER)
self.assertEqual(sa.warmup_mode, "request")
self.assertTrue(sa.warmup)
self.assertFalse(sa.server_warmup)
def test_invalid_mode_raises(self):
with self.assertRaises(ValueError):
self._resolve(warmup_mode="bogus", explicit=("warmup_mode",))
class TestWarmupImageIsModelValid(unittest.TestCase):
"""The server-warmup placeholder image must be large enough for real pipelines."""
def test_minimum_warmup_image_is_at_least_64px(self):
import base64
import struct
from sglang.multimodal_gen.runtime.server_warmup import (
MINIMUM_PICTURE_BASE64_FOR_WARMUP,
)
payload = MINIMUM_PICTURE_BASE64_FOR_WARMUP.split(",", 1)[-1]
raw = base64.b64decode(payload)
self.assertEqual(raw[:8], b"\x89PNG\r\n\x1a\n")
# IHDR width/height are the two big-endian uint32 after the chunk header.
width, height = struct.unpack(">II", raw[16:24])
self.assertGreaterEqual(width, 64)
self.assertGreaterEqual(height, 64)
class TestOffloadDefaults(unittest.TestCase):
def _from_dict_with_pipeline_config(
self,
pipeline_config,
*,
memory_gb=80,
available_memory_gb=None,
kwargs=None,
):
def get_available_gpu_memory(device_id=0, **_kwargs):
if isinstance(available_memory_gb, dict):
return available_memory_gb[device_id]
if available_memory_gb is not None:
return available_memory_gb
return memory_gb
with (
patch.object(PipelineConfig, "from_kwargs", return_value=pipeline_config),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_mps",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.enable_dit_layerwise_offload_for_wan_by_default",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=memory_gb * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
side_effect=get_available_gpu_memory,
),
):
return ServerArgs.from_dict({"model_path": "/fake", **(kwargs or {})})
def _from_dict_with_task_type(
self,
task_type,
*,
memory_gb=80,
kwargs=None,
):
pipeline_config = PipelineConfig()
pipeline_config.task_type = task_type
with (
patch.object(PipelineConfig, "from_kwargs", return_value=pipeline_config),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=memory_gb * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
return_value=memory_gb,
),
):
return ServerArgs.from_dict({"model_path": "/fake", **(kwargs or {})})
def test_vae_cpu_offload_defaults_false_for_video_generation(self):
args = self._from_dict_with_task_type(ModelTaskType.T2V)
self.assertFalse(args.vae_cpu_offload)
def test_vae_cpu_offload_defaults_false_on_low_memory_gpu(self):
args = self._from_dict_with_task_type(
ModelTaskType.T2V,
memory_gb=16,
kwargs={"performance_mode": "memory"},
)
self.assertFalse(args.vae_cpu_offload)
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_explicit_vae_cpu_offload_true_is_preserved_by_default_layerwise(
self,
):
args = self._from_dict_with_task_type(
ModelTaskType.T2V,
kwargs={"vae_cpu_offload": True},
)
self.assertTrue(args.vae_cpu_offload)
self.assertEqual(
args.layerwise_offload_components, ["text_encoder", "image_encoder"]
)
def test_explicit_component_resident_is_preserved_by_default_layerwise(self):
args = self._from_dict_with_task_type(
ModelTaskType.T2V,
kwargs={"text_encoder_cpu_offload": False},
)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertEqual(args.layerwise_offload_components, ["image_encoder", "vae"])
def test_layerwise_components_disable_matching_non_dit_cpu_offloads(self):
args = self._from_dict_with_task_type(
ModelTaskType.T2V,
memory_gb=16,
kwargs={
"performance_mode": "manual",
"dit_cpu_offload": True,
"text_encoder_cpu_offload": True,
"image_encoder_cpu_offload": True,
"vae_cpu_offload": True,
},
)
args.layerwise_offload_components = [
"text_encoder",
"image_encoder",
"video_dit",
"vae",
]
args._adjust_layerwise_offload_components()
self.assertTrue(args.layerwise_offload_components)
# dit_cpu_offload is complementary to DiT layerwise offload (keeps
# weights off-device during load), so it must be preserved here.
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertFalse(args.vae_cpu_offload)
def test_dit_layerwise_offload_preserves_dit_cpu_offload(self):
"""Combining --dit-cpu-offload with --dit-layerwise-offload must keep both on.
dit_cpu_offload controls initial residency (host memory), while
dit_layerwise_offload only swaps layers on/off device at inference.
Force-disabling dit_cpu_offload here would push the full DiT to GPU at
load time and OOM low-VRAM cards.
"""
args = self._from_dict_with_task_type(
ModelTaskType.T2I,
memory_gb=32,
kwargs={
"dit_cpu_offload": True,
"dit_layerwise_offload": True,
},
)
self.assertTrue(args.dit_cpu_offload)
self.assertTrue(args.dit_layerwise_offload)
self.assertEqual(args.layerwise_offload_components, ["dit"])
def test_pipeline_configs_declare_auto_tune_hints(self):
qwen_deployment = QwenImagePipelineConfig().get_model_deployment_config()
wan_deployment = WanT2V480PConfig().get_model_deployment_config()
mova_deployment = MOVAPipelineConfig().get_model_deployment_config()
zimage_deployment = ZImagePipelineConfig().get_model_deployment_config()
ltx_deployment = LTX2PipelineConfig().get_model_deployment_config()
ltx23_config = LTX23PipelineConfig()
sana_wm_deployment = SanaWMPipelineConfig().get_model_deployment_config()
self.assertIsNone(qwen_deployment.fsdp_auto_min_available_memory_gb)
self.assertFalse(qwen_deployment.auto_dit_layerwise_offload)
self.assertIsNone(wan_deployment.fsdp_auto_min_available_memory_gb)
self.assertTrue(wan_deployment.auto_dit_layerwise_offload)
self.assertIsNone(mova_deployment.fsdp_auto_min_available_memory_gb)
self.assertTrue(mova_deployment.auto_dit_layerwise_offload)
self.assertEqual(zimage_deployment.fsdp_auto_min_available_memory_gb, 40)
self.assertTrue(zimage_deployment.fsdp_auto_requires_cfg)
self.assertFalse(zimage_deployment.auto_dit_layerwise_offload)
self.assertEqual(ltx_deployment.keep_resident_min_available_gb, 70)
self.assertEqual(ltx_deployment.keep_resident_components, ("dit",))
self.assertEqual(
ltx_deployment.auto_cfg_parallel_degree_by_num_gpus, ((4, 1), (8, 1))
)
self.assertEqual(ltx_deployment.get_auto_cfg_parallel_degree(4), 1)
self.assertEqual(ltx_deployment.get_auto_cfg_parallel_degree(8), 1)
self.assertEqual(ltx_deployment.get_auto_cfg_parallel_degree(2), 2)
self.assertFalse(
LTX2PipelineConfig().dit_config.arch_config.enable_packed_qkv_input_a2a
)
self.assertFalse(
ltx23_config.dit_config.arch_config.enable_packed_qkv_input_a2a
)
self.assertEqual(sana_wm_deployment.fsdp_auto_min_available_memory_gb, 60)
self.assertTrue(sana_wm_deployment.auto_dit_layerwise_offload)
# fasthunyuan no longer pins 150gb -- falls back to the global video default
fast_hunyuan_deployment = FastHunyuanConfig().get_model_deployment_config()
self.assertIsNone(fast_hunyuan_deployment.keep_resident_min_available_gb)
self.assertEqual(fast_hunyuan_deployment.keep_resident_components, ("vae",))
# default keeps only vae resident (encoders are large, dit owned by FSDP)
self.assertEqual(qwen_deployment.keep_resident_components, ("vae",))
self.assertIsNone(qwen_deployment.keep_resident_min_available_gb)
def test_auto_multi_gpu_sana_wm_prefers_fsdp_and_cfg_parallel(self):
args = self._from_dict_with_pipeline_config(
SanaWMPipelineConfig(),
kwargs={
"model_path": "Efficient-Large-Model/SANA-WM_bidirectional",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertTrue(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
def test_cache_dit_rejects_explicit_fsdp(self):
with patch.dict(os.environ, {"SGLANG_CACHE_DIT_ENABLED": "true"}):
with self.assertRaisesRegex(ValueError, "FSDP inference"):
self._from_dict_with_pipeline_config(
SanaWMPipelineConfig(),
kwargs={
"model_path": "Efficient-Large-Model/SANA-WM_bidirectional",
"num_gpus": 2,
"use_fsdp_inference": True,
},
)
def test_cache_dit_auto_disables_implicit_fsdp(self):
with patch.dict(os.environ, {"SGLANG_CACHE_DIT_ENABLED": "true"}):
args = self._from_dict_with_pipeline_config(
SanaWMPipelineConfig(),
kwargs={
"model_path": "Efficient-Large-Model/SANA-WM_bidirectional",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
def test_auto_multi_gpu_sana_wm_realtime_disables_cfg_parallel(self):
args = self._from_dict_with_pipeline_config(
SanaWMRealtimeConfig(),
kwargs={
"model_path": "Efficient-Large-Model/SANA-WM_streaming",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.enable_cfg_parallel)
def test_auto_ltx23_large_gpu_counts_prefer_sp_over_cfg_parallel(self):
for num_gpus in (4, 8):
with self.subTest(num_gpus=num_gpus):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
kwargs={
"model_path": "Lightricks/LTX-2.3",
"num_gpus": num_gpus,
"performance_mode": "auto",
},
)
self.assertFalse(args.enable_cfg_parallel)
self.assertEqual(args.cfg_parallel_degree, 1)
self.assertEqual(args.sp_degree, num_gpus)
self.assertEqual(args.ulysses_degree, num_gpus)
self.assertEqual(args.ring_degree, 1)
def test_manual_mode_preserves_unset_performance_args(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"num_gpus": 2,
"performance_mode": "manual",
},
)
self.assertEqual(args.performance_mode, "manual")
self.assertIsNone(args.use_fsdp_inference)
self.assertIsNone(args.dit_cpu_offload)
self.assertIsNone(args.dit_layerwise_offload)
self.assertIsNone(args.layerwise_offload_components)
self.assertIsNone(args.text_encoder_cpu_offload)
self.assertIsNone(args.image_encoder_cpu_offload)
self.assertFalse(args.enable_cfg_parallel)
def test_default_auto_keeps_image_vae_resident_when_memory_allows(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={"model_path": "Qwen/Qwen-Image"},
)
self.assertEqual(args.performance_mode, "auto")
self.assertFalse(args.use_fsdp_inference)
# 80gb > image threshold (45gb): only vae kept resident, encoders stay
# offloaded layerwise, dit unchanged
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder"],
)
self.assertFalse(args.vae_cpu_offload)
def test_auto_image_offloads_aux_below_resident_threshold(self):
# 40gb < image threshold (45gb): aux incl. vae still offloaded to save vram
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
memory_gb=40,
kwargs={"model_path": "Qwen/Qwen-Image"},
)
self.assertEqual(args.performance_mode, "auto")
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_ltx_original_replaces_component_cpu_offload(
self,
):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
available_memory_gb=76,
kwargs={
"model_path": "Lightricks/LTX-2.3",
"pipeline_class_name": "LTX2TwoStageHQPipeline",
"performance_mode": "auto",
},
)
self.assertEqual(args.ltx2_two_stage_device_mode, "original")
self.assertFalse(args.dit_cpu_offload)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_wan_layerwise_offload_is_enabled_without_fsdp(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={"performance_mode": "auto"},
)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_wan2_2_a14b_layerwise_offload_adds_dit(self):
for pipeline_config, model_path in (
(Wan2_2_T2V_A14B_Config(), "Wan-AI/Wan2.2-T2V-A14B-Diffusers"),
(Wan2_2_I2V_A14B_Config(), "Wan-AI/Wan2.2-I2V-A14B-Diffusers"),
):
with self.subTest(pipeline_config=pipeline_config.__class__.__name__):
args = self._from_dict_with_pipeline_config(
pipeline_config,
kwargs={
"model_path": model_path,
"performance_mode": "auto",
},
)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.use_fsdp_inference)
# dit_cpu_offload is complementary to DiT layerwise offload:
# layerwise only moves layers on/off device at runtime, while
# dit_cpu_offload keeps the initial weights on host memory.
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(args.dit_offload_prefetch_size, 2)
self.assertEqual(
args.layerwise_offload_components,
["dit", "text_encoder", "image_encoder", "vae"],
)
def test_auto_wan2_1_14b_layerwise_offload_uses_non_dit_default(self):
for pipeline_config, model_path in (
(WanT2V720PConfig(), "Wan-AI/Wan2.1-T2V-14B-Diffusers"),
(WanI2V480PConfig(), "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers"),
(WanI2V720PConfig(), "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers"),
):
with self.subTest(pipeline_config=pipeline_config.__class__.__name__):
args = self._from_dict_with_pipeline_config(
pipeline_config,
kwargs={
"model_path": model_path,
"performance_mode": "auto",
},
)
self.assertTrue(args.layerwise_offload_components)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(args.dit_offload_prefetch_size, 0.0)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_memory_wan_layerwise_offload_is_enabled_without_fsdp(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={"performance_mode": "memory"},
)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["dit", "text_encoder", "image_encoder", "vae"],
)
def test_auto_wan_layerwise_offload_does_not_disable_explicit_fsdp(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"num_gpus": 2,
"performance_mode": "auto",
"use_fsdp_inference": True,
},
)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
self.assertTrue(args.use_fsdp_inference)
def test_auto_wan_layerwise_offload_preserves_explicit_dit_cpu_offload(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"performance_mode": "auto",
"dit_cpu_offload": True,
},
)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_mova_layerwise_offload_does_not_implicitly_add_dit(self):
args = self._from_dict_with_pipeline_config(
MOVAPipelineConfig(),
kwargs={
"model_path": "OpenMOSS-Team/MOVA-360p",
"performance_mode": "auto",
},
)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_fastwan_layerwise_offload_does_not_implicitly_add_dit(self):
args = self._from_dict_with_pipeline_config(
FastWan2_2_TI2V_5B_Config(),
kwargs={
"model_path": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers",
"performance_mode": "auto",
},
)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_turbo_wan_layerwise_offload_does_not_implicitly_add_dit(self):
args = self._from_dict_with_pipeline_config(
TurboWanT2V480PConfig(),
kwargs={
"model_path": "IPostYellow/TurboWan2.1-T2V-1.3B-Diffusers",
"performance_mode": "auto",
},
)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_explicit_fastwan_dit_layerwise_still_selects_dit_group(self):
args = self._from_dict_with_pipeline_config(
FastWan2_2_TI2V_5B_Config(),
kwargs={
"model_path": "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers",
"dit_layerwise_offload": True,
},
)
# dit_cpu_offload defaults to True from _adjust_offload and is now
# preserved alongside DiT layerwise offload (the two are complementary).
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(args.layerwise_offload_components, ["dit"])
def test_auto_multi_gpu_wan_uses_layerwise_offload_without_cfg(self):
with patch.object(ServerArgs, "_model_default_uses_cfg", return_value=False):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.enable_cfg_parallel)
self.assertTrue(args.dit_cpu_offload)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_explicit_multi_gpu_dit_layerwise_only_selects_dit_group(self):
args = self._from_dict_with_pipeline_config(
MOVAPipelineConfig(),
kwargs={
"model_path": "OpenMOSS-Team/MOVA-360p",
"num_gpus": 2,
"dit_layerwise_offload": True,
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.dit_cpu_offload)
self.assertTrue(args.layerwise_offload_components)
self.assertTrue(args.text_encoder_cpu_offload)
self.assertTrue(args.image_encoder_cpu_offload)
self.assertEqual(args.layerwise_offload_components, ["dit"])
def test_auto_multi_gpu_ltx_replaces_component_cpu_offload_with_resident_dit(self):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
available_memory_gb=76,
kwargs={
"model_path": "Lightricks/LTX-2",
"num_gpus": 2,
"pipeline_class_name": "LTX2TwoStagePipeline",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.dit_cpu_offload)
self.assertTrue(args.layerwise_offload_components)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_auto_high_memory_ltx23_resident_keeps_aux_components_resident(self):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
memory_gb=140,
available_memory_gb=134,
kwargs={
"model_path": "Lightricks/LTX-2.3",
"num_gpus": 2,
"pipeline_class_name": "LTX2TwoStagePipeline",
},
)
self.assertEqual(args.ltx2_two_stage_device_mode, "resident")
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertFalse(args.vae_cpu_offload)
self.assertIsNone(args.layerwise_offload_components)
def test_auto_high_memory_ltx23_original_keeps_default_layerwise_components(self):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
memory_gb=140,
available_memory_gb=134,
kwargs={
"model_path": "Lightricks/LTX-2.3",
"num_gpus": 2,
"pipeline_class_name": "LTX2TwoStagePipeline",
"ltx2_two_stage_device_mode": "original",
},
)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_ltx23_snapshot_device_mode_is_deprecated_alias_for_original(self):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
memory_gb=140,
available_memory_gb=134,
kwargs={
"model_path": "Lightricks/LTX-2.3",
"num_gpus": 2,
"pipeline_class_name": "LTX2TwoStagePipeline",
"ltx2_two_stage_device_mode": "snapshot",
},
)
self.assertEqual(args.ltx2_two_stage_device_mode, "original")
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
def test_explicit_layerwise_components_preserved_in_ltx23_resident(self):
args = self._from_dict_with_pipeline_config(
LTX2PipelineConfig(),
memory_gb=140,
available_memory_gb=134,
kwargs={
"model_path": "Lightricks/LTX-2.3",
"num_gpus": 2,
"pipeline_class_name": "LTX2TwoStagePipeline",
"layerwise_offload_components": ["text_encoder"],
},
)
self.assertEqual(args.ltx2_two_stage_device_mode, "resident")
self.assertEqual(args.layerwise_offload_components, ["text_encoder"])
def test_auto_multi_gpu_qwen_keeps_vae_resident_with_cfg(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
# 80gb > image threshold (45gb): only vae resident, encoders offloaded;
# cfg/dit unchanged
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder"],
)
self.assertFalse(args.vae_cpu_offload)
def test_auto_multi_gpu_zimage_base_prefers_fsdp(self):
args = self._from_dict_with_pipeline_config(
ZImagePipelineConfig(),
kwargs={
"model_path": "Tongyi-MAI/Z-Image",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertTrue(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
def test_auto_multi_gpu_zimage_turbo_skips_fsdp(self):
args = self._from_dict_with_pipeline_config(
ZImagePipelineConfig(),
kwargs={
"model_path": "Tongyi-MAI/Z-Image-Turbo",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.enable_cfg_parallel)
def test_auto_multi_gpu_qwen_preserves_explicit_fsdp_false(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"num_gpus": 2,
"performance_mode": "auto",
"use_fsdp_inference": False,
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.vae_cpu_offload)
# explicit use_fsdp_inference skips the residency pass, but the layerwise
# filter still drops vae (kept resident); encoders stay offloaded
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder"],
)
def test_auto_multi_gpu_qwen_skips_fsdp_when_available_memory_is_low(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
memory_gb=50,
kwargs={
"model_path": "Qwen/Qwen-Image",
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
# 50gb still > image threshold (45gb): vae resident, encoders offloaded;
# fsdp skipped (qwen does not opt into auto fsdp)
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder"],
)
self.assertFalse(args.vae_cpu_offload)
def test_auto_multi_gpu_qwen_uses_selected_gpu_min_available_memory(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
available_memory_gb={1: 50, 2: 80},
kwargs={
"model_path": "Qwen/Qwen-Image",
"base_gpu_id": 1,
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
def test_auto_multi_gpu_qwen_keeps_vae_resident_with_headroom(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
available_memory_gb={1: 72, 2: 80},
kwargs={
"model_path": "Qwen/Qwen-Image",
"base_gpu_id": 1,
"num_gpus": 2,
"performance_mode": "auto",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.enable_cfg_parallel)
# min available across selected gpus is 72gb > image threshold (45gb):
# vae resident, encoders offloaded
self.assertTrue(args.dit_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder"],
)
self.assertFalse(args.vae_cpu_offload)
def test_speed_mode_single_gpu_disables_offload(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"performance_mode": "speed",
},
)
self.assertEqual(args.performance_mode, "speed")
self.assertFalse(args.use_fsdp_inference)
self.assertFalse(args.dit_cpu_offload)
self.assertFalse(args.layerwise_offload_components)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
def test_speed_mode_preserves_explicit_offload(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"performance_mode": "speed",
"dit_cpu_offload": True,
},
)
self.assertEqual(args.performance_mode, "speed")
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
def test_speed_mode_enables_torch_compile_by_default(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"performance_mode": "speed",
},
)
self.assertTrue(args.enable_torch_compile)
def test_speed_mode_preserves_explicit_torch_compile_off(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"performance_mode": "speed",
"enable_torch_compile": False,
},
)
self.assertFalse(args.enable_torch_compile)
def test_auto_mode_leaves_torch_compile_off(self):
args = self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={
"model_path": "Qwen/Qwen-Image",
"performance_mode": "auto",
},
)
self.assertFalse(args.enable_torch_compile)
def test_memory_mode_wan_uses_layerwise_offload(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"performance_mode": "memory",
},
)
self.assertFalse(args.use_fsdp_inference)
self.assertTrue(args.layerwise_offload_components)
self.assertTrue(args.dit_cpu_offload)
self.assertFalse(args.text_encoder_cpu_offload)
self.assertFalse(args.image_encoder_cpu_offload)
self.assertEqual(
args.layerwise_offload_components,
["dit", "text_encoder", "image_encoder", "vae"],
)
def test_memory_mode_preserves_explicit_fsdp(self):
args = self._from_dict_with_pipeline_config(
WanT2V480PConfig(),
kwargs={
"model_path": "Wan-AI/Wan2.1-T2V-1.3B-Diffusers",
"num_gpus": 2,
"performance_mode": "memory",
"use_fsdp_inference": True,
},
)
self.assertTrue(args.use_fsdp_inference)
self.assertEqual(
args.layerwise_offload_components,
["text_encoder", "image_encoder", "vae"],
)
self.assertFalse(args.dit_cpu_offload)
def test_invalid_performance_mode_raises(self):
with self.assertRaises(ValueError):
self._from_dict_with_pipeline_config(
QwenImagePipelineConfig(),
kwargs={"performance_mode": "turbo"},
)
def test_cfg_parallel_cli_can_be_disabled_explicitly(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"Qwen/Qwen-Image",
"--num-gpus",
"2",
"--performance-mode",
"auto",
"--enable-cfg-parallel",
"false",
]
with (
patch.object(sys, "argv", ["sglang"] + argv),
patch.object(
PipelineConfig, "from_kwargs", return_value=QwenImagePipelineConfig()
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_mps",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=80 * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
return_value=80,
),
):
args, unknown_args = parser.parse_known_args(argv)
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertFalse(server_args.use_fsdp_inference)
self.assertFalse(server_args.enable_cfg_parallel)
def test_ltx23_snapshot_device_mode_cli_alias_is_accepted(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"Lightricks/LTX-2.3",
"--pipeline-class-name",
"LTX2TwoStagePipeline",
"--ltx2-two-stage-device-mode",
"snapshot",
]
with (
patch.object(sys, "argv", ["sglang"] + argv),
patch.object(
PipelineConfig, "from_kwargs", return_value=LTX2PipelineConfig()
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cpu",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_mps",
return_value=False,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.is_cuda",
return_value=True,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_device_total_memory",
return_value=140 * 1024**3,
),
patch(
"sglang.multimodal_gen.runtime.platforms.current_platform.get_available_gpu_memory",
return_value=134,
),
):
args, unknown_args = parser.parse_known_args(argv)
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertEqual(server_args.ltx2_two_stage_device_mode, "original")
class TestFSDPShardConditions(unittest.TestCase):
def test_helpers_match_only_direct_block_entries(self):
self.assertTrue(
is_module_list_entry("transformer_blocks.0", "transformer_blocks")
)
self.assertFalse(
is_module_list_entry("transformer_blocks.0.ff.net.0", "transformer_blocks")
)
self.assertTrue(
is_module_list_entry_in(
"single_transformer_blocks.12",
("transformer_blocks", "single_transformer_blocks"),
)
)
self.assertFalse(
is_module_list_entry_in(
"single_transformer_blocks.12.attn.to_out.0",
("transformer_blocks", "single_transformer_blocks"),
)
)
def test_qwen_dit_has_fsdp_shard_condition(self):
conditions = QwenImageTransformer2DModel._fsdp_shard_conditions
self.assertTrue(conditions)
self.assertTrue(conditions[0]("transformer_blocks.0", None))
self.assertFalse(conditions[0]("transformer_blocks.0.attn", None))
self.assertFalse(conditions[0]("transformer_blocks.0.ff.net.0", None))
def test_zimage_condition_keeps_inner_numbered_modules(self):
self.assertTrue(is_zimage_layer("layers.0.mlp.0", None))
self.assertTrue(is_zimage_layer("noise_refiner.0.attention.to_out.0", None))
self.assertFalse(is_zimage_layer("transformer_blocks.0", None))
class TestModelIdResolution(unittest.TestCase):
def setUp(self):
_get_config_info.cache_clear()
def test_model_id_overrides_arbitrary_local_path(self):
# a local path whose directory name does not match any HF repo name;
# --model-id tells the engine which config to use
info = _get_config_info("/data/my-custom-qwen", model_id="Qwen-Image")
self.assertIsNotNone(info)
self.assertIs(info.pipeline_config_cls, QwenImagePipelineConfig)
def test_model_id_works_after_tilde_expansion(self):
# simulate the full flow: user passes ~/..., engine expands and resolves
expanded = os.path.expanduser("~/.cache/huggingface/hub/bbb/snapshots/ccc")
_get_config_info.cache_clear()
info = _get_config_info(expanded, model_id="Qwen-Image")
self.assertIsNotNone(info)
def test_hf_cache_snapshot_path_resolves_registered_nvfp4_model(self):
path = (
"/root/.cache/huggingface/hub/"
"models--black-forest-labs--FLUX.2-dev-NVFP4/"
"snapshots/142b87e70bc3006937b7093d89ff287b5f59f071"
)
info = _get_config_info(path)
self.assertIsNotNone(info)
def test_sana_wm_model_path_resolves_registry(self):
info = _get_config_info("Efficient-Large-Model/SANA-WM_bidirectional")
self.assertIs(info.pipeline_config_cls, SanaWMPipelineConfig)
def test_model_id_unknown_falls_back_without_crash(self):
# unrecognized model_id: should warn and fall back to path-based detection
# with an unresolvable path, expect RuntimeError from the detector step
with self.assertRaises((RuntimeError, Exception)):
_get_config_info("/data/no-such-model", model_id="NonExistentModelXYZ")
class TestPerRoleParallelism(unittest.TestCase):
"""Test per-role parallelism args and get_role_parallelism helper."""
def _from_dict(self, kwargs):
return _from_dict_without_model_resolution(kwargs)
def test_defaults_are_none(self):
args = self._from_dict({"model_path": "/fake"})
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
for role in [RoleType.ENCODER, RoleType.DENOISER, RoleType.DECODER]:
par = args.get_role_parallelism(role)
self.assertIsNone(par["tp_size"])
self.assertIsNone(par["sp_degree"])
self.assertIsNone(par["ulysses_degree"])
self.assertIsNone(par["ring_degree"])
def test_encoder_overrides(self):
args = self._from_dict({"model_path": "/fake", "encoder_tp": 2})
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
par = args.get_role_parallelism(RoleType.ENCODER)
self.assertEqual(par["tp_size"], 2)
self.assertIsNone(par["sp_degree"])
self.assertIsNone(par["ulysses_degree"])
self.assertIsNone(par["ring_degree"])
def test_denoiser_overrides(self):
args = self._from_dict(
{
"model_path": "/fake",
"denoiser_tp": 1,
"denoiser_sp": 8,
"denoiser_ulysses": 4,
"denoiser_ring": 2,
}
)
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
par = args.get_role_parallelism(RoleType.DENOISER)
self.assertEqual(par["tp_size"], 1)
self.assertEqual(par["sp_degree"], 8)
self.assertEqual(par["ulysses_degree"], 4)
self.assertEqual(par["ring_degree"], 2)
def test_decoder_overrides(self):
args = self._from_dict({"model_path": "/fake", "decoder_sp": 2})
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
par = args.get_role_parallelism(RoleType.DECODER)
self.assertIsNone(par["tp_size"])
self.assertEqual(par["sp_degree"], 2)
self.assertIsNone(par["ulysses_degree"])
self.assertIsNone(par["ring_degree"])
def test_decoder_tp_is_alias_of_decoder_sp(self):
args = self._from_dict({"model_path": "/fake", "decoder_tp": 2})
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
self.assertEqual(args.decoder_sp, 2)
par = args.get_role_parallelism(RoleType.DECODER)
self.assertIsNone(par["tp_size"])
self.assertEqual(par["sp_degree"], 2)
def test_conflicting_decoder_tp_and_decoder_sp_raise(self):
with self.assertRaisesRegex(ValueError, "decoder_tp is deprecated"):
self._from_dict(
{
"model_path": "/fake",
"decoder_tp": 2,
"decoder_sp": 4,
}
)
def test_monolithic_returns_all_none(self):
args = self._from_dict({"model_path": "/fake", "encoder_tp": 2})
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
par = args.get_role_parallelism(RoleType.MONOLITHIC)
self.assertIsNone(par["tp_size"])
self.assertIsNone(par["sp_degree"])
def test_mixed_roles_independent(self):
"""Per-role args don't interfere with each other."""
args = self._from_dict(
{
"model_path": "/fake",
"encoder_tp": 1,
"denoiser_tp": 2,
"decoder_sp": 4,
}
)
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
self.assertEqual(args.get_role_parallelism(RoleType.ENCODER)["tp_size"], 1)
self.assertEqual(args.get_role_parallelism(RoleType.DENOISER)["tp_size"], 2)
self.assertEqual(args.get_role_parallelism(RoleType.DECODER)["sp_degree"], 4)
def test_disagg_args_import_path_matches_server_args_package(self):
from sglang.multimodal_gen.runtime.disaggregation import disagg_args
from sglang.multimodal_gen.runtime.server_args.disagg import (
DisaggServerArgsMixin,
)
self.assertIs(disagg_args.DisaggArgsMixin, DisaggServerArgsMixin)
self.assertIs(
disagg_args.DISAGG_RESULT_PORT_OFFSETS,
DisaggServerArgsMixin.DISAGG_RESULT_PORT_OFFSETS,
)
def test_gpu_ids_normalize_lists_and_commas(self):
args = self._from_dict({"model_path": "/fake", "gpu_ids": ["0,1", "6", "7 8"]})
self.assertEqual(args.gpu_ids, [0, 1, 6, 7, 8])
def test_gpu_ids_reject_duplicates(self):
with self.assertRaisesRegex(ValueError, "duplicate GPU ids"):
self._from_dict({"model_path": "/fake", "gpu_ids": ["0,1", "1"]})
def test_pool_endpoints_use_role_and_scheduler_ports(self):
args = self._from_dict(
{
"model_path": "/fake",
"disagg_role": "denoiser",
"disagg_server_addr": "tcp://127.0.0.1:30000",
"scheduler_port": 5600,
"host": "0.0.0.0",
"disagg_p2p_hostname": "10.0.0.7",
}
)
self.assertEqual(args.derive_pool_result_endpoint(), "tcp://127.0.0.1:30002")
self.assertEqual(
args.derive_pool_work_endpoint(),
f"tcp://0.0.0.0:{args.scheduler_port}",
)
self.assertEqual(
args.derive_pool_control_endpoint(),
f"tcp://0.0.0.0:{args.scheduler_port + 1}",
)
self.assertEqual(
args.derive_pool_control_advertised_endpoint(),
f"tcp://10.0.0.7:{args.scheduler_port + 1}",
)
def test_pool_result_endpoint_validates_addr_and_role(self):
args = self._from_dict({"model_path": "/fake", "disagg_server_addr": "bad"})
with self.assertRaisesRegex(ValueError, "disagg_server_addr must be"):
args.derive_pool_result_endpoint()
args = self._from_dict(
{"model_path": "/fake", "disagg_server_addr": "127.0.0.1:30000"}
)
with self.assertRaisesRegex(ValueError, "only defined for encoder"):
args.derive_pool_result_endpoint()
def test_cli_args_parsed(self):
"""Per-role parallelism args are parsed from CLI."""
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--denoiser-tp",
"2",
"--denoiser-sp",
"4",
"--denoiser-ulysses",
"2",
"--denoiser-ring",
"2",
"--encoder-tp",
"1",
"--decoder-sp",
"8",
]
args, unknown = parser.parse_known_args(argv)
self.assertEqual(args.denoiser_tp, 2)
self.assertEqual(args.denoiser_sp, 4)
self.assertEqual(args.denoiser_ulysses, 2)
self.assertEqual(args.denoiser_ring, 2)
self.assertEqual(args.encoder_tp, 1)
self.assertEqual(args.decoder_sp, 8)
self.assertIsNone(args.decoder_tp)
class TestPipelineResolutionCliOverride(unittest.TestCase):
def setUp(self):
_get_config_info.cache_clear()
def test_resolution_flag_overrides_qwen_image_layered_pipeline_config(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"Qwen/Qwen-Image-Layered",
"--resolution",
"768",
]
with (
patch.object(sys, "argv", ["sglang"] + argv),
_mock_cuda_platform(),
):
args, unknown_args = parser.parse_known_args(argv)
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertEqual(server_args.pipeline_config.resolution, 768)
def test_disable_autocast_is_preserved_after_pipeline_config_resolution(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"Qwen/Qwen-Image-Layered",
"--disable-autocast",
"true",
]
with (
patch.object(sys, "argv", ["sglang"] + argv),
_mock_cuda_platform(),
):
args, unknown_args = parser.parse_known_args(argv)
server_args = ServerArgs.from_cli_args(args, unknown_args)
self.assertTrue(server_args.pipeline_config.disable_autocast)
self.assertTrue(server_args.disable_autocast)
class TestDisaggTimeoutArgs(unittest.TestCase):
def test_disagg_defaults_match_reviewed_values(self):
args = _from_dict_without_model_resolution({"model_path": "/fake"})
self.assertEqual(args.disagg_max_slots_per_instance, 8)
self.assertEqual(args.disagg_downstream_wait_timeout, 1800)
self.assertEqual(args.disagg_timeout, 3600)
def test_downstream_wait_timeout_cli_arg_is_parsed(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--disagg-downstream-wait-timeout",
"45",
]
args, _unknown = parser.parse_known_args(argv)
self.assertEqual(args.disagg_downstream_wait_timeout, 45)
def test_disagg_timeout_help_uses_current_defaults(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
help_text = parser.format_help()
self.assertIn("Default: 3600.", help_text)
self.assertIn("Default: 1800.", help_text)
def test_disagg_role_alias_cli_arg_is_accepted(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
args, _unknown = parser.parse_known_args(
["--model-path", "/fake", "--disagg-role", "denoising"]
)
self.assertEqual(args.disagg_role, "denoising")
def test_disagg_role_alias_normalizes_to_denoiser(self):
from sglang.multimodal_gen.runtime.disaggregation.roles import RoleType
args = _from_dict_without_model_resolution(
{"model_path": "/fake", "disagg_role": "denoising"}
)
self.assertEqual(args.disagg_role, RoleType.DENOISER)
class TestDisaggTransferBackendArgs(unittest.TestCase):
def test_transfer_backend_defaults_to_auto(self):
args = _from_dict_without_model_resolution({"model_path": "/fake"})
self.assertEqual(args.disagg_transfer_backend, "auto")
def test_transfer_backend_cli_arg_is_parsed(self):
parser = FlexibleArgumentParser()
ServerArgs.add_cli_args(parser)
argv = [
"--model-path",
"/fake",
"--disagg-transfer-backend",
"mock",
]
args, _unknown = parser.parse_known_args(argv)
self.assertEqual(args.disagg_transfer_backend, "mock")
if __name__ == "__main__":
unittest.main()