Skip to content
Merged
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
105 changes: 81 additions & 24 deletions python/sglang/test/ci/ci_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@
"ut_parse_one_file",
]

# `suite` stays in positional slot 2 for backward compat with existing
# `register_cpu_ci(5, "stage-a-test-cpu")` style positional calls. New fields
# (`stage`, `runner_config`) are kwarg-only.
_PARAM_ORDER = ("est_time", "suite", "nightly", "disabled")
_KWARG_ONLY = ("stage", "runner_config")
_ALL_PARAMS = _PARAM_ORDER + _KWARG_ONLY
_UNSET = object()


Expand All @@ -31,45 +36,69 @@ class HWBackend(Enum):
class CIRegistry:
backend: HWBackend
filename: str
# Estimated time to run the test in seconds.
est_time: float
# The suite this test is registered in.
suite: str
# Whether the test is a nightly test.
stage: Optional[str] = None
runner_config: Optional[str] = None
# Legacy single-string suite; kept for nightly/stress/weekly + AMD/CPU/NPU
# suites whose names don't follow `{stage}-test-{runner_config}` shape.
suite: Optional[str] = None
nightly: bool = False
# Reason for disabling the test. None = enabled, string = disabled with reason.
disabled: Optional[str] = None

@property
def effective_suite(self) -> Optional[str]:
if self.stage is not None and self.runner_config is not None:
return f"{self.stage}-test-{self.runner_config}"
return self.suite


def register_cpu_ci(
est_time: float, suite: str, nightly: bool = False, disabled: Optional[str] = None
est_time: float,
suite: Optional[str] = None,
nightly: bool = False,
disabled: Optional[str] = None,
*,
stage: Optional[str] = None,
runner_config: Optional[str] = None,
):
"""Marker for CPU CI registration (parsed via AST; runtime no-op)."""
return None


def register_cuda_ci(
est_time: float, suite: str, nightly: bool = False, disabled: Optional[str] = None
est_time: float,
suite: Optional[str] = None,
nightly: bool = False,
disabled: Optional[str] = None,
*,
stage: Optional[str] = None,
runner_config: Optional[str] = None,
):
"""Marker for CUDA CI registration (parsed via AST; runtime no-op)."""
return None


def register_amd_ci(
est_time: float,
suite: str,
suite: Optional[str] = None,
nightly: bool = False,
disabled: Optional[str] = None,
*,
stage: Optional[str] = None,
runner_config: Optional[str] = None,
):
"""Marker for AMD CI registration (parsed via AST; runtime no-op)."""
return None


def register_npu_ci(
est_time: float,
suite: str,
suite: Optional[str] = None,
nightly: bool = False,
disabled: Optional[str] = None,
*,
stage: Optional[str] = None,
runner_config: Optional[str] = None,
):
"""Marker for NPU CI registration (parsed via AST; runtime no-op)."""
return None
Expand All @@ -94,10 +123,8 @@ def _constant_value(self, node: ast.AST) -> object:
return node.value
return _UNSET

def _parse_call_args(
self, func_call: ast.Call
) -> tuple[float, str, bool, Optional[str]]:
args = {name: _UNSET for name in _PARAM_ORDER}
def _parse_call_args(self, func_call: ast.Call) -> dict:
args = {name: _UNSET for name in _ALL_PARAMS}
seen = set()

