Prhub

#28093 [Spec] Move draft-extend prep to `EagleDraftWorkerBase`; unify `prepare_for_*` names

原始 PR 作者 hnyls2002 合并时间 2026-06-13 07:44 文件变更 12 提交数 5 评论 6 代码增减 +126 / -119

执行摘要

将 draft-extend prep 移到 Worker 基类并统一方法命名

PR 说明指出:“Building the draft-extend forward is worker behavior, not data carried by the spec_info。” 因此将 prepare_for_extend_to_fill_draft_kvcache 从混入数据类移至 Worker 基类,更忠实于职责分离原则。

值得精读。该 PR 展示了如何通过重构消除架构异味(数据类不该持有方法)、统一命名规范,并体现了良好的小型重构实践。对于关注 speculative decoding 模块的开发者,这是熟悉模块结构的好机会。

讨论亮点

无实质性讨论;仅包含自动化 bot 的 CI 触发和状态更新(/rerun-test/tag-and-rerun-ci)。

实现拆解

  1. 移动 prepare_for_draft_extend 到基类:将 EagleDraftExtendInputV2Mixin.prepare_for_extend_to_fill_draft_kvcache 方法(byte-identical)复制到 EagleDraftWorkerBase 作为新方法 prepare_for_draft_extend,并将原方法中的 self 改为 draft_extend_input 参数;删除 EagleDraftExtendInputV2Mixin 类,EagleDraftExtendInput 不再继承它。涉及文件:base_spec_worker.py(新增方法)、eagle_info_v2.py(移除类和方法)、eagle_info.py(调整继承)。
  2. 重命名基类:将 BaseDraftWorker 重命名为 EagleDraftWorkerBase,以准确反映其职责(仅 EAGLE 风格 draft worker 使用;NGRAM 没有 draft worker)。更新所有文件中的导入和类继承声明,包括 eagle_worker_v2.pymulti_layer_eagle_worker_v2.pyfrozen_kv_mtp_worker_v2.pyngram_worker.pyspec_utils.pykv_canary/plan_input.pydflash_worker_v2.py 等。
  3. 统一 prep 方法命名:在 eagle_info_v2.py 中将 prepare_for_v2_draft 重命名为 prepare_for_draft,在 dflash_info.py 中将 prepare_for_v2_verify 重命名为 prepare_for_verify;在所有调用处(eagle_worker_v2.pymulti_layer_eagle_worker_v2.pydflash_worker_v2.py 等)同步更新方法名和注释。
  4. 更新 worker 调用:在 eagle_worker_v2.pymulti_layer_eagle_worker_v2.py_draft_extend_for_decode 方法中,将原先 draft_extend_input.prepare_for_extend_to_fill_draft_kvcache(...) 改为 self.prepare_for_draft_extend(draft_extend_input, ...)verify 方法中的调用也相应重命名。所有改动保持字节级别语义一致。
  5. 测试适配:更新 test_decode_bookkeeping_ownership.py 中的类型守卫引用,将 BaseDraftWorker 替换为 EagleDraftWorkerBase,确保类型检查通过。
文件 模块 状态 重要度
python/sglang/srt/speculative/base_spec_worker.py spec 基类 modified 8.46
python/sglang/srt/speculative/eagle_info_v2.py spec 信息 modified 8.4
python/sglang/srt/speculative/eagle_worker_v2.py eagle worker modified 6.85
python/sglang/srt/speculative/multi_layer_eagle_worker_v2.py 多层 eagle modified 6.81

关键符号

prepare_for_draft_extend prepare_for_draft prepare_for_verify

关键源码片段

python/sglang/srt/speculative/base_spec_worker.py core-logic

核心变更文件:新增 `prepare_for_draft_extend` 方法并重命名类为 `EagleDraftWorkerBase`,承担 draft-extend forward batch 构建职责。

# 基类从 BaseDraftWorker 重命名为 EagleDraftWorkerBase,明确 EAGLE 风格
class EagleDraftWorkerBase(ABC):
    # draft() 和 draft_extend() 保持抽象
​
    def prepare_for_draft_extend(
        self,
        draft_extend_input: EagleDraftExtendInput,
        batch: ScheduleBatch,
        predict: torch.Tensor,
        num_draft_tokens: int,
        draft_model_runner: Any,
        cuda_graph_runner: Any,
    ):
        # 原属于 EagleDraftExtendInputV2Mixin,因“构建 forward batch
        # 属于 worker 责任”而被迁移至此
        gpu_only = batch.seq_lens_cpu is None
        batch.spec_info = draft_extend_input
        batch.input_ids = predict
        maybe_detect_oob(...) # 输入校验
        if gpu_only:
            batch.prefix_lens = batch.seq_lens.to(torch.int32)
            batch.extend_lens = torch.full(
                (bs,), num_draft_tokens, dtype=torch.int32, device=batch.seq_lens.device
            )
        else:
            batch.prefix_lens = batch.seq_lens_cpu.tolist()
            batch.extend_lens = [num_draft_tokens] * bs
        forward_batch = ForwardBatch.init_new(batch, draft_model_runner)
        # 在 forward_batch 上增加 seq_lens 以反映 draft extend 的写入
        forward_batch.seq_lens = forward_batch.seq_lens + num_draft_tokens
        can_cuda_graph = cuda_graph_runner and cuda_graph_runner.can_run(forward_batch)
        if not batch.forward_mode.is_idle() and not can_cuda_graph:
            draft_model_runner.attn_backend.init_forward_metadata(forward_batch)
            if not is_npu() or can_cuda_graph:
                forward_batch.mark_forward_metadata_ready()
        return forward_batch
python/sglang/srt/speculative/eagle_info_v2.py core-logic

移除 `EagleDraftExtendInputV2Mixin` 及其方法;重命名 `prepare_for_v2_draft` 和 `prepare_for_v2_verify`,清理导入。

# eagle_info_v2.py — 重构后:不再引入 EagleDraftExtendInput 和 Any
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING # 移除了 Anyimport torch
import torch.nn.functional as F
# ... 导入省略 ...# EagleDraftExtendInputV2Mixin 类已被彻底移除
# 原 prepare_for_extend_to_fill_draft_kvcache 方法移至 base_spec_worker.py# 方法名统一:prepare_for_v2_draft -> prepare_for_draft
def prepare_for_draft(
    self: EagleDraftInput,
    req_to_token_pool: ReqToTokenPool,
    batch: ScheduleBatch,
    cuda_graph_runner: EAGLEDraftCudaGraphRunner,
    draft_model_runner: ModelRunner,
    topk: int,
    num_steps: int,
):
    # 方法体未变,仅重命名
    if not batch.forward_mode.is_idle():
        bs = len(batch.seq_lens)
        page_size = batch.token_to_kv_pool_allocator.page_size
        # ... 分配 cache 位置等

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

由于是纯重构(代码移动+重命名),运行时行为保持不变,回归风险较低。但涉及 speculative decoding 核心路径,若导入或调用点遗漏更新可能导致运行时异常。CI 中已运行 spec 相关测试(test_spec_eagle.pytest_frozen_kv_mtp.pytest_dflash.pytest_spec_ngram.pytest_spec_standalone.py)并全部通过,风险可控。

对用户完全透明,无功能变化。系统代码结构更加清晰:draft-extend 的 forward batch 构建职责由 Worker 基类承担,数据类回归纯数据角色。对团队而言,新开发者更容易理解架构分层;后续若要添加新的 draft worker 变体,只需继承 EagleDraftWorkerBase 并实现抽象方法。

核心路径变更 跨文件命名调整 回归风险低

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论