[Build] Add srt_empty extra group for device-agnostic install (#31300)

Co-authored-by: yixiaodapeng <yixiaodapeng@example.com>
Co-authored-by: Alex Nails <alex.nails@radixark.ai>
This commit is contained in:
John
2026-08-05 13:17:46 -07:00
committed by GitHub
co-authored by yixiaodapeng Alex Nails
parent de34dd11e9
commit 5c4f72f92a
4 changed files with 130 additions and 12 deletions
+19 -5
View File
@@ -17,14 +17,16 @@ classifiers = [
dependencies = ["aiohttp", "IPython", "numpy", "requests", "setproctitle", "tqdm"]
[project.optional-dependencies]
runtime_common = [
# runtime_base: torch-free subset of runtime_common.
# Used by srt_empty for device-agnostic install on non-NVIDIA platforms.
# NOTE: Do NOT add packages that depend on torch/triton here — they belong in runtime_common.
runtime_base = [
"aiohttp",
"anthropic>=0.20.0",
"apache-tvm-ffi",
"av",
"blobfile==3.0.0",
"build",
"compressed-tensors",
"datasets",
"easydict",
"einops",
@@ -42,7 +44,6 @@ runtime_common = [
"openai==2.6.1",
"openai-harmony==0.0.4",
"orjson",
"outlines==0.1.11",
"packaging",
"partial_json_parser",
"pillow",
@@ -60,16 +61,29 @@ runtime_common = [
"smg-grpc-servicer>=0.5.0",
"soundfile==0.13.1",
"tiktoken",
"timm==1.0.16",
"torchao==0.9.0",
"tqdm",
"transformers==5.12.1",
"uvicorn",
"uvloop",
"xxhash",
]
# runtime_common: backward-compatible — same install result as before the split.
runtime_common = [
"sglang[runtime_base]",
"compressed-tensors",
"outlines==0.1.11",
"timm==1.0.16",
"torchao==0.9.0",
"xgrammar==0.2.1",
]
# srt_empty: device-agnostic install — pure Python packages only, no torch dependency chain.
# Enables OOT plugins (e.g. sglang-plugin-FL) to install sglang without conflicting with
# vendor-specific PyTorch builds (torch_npu, torch_musa, etc.).
# Usage: cp pyproject_other.toml pyproject.toml && pip install -e ".[srt_empty]"
srt_empty = ["sglang[runtime_base]"]
diffusion_common = [
"addict",
"cloudpickle",
+12 -6
View File
@@ -25,7 +25,7 @@ from collections import deque
from contextlib import contextmanager, nullcontext
from functools import partial
from http import HTTPStatus
from typing import Any, Deque, Dict, List, Optional, Tuple, Union
from typing import TYPE_CHECKING, Any, Deque, Dict, List, Optional, Tuple, Union
from sglang.srt.runtime_context import (
get_device,
@@ -50,12 +50,17 @@ import psutil # isort: skip
import setproctitle
import torch
import torch.distributed
from torch.cuda import Stream as CudaStream
from torch.distributed import barrier
from sglang.kernels.ops.mamba.triton_ops import (
initialize_mamba_selective_state_update_backend,
)
if TYPE_CHECKING:
from torch.cuda import Stream as CudaStream
try:
from sglang.kernels.ops.mamba.triton_ops import (
initialize_mamba_selective_state_update_backend,
)
except ImportError:
initialize_mamba_selective_state_update_backend = None
from sglang.srt.configs.model_config import (
ModelConfig,
ModelImpl,
@@ -851,7 +856,8 @@ class Scheduler(
)
def init_mamba_backend(self) -> None:
initialize_mamba_selective_state_update_backend(self.server_args)
if initialize_mamba_selective_state_update_backend is not None:
initialize_mamba_selective_state_update_backend(self.server_args)
def init_moe_gemm_config(self):
# For the MM models, check the text_config for MoE settings
+8 -1
View File
@@ -31,7 +31,6 @@ import uuid
from functools import cached_property
from typing import Any, Callable, Dict, List, Literal, Optional, Union
from sglang.kernels.ops.attention.fla.chunk_delta_h import CHUNK_SIZE as FLA_CHUNK_SIZE
from sglang.kernels.ops.kv_canary.consts import RealKvHashMode
from sglang.srt.arg_groups.arg_utils import NS, A, Arg, add_cli_args_from_dataclass
from sglang.srt.arg_groups.argparse_actions import (
@@ -8675,6 +8674,14 @@ class ServerArgs:
# It is used to determine the caching point in a sequence during prefill.
if not hasattr(self, "_mamba_cache_chunk_size"):
try:
from sglang.kernels.ops.attention.fla.chunk_delta_h import (
CHUNK_SIZE as FLA_CHUNK_SIZE,
)
except ImportError:
# Must match sglang.kernels.ops.attention.fla.chunk_delta_h.CHUNK_SIZE
FLA_CHUNK_SIZE = 64
hf_config = self.get_model_config().hf_config
chunk_size = getattr(hf_config, "mamba_chunk_size", FLA_CHUNK_SIZE)
page_size = resolved_view(self).page_size
@@ -0,0 +1,91 @@
# Copyright 2024 SGLang Team
# Licensed under the Apache License, Version 2.0
"""Test that runtime_base in pyproject_other.toml remains torch-free.
This prevents accidental introduction of packages that transitively pull
torch/triton into the srt_empty install target.
"""
from pathlib import Path
import pytest
from sglang.test.ci.ci_register import register_cpu_ci
register_cpu_ci(est_time=10, suite="base-a-test-cpu")
# Packages known to transitively depend on torch or triton.
# If a new package is added to runtime_base and it pulls torch,
# add it here and move it to runtime_common instead.
TORCH_PULLING_PACKAGES = frozenset(
{
"torch",
"torchao",
"timm",
"xgrammar",
"compressed-tensors",
"outlines",
"flashinfer",
"sgl-kernel",
}
)
def _parse_runtime_base() -> set:
"""Parse runtime_base package names from pyproject_other.toml."""
# Try tomllib (3.11+) or tomli
try:
import tomllib
except ModuleNotFoundError:
import tomli as tomllib # type: ignore[no-redef]
toml_path = Path(__file__).resolve().parents[3] / "python" / "pyproject_other.toml"
if not toml_path.exists():
pytest.skip(f"pyproject_other.toml not found at {toml_path}")
with open(toml_path, "rb") as f:
data = tomllib.load(f)
runtime_base = data["project"]["optional-dependencies"]["runtime_base"]
# Extract bare package names (strip version specifiers and extras)
pkg_names = set()
for dep in runtime_base:
# "package[extra]>=1.0,<2.0" -> "package"
name = (
dep.split("[")[0]
.split(">")[0]
.split("<")[0]
.split("=")[0]
.split("!")[0]
.split(";")[0]
.strip()
)
pkg_names.add(name.lower())
return pkg_names
def test_runtime_base_no_torch_deps():
"""runtime_base must not contain packages that pull in torch."""
pkg_names = _parse_runtime_base()
violations = pkg_names & TORCH_PULLING_PACKAGES
assert not violations, (
f"runtime_base contains torch-pulling packages: {sorted(violations)}. "
f"Move them to runtime_common to keep srt_empty torch-free."
)
def test_runtime_base_not_empty():
"""Sanity check: runtime_base should have a reasonable number of packages."""
pkg_names = _parse_runtime_base()
assert len(pkg_names) >= 20, (
f"runtime_base only has {len(pkg_names)} packages, expected >= 20. "
f"Did the toml structure change?"
)
if __name__ == "__main__":
import sys
sys.exit(pytest.main([__file__, "-v"]))