Prhub

#23454 [srt] Add Moss-VL Python runtime support

原始 PR 作者 zsj555 合并时间 2026-04-24 11:14 文件变更 10 提交数 4 评论 16 代码增减 +2401 / -6

执行摘要

新增 Moss-VL 多模态模型 SRT 运行时支持

支持 Moss-VL 模型在 SGLang 中运行,该模型使用交叉注意力融合视觉和文本特征,需要特定的预处理、调度和注意力后端支持。PR body 明确列出需要新增模型、处理器、对话模板、调度字段以及交叉注意力掩码支持。

该 PR 值得精读,特别是交叉注意力自定义掩码的实现、prepare_forward_batch 钩子模式、以及如何在现有调度框架中扩展多模态模型。建议关注性能优化的后续工作(如向量化位置插值和分隔符插入)。

讨论亮点

mickqian: "should we restrict the cross-attn backend for this model to flash_infer only?"
zsj555: Yes,已通过 server_args.py 强制 prefill_attention_backend == "flashinfer",解码复用缓存不需要自定义掩码。

mickqian: "add a one-line document explaining what kind of preparation is needed"
zsj555: 已在 model_runner.py 中添加注释,说明该钩子用于准备模型特定的注意力元数据。

mickqian: "have we looked at general_mm_embed_routine yet?"
zsj555: Moss-VL 需要 encoder-decoder 路径而非输入嵌入替换,因此不使用通用嵌入例行程序。

mickqian: "please make sure the modeling part is compatible with radix cache"
zsj555: 已测试,Moss-VL 通过 encoder_lens/encoder_out_cache_loc 缓存 encoder KV,解码时 encoder_cached=True 避免重复计算。

gemini-code-assist[bot]: 提出 fast_pos_embed_interpolate_insert_separator_tokens 的性能优化建议(向量化操作),以及 view(-1) 可能因非连续张量崩溃的问题。
zsj555: 在后续提交中未直接解决,但通过 commit dc5b578 添加了 release_features 逻辑以减少长期解码的内存压力。

实现拆解

  1. 模型定义python/sglang/srt/models/moss_vl.py):新增 MossVLForConditionalGeneration 类,包含视觉编码器(MossVLVisionModel 及其子模块:PatchEmbed、VisionBlock、PatchMerger 等)和语言模型部分,采用交叉注意力架构。视觉输出通过 encoder_lens 机制作为 encoder KV 缓存,复用现有的 RadixAttention 进行预填充和解码。关键函数 forward_extend 中处理自定义交叉注意力掩码。

  2. 多模态处理器python/sglang/srt/multimodal/processors/moss_vl.py):新增 MossVLImageProcessor,继承自 BaseMultimodalProcessor,实现图像处理、视觉 token 信息构建、位置 ID 计算等。处理 grid_thw 和帧级可见性元数据,生成 MultimodalInputs 所需的字段。

  3. 调度层数据模型扩展python/sglang/srt/managers/schedule_batch.py):在 MultimodalProcessorOutputMultimodalInputs 中添加 Moss-VL 专用字段:vision_position_idsmedia_nums_per_samplevisible_frame_counts,并在 from_processor_output 中传播这些字段。

  4. 对话模板与模型匹配python/sglang/srt/parser/conversation.py):注册 moss-vl 对话模板,添加 match_moss_vl 匹配函数,将模型路径或类型映射到模板。同时调整 image_token 处理逻辑,使 Moss-VL 不附加换行符。

  5. 注意力后端集成python/sglang/srt/layers/attention/flashinfer_backend.pypython/sglang/srt/server_args.py):在 FlashInfer prefill 初始化元数据时传入 cross_attention_custom_mask;在 server_args.py 中为 MossVLForConditionalGeneration 强制要求 prefill_attention_backend == "flashinfer",以确保自定义掩码支持。

  6. 模型运行前钩子python/sglang/srt/model_executor/model_runner.py):在 forward_extend 中增加对 self.model.prepare_forward_batch 的调用,使模型能在注意力后端初始化前准备特定元数据(如交叉注意力掩码)。

  7. 配置注册python/sglang/srt/configs/model_config.py):添加 MossVLForConditionalGeneration 架构映射。

  8. ForwardBatch信息扩展python/sglang/srt/model_executor/forward_batch_info.py):增加 cross_attention_custom_mask 字段。

  9. Tokenizer管理调整python/sglang/srt/managers/tokenizer_manager.py):扩展多模态处理器触发条件,即使请求不包含多媒体输入,当模型架构为 Moss-VL 时也运行多模态处理器(因为 Moss-VL 始终需要处理器进行位置计算)。

文件 模块 状态 重要度
python/sglang/srt/models/moss_vl.py 模型层 added 9.36
python/sglang/srt/multimodal/processors/moss_vl.py 多模态处理 added 9.08
python/sglang/srt/parser/conversation.py 对话模板 modified 6.97
python/sglang/srt/managers/schedule_batch.py 调度层 modified 6.56
python/sglang/srt/server_args.py 服务器配置 modified 5.59
python/sglang/srt/layers/attention/flashinfer_backend.py 注意力层 modified 5.57
python/sglang/srt/model_executor/model_runner.py 模型执行器 modified 5.48
python/sglang/srt/managers/tokenizer_manager.py Tokenizer 管理 modified 5.22
python/sglang/srt/configs/model_config.py 模型配置 modified 4.23
python/sglang/srt/model_executor/forward_batch_info.py 前向批信息 modified 4.16

关键符号

MossVLForConditionalGeneration MossVLImageProcessor._build_mm_items MossVLImageProcessor._build_vision_token_info match_moss_vl prepare_forward_batch init_forward_metadata (flashinfer_backend)

关键源码片段

python/sglang/srt/models/moss_vl.py data-contract

核心模型实现,包含视觉编码器、语言模型、交叉注意力路径,影响整个推理流程。

"""Moss-VL 模型 - 视觉 + 文本交叉注意力,基于 Qwen2.5-VL 架构。"""
class MossVLVisionMLP(nn.Module):
    # 使用 ColumnParallelLinear / RowParallelLinear 支持张量并行
    def __init__(self, in_features, hidden_features, bias=True, hidden_act="silu", quant_config=None, prefix=""):
        super().__init__()
        self.linear_fc1 = ColumnParallelLinear(in_features, hidden_features, bias=bias, quant_config=quant_config, prefix=add_prefix("linear_fc1", prefix))
        self.linear_fc2 = RowParallelLinear(hidden_features, in_features, bias=bias, quant_config=quant_config, prefix=add_prefix("linear_fc2", prefix))
        self.act = ACT2FN[hidden_act]
​
    def forward(self, x):
        x_fc1, _ = self.linear_fc1(x)
        return self.linear_fc2(self.act(x_fc1))[0]class MossVLVisionPatchEmbed(nn.Module):
    # 3D 卷积将时空 patch 映射到嵌入
    def __init__(self, config):
        super().__init__()
        kernel_size = [config.temporal_patch_size, config.patch_size, config.patch_size]
        self.proj = nn.Conv3d(config.in_channels, config.hidden_size, kernel_size=kernel_size, stride=kernel_size, bias=True)
​
    def forward(self, hidden_states):
        # 输入形状 : (B, C, T, H, W) -> 投影后展平为 (B, hidden_size)
        return self.proj(hidden_states.view(-1, self.in_channels, self.temporal_patch_size, self.patch_size, self.patch_size)).view(-1, self.embed_dim)
python/sglang/srt/multimodal/processors/moss_vl.py dependency-wiring

多模态处理器,负责图像预处理、视觉 token 布局、位置 ID 计算等,是输入处理的关键部分。

class MossVLImageProcessor(SGLangBaseProcessor):
    models = [MossVLForConditionalGeneration]
​
    def _build_mm_items(self, processor_output, input_ids):
        pixel_values = processor_output.get("pixel_values")
        if pixel_values is None:
            return []
        item = MultimodalDataItem(modality=Modality.IMAGE, feature=pixel_values, model_specific_data={})
        if (grid_thw := processor_output.get("grid_thw")) is not None:
            item.set("grid_thw", grid_thw)
        return [item]
​
    def _build_vision_token_info(self, grid_thw, media_nums_per_sample):
        # 计算每张图片 / 每帧的 token 数及分隔符布局
        tokens_per_media = (grid_thw[:, 0] * grid_thw[:, 1] * grid_thw[:, 2]) // (self.spatial_merge_size ** 2)
        # 逐样本构建 media 元数据,包括 start/end 位置、帧数等
        ...

评论区精华

交叉注意力后端限制 设计

mickqian 询问是否应将 Moss-VL 的交叉注意力后端限制为 flashinfer。zsj555 确认仅 prefill 需要 flashinfer 的自定义掩码支持,解码由缓存 KV 覆盖,并在 server_args 中强制设置了 prefill 后端。

结论:决定强制使用 flashinfer 作为 prefill 注意力后端,解码无限制。 · 已解决

非连续张量风险 正确性

gemini-code-assist[bot] 指出 `view(-1)` 在非连续张量上会失败,建议使用 `reshape(-1)`。

结论:未在后续提交中明确修复,但 zsj555 通过其他方式如 `release_features` 进行优化。风险依然存在。 · unresolved

Radix Cache 兼容性 正确性

mickqian 要求确认模型与 radix cache 兼容。zsj555 回复已测试,通过 encoder_lens 机制缓存 encoder KV,解码复用缓存。

结论:确认兼容。 · 已解决

Prepare 钩子文档 documentation

mickqian 要求为 prepare_forward_batch 添加注释说明其作用。zsj555 已添加。

结论:已添加注释。 · 已解决

性能优化建议 性能

gemini-code-assist 建议将 fast_pos_embed_interpolate 和 _insert_separator_tokens 中的循环替换为向量化操作。

结论:未采纳,但 PR 在 decode 阶段通过 release_features 释放了显存作为部分优化。多个帧场景可能仍有性能问题。 · unresolved

风险与影响

  1. 性能风险fast_pos_embed_interpolate_insert_separator_tokens 当前使用循环实现(Python 循环和 np.linspace),在高分辨率或大批量场景下可能成为性能瓶颈(gemini-code-assist 指出)。
  2. 稳定性风险MultimodalProcessorOutput 中新增字段未提供默认值以外的验证,若处理器输出缺少这些字段可能导致 AttributeError
  3. 兼容性风险:强制要求 flashinfer 作为 prefill 注意力后端,若用户使用其他后端(如 triton)将导致断言失败。
  4. 内存风险:Moss-VL 的视觉编码器输出可能占用大量显存,虽然 PR 在 decode 阶段释放了部分张量(release_features),但 prefill 阶段仍需关注。

影响范围:此 PR 为新增模型支持,不影响现有模型。但修改了通用调度层(schedule_batch.py)和注意力后端(flashinfer_backend.py),引入的新字段可能增加 MultimodalInputs 的内存占用。

影响程度:中等到高。Moss-VL 是一个较复杂的多模态模型,对 SGLang 的调度和注意力机制有定制需求。团队后续维护需要关注 FlashInfer 后端的兼容性以及 mask 传递的正确性。

交叉注意力后端依赖 flashinfer 非连续张量 view 调用风险 性能瓶颈(循环实现) 缺少测试覆盖

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论