if any(isinstance(arg, ast.Starred) for arg in func_call.args):
Expand Down Expand Up @@ -129,23 +156,49 @@ def _parse_call_args(
seen.add(kw.arg)
args[kw.arg] = self._constant_value(kw.value)

if args["est_time"] is _UNSET or args["suite"] is _UNSET:
if args["est_time"] is _UNSET:
raise ValueError(
f"{self.filename}: est_time and suite are required constants in {func_call.func.id}()"
f"{self.filename}: est_time is a required constant in {func_call.func.id}()"
)

est_time, suite = args["est_time"], args["suite"]
nightly_value = args["nightly"]
# The only valid (stage, runner_config, suite) shapes are:
# (set, set, unset) -> new-style pair
# (unset, unset, set) -> legacy single-string
# Any other combination is rejected with the actual triple in the error.
stage_set = args["stage"] is not _UNSET
runner_set = args["runner_config"] is not _UNSET
suite_set = args["suite"] is not _UNSET
valid_shape = (stage_set and runner_set and not suite_set) or (
not stage_set and not runner_set and suite_set
)
if not valid_shape:
raise ValueError(
f"{self.filename}: {func_call.func.id}() must specify exactly one of "
f"(stage, runner_config) pair or suite; got stage={stage_set}, "
f"runner_config={runner_set}, suite={suite_set}"
)

est_time = args["est_time"]
if not isinstance(est_time, (int, float)):
raise ValueError(
f"{self.filename}: est_time must be a number in {func_call.func.id}()"
)
if not isinstance(suite, str):

suite = args["suite"] if suite_set else None
if suite is not None and not isinstance(suite, str):
raise ValueError(
f"{self.filename}: suite must be a string in {func_call.func.id}()"
)

stage = args["stage"] if stage_set else None
runner_config = args["runner_config"] if runner_set else None
for name, value in (("stage", stage), ("runner_config", runner_config)):
if value is not None and not isinstance(value, str):
raise ValueError(
f"{self.filename}: {name} must be a string in {func_call.func.id}()"
)

nightly_value = args["nightly"]
if nightly_value is _UNSET:
nightly = False
elif isinstance(nightly_value, bool):
Expand All @@ -161,7 +214,14 @@ def _parse_call_args(
f"{self.filename}: disabled must be a string in {func_call.func.id}()"
)

return float(est_time), suite, nightly, disabled
return {
"est_time": float(est_time),
"stage": stage,
"runner_config": runner_config,
"suite": suite,
"nightly": nightly,
"disabled": disabled,
}

def _collect_ci_registry(self, func_call: ast.Call):
if not isinstance(func_call.func, ast.Name):
Expand All @@ -171,14 +231,11 @@ def _collect_ci_registry(self, func_call: ast.Call):
if backend is None:
return None

est_time, suite, nightly, disabled = self._parse_call_args(func_call)
parsed = self._parse_call_args(func_call)
return CIRegistry(
backend=backend,
filename=self.filename,
est_time=est_time,
suite=suite,
nightly=nightly,
disabled=disabled,
**parsed,
)

@staticmethod
Expand Down
14 changes: 8 additions & 6 deletions scripts/ci/utils/ci_coverage_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ def generate_summary_section(data: dict) -> str:
for t in sorted(disabled_tests, key=lambda x: (x.backend.name, x.filename)):
test_name = get_test_basename(t.filename)
reason = t.disabled[:50] + "..." if len(t.disabled) > 50 else t.disabled
lines.append(f"| `{test_name}` | {t.backend.name} | {t.suite} | {reason} |")
lines.append(
f"| `{test_name}` | {t.backend.name} | {t.effective_suite} | {reason} |"
)
lines.append("\n</details>\n")

return "\n".join(lines)
Expand Down Expand Up @@ -197,7 +199,7 @@ def generate_by_folder_section(data: dict) -> str:
else ("Nightly" if t.nightly else "Per-Commit")
)
lines.append(
f"| `{test_name}` | {t.suite} | {t.est_time:.0f}s | {status} |"
f"| `{test_name}` | {t.effective_suite} | {t.est_time:.0f}s | {status} |"
)

lines.append("")
Expand Down Expand Up @@ -231,7 +233,7 @@ def generate_by_suite_section(data: dict) -> str:
# Group by suite within backend
backend_suites = defaultdict(list)
for t in backend_tests:
backend_suites[t.suite].append(t)
backend_suites[t.effective_suite].append(t)

for suite in sorted(backend_suites.keys()):
suite_tests = backend_suites[suite]
Expand Down Expand Up @@ -336,7 +338,7 @@ def generate_json_report(tests: list[CIRegistry]) -> str:
data["tests_by_folder"][folder]["backends"][backend] = [
{
"filename": get_test_basename(t.filename),
"suite": t.suite,
"suite": t.effective_suite,
"est_time": t.est_time,
"status": (
"disabled"
Expand All @@ -355,7 +357,7 @@ def generate_json_report(tests: list[CIRegistry]) -> str:

backend_suites = defaultdict(list)
for t in backend_tests:
backend_suites[t.suite].append(t)
backend_suites[t.effective_suite].append(t)

data["tests_by_suite"][backend] = {
"total": len(backend_tests),
Expand Down Expand Up @@ -423,7 +425,7 @@ def generate_json_report(tests: list[CIRegistry]) -> str:
{
"filename": get_test_basename(t.filename),
"backend": t.backend.name,
"suite": t.suite,
"suite": t.effective_suite,
"reason": t.disabled,
}
)
Expand Down
2 changes: 1 addition & 1 deletion scripts/ci/utils/compute_partitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def compute_partitions(tests, full_parallel=False):
continue
if t.nightly or t.disabled is not None:
continue
suite_tests[t.suite].append(t)
suite_tests[t.effective_suite].append(t)

result = {}
for suite, group in suite_tests.items():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
try_cached_model,
)

register_cuda_ci(est_time=1800, suite="stage-c-test-4-gpu-gb200")
register_cuda_ci(est_time=1800, stage="stage-c", runner_config="4-gpu-gb200")


class TestDeepseekR1Nvfp4CuteDSLDeepEP(CustomTestCase):
Expand Down
4 changes: 2 additions & 2 deletions test/registered/4-gpu-models/test_gpt_oss_4gpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.gpt_oss_common import BaseTestGptOss

register_cuda_ci(est_time=392, suite="stage-c-test-4-gpu-h100")
register_cuda_ci(est_time=740, suite="stage-c-test-4-gpu-b200")
register_cuda_ci(est_time=392, stage="stage-c", runner_config="4-gpu-h100")
register_cuda_ci(est_time=740, stage="stage-c", runner_config="4-gpu-b200")


class TestGptOss4Gpu(BaseTestGptOss):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=710, suite="stage-c-test-4-gpu-b200")
register_cuda_ci(est_time=710, stage="stage-c", runner_config="4-gpu-b200")

NEMOTRON_3_SUPER_NVFP4_MODEL = "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-NVFP4"

Expand Down
2 changes: 1 addition & 1 deletion test/registered/4-gpu-models/test_qwen35_fp4_mtp_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=540, suite="stage-c-test-4-gpu-b200")
register_cuda_ci(est_time=540, stage="stage-c", runner_config="4-gpu-b200")

QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}}
Expand Down
2 changes: 1 addition & 1 deletion test/registered/4-gpu-models/test_qwen35_hicache.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=540, suite="stage-c-test-4-gpu-h100")
register_cuda_ci(est_time=540, stage="stage-c", runner_config="4-gpu-h100")

QWEN35_27B_MODEL = "Qwen/Qwen3.5-27B"
ACC_THRESHOLDS = {QWEN35_27B_MODEL: {"gsm8k": 0.8}}
Expand Down
2 changes: 1 addition & 1 deletion test/registered/4-gpu-models/test_qwen35_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=260, suite="stage-c-test-4-gpu-b200")
register_cuda_ci(est_time=260, stage="stage-c", runner_config="4-gpu-b200")

QWEN35_FP4_MODEL = "nvidia/Qwen3.5-397B-A17B-NVFP4"
ACC_THRESHOLDS = {QWEN35_FP4_MODEL: {"gsm8k": 0.95}}
Expand Down
2 changes: 1 addition & 1 deletion test/registered/4-gpu-models/test_qwen3_30b.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=261, suite="stage-c-test-4-gpu-h100")
register_cuda_ci(est_time=261, stage="stage-c", runner_config="4-gpu-h100")

QWEN3_30B_MODEL_PATH = "Qwen/Qwen3-30B-A3B-FP8"

Expand Down
2 changes: 1 addition & 1 deletion test/registered/4-gpu-models/test_qwen3_next_models_mtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sglang.test.kits.prefix_cache_branching_kit import PrefixCacheBranchingMixin
from sglang.test.server_fixtures.default_fixture import DefaultServerBase

register_cuda_ci(est_time=290, suite="stage-c-test-4-gpu-h100")
register_cuda_ci(est_time=290, stage="stage-c", runner_config="4-gpu-h100")

QWEN3_NEXT_MODEL = "Qwen/Qwen3-Next-80B-A3B-Instruct"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
write_github_step_summary,
)

register_cuda_ci(est_time=492, suite="stage-c-test-8-gpu-h200")
register_cuda_ci(est_time=492, stage="stage-c", runner_config="8-gpu-h200")

DEEPSEEK_V32_MODEL_PATH = "deepseek-ai/DeepSeek-V3.2"

Expand Down
2 changes: 1 addition & 1 deletion test/registered/8-gpu-models/test_deepseek_v3_mtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
write_github_step_summary,
)

register_cuda_ci(est_time=309, suite="stage-c-test-8-gpu-h200")
register_cuda_ci(est_time=309, stage="stage-c", runner_config="8-gpu-h200")

FULL_DEEPSEEK_V3_MODEL_PATH = "deepseek-ai/DeepSeek-V3-0324"

Expand Down
4 changes: 3 additions & 1 deletion test/registered/8-gpu-models/test_dsa_models_hisparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
write_github_step_summary,
)

register_cuda_ci(est_time=720, suite="stage-c-test-8-gpu-h200", nightly=True)
register_cuda_ci(
est_time=720, stage="stage-c", runner_config="8-gpu-h200", nightly=True
)

GLM5_MODEL_PATH = "zai-org/GLM-5-FP8"

Expand Down
3 changes: 2 additions & 1 deletion test/registered/8-gpu-models/test_dsa_models_mtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

register_cuda_ci(
est_time=1048,
suite="stage-c-test-8-gpu-h200",
stage="stage-c",
runner_config="8-gpu-h200",
)

FULL_DEEPSEEK_V32_MODEL_PATH = "deepseek-ai/DeepSeek-V3.2"
Expand Down
2 changes: 1 addition & 1 deletion test/registered/8-gpu-models/test_mimo_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from sglang.test.server_fixtures.default_fixture import DefaultServerBase
from sglang.test.server_fixtures.mmmu_fixture import MMMUServerBase

register_cuda_ci(est_time=610, suite="stage-c-test-8-gpu-h200")
register_cuda_ci(est_time=610, stage="stage-c", runner_config="8-gpu-h200")


class TestMiMoV2Flash(GSM8KMixin, SpecDecodingMixin, DefaultServerBase):
Expand Down
2 changes: 1 addition & 1 deletion test/registered/8-gpu-models/test_minimax_m25_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
write_github_step_summary,
)

register_cuda_ci(est_time=307, suite="stage-c-test-8-gpu-h200")
register_cuda_ci(est_time=307, stage="stage-c", runner_config="8-gpu-h200")

MINIMAX_M25_MODEL_PATH = "MiniMaxAI/MiniMax-M2.5"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
popen_launch_server,
)

register_cuda_ci(est_time=376, suite="stage-c-test-8-gpu-h200")
register_cuda_ci(est_time=376, stage="stage-c", runner_config="8-gpu-h200")

NEMOTRON_3_SUPER_BF16_MODEL = "nvidia/NVIDIA-Nemotron-3-Super-120B-A12B-BF16"

Expand Down
Loading
Loading