Prhub

#22490 [EPD][VLM] Support Kimi VL EPD

sgl-project/sglang · 作者 LHXuuu · 合并时间 2026-04-16 12:40

分析状态 已生成
文件变更 6提交数 7 · 评论 13
代码增减 +268 / -102
feature multimodal consistency run-ci

执行摘要

扩展 EPD 分解管道以支持 Kimi VL 多模态模型。

PR body 指出 Kimi VL (kimi-vl-a3b) 与 Kimi K2.5 共享相同的 MoonViT 视觉塔,但尚未在 EPD(Encode-Prefill-Decode)管道中支持。目标是扩展支持以启用分解式服务,其中视觉编码器运行在编码服务器,语言模型运行在解码服务器。

建议技术管理者和工程师精读此 PR,重点关注 KimiGridMMDataMixin 的设计如何优雅地提取共享逻辑,以及编码服务器中模型类型检查的扩展方式。这对于理解多模态 EPD 管道的演进和代码重构最佳实践有重要参考价值。

讨论亮点

Review 中,ZhengWG 指出新增的方法 _num_image_tokens_from_grid_build_kimi_mm_data_from_grids 最初放在 BaseMultimodalProcessor 中,这会使基类变得 Kimi 特定,建议提取到共享工具或 Mixin 中。LHXuuu 回应已经创建了 KimiGridMMDataMixin 并让相关处理器继承,从而保持基类的通用性。结论是重构解决了设计问题,其他评论关注 lint 修复和 CI 运行,确保代码质量。

实现拆解

  1. 创建共享 Mixin:新增文件 python/sglang/srt/multimodal/processors/kimi_common.py,定义 KimiGridMMDataMixin 类,包含 _num_image_tokens_from_grid_build_kimi_mm_data_from_grids 方法。这提取了 Kimi 家族共享的逻辑,避免了在基类中引入特定代码,促进代码重用。
  2. 更新多模态处理器:修改 kimi_k25.pykimi_vl.py,让 KimiK2_5VLImageProcessorKimiVLImageProcessor 继承 KimiGridMMDataMixin,并添加 get_mm_data 方法。这确保了两种处理器都能重用共享逻辑,减少重复代码。
  3. 扩展编码服务器:在 encode_server.py 中,更新 _get_mm_grid_dimget_num_tokens 等方法,将模型类型检查从 "kimi_k25" 扩展到 ["kimi_k25", "kimi_vl"],并重命名 _kimi_k25_tokens_from_patch_grid_kimi_tokens_from_patch_grid。同时添加 _flatten_nested_items 静态方法和 _normalize_kimi_encoder_images 以处理 Kimi VL 的图像输入,确保 EPD 管道兼容新模型。
  4. 增强编码接收器:在 encode_receiver.py 中,改进 _cat_grid 函数以支持 numpy.ndarray 和列表输入,并添加 flatten_mm_itemsto_raw_url 助手函数,以处理嵌套的多模态项目,提高数据处理的鲁棒性。
  5. 调整模型配置:在 kimi_vl.py 中,添加 encoder_onlylanguage_only 配置标志,在相应模式下跳过语言模型或视觉权重的实例化,并增加防御性检查以容忍缺失参数,支持分解式部署场景。
    测试、配置或部署配套:本次改动未包含测试文件变更,但需注意依赖 PR #22442 以支持完整功能。
文件 模块 状态 重要度
python/sglang/srt/multimodal/processors/kimi_common.py 多模态处理器 added 8.39
python/sglang/srt/disaggregation/encode_server.py 分解管道 modified 8.0
python/sglang/srt/disaggregation/encode_receiver.py 分解管道 modified 7.34
python/sglang/srt/multimodal/processors/kimi_k25.py 多模态处理器 modified 7.14
python/sglang/srt/multimodal/processors/kimi_vl.py 多模态处理器 modified 6.19
python/sglang/srt/models/kimi_vl.py 模型层 modified 6.09
python/sglang/srt/disaggregation/encode_server.py core-logic

核心逻辑变更,扩展模型类型支持并添加工具函数,直接影响 EPD 管道行为,确保 Kimi VL 兼容性。

def _get_mm_grid_dim(mm_inputs, modality, model_type: Optional[str] = None):
    # Kimi K2.5 vision processor only emits `grid_thws`; prefer it over generic keys
    # so we never pick a mis-typed or stale `image_grid_hws` field from kwargs.
    attrs = _mm_grid_attrs[modality]
    if (model_type or "").lower() in ["kimi_k25", "kimi_vl"] and modality == Modality.IMAGE:
        attrs = ("grid_thws", "image_grid_thw", "image_grid_hws") # 扩展支持 Kimi VL
    for attr in attrs:
        if attr in mm_inputs and mm_inputs[attr] is not None:
            return mm_inputs[attr]
    raise ValueError(f"Grid dim ({_mm_grid_attrs[modality]}) not found in {mm_inputs}")def get_num_tokens(self, grid: Union[torch.Tensor, List[int]], modality: Modality) -> int:
    """Calculate number of tokens (after 2x2 merge). Used for mm_embedding slicing."""
    if modality == Modality.AUDIO:
        input_length = self.get_num_patches(grid, modality)
        return self._get_feat_extract_output_lengths(input_length)
    else:
        if self.model_type in ["kimi_k25", "kimi_vl"] and modality == Modality.IMAGE:
            return self._kimi_tokens_from_patch_grid(grid) # 统一处理 Kimi 家族
        merge_size = getattr(self.image_processor, "merge_size", 2)
        return self.get_num_patches(grid, modality) // (merge_size**2)@staticmethod
def _flatten_nested_items(items):
    """递归展平嵌套列表,用于处理分组路由中的多模态项目。"""
    if not isinstance(items, (list, tuple)):
        return [items]
​
    flat = []
    for item in items:
        if isinstance(item, (list, tuple)):
            flat.extend(MMEncoder._flatten_nested_items(item))
        else:
            flat.append(item)
    return flat

关键符号

_num_image_tokens_from_grid _build_kimi_mm_data_from_grids _kimi_tokens_from_patch_grid _normalize_kimi_encoder_images flatten_mm_items to_raw_url

评论区精华

代码结构优化 设计

ZhengWG 建议将 Kimi 特定方法 _num_image_tokens_from_grid 和 _build_kimi_mm_data_from_grids 移出 BaseMultimodalProcessor,以保持基类的通用性,避免引入模型特定逻辑。

结论:LHXuuu 创建了 KimiGridMMDataMixin 并让 KimiVLImageProcessor 和 KimiK2_5VLImageProcessor 继承,从而提取共享逻辑,解决了设计问题。 · 已解决

风险与影响

技术风险包括:1) 回归风险:对现有 Kimi K2.5 EPD 功能的潜在影响,改动可能引入错误,需通过测试验证。2) 缺少测试覆盖:本次 PR 未包含测试文件变更,新功能可能未经充分验证,增加生产环境风险。3) 兼容性问题:PR body 提到运行 Kimi VL 还需要 #22442,存在依赖风险,若不协同部署可能导致功能不完整。4) 代码复杂度:新增 Mixin 和多个修改点可能增加维护负担,尤其是对不熟悉 EPD 管道的开发人员。

对用户的影响:Kimi VL 用户现在可以利用 EPD 管道进行分解式服务,允许视觉编码器和语言模型在独立服务器上运行,提升部署灵活性和资源利用率。对系统的影响:扩展了多模态模型支持范围,增加了新的代码路径,可能需要额外监控和性能调优。对团队的影响:开发人员需要了解新的 Mixin 结构和 EPD 逻辑,但重构提高了代码重用性,长期看有助于降低维护成本。

核心路径变更 缺少测试覆盖 依赖外部 PR

关联 Issue

未识别关联 Issue

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

完整报告

执行摘要

  • 一句话:扩展 EPD 分解管道以支持 Kimi VL 多模态模型。
  • 推荐动作:建议技术管理者和工程师精读此 PR,重点关注 KimiGridMMDataMixin 的设计如何优雅地提取共享逻辑,以及编码服务器中模型类型检查的扩展方式。这对于理解多模态 EPD 管道的演进和代码重构最佳实践有重要参考价值。

功能与动机

PR body 指出 Kimi VL (kimi-vl-a3b) 与 Kimi K2.5 共享相同的 MoonViT 视觉塔,但尚未在 EPD(Encode-Prefill-Decode)管道中支持。目标是扩展支持以启用分解式服务,其中视觉编码器运行在编码服务器,语言模型运行在解码服务器。

实现拆解

  1. 创建共享 Mixin:新增文件 python/sglang/srt/multimodal/processors/kimi_common.py,定义 KimiGridMMDataMixin 类,包含 _num_image_tokens_from_grid_build_kimi_mm_data_from_grids 方法。这提取了 Kimi 家族共享的逻辑,避免了在基类中引入特定代码,促进代码重用。
  2. 更新多模态处理器:修改 kimi_k25.pykimi_vl.py,让 KimiK2_5VLImageProcessorKimiVLImageProcessor 继承 KimiGridMMDataMixin,并添加 get_mm_data 方法。这确保了两种处理器都能重用共享逻辑,减少重复代码。
  3. 扩展编码服务器:在 encode_server.py 中,更新 _get_mm_grid_dimget_num_tokens 等方法,将模型类型检查从 "kimi_k25" 扩展到 ["kimi_k25", "kimi_vl"],并重命名 _kimi_k25_tokens_from_patch_grid_kimi_tokens_from_patch_grid。同时添加 _flatten_nested_items 静态方法和 _normalize_kimi_encoder_images 以处理 Kimi VL 的图像输入,确保 EPD 管道兼容新模型。
  4. 增强编码接收器:在 encode_receiver.py 中,改进 _cat_grid 函数以支持 numpy.ndarray 和列表输入,并添加 flatten_mm_itemsto_raw_url 助手函数,以处理嵌套的多模态项目,提高数据处理的鲁棒性。
  5. 调整模型配置:在 kimi_vl.py 中,添加 encoder_onlylanguage_only 配置标志,在相应模式下跳过语言模型或视觉权重的实例化,并增加防御性检查以容忍缺失参数,支持分解式部署场景。
    测试、配置或部署配套:本次改动未包含测试文件变更,但需注意依赖 PR #22442 以支持完整功能。

关键文件:

  • python/sglang/srt/multimodal/processors/kimi_common.py(模块 多模态处理器;类别 source;类型 dependency-wiring;符号 KimiGridMMDataMixin, _num_image_tokens_from_grid, _build_kimi_mm_data_from_grids): 新增共享 Mixin,提取 Kimi 家族多模态数据处理的通用逻辑,是重构的核心,避免了代码重复并保持基类通用性。
  • python/sglang/srt/disaggregation/encode_server.py(模块 分解管道;类别 source;类型 core-logic;符号 _kimi_tokens_from_patch_grid, _normalize_kimi_encoder_images, _flatten_nested_items): 核心逻辑变更,扩展模型类型支持并添加工具函数,直接影响 EPD 管道行为,确保 Kimi VL 兼容性。
  • python/sglang/srt/disaggregation/encode_receiver.py(模块 分解管道;类别 source;类型 core-logic;符号 _to_tensor, flatten_mm_items, to_raw_url): 增强编码接收器以处理多样化的输入格式,提升数据处理的鲁棒性和兼容性。
  • python/sglang/srt/multimodal/processors/kimi_k25.py(模块 多模态处理器;类别 source;类型 dependency-wiring;符号 KimiK2_5VLImageProcessor, _num_image_tokens_from_grid): 重构以继承共享 Mixin,简化代码并确保与 Kimi VL 的逻辑一致性。
  • python/sglang/srt/multimodal/processors/kimi_vl.py(模块 多模态处理器;类别 source;类型 core-logic;符号 KimiVLImageProcessor, get_mm_data): 扩展 KimiVLImageProcessor 以支持 EPD 接收器端的数据重建,添加 get_mm_data 方法。
  • python/sglang/srt/models/kimi_vl.py(模块 模型层;类别 source;类型 data-contract): 调整模型配置以支持 encoder_only 和 language_only 模式,并增加防御性检查,适应分解式部署。

关键符号:_num_image_tokens_from_grid, _build_kimi_mm_data_from_grids, _kimi_tokens_from_patch_grid, _normalize_kimi_encoder_images, flatten_mm_items, to_raw_url

关键源码片段

python/sglang/srt/disaggregation/encode_server.py

核心逻辑变更,扩展模型类型支持并添加工具函数,直接影响 EPD 管道行为,确保 Kimi VL 兼容性。

def _get_mm_grid_dim(mm_inputs, modality, model_type: Optional[str] = None):
    # Kimi K2.5 vision processor only emits `grid_thws`; prefer it over generic keys
    # so we never pick a mis-typed or stale `image_grid_hws` field from kwargs.
    attrs = _mm_grid_attrs[modality]
    if (model_type or "").lower() in ["kimi_k25", "kimi_vl"] and modality == Modality.IMAGE:
        attrs = ("grid_thws", "image_grid_thw", "image_grid_hws") # 扩展支持 Kimi VL
    for attr in attrs:
        if attr in mm_inputs and mm_inputs[attr] is not None:
            return mm_inputs[attr]
    raise ValueError(f"Grid dim ({_mm_grid_attrs[modality]}) not found in {mm_inputs}")def get_num_tokens(self, grid: Union[torch.Tensor, List[int]], modality: Modality) -> int:
    """Calculate number of tokens (after 2x2 merge). Used for mm_embedding slicing."""
    if modality == Modality.AUDIO:
        input_length = self.get_num_patches(grid, modality)
        return self._get_feat_extract_output_lengths(input_length)
    else:
        if self.model_type in ["kimi_k25", "kimi_vl"] and modality == Modality.IMAGE:
            return self._kimi_tokens_from_patch_grid(grid) # 统一处理 Kimi 家族
        merge_size = getattr(self.image_processor, "merge_size", 2)
        return self.get_num_patches(grid, modality) // (merge_size**2)@staticmethod
def _flatten_nested_items(items):
    """递归展平嵌套列表,用于处理分组路由中的多模态项目。"""
    if not isinstance(items, (list, tuple)):
        return [items]
​
    flat = []
    for item in items:
        if isinstance(item, (list, tuple)):
            flat.extend(MMEncoder._flatten_nested_items(item))
        else:
            flat.append(item)
    return flat

评论区精华

Review 中,ZhengWG 指出新增的方法 _num_image_tokens_from_grid_build_kimi_mm_data_from_grids 最初放在 BaseMultimodalProcessor 中,这会使基类变得 Kimi 特定,建议提取到共享工具或 Mixin 中。LHXuuu 回应已经创建了 KimiGridMMDataMixin 并让相关处理器继承,从而保持基类的通用性。结论是重构解决了设计问题,其他评论关注 lint 修复和 CI 运行,确保代码质量。

  • 代码结构优化 (design): LHXuuu 创建了 KimiGridMMDataMixin 并让 KimiVLImageProcessor 和 KimiK2_5VLImageProcessor 继承,从而提取共享逻辑,解决了设计问题。

风险与影响

  • 风险:技术风险包括:1) 回归风险:对现有 Kimi K2.5 EPD 功能的潜在影响,改动可能引入错误,需通过测试验证。2) 缺少测试覆盖:本次 PR 未包含测试文件变更,新功能可能未经充分验证,增加生产环境风险。3) 兼容性问题:PR body 提到运行 Kimi VL 还需要 #22442,存在依赖风险,若不协同部署可能导致功能不完整。4) 代码复杂度:新增 Mixin 和多个修改点可能增加维护负担,尤其是对不熟悉 EPD 管道的开发人员。
  • 影响:对用户的影响:Kimi VL 用户现在可以利用 EPD 管道进行分解式服务,允许视觉编码器和语言模型在独立服务器上运行,提升部署灵活性和资源利用率。对系统的影响:扩展了多模态模型支持范围,增加了新的代码路径,可能需要额外监控和性能调优。对团队的影响:开发人员需要了解新的 Mixin 结构和 EPD 逻辑,但重构提高了代码重用性,长期看有助于降低维护成本。
  • 风险标记:核心路径变更, 缺少测试覆盖, 依赖外部PR

关联脉络

  • PR #22442 未知(PR body 提及): PR body 提到运行 Kimi VL 还需要此 PR 以支持 FlashMLA 或多图像输入,存在功能依赖。

参与讨论