Prhub

#52697 [EPD] Allow KV consumers to omit MM embeddings

原始 PR 作者 zhenwei-intel 合并时间 2026-08-19 10:16 文件变更 16 提交数 3 评论 11 代码增减 +43 / -46

PR 正在重新分析中,请等待完成后再刷新查看。

执行摘要

KV consumer 可省略多模态 embedding,统一 EC/KV 消费端输入契约

PR body 明确说明这是对 #50390 的 follow-up:在 EPD 部署中,decode worker 接收的是 prefetched KV cache,不再需要原始多模态 embedding 张量,因此应当把 KV consumer 与 EC consumer 同等对待,允许它们省略 *_embeds。review 中 gty111 也追问“Why does decode instance require embeddings input?”,进一步确认了 decode 实例只需要 placeholder 元数据来定位 KV 范围,不需要真正计算 embedding。

值得精读,重点学习其“derived, not user-settable”的配置派生模式:派生字段放在 VllmConfig.__post_init__ 中无条件覆盖,避免手工设置与运行时角色不一致;以及 embedding_field_sets 用 required/optional 集合表达输入契约的分流设计。若团队正在做解耦推理或 kv-offload,此 PR 是理解多模态输入如何在消费端“瘦身”的良好范例。

讨论亮点

核心讨论围绕需求合理性与命名设计展开:

  • gty111 提问 “Why does decode instance require embeddings input?”,确认 decode worker 只需 KV cache 与占位符元数据,不需要原始 embedding,这是本 PR 的需求基础。
  • gty111 建议将 mm_embeds_from_ec_connector 重命名为 allow_missing_mm_embeddings,并给出精确布尔表达式 (ec_config is not None and ec_config.is_ec_consumer) or (kv_config is not None and kv_config.is_kv_consumer),作者立即采纳并按此实现。
  • review 中 gty111 指出日志文案不准确:“This log info may be not accurate. KV consumer should also be considered.”,作者回复 “Done, thanks”,最终日志更新为 “EC/KV consumer: pre-computed-embedding inputs may omit the embedding tensor.”。

实现拆解

  1. 重命名配置字段vllm/config/multimodal.py):将 MultiModalConfig.mm_embeds_from_ec_connector 改为 allow_missing_mm_embeddings,并同步更新 docstring,说明 EC consumer 从连接器加载 embedding、KV consumer 接收提示 KV,二者请求都只需 grid/size 元数据;该字段保持“derived, not user-settable”的契约。
  2. 扩展派生逻辑vllm/config/vllm.py):VllmConfig.__post_init__ 中把 _resolve_mm_embeds_from_ec_connector() 重命名为 _resolve_allow_missing_mm_embeddings(),判定条件从 ec_config.is_ec_consumer 扩展为 (ec_config is not None and ec_config.is_ec_consumer) or (kv_config is not None and kv_config.is_kv_consumer),同时把启动日志从 “EC consumer” 改为 “EC/KV consumer”,避免误导。
  3. 打通多模态处理管线vllm/multimodal/processing/context.pyvllm/multimodal/parse.py):BaseProcessingInfo.embeds_from_ec_connector 属性改名为 allow_missing_mm_embeddingsMultiModalDataParser 的构造参数与 embedding_field_sets 的分支条件同步改名。embedding_field_sets 因此在 consumer 端把 *_embeds 值字段从 required 移到 optional,只保留 metadata 字段必填;非 consumer 部署仍保持缺失即快速失败。
  4. 同步全部模型 processor 适配点colqwen3.pycolqwen3_5.pyhunyuan_vision.pykeye.pykeye_vl1_5.pyllava_onevision2.pyminicpmv.pyopencua.pyqwen2_5_omni_thinker.pyqwen2_vl.pyqwen3_vl.py 共 11 个文件的 get_data_parser() 均将 embeds_from_ec_connector=self.embeds_from_ec_connector 改为 allow_missing_mm_embeddings=self.allow_missing_mm_embeddings,保证新契约对所有多模态模型族一致生效。
  5. 测试配套tests/v1/ec_connector/integration/run_epd_correctness_test.sh 增加 3 行,补强 EPD 正确性测试对 KV consumer 省略 embedding 场景的覆盖;上下文显示测试与源码联动但未新增独立单元测试文件,覆盖是否充分需人工确认。
文件 模块 状态 重要度
vllm/config/vllm.py 配置解析 modified 7.28
vllm/config/multimodal.py 模型配置 modified 5.97
vllm/multimodal/processing/context.py 多模态处理 modified 6.45
vllm/multimodal/parse.py 输入解析 modified 5.99
vllm/model_executor/models/qwen3_vl.py 模型适配 modified 5.1
vllm/model_executor/models/colqwen3.py 模型适配 modified 5.1
tests/v1/ec_connector/integration/run_epd_correctness_test.sh 集成测试 modified 3.55

关键符号

_resolve_allow_missing_mm_embeddings allow_missing_mm_embeddings embedding_field_sets get_data_parser

关键源码片段

vllm/config/vllm.py core-logic

核心派生逻辑所在: `VllmConfig.__post_init__` 调用 `_resolve_allow_missing_mm_embeddings`,把允许省略 embedding 的判定从仅 EC consumer 扩展为 EC/KV consumer,并更新日志文案。

def _resolve_allow_missing_mm_embeddings(self) -> None:
    """Allow `*_embeds` tensors to be omitted on disaggregated consumers.    An EC consumer loads embeddings from its connector. A KV consumer
    receives the prompt KV produced from those embeddings, so it does not
    need the tensors either. On every other deployment a missing tensor is
    a client error and must keep failing fast in the frontend.
    """
    model_config = self.model_config
    if model_config is None:
        return
    mm_config = model_config.multimodal_config
    if mm_config is None:
        return
​
    ec_config = self.ec_transfer_config
    kv_config = self.kv_transfer_config
    # 该值是派生出来的(derived, not user-settable),因此无条件覆盖,
    # 不信任任何手工设置的值;EC consumer 与 KV consumer 都允许省略。
    mm_config.allow_missing_mm_embeddings = (
        ec_config is not None and ec_config.is_ec_consumer
    ) or (kv_config is not None and kv_config.is_kv_consumer)
    if mm_config.allow_missing_mm_embeddings:
        logger.info_once(
            "EC/KV consumer: pre-computed-embedding inputs may "
            "omit the embedding tensor."
        )
vllm/multimodal/parse.py core-logic

`MultiModalDataParser.embedding_field_sets` 的分支条件改为 `allow_missing_mm_embeddings`,决定请求中 `*_embeds` 值字段是 required 还是 optional,是输入校验行为的直接控制点。

class MultiModalDataParser:
    def embedding_field_sets(self, modality: str) -> tuple[set[str], set[str]]:
        """返回该 modality 在本次部署中的 (required, optional) 字段集合。        在 consumer 端(EC 或 KV)允许省略 embedding 张量,只保留
        placeholder 元数据字段;其他部署中一旦请求声明携带预计算
        embedding,就必须真正带上张量,否则在 frontend 快速失败。
        """
        metadata = self.placeholder_metadata_fields(modality)
        values = set(self.embedding_fields.get(modality, {})) - metadata
        if self.allow_missing_mm_embeddings:
            return metadata, values # 只要求元数据字段
        return metadata | values, set() # 元数据与 embedding 值都必填
​
    def __init__(
        self,
        *,
        # ... 其他参数省略
        allow_missing_mm_embeddings: bool = False,
    ) -> None:
        super().__init__()
        self.allow_missing_mm_embeddings = allow_missing_mm_embeddings
        # ... 其余初始化逻辑保持不变

评论区精华

decode 实例为什么需要 embedding 输入 question

gty111 在 Issue 评论中提问:“Why does decode instance require embeddings input?”,意在确认 EPD 部署中 decode worker 是否真的需要原始多模态 embedding。

结论:EPD 部署中 decode worker 只消费预取 KV cache,不需要原始 embedding;请求只需携带 grid/size 元数据来定位 placeholder 范围。 · 已解决

重命名 mm_embeds_from_ec_connector 为 allow_missing_mm_embeddings 设计

gty111 建议:“Maybe we should rename `mm_embeds_from_ec_connector` to `allow_missing_mm_embeddings`.”,并给出 `(ec_config is not None and ec_config.is_ec_consumer) or (kv_config is not None and kv_config.is_kv_consumer)` 的布尔表达式。

结论:作者采纳建议,完成重命名并实现该判定表达式。 · 已解决

日志文案忽略 KV consumer 正确性

gty111 在 review 中针对 `vllm/config/vllm.py` 指出:“This log info may be not accurate. KV consumer should also be considered.”,因为当时日志仍写 “EC consumer”。

结论:作者回复 “Done, thanks”,日志更新为 “EC/KV consumer: pre-computed-embedding inputs may omit the embedding tensor.”。 · 已解决

风险与影响

  1. 公共参数重命名兼容性MultiModalDataParser 是公开类,embeds_from_ec_connector 作为构造参数被删除,外部插件或用户代码若直接使用旧参数名将报 TypeError。该参数由 #50390 新引入不久,影响面可控,但仍属 breaking change。
  2. KV consumer 判定依赖配置契约:新逻辑依赖 kv_transfer_config.is_kv_consumer 语义正确。若某部署同时配置了 EC 与 KV 传输,或 is_kv_consumer 判定边界不清,可能误放行缺失 embedding,导致下游在模型内才暴露错误。
  3. 隐藏尺寸校验可能被绕过_get_expected_hidden_size()enable_mm_embeds 时仍会校验 hidden size,但允许缺失张量后,校验是否对所有路径生效、缺失时如何走占位符分支,上下文中未完整展示,存在校验盲区的可能。
  4. 测试覆盖偏弱:仅修改了一个 shell 脚本(+3 行),没有针对 KV consumer 省略 embedding 的新增单元测试(如 parser 层或 config 层),回归风险主要靠 EPD 集成测试兜底。

对用户:EPD/kv-offload 部署中,decode worker 的客户端请求不再需要携带体积较大的多模态 embedding 张量,只需传元数据,可显著降低请求体大小与传输开销;对 Qwen2-VL、Qwen3-VL、Hunyuan、Keye、MiniCPM-V、OpenCUA、LLaVA-OneVision2 等多模态模型族统一了输入契约。对系统:allow_missing_mm_embeddings 成为区分“disaggregated consumer”与“普通部署”的统一开关,后续新增 EC/KV consumer 类型只需在 _resolve_allow_missing_mm_embeddings 中扩展条件。对团队:这是一次跨 config、multimodal、model_executor 三个子系统的命名与语义统一,降低了 EC/KV 两条连接器路径的认知分叉。影响程度中等:改动面广但逻辑集中、性质机械。

公共参数重命名破坏兼容 依赖 kv_config.is_kv_consumer 契约 缺失张量路径校验覆盖不足 缺少新增单元测试

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论