# PR #30064 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[refactor] Move ServerArgs ownership into the runtime context (stack 2/15)
- 合并时间：2026-07-04 17:20
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/30064

---

# 执行摘要

- 一句话：将 ServerArgs 所有权移入 RuntimeContext
- 推荐动作：值得精读，特别是需要理解配置层架构的开发者。设计决策（基于 shim 的状态迁移模式、延迟导入避免循环依赖、reset_context 支持测试）具有通用参考价值。

# 功能与动机

作为声明式配置解析栈（stack 2/15）的核心步骤，将配置对象的所有权集中到 RuntimeContext，为后续声明式覆盖系统（如 #30067、#30068）提供统一入口。避免依赖模块级全局变量带来的测试隔离困难和循环导入问题。

# 实现拆解

1. 在 RuntimeContext 类中添加 `_server_args` 实例变量和 `set_server_args()` 方法，令属性 `server_args` 直接返回该槽位，未设置时抛出 ValueError。
2. 在 `runtime_context.py` 模块级新增 `reset_context()` 函数，清空 `_server_args` 槽，用于单元测试拆卸。
3. 删除 `_sa()` 辅助函数（原导入 `server_args` 模块的延迟函数），不再需要。
4. 在 `server_args.py` 中删除模块全局变量 `_global_server_args`，将 `set_global_server_args_for_scheduler` 和 `get_global_server_args` 改为通过惰性导入的 `get_context().set_server_args` / `get_context().server_args` 代理，保持 API 签名和行为完全一致。
5. 将 `set_global_server_args_for_tokenizer` 保持为同一函数的别名。
6. 更新测试：`test_runtime_context.py` 中重写 `TestServerArgs` 相关测试，引入 `_IsolatedServerArgs` mixin 在 setUp/tearDown 中保存 / 恢复上下文状态；`test_disagg_trace.py` 中修改 `_srt_trace_server_args` 从直接访问私有全局变量改为使用公共 API 和 `reset_context`。

关键文件：
- `python/sglang/srt/runtime_context.py`（模块 运行时上下文；类别 source；类型 dependency-wiring；符号 _sa, set_server_args, reset_context）: 核心变更文件：实现所有权转移。添加 `_server_args` 实例变量、`set_server_args()`、`reset_context()`，修改 `server_args` 属性直接读取槽位。
- `python/sglang/srt/server_args.py`（模块 服务参数；类别 source；类型 dependency-wiring）: 旧全局变量被删除，set_global_server_args_for_scheduler 和 get_global_server_args 退化为 RuntimeContext 的 shim。
- `test/registered/unit/test_runtime_context.py`（模块 上下文测试；类别 test；类型 test-coverage；符号 TestServerArgsReadThrough, _IsolatedServerArgs, setUp, tearDown）: 重写 ServerArgs 相关测试，新增 _IsolatedServerArgs 基类，测试所有权双向可见性。
- `python/sglang/multimodal_gen/test/unit/test_disagg_trace.py`（模块 追踪测试；类别 test；类型 test-coverage）: 修复测试辅助函数 `_srt_trace_server_args`，从直接访问私有全局变量改为使用公共 API 和 reset_context。

关键符号：RuntimeContext.set_server_args, RuntimeContext.server_args (property), reset_context, set_global_server_args_for_scheduler (modified), get_global_server_args (modified)

## 关键源码片段

### `python/sglang/srt/runtime_context.py`

核心变更文件：实现所有权转移。添加 `_server_args` 实例变量、`set_server_args()`、`reset_context()`，修改 `server_args` 属性直接读取槽位。

```python
class RuntimeContext:
    """Container for the structured runtime accessors; exposes ``parallel`` and
    ``server_args``."""

    __slots__ = ("parallel", "_server_args")  # 新增 _server_args 槽

    def __init__(self, parallel: ParallelContext):
        self.parallel = parallel
        self._server_args: ServerArgs | None = None  # 初始化 None

    @property
    def server_args(self) -> ServerArgs:
        """The process-wide ``ServerArgs`` (context-owned slot)."""
        server_args = self._server_args
        if server_args is None:
            # 保持与旧行为一致的错误消息，方便测试和用户代码匹配
            raise ValueError("Global server args is not set yet!")
        return server_args

    def set_server_args(self, server_args: ServerArgs) -> None:
        """Publish the process-wide ``ServerArgs`` into the context-owned slot.
        Overwrite-allowed: a re-publish replaces the slot (test kits re-publish
        per test; production ordering discipline lives at the call-sites)."""
        self._server_args = server_args


# --- 模块级单例和访问函数 ---

_PARALLEL = ParallelContext()
_CONTEXT = RuntimeContext(parallel=_PARALLEL)

def reset_context() -> None:
    """Clear the context-owned store (unit-test teardown).
    Wrapper subsystems (``parallel``) hold no state and are unaffected."""
    _CONTEXT._server_args = None

```

### `python/sglang/srt/server_args.py`

旧全局变量被删除，set_global_server_args_for_scheduler 和 get_global_server_args 退化为 RuntimeContext 的 shim。

```python
# NOTE: The process-wide ServerArgs is owned by the runtime context
# (sglang.srt.runtime_context). The two functions below are thin shims kept for
# the existing call-sites; they publish/read the same live object by reference.
# Imports are in-function so the two modules stay cycle-free at import time.

def set_global_server_args_for_scheduler(server_args: ServerArgs):
    from sglang.srt.runtime_context import get_context  # 延迟导入避免循环

    get_context().set_server_args(server_args)


# tokenizer 别名指向完全相同的函数
set_global_server_args_for_tokenizer = set_global_server_args_for_scheduler


def get_global_server_args() -> ServerArgs:
    from sglang.srt.runtime_context import get_context

    return get_context().server_args  # 可能抛出 ValueError（与旧行为一致）

```

# 评论区精华

该 PR 无人工 review 评论，所有决策由作者独立做出。bot 自动生成了一个每日配额提示，未产生实质性讨论。

- 暂无高价值评论线程

# 风险与影响

- 风险：
 1. 回归风险：所有现有 `set_global_server_args_for_scheduler` 和 `get_global_server_args` 调用点必须保持行为等价，shim 可能导致意外错误。
 2. 导入顺序风险：惰性内函数导入确保循环安全，但若在 RuntimeContext 尚未初始化时调用 getter 仍会触发 ValueError（与旧行为一致）。
 3. 测试隔离风险：`reset_context` 仅清空 ServerArgs 槽，不影响 `parallel` 上下文；但若测试依赖全局 ServerArgs 跨用例持久化，需显式管理。
 - 影响：用户：无直接用户影响，所有公共 API 未变更。系统：配置层所有权清晰化，为后续声明式覆盖系统奠定基础。团队：需要理解新的所有权模型，测试中推荐使用 `_IsolatedServerArgs` 模式。
 - 风险标记：核心路径变更 , 全局状态迁移

# 关联脉络

- PR #30067 [refactor] Add the declarative model-override registry and resolution gate (stack 5/15): 同一声明式配置解析栈的后续 PR，依赖于本 PR 建立的统一所有权。
- PR #30068 [refactor] Wire the config resolution pipeline (dispatch, stash, dual-apply, publish) (stack 6/15): 配置解析管线，利用本 PR 的 RuntimeContext 槽位进行发布。
- PR #30077 [refactor] Rename Arg.model_overridable to Arg.resolvable (stack 15/15): 声明式系统最终收尾，与所有权转移同一系列。