Prhub

#35062 [Misc] Clean up python/sglang package structure

原始 PR 作者 merrymercy 合并时间 2026-08-18 05:24 文件变更 54 提交数 3 评论 2 代码增减 +319 / -830

执行摘要

清理 sglang 顶层包结构:删 eval、迁移配置与内核日志

PR body 明确这是结构性清理:Clean up the top-level python/sglang package organization and remove stale modules. 此前顶层包混有 sglang.global_configsglang.kernel_api_logging 等游离模块,__init__.py 内含大段平台分支、try/except 与临时变量 del 清理逻辑,sglang/eval 下的评测脚本已过时。目标是把目录组织与子包边界理清,让 sglang.langsglang.kernels 各归其位,并通过 README 固化当前结构。

值得精读,尤其适合大型 Python 引擎包的维护团队。本 PR 体现了三类可复用的工程模式:

1) 平台兼容桩用 PEP 451 meta-path finder + 按平台条件懒加载统一收敛,保持顶层包轻量;
2) 公共模块迁移采用“顶层属性重导出 + 内部导入统一改源”的双轨策略,兼顾老用户与内部一致性;
3) 纯结构性清理也借助 ratchet 类测试守住导入与模块状态,防止后续回归。建议 merge 后关注是否有外部 issue 反馈 sglang.global_config / sglang.kernel_api_logging 直接子模块导入失败,必要时补一层兼容 shim。

讨论亮点

本 PR 的 2 条 review 评论均来自作者 merrymercy 的自我审核,核心争论点是 __init__.py 该保持多干净:

  • 作者先指出:"i mean move the whole function into a separate file. you can merge sglang._mps_stub, sglang._triton_stub and this function all into a single function in a separate file. let us keep the import and function calls clean in python/sglang/init.py and only run them / import them if needed." 即反对在包初始化文件里塞平台安装函数,要求合并三个来源为一个独立文件的单一函数。
  • 随后作者在最新 review 中确认:"Addressed in 646527be9e: the MPS and Triton stubs now live behind a single install_platform_stubs() entry point in sglang._platform_stubs, and sglang.__init__ imports/calls it only on Apple Silicon." 决策结论是懒加载 + 按平台条件导入,无未解决疑虑。

实现拆解

  1. 删除过时 eval 包:移除 python/sglang/eval/llama3_eval.py(315 行)与 loogle_eval.py(164 行),整个 sglang/eval 目录消失,其中的 benchmarkfetch_responsesCustomAsyncHTTPXClientget_mmlu_answer 等符号不再随包分发,评测能力改由外部评测工具(如 sgl-eval)承担。
  2. 迁移前端全局配置python/sglang/global_config.py 移到 python/sglang/lang/global_config.py,删除文件内 FIXME: deprecate this file 注释并补充 __all__;同步把 lang/api.pylang/backend/runtime_endpoint.pylang/interpreter.pylang/ir.py 的导入改为 from sglang.lang.global_config import global_config。顶层 sglang.global_config 属性通过 __init__.py 重新导出保留,兼顾老用户。
  3. 迁移内核 API 日志python/sglang/kernel_api_logging.py 移到 python/sglang/kernels/kernel_api_logging.py,所有调用方统一改为 from sglang.kernels.kernel_api_logging import debug_kernel_api,涉及 srt/models/deepseek_common/utils.pysrt/models/minimax_m2.pykernels/fused_op.pykernels/aot/python/sgl_kernel/debug_utils.py 以及 multimodal_gen/runtime/layers 下的 4 个层文件。
  4. 合并 Apple 平台桩_mps_stub.py_triton_stub.py 合并为 _platform_stubs.py,对外只暴露 install_platform_stubs() 一个入口;平台判断、triton mock 注册与 torch.mps 属性补齐全部下沉到该文件,__init__.py 只剩一行 darwin/arm64 条件判断。
  5. 简化包初始化与插件加载__init__.py 删除临时变量别名与 del 语句,环境变量重定向只保留 _redirect_third_party_caches 一个别名;launch_server.pyload_plugins()run_server 函数内部提前到模块顶层导入,保证无论从哪个入口启动、甚至在 prepare_server_args 之前插件就已注册。
  6. 文档与测试配套:重写 python/sglang/README.md 说明当前目录与文件组织;测试方面沿用 test_global_config_read_ratchet.pytest_module_state_ratchet.py,并跑通 kernel inventory、fused-op dispatch 与包/移动模块的 import smoke 检查。
文件 模块 状态 重要度
python/sglang/_platform_stubs.py 平台兼容层 renamed 8.94
python/sglang/__init__.py 包入口 modified 7.01
python/sglang/eval/llama3_eval.py 评估脚本 removed 8.22
python/sglang/lang/global_config.py 前端配置 renamed 5.75
python/sglang/kernels/kernel_api_logging.py 内核日志 renamed 4.96
python/sglang/launch_server.py 启动入口 modified 4.99

关键符号

install_platform_stubs _MockModule.__getattr__ _TritonFinder.find_spec _make_mock load_plugins

关键源码片段

python/sglang/_platform_stubs.py rename-or-move

Apple 平台 MPS 与 Triton 兼容桩合并为单一入口,是 `__init__.py` 轻量化与平台兼容的关键承载文件,包含 PEP 451 meta-path finder 与懒加载设计。

def install_platform_stubs() -> None:
    """在 Apple Silicon 上统一安装 Triton 与 MPS 兼容桩。    该函数仅在 ``sglang/__init__.py`` 中按平台条件调用;非 darwin/arm64
    环境直接返回,保持零导入开销。先检查真实 triton 是否存在,若不存在则
    通过 meta-path finder 拦截后续所有 ``import triton.*``,再补齐
    ``torch.mps`` 缺失的 CUDA 风格 API(Stream / set_device /
    get_device_properties 等)。
    """
    global _platform_stubs_installed
    if _platform_stubs_installed:
        return
​
    # 只有 Apple Silicon + MPS 可用时才需要这些桩
    if sys.platform != "darwin" or platform.machine() != "arm64":
        return
    try:
        import torch
    except ImportError:
        return
    if not torch.backends.mps.is_available():
        return
​
    # 真实 triton 不可用时注入 mock,让 kernel 定义语法合法,
    # 实际计算走 SDPA 等替代后端
    if "triton" not in sys.modules and importlib.util.find_spec("triton") is None:
        # finder 必须先注册,后续 ``import triton.X`` 才能被接管
        sys.meta_path.insert(0, _TritonFinder())
​
        triton = _make_mock("triton")
        triton.__version__ = "3.0.0"
        triton.cdiv = _cdiv
        triton.next_power_of_2 = _next_power_of_2
        triton.Config = _Config
​
        # triton.language(惯用别名 ``tl``)
        tl = _make_mock("triton.language")
​
        class _constexpr:
            """同时充当 ``tl.constexpr`` 的注解与值包装。"""
​
            def __init__(self, value=None):
                self.value = value
​
            def __repr__(self):
                return f"constexpr({self.value!r})"
​
        tl.constexpr = _constexpr
        triton.language = tl
​
        # 其余子模块(extra.libdevice / runtime.jit / testing /
        # tools.tensor_descriptor / backends.compiler ...)由 _TritonFinder
        # 在首次被 import 时动态创建,无需在此逐一列举
​
    mps = torch.mps
    # 只补齐缺失的属性,避免覆盖真实实现
    for name, obj in [
        ("Stream", Stream),
        ("StreamContext", StreamContext),
        ("Event", Event),
        ("current_stream", current_stream),
        ("stream", stream),
        ("set_device", set_device),
        ("current_device", current_device),
        ("device_count", device_count),
        ("get_device_properties", get_device_properties),
        ("reset_peak_memory_stats", _memory_tracker.reset_peak_memory_stats),
        ("memory_allocated", _memory_tracker.memory_allocated),
        ("memory_reserved", _memory_tracker.memory_reserved),
        ("max_memory_allocated", _memory_tracker.max_memory_allocated),
        ("max_memory_reserved", _memory_tracker.max_memory_reserved),
    ]:
        if not hasattr(mps, name):
            setattr(mps, name, obj)
​
    _patch_non_blocking()
    _platform_stubs_installed = True
python/sglang/__init__.py dependency-wiring

包初始化入口,集中体现全局配置迁移、平台桩懒加载、环境变量重定向顺序等结构性决策。

"""SGLang public API."""import platform as _platform
import sys as _sys# sglang.srt.environ 必须先于其余导入执行:hf_transformers_patches 与
# lang.api 会引入 torch 与 FlashInfer,并提前抢占缓存目录;而 environ
# 无重依赖(不依赖 torch),先执行才能保证目录配置生效。
from sglang.srt.environ import redirect_third_party_caches as _redirect_third_party_caches_redirect_third_party_caches()# 仅在 Apple Silicon 上安装 MPS / Triton 兼容桩,其他平台零开销。
# 平台判断与安装逻辑全部收敛到 sglang._platform_stubs,保持
# __init__.py 的导入路径干净、可读。
if _sys.platform == "darwin" and _platform.machine() == "arm64":
    from sglang._platform_stubs import install_platform_stubs as _install_platform_stubs
​
    _install_platform_stubs()# 对 HuggingFace Transformers 打运行时补丁,必须在任何模型导入之前完成
from sglang.srt.utils.hf_transformers_patches import apply_all as _apply_hf_patches_apply_hf_patches()# 前端语言 API 统一从 sglang.lang 子包导出,global_config 也随之
# 收敛到 sglang.lang.global_config
from sglang.lang.api import (
    Runtime,
    assistant,
    assistant_begin,
    assistant_end,
    flush_cache,
    function,
    gen,
    gen_int,
    gen_string,
    get_server_info,
    image,
    select,
    separate_reasoning,
    set_default_backend,
    system,
    system_begin,
    system_end,
    user,
    user_begin,
    user_end,
    video,
)
from sglang.lang.backend.runtime_endpoint import RuntimeEndpoint
from sglang.lang.choices import (
    greedy_token_selection,
    token_length_normalized,
    unconditional_likelihood_normalized,
)
from sglang.lang.global_config import global_config# 后端客户端采用 LazyImport,避免导入 sglang 时拉起 openai 等重依赖
from sglang.utils import LazyImport
from sglang.version import __version__Anthropic = LazyImport("sglang.lang.backend.anthropic", "Anthropic")
Crusoe = LazyImport("sglang.lang.backend.crusoe", "Crusoe")
LiteLLM = LazyImport("sglang.lang.backend.litellm", "LiteLLM")
OpenAI = LazyImport("sglang.lang.backend.openai", "OpenAI")
VertexAI = LazyImport("sglang.lang.backend.vertexai", "VertexAI")# 运行时 API 同样保持惰性加载
ServerArgs = LazyImport("sglang.srt.server_args", "ServerArgs")
Engine = LazyImport("sglang.srt.entrypoints.engine", "Engine")

评论区精华

平台桩安装逻辑是否应整体外置,保持 __init__.py 干净 设计

merrymercy 在 self-review 中指出:"i mean move the whole function into a separate file. you can merge sglang._mps_stub, sglang._triton_stub and this function all into a single function in a separate file. let us keep the import and function calls clean in python/sglang/__init__.py and only run them / import them if needed."

结论:commit 646527be9 实现:MPS 与 Triton 桩统一收敛到 sglang._platform_stubs.install_platform_stubs(),sglang.__init__ 仅在 Apple Silicon 上导入并调用。 · 已解决

确认平台桩合并后的入口与调用方式 question

merrymercy 回复自身评论:"Addressed in 646527be9e: the MPS and Triton stubs now live behind a single install_platform_stubs() entry point in sglang._platform_stubs, and sglang.__init__ imports/calls it only on Apple Silicon."

结论:按平台懒加载的单一入口方案落地,无遗留疑虑。 · 已解决

风险与影响

  1. 导入路径破坏风险(外部兼容性)sglang.global_configsglang.kernel_api_logging 作为模块文件被移动,使用 from sglang.global_config import global_configfrom sglang.kernel_api_logging import debug_kernel_api 的第三方代码会直接 ImportError。顶层属性 sglang.global_config 仍通过 __init__.py 导出、旧式写法兼容,但子模块路径不兼容,属于隐性 breaking change。
  2. eval 包删除风险sglang/eval/ 下两个脚本被整体删除,若 CI 流水线、文档或外部脚本仍引用 sglang.eval 会断裂;PR 内未查到同步删除的文档引用,需确认仓库外依赖。
  3. 插件加载时机提前launch_server.pyload_plugins()run_server 内提前到模块导入阶段,插件注册早于 prepare_server_args。若某插件依赖 server args 或运行期环境才可安全初始化,可能出现行为差异;这是本 PR 中唯一影响运行期时序的改动。
  4. 平台桩回归风险_platform_stubs.py 把 triton 桩的安装条件收敛到 darwin/arm64 && torch 可导入 && MPS available 三重判断下。与旧逻辑基本等价,但因逻辑合并,MPS 不可用但需要 triton 桩的边缘环境行为可能变化,缺少针对该文件的独立平台测试。

影响范围覆盖所有 import sglang 的进程:运行时行为无变化,但包边界与初始化顺序被重新梳理。对用户而言,顶层 sglang.global_config 属性保留,常规用法不受影响;直接按旧子模块路径导入的用户需要适配。对团队而言,langkernels 子包边界更清晰,后续定位前端配置与内核日志的成本下降;eval 脚本移除后评测工作流需迁移到 sgl-eval。对系统而言,插件注册提前到模块导入阶段,使非 launch_server 入口也能统一加载插件;Apple Silicon 平台的兼容桩入口收敛但行为等价。整体影响程度中低,主要作用于工程可维护性与开发体验。

公共模块删除 导入路径破坏风险 插件加载时机提前 平台桩回归风险 外部兼容性

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论