# PR #33087 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[Fix] Repair verify mask test fixture
- 合并时间：2026-08-01 05:48
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/33087

---

# 执行摘要

- 一句话：修复 verify-mask 测试夹具，补齐缺失的 context_len
- 推荐动作：值得快速浏览，不建议精读。这是一个典型的“跨 PR 契约漂移”案例：一个 PR 引入新构造依赖，另一个 PR 的 fixture 未同步，导致确定性 CI 失败。值得关注的实践是 PR body 中的回归时间线定位方法（通过 CI 运行时间与合并时间交叉比对锁定触发 PR），以及用 `/rerun-test` 单独验证目标测试的流程。

# 功能与动机

PR body 明确指出这是修复当前 main 的确定性 Base CPU CI 失败。根因是 #32690 引入的 HybridAttnBackend 无条件下读 model_runner.model_config.context_len，但 #32920 引入的 test_verify_mask.py 轻量夹具没有提供 model_config，导致 registered Base CPU 测试在后端构造阶段就抛 AttributeError，未走到任何断言。

# 实现拆解

该 PR 的修复分两层：

1. **测试夹具补契约**：在 `test/registered/unit/layers/attention/test_verify_mask.py` 的 `_make_hybrid_backend` 中，向 `model_runner` 的 `SimpleNamespace` 增加 `model_config=SimpleNamespace(context_len=_MAX_CONTEXT_LEN)`，对齐 `HybridAttnBackend.__init__` 新增的无条件读取 `model_runner.model_config.context_len` 的构造契约。这是让测试能走到断言的关键修复。

2. **源码注释同步**：在 `python/sglang/srt/layers/attention/verify_mask.py` 的 `VerifyMask.fits()` 文档字符串中，删除关于“composite backends do not carry max_context_len”的过时描述，改为更准确的“FULL_MASK keeps unconditional reuse because its runtime bound depends on sequence lengths not passed to fits()”。原因是 #32690 之后 composite backend 实际已经携带 context_len，旧注释已失真。

3. **测试描述清理**：移除 `test_capacity_check_needs_nothing_from_the_backend` 中“fits() reaching back through the backend would raise AttributeError here”的过时 docstring，因为夹具现在提供 model_config 后该描述不再成立。

验证方式：py_compile、pre-commit、git diff --check、局部冒烟测试；完整测试文件因本机解释器无 torch 未运行，交由 registered CI 执行，并通过 `/rerun-test` 确认了 `ubuntu-latest` 上 `test_verify_mask.py` 通过。

关键文件：
- `test/registered/unit/layers/attention/test_verify_mask.py`（模块 测试夹具；类别 test；类型 test-coverage；符号 _make_hybrid_backend, TestHybridAttnBackendHandsOutSelectedChildMask.test_capacity_check_needs_nothing_from_the_backend）: 修复的核心所在：为 _make_hybrid_backend 夹具补齐 model_config.context_len，对齐 HybridAttnBackend 构造契约，使 registered CPU 测试不再在构造阶段抛 AttributeError。
- `python/sglang/srt/layers/attention/verify_mask.py`（模块 注意力层；类别 source；类型 core-logic；符号 VerifyMask.fits）: 源码侧虽仅改 docstring，但修正了 fits() 中关于 composite backend 不携带 max_context_len 的过时描述（该描述在 #32690 后已失真），避免后续维护者被误导。

关键符号：_make_hybrid_backend, VerifyMask.fits

## 关键源码片段

### `test/registered/unit/layers/attention/test_verify_mask.py`

修复的核心所在：为 _make_hybrid_backend 夹具补齐 model_config.context_len，对齐 HybridAttnBackend 构造契约，使 registered CPU 测试不再在构造阶段抛 AttributeError。

```python
def _make_hybrid_backend(speculative_attention_mode, prefill_mask, decode_mask):
    # 轻量夹具需要对齐 HybridAttnBackend.__init__ 的构造契约：
    # #32690 之后构造函数会无条件读取 model_runner.model_config.context_len，
    # 缺少该字段会在后端构造阶段直接抛 AttributeError，测试根本走不到断言。
    model_runner = SimpleNamespace(
        kv_cache_dtype=None,
        token_to_kv_pool=object(),
        req_to_token_pool=object(),
        server_args=SimpleNamespace(
            speculative_attention_mode=speculative_attention_mode
        ),
        model_config=SimpleNamespace(context_len=_MAX_CONTEXT_LEN),  # 修复点
    )
    return HybridAttnBackend(
        model_runner,
        prefill_backend=_FakeAttnBackend(prefill_mask),
        decode_backend=_FakeAttnBackend(decode_mask),
    )

```

# 评论区精华

该 PR 没有 review comments，评论主要是 bot 与 CI 操作记录。核心讨论价值在 PR body 的回归时间线分析：作者精确锁定了 #32920（引入夹具）与 #32690（引入 model_config.context_len 读取但漏改该夹具）两处合并之间的窗口，并指出 #33074 只是受影响方而非触发方。另外通过 `/rerun-test` 命令单独重跑目标测试文件，确认修复有效。

- 暂无高价值评论线程

# 风险与影响

- 风险：风险极低：
 - 源码侧仅修改 docstring（verify_mask.py 的 fits() 注释），无任何逻辑行为变化，不引入回归面。
 - 测试侧只给 fixture 增加 model_config 属性、删除两条过时 docstring，不影响断言逻辑。
 - 唯一潜在问题是若未来 HybridAttnBackend 构造契约再次变化（例如 context_len 变为可选），该 fixture 的硬编码 `_MAX_CONTEXT_LEN` 可能再次产生偏差，但这是夹具类测试的常态维护成本。
 - 影响：影响面限定在 CI 与测试链路：修复 registered Base CPU 测试 `test_verify_mask.py` 的确定性失败，恢复对 verify-mask 混合后端子掩码分发逻辑的回归保护；同时消除该失败对无关 PR 检查的级联干扰（如 #33074 因 merge main 后无辜失败）。对生产运行时无影响，因为源码改动仅为注释。
 - 风险标记：测试夹具契约漂移 , 仅影响 CI 测试链路

# 关联脉络

- PR #32690 [Fix] missing max_context_len on HybridAttnBackend: 直接回归触发源：该 PR 让 HybridAttnBackend 无条件读取 model_runner.model_config.context_len，但遗漏了 test_verify_mask.py 的夹具，导致本 PR 修复的确定性 CI 失败。
- PR #32920 [Spec] Compact the target-verify mask when nothing reads it: 引入 test_verify_mask.py 的轻量 _make_hybrid_backend 夹具，当时 HybridAttnBackend 尚未读取 model_config，夹具与构造契约匹配；本 PR 修复了该夹具与后续契约的漂移。
- PR #33074 reproduced the failure after incorporating main: 受影响 PR：合入 main 后被同一确定性失败波及（base-a-test-cpu 失败），其自身改动与失败无关，本 PR 修复后其检查恢复。