Prhub

#27242 [MUSA][23/N] CI: Fix torchada preflight lock cleanup and add LLM server smoke test

原始 PR 作者 Stella-17 合并时间 2026-06-08 23:45 文件变更 7 提交数 1 评论 6 代码增减 +226 / -9

执行摘要

MUSA CI: 修复 torchada 锁清理并新增 LLM Server 冒烟测试

PR 旨在提升 MUSA CI 的可靠性,解决 sgl-kernel 发布流水线中 observed 的构建挂起问题(原因为 torchada 扩展锁文件残留),并为 MUSA 后端增加 LLM server 启动和基本生成请求的测试覆盖,确保 nightly CI 中 MUSA 平台的基本可用性。

建议精读并关注设计决策:CI 注册模式(AST 解析注册)、锁清理的防御性编程模式(进程检测+目录检查)值得在类似场景复用。对于 MUSA 平台维护者,应关注测试稳定性;对于 CI 团队,可参考其工作流定义模板。

讨论亮点

进程检测方法gemini-code-assist[bot] 建议使用 pgrep 替代 ps|grep 以避免脚本自身进程或目录名称包含关键字导致的误判。最终代码采纳了 pgrep -af ... | awk -v self="$$" '$1 != self' 模式,提升了检测鲁棒性。

目录存在性检查:同样来自 gemini-code-assist[bot] 的建议:在删除锁文件前应检查目录是否存在,避免 find 输出 stderr 错误。最终代码添加了 if [ -d "$torch_extensions_dir" ]; then ... fi 包裹。

断言大小写不敏感gemini-code-assist[bot] 指出 self.assertIn("Beijing", content) 是大小写敏感的,建议使用 content.lower() 避免因模型输出大小写变化导致假失败。最终代码改为 self.assertIn("beijing", content.lower())

超时时间调整yeahdongcn 要求将多个 job 的安装依赖超时统一从 10 分钟改为 15 分钟,以避免超时。该请求被采纳并在工作流中批量应用。

实现拆解

  1. 注册 MUSA CI 后端:在 python/sglang/test/ci/ci_register.py 中添加 register_musa_ci 标记函数和 HWBackend.MUSA 枚举,使测试能通过 AST 解析注册。同时将 register_musa_ci 加入 __all__REGISTER_MAPPING

  2. 新增 MUSA LLM Server 冒烟测试:创建 test/registered/musa/test_llm_server_smoke_musa.py,定义 TestMusaDeepSeekV2LiteChatServerSmoke 类,继承 DefaultServerBase,配置 DeepSeek-V2-Lite-Chat 模型和特定参数(fa3 注意力、禁用分段 CUDA Graph 等)。包含四个测试用例:test_health(健康检查)、test_health_generatetest_send_receive_chat_message_contains_beijing(验证聊天补全会话接口)、test_generate(验证生成接口非空)。使用 @unittest.skipIf 确保只在具备 MUSA 的设备上运行,并用 register_musa_ci 注册为夜间 1-GPU 套件,预计耗时 1200 秒。

  3. 更新测试套件运行配置:在 test/run_suite.py 中,将 "musa" 映射到 HWBackend.MUSA,在 PER_COMMIT_SUITES 中添加 HWBackend.MUSA: [],在 NIGHTLY_SUITES 中添加 HWBackend.MUSA: ["nightly-musa-1-gpu"],并将 HWBackend.MUSA 加入 _SUITE_CHECKED_BACKENDS,以启用套件验证。

  4. 配置 MUSA CI 工作流:在 .github/workflows/nightly-test-musa.yml 中添加新的 job nightly-test-llm-server-1-gpu-musa,指定 runner、超时 240 分钟,执行依赖安装后通过 run_suite.py 运行 nightly-musa-1-gpu 套件。同时将多个 job 的安装依赖超时从 10 分钟提升至 15 分钟,以应对安装负载。

  5. 修复 torchada 扩展锁清理:在 scripts/ci/musa/musa_install_dependency.sh 中,在升级 torchada 之前,先检测是否有活跃的构建进程(mcc、ninja、torchada_cpp_ops);若存在则拒绝清理锁文件并退出(避免竞态),否则删除 ${HOME}/.cache/torch_extensions 下的锁文件。在 .github/workflows/release-whl-kernel.yml 中也加入类似步骤,确保发布流水线同样受益。

文件 模块 状态 重要度
test/registered/musa/test_llm_server_smoke_musa.py 冒烟测试 added 7.78
python/sglang/test/ci/ci_register.py CI 注册 modified 5.22
.github/workflows/nightly-test-musa.yml CI 工作流 modified 4.55
test/run_suite.py 套件配置 modified 4.33
scripts/ci/musa/musa_install_dependency.sh 安装脚本 modified 4.18
.github/workflows/release-whl-kernel.yml 发布流水线 modified 3.63
.github/workflows/pr-test-musa.yml PR 测试 modified 3.33

关键符号

register_musa_ci TestMusaDeepSeekV2LiteChatServerSmoke test_health test_health_generate test_send_receive_chat_message_contains_beijing test_generate

关键源码片段

test/registered/musa/test_llm_server_smoke_musa.py test-coverage

新增 MUSA LLM Server 冒烟测试文件,包含四个测试用例覆盖健康检查和生成端点,是本次变更的核心测试配套。

import os
import unittestimport requests
import torchfrom sglang.test.ci.ci_register import register_musa_ci
from sglang.test.server_fixtures.default_fixture import DefaultServerBase# 注册为 MUSA nightly 1-GPU 套件,预计耗时 1200 秒
register_musa_ci(est_time=1200, suite="nightly-musa-1-gpu", nightly=True)_REQUEST_TIMEOUT = 60
​
​
@unittest.skipIf(
    not (hasattr(torch, "musa") and torch.musa.is_available()),
    "MUSA device not available",
)
class TestMusaDeepSeekV2LiteChatServerSmoke(DefaultServerBase):
    """MUSA LLM server 冒烟测试:启动、健康检查、非空生成。"""
​
    model = os.getenv("SGLANG_MUSA_LLM_MODEL", "deepseek-ai/DeepSeek-V2-Lite-Chat")
    served_model_name = "deepseek-v2-lite-chat"
    other_args = [
        "--trust-remote-code",
        "--served-model-name", served_model_name,
        "--attention-backend", "fa3", # 使用 flash attention 3 后端
        "--cuda-graph-max-bs", "32",
        "--tp-size", "1", # 单 GPU 测试
        "--chunked-prefill-size", "-1",
        "--disable-piecewise-cuda-graph", # 禁用分段 CUDA Graph 简化测试
        "--context-length", "4096",
        "--max-total-tokens", "8192",
        "--max-running-requests", "4",
    ]
​
    def test_health(self):
        """验证 /health 返回 200。"""
        resp = requests.get(self.base_url + "/health", timeout=10)
        self.assertEqual(resp.status_code, 200, resp.text)
​
    def test_health_generate(self):
        """验证 /health_generate 返回 200。"""
        resp = requests.get(self.base_url + "/health_generate", timeout=_REQUEST_TIMEOUT)
        self.assertEqual(resp.status_code, 200, resp.text)
​
    def test_send_receive_chat_message_contains_beijing(self):
        """验证聊天补全会话接口能够返回包含 'Beijing' 的回答。"""
        resp = requests.post(
            self.base_url + "/v1/chat/completions",
            json={
                "model": self.served_model_name,
                "messages": [{"role": "user", "content": "What is the capital of China? Answer in one word."}],
                "temperature": 0.0,
                "max_tokens": 16,
            },
            timeout=_REQUEST_TIMEOUT,
        )
        self.assertEqual(resp.status_code, 200, resp.text)
        body = resp.json()
        self.assertIn("choices", body)
        content = body["choices"][0]["message"]["content"]
        # 注意:大小写不敏感断言,避免因模型输出大小写变化导致假失败
        self.assertIn("beijing", content.lower())
​
    def test_generate(self):
        """验证 /generate 端点返回非空文本。"""
        resp = requests.post(
            self.base_url + "/generate",
            json={
                "text": "The capital of France is",
                "sampling_params": {"temperature": 0.0, "max_new_tokens": 16},
                "stream": False,
            },
            timeout=_REQUEST_TIMEOUT,
        )
        self.assertEqual(resp.status_code, 200, resp.text)
        body = resp.json()
        if isinstance(body, list):
            body = body[0]
        self.assertIn("text", body)
        self.assertGreater(len(body["text"].strip()), 0)
​
​
if __name__ == "__main__":
    unittest.main()
python/sglang/test/ci/ci_register.py test-coverage

添加 MUSA CI 注册函数和枚举,使测试能被 AST 解析并分配到 MUSA 后端运行。

# 在 HWBackend 枚举中添加 MUSA 后端
class HWBackend(Enum):
    CPU = auto()
    CUDA = auto()
    AMD = auto()
    NPU = auto()
    XPU = auto()
    MUSA = auto() # 新增 MUSA 后端枚举# 注册函数:作为 AST 标记使用,运行时无操作
def register_musa_ci(
    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 MUSA CI registration(parsed via AST; runtime no-op)。"""
    return None# 在 REGISTER_MAPPING 中添加映射
REGISTER_MAPPING = {
    "register_cpu_ci": HWBackend.CPU,
    "register_cuda_ci": HWBackend.CUDA,
    "register_amd_ci": HWBackend.AMD,
    "register_musa_ci": HWBackend.MUSA, # 新增映射
    "register_npu_ci": HWBackend.NPU,
    "register_xpu_ci": HWBackend.XPU,
}

评论区精华

进程检测使用 pgrep 替代 ps|grep 正确性

gemini-code-assist[bot] 建议使用 pgrep 以避免 ps|grep 匹配到脚本自身路径或目录名称导致误判。

结论:最终采纳 pgrep -af ... | awk -v self="$$" '$1 != self',提升了检测鲁棒性。 · 已解决

锁文件删除前的目录存在性检查 正确性

gemini-code-assist[bot] 建议检查目录是否存在以避免 find 输出 stderr。

结论:添加 if [ -d "$torch_extensions_dir" ]; then ... fi 包裹。 · 已解决

断言大小写不敏感 正确性

gemini-code-assist[bot] 指出 self.assertIn("Beijing", content) 大小写敏感,建议用 lower()。

结论:改为 self.assertIn("beijing", content.lower())。 · 已解决

统一安装依赖超时时间至 15 分钟 infra

yeahdongcn 要求将多个 job 的 timeout-minutes 从 10 改为 15。

结论:在 nightly-test-musa.yml 和 pr-test-musa.yml 中批量更新。 · 已解决

风险与影响

  • 锁清理逻辑误判风险:如果系统中存在与 mccninjatorchada_cpp_ops 同名的非构建进程(例如代码编辑器、CI 子进程),可能导致误判而阻止清理。当前使用 pgrep 并排除自身 PID 来缓解,但仍有可能覆盖不完整。建议后续持续关注。
  • 冒烟测试可靠性:新增测试基于 DefaultServerBase,依赖预缓存模型和稳定环境。模型加载失败、网络波动或 MUSA 驱动问题可能导致 flakes。测试超时设置为 120 分钟足够宽松,但仍可能因环境问题不稳定。
  • CI 作业时长增加:新增 LLM server 测试预计耗时 1200 秒(20 分钟),加上其他 job 的超时提升,整体 MUSA nightly CI 运行时间有所增加,可能影响资源调度。
  • 多 GPU 场景覆盖缺失:当前仅 1-GPU 测试,未涉及 TP/EP 等并行策略,生产风险暴露不全面。
  • 对用户:MUSA 硬件用户将受益于更可靠的 CI,确保 nightly 构建中的 SGLang 服务器在 MUSA 上可用。无直接用户功能变更。
  • 对系统:新增 nightly CI 作业增加约 20 分钟运行时长,但属预期内。
  • 对团队:需要维护新测试和锁清理脚本;测试中硬编码了 DeepSeek-V2-Lite-Chat 参数,未来模型更新需同步。
锁清理逻辑误判风险 冒烟测试依赖环境稳定性 CI 作业时长增加

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论