# PR #27863 完整报告

- 仓库：`sgl-project/sglang`
- 标题：[Fix][MTP][MM] Fix EAGLE v2 chunked-prefill next-token chain crash on multimodal models due to placeholder tokens
- 合并时间：2026-06-15 21:30
- 原文链接：http://prhub.com.cn/sgl-project/sglang/pull/27863

---

# 执行摘要

- 一句话：修复 EAGLE3 多模态 chunked-prefill crash
- 推荐动作：该 PR 是一个精准的定点 bugfix，改动量小，逻辑清晰，值得精读以理解多模态与 speculative decoding 交互时的边界情况。设计决策中值得关注的是：不对占位符 token 做硬编码过滤，而是依赖 `vocab_size` 作为通用判据——既简洁又与模型无关。

# 功能与动机

运行多模态模型并启用 SpecV2 时触发崩溃。调试发现崩溃发生在 embedding 层，归因于 PR #26800 引入的 `_compute_chunked_req_next_prompt_token` 方法未考虑多模态场景——多模态输入中的 placeholder/padding token 值可能远超 `vocab_size`，导致 embedding 层越界错误。

# 实现拆解

1. **增加 `vocab_size` 参数并在调用处传参**：在 `python/sglang/srt/managers/schedule_batch.py` 中，修改 `_compute_chunked_req_next_prompt_token` 函数签名，新增 `vocab_size: int` 参数。在 `ScheduleBatch.init_new` 中调用该函数时，传入 `model_config.vocab_size`。
2. **添加 token 有效性检查**：在函数内部，从 `chunked_req.origin_input_ids` 获取待处理的 token id 后，先检查该 id 是否小于 `vocab_size`；只有合法 token 才返回，否则返回 None。
3. **简化局部变量**：将 `chunked_req.origin_input_ids` 提取为局部变量 `origin_ids`，提升可读性。
4. **添加文档字符串**：为新逻辑添加 docstring，说明其作用是跳过多模态占位符（hash）token。

关键文件：
- `python/sglang/srt/managers/schedule_batch.py`（模块 调度器；类别 source；类型 core-logic；符号 _compute_chunked_req_next_prompt_token, ScheduleBatch.init_new）: 核心修复文件，包含 `_compute_chunked_req_next_prompt_token` 的修改和调用处传参调整。

关键符号：_compute_chunked_req_next_prompt_token, ScheduleBatch.init_new

## 关键源码片段

### `python/sglang/srt/managers/schedule_batch.py`

核心修复文件，包含 `_compute_chunked_req_next_prompt_token` 的修改和调用处传参调整。

```python
def _compute_chunked_req_next_prompt_token(
    chunked_req: Optional[Req],
    vocab_size: int,
) -> Optional[int]:
    """Return the next real prompt token after the fill boundary, skipping
    multimodal placeholder (hash) tokens that lie outside the model vocab."""
    if chunked_req is None:
        return None
    fill_len = chunked_req.fill_len
    origin_ids = chunked_req.origin_input_ids
    if fill_len >= len(origin_ids):
        return None
    # 多模态 placeholder 的 token id 往往远大于 vocab_size（例如 hash 值），
    # 直接索引 embedding 会越界崩溃。这里通过 vocab_size 边界检查过滤之。
    if origin_ids[fill_len] < vocab_size:
        return int(origin_ids[fill_len])
    return None

```

# 评论区精华

未发现实质性的审查讨论。PR 由 `sglang-npu-bot` 自动批准，无 review 评论。

- 暂无高价值评论线程

# 风险与影响

- 风险：修复非常集中，仅修改一个函数的参数和逻辑，且增加了显式的词表边界检查，回归风险极低。风险主要是：若 `vocab_size` 获取不正确（例如 `model_config.vocab_size` 为 None 或错误值），可能导致意外行为；但现有代码中该值总能正确获取。另外，若模型 tokenizer 在特殊 token 的 id 设计上小于 vocab_size 但实际不应作为下一个 prompt token，本次修复不会过滤此类情况——不过此类场景很少，不影响本次 fix 目标。
- 影响：**影响范围**：启用 Speculative Decoding V2（EAGLE3）且处理多模态请求的场景。修复前会崩溃，修复后正常运行。**影响程度**：仅对特定配置组合生效，不影响普通 prefill 或非多模态请求。**用户影响**：修复了特定场景的崩溃，用户无需修改配置即可受益。
- 风险标记：修复范围小，风险低

# 关联脉络

- PR #26800 引入 `_compute_chunked_req_next_prompt_token` 的原始 PR: 本次修复针对的是 PR #26800 引入的回归，该 PR 添加了 `_compute_chunked_req_next_prompt_token` 但未考虑多模态占位符。