Prhub

#36973 config: six more runtime readers ask the bags

原始 PR 作者 ch-wan 合并时间 2026-08-29 19:19 文件变更 20 提交数 2 评论 5 代码增减 +106 / -152

执行摘要

运行时 6 个模块改为从配置 bag 读参数

PR body 指出:srt/ 全树普查发现 53 个文件中存在 295 处对持有的 ServerArgs 的原始字段读取,而这些字段全部已在发布的命名空间中。上一 PR 已把 resolution 包从 record 上摘除,本 PR 处理 runtime 部分。其中 expert_distribution.py 是最典型例子:ExpertDistributionRecorder 被传入 ServerArgs 并穿过 6 个构造函数只为 4 处读取,而文件本身已经通过 get_exec().moe 读取 recorder 模式,record 只是与 bag 并行旅行。去除 record 依赖是系列目标(系列 PR #36896→#36975)的一部分。

值得精读。这是 config 系列重构的关键一环,展示了「先理清数据流、再逐层去掉参数透传」的系统性方法。重点学习:

1) 以「读取是原字段还是命名空间叶子」为判据决定可否迁移;
2) 用 is_config_namespace_published 把「None 用默认」重语义化为「未发布用默认」;
3) 对构造函数签名变更做全仓库调用点普查(含 srt/ 之外)。建议在阅读时对照系列内 #36896、#36972、#36974、#36975 一起看,才能理解完整演进。

讨论亮点

Codex 机器人提交了 4 条 review 评论,但 reviewed commit 为 97f9b5bb,与本 PR head(22f94e00)不一致,内容涉及 sgl_kernelflash_ops/flashmla_ops/spatial_ops 副作用导入被替换为 pass、以及 notebook 中删除 import os 的问题——这些文件不在本 PR 的变更范围内,可能是机器人对其他 commit 的评论被误挂。若这些评论实际属于本系列后续 PR(#36975 涉及 lazy imports),其技术要点仍值得注意:

P1: Restore the FlashAttention extension registration import — the empty try block never imports sgl_kernel.flash_ops, whose initialization registers torch.ops.sgl_kernel.fwd and get_scheduler_metadata.

P1: Restore the FlashMLA extension registration import — this pass sets _flashmla_import_error to None without loading sgl_kernel.flashmla_ops, so the backend reaches an unregistered operator.

P1: Restore the spatial extension registration import — create_greenctx_stream_by_value() reaches the missing operator.

P2: Keep os available for the documented token setup in docs/demo/deepseek_v4_flash.ipynb.

结论:这些意见指向“把副作用导入替换为 pass 会破坏 TORCH 扩展注册”的真实风险,但在本 PR 中无对应改动,建议在后续 PR 中核实。

实现拆解

  1. EPLB 记录器去参python/sglang/srt/eplb/expert_distribution.pyExpertDistributionRecorder.init_new_ExpertDistributionRecorderReal.__init___Accumulator.init_new_SinglePassGatherer.init_new 及子类共 6 个构造函数移除 server_args 参数;4 处读取改为 runtime_contextreports_expert_balancedness() 等 bag 访问器;model_runner.py 中 5 处调用点同步更新。
  2. 三个 balancedness 谓词发布为 runtime_context 访问器runtime_context.py 新增 reports_expert_balancednesslogs_expert_balancedness_to_server_logexports_expert_balancedness_to_prometheus,均读取 get_exec().moe.expert_balancedness_report_mode;对应删除 ServerArgs.should_log_expert_balancedness_to_server_logshould_export_expert_balancedness_to_prometheus。metrics_collector.py 与 metrics_reporter.py 改用新访问器。
  3. resolve_collector_class 改为读 bagobservability/metrics_collector.py 中该函数签名从 (server_args, role, default_cls) 改为 (role, default_cls),内部通过 get_context().is_config_namespace_published("observability") 判断命名空间是否发布,再读 get_observability().stat_loggers。三个调用点(含 init_new)同步更新。
  4. 其余 6 个 runtime reader 迁移hybrid_pool_assemblerscheduler 改读 get_memory().hicache_host_memory_modekt_ep_wrappercreate_kt_config_from_server_args 改读 get_exec().moe.kt_*get_schedule().chunked_prefill_sizeexpert_backup_client 改读 get_parallel()dspark_plannerbuild_sps_cost_table 删除关键字参数并改读 get_spec() / get_schedule()prefill_delayer 删除构造参数与存储属性,直接读 bag。
  5. 调用点连带修复:DSpark worker 构造 DSparkVerifyPlanner 的调用点(P1 级,否则启动即 TypeError),以及 registered 测试中的 prefill-delayer 与 SPS-table 用例改为通过 override_server_args 发布测试配置;删除不再需要的 _StubArgs 替身并改用真实 bag 发布。
  6. 测试配套test_stat_loggers_di.py 重写 TestResolveCollectorClass,用 get_context().override_server_args() 注入 stat_loggers,新增 test_returns_default_when_nothing_is_published;62-shape 解析探针与基线字节级一致,受影响套件 3038 通过,失败集与 main 相同。
文件 模块 状态 重要度
python/sglang/srt/observability/metrics_collector.py 指标采集 modified 6.91
python/sglang/srt/runtime_context.py 运行时上下文 modified 6.91
python/sglang/srt/eplb/expert_distribution.py 专家分布 modified 6.81

关键符号

resolve_collector_class reports_expert_balancedness logs_expert_balancedness_to_server_log exports_expert_balancedness_to_prometheus ExpertDistributionRecorder.init_new _Accumulator.init_new _SinglePassGatherer.init_new create_kt_config_from_server_args build_sps_cost_table PrefillDelayer.__init__ ExpertBackupClient.__init__

关键源码片段

python/sglang/srt/observability/metrics_collector.py core-logic

核心 DI 解析函数 `resolve_collector_class` 从接收 `server_args` 改为从已发布的 observability bag 读取,涉及 3 个调用点,是本 PR 语义变化最集中的地方。

# python/sglang/srt/observability/metrics_collector.pydef resolve_collector_class(role: str, default_cls: type) -> type:
    """返回 `role` 在已发布的 observability bag 中注册的收集器子类,
    未注册或命名空间未发布时返回 `default_cls`。    注意:语义从「server_args=None 用默认」迁移为「命名空间未发布用默认」,
    因此调用方必须保证在 config 发布之后调用。
    """
    from sglang.srt.runtime_context import get_context, get_observability
​
    # 命名空间未发布时直接兜底,避免 get_observability() 访问未初始化字段
    if not get_context().is_config_namespace_published("observability"):
        return default_cls
    stat_loggers = get_observability().stat_loggers
    if not stat_loggers:
        return default_cls
    # 按角色(scheduler / tokenizer / storage / radix_cache / expert_dispatch)查表
    return stat_loggers.get(role, default_cls)
python/sglang/srt/eplb/expert_distribution.py dependency-wiring

最典型的「record 旅行」消除案例:ServerArgs 参数从 6 个构造函数中移除,存储属性删除,读取全部走 bag。

# python/sglang/srt/eplb/expert_distribution.pyclass ExpertDistributionRecorder(ABC):
    """全局专家分布记录器入口。"""
​
    @staticmethod
    def init_new(expert_location_metadata, rank):
        # 之前这里还接收 server_args:它只服务于下面 4 处读取,
        # 而这些字段全部已在 exec.moe 命名空间中,因此直接读 bag
        if get_exec().moe.expert_distribution_recorder_mode is not None:
            assert expert_location_metadata is not None, (
                "ExpertLocationMetadata is required for expert distribution recording. "
                "One possible reason is that you are using a model that does not support "
                "expert distribution recording. Try setting "
                "`get_model_config_for_expert_location` in your model."
            )
            return _ExpertDistributionRecorderReal(expert_location_metadata, rank)
        else:
            return _ExpertDistributionRecorderNoop()

评论区精华

Codex P1:flash_attn.py 中副作用导入被替换为 pass 导致 FA3 算子未注册 正确性

Codex 指出 `try: from sgl_kernel import flash_ops` 被改为 `pass` 后,`sgl_kernel.flash_ops` 的初始化不再执行,`torch.ops.sgl_kernel.fwd` 和 `get_scheduler_metadata` 从未注册,首次 FlashAttention 调用会失败。

结论:评论针对 commit 97f9b5bb,与本 PR head 不符,疑似误挂;若属于系列后续 PR,应恢复副作用导入并改用 lint 抑制。 · unresolved

Codex P1:flash_mla.py 中副作用导入被替换为 pass 导致 FlashMLA 算子未注册 正确性

Codex 指出 `pass` 使 `_flashmla_import_error` 置空但未加载 `sgl_kernel.flashmla_ops`,导致 `get_mla_decoding_metadata` 等算子未注册,后端到达未注册的 `torch.ops.sgl_kernel` 算子。

结论:同上,疑似与本 PR 无关;若相关应恢复导入并保持错误上报语义。 · unresolved

Codex P1:spatial.py 中副作用导入被替换为 pass 导致 green-context 流算子未注册 正确性

Codex 指出 `from . import spatial_ops` 被替换为 `pass` 后,`create_greenctx_stream_by_value()` 到达缺失的 `torch.ops.sgl_kernel.create_greenctx_stream_by_value` 算子。

结论:同上,疑似与本 PR 无关;若相关应保留副作用导入。 · unresolved

Codex P2:deepseek_v4_flash.ipynb 删除 import os 导致文档示例 NameError documentation

Codex 指出 notebook 中删除了 `import os`,但同一 cell 的注释提示读者取消注释 `os.environ["HF_TOKEN"] = ...`,按文档操作会 NameError。

结论:与 PR 无关(notebook 不在变更列表),但文档可执行性问题值得在后续维护中修复。 · unresolved

风险与影响

主要风险是构造函数签名变更的连锁遗漏:删除 server_args 参数后,任何漏改的调用点都会在运行时抛 TypeError。PR 已处理 srt/ 外 3 个调用点(DSpark worker 是 P1 级,否则每次 DSPARK 启动即崩溃),但需人工确认其覆盖完整。其次,resolve_collector_class 的语义从「server_args=None 用默认值」变为「命名空间未发布用默认值」,依赖调用时机:若在 config 发布前调用,行为可能从「读到用户配置」变为「返回默认」,需确保所有调用点都在 publish 之后。kt_ep_wrapper 等核心路径(MoE 推理)改读 get_exec() 也可能在早期初始化阶段因命名空间未发布而取不到值。scheduler.py 是调度热路径,虽然 bag 读取是 dataclass 属性读取,性能风险低,但存在索引键(如 hicache_host_memory_mode)拼写错误导致的兜底失败。测试方面,PR 自述部分套件(observabilitymem_cachemanagersspecserver_args)与 main 失败集一致,但某些迁移(如 expert_backup_clientprefill_delayer)未见新增针对性测试,存在覆盖缺口。

影响范围集中在 sglang/srt 的配置读取路径:EPLB 专家均衡记录、Prometheus 指标采集、KT 专家并行、HiCache 混合池组装、调度器、弹性 EP 备份、DSPark 投机解码和 prefill 延迟。对用户无功能影响(行为不变),但为后续 #36974 删除死参数和 #36975 消除惰性导入铺路。对团队而言,该系列确立了「运行时从 bag 读配置、ServerArgs 只作为发布前载体」的新契约,后续新增配置读取应优先走 runtime_context 访问器。规模上 20 个文件、净删 46 行,属于跨模块中等规模重构。

核心路径变更 跨模块签名变更 调用点遗漏风险 部分迁移缺少新增测试 代码评审疑点未落地

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论