Prhub

#53699 [Bugfix] Fix Qwen3-VL and Cosmos3-Edge text architectures for CPU and pipeline parallelism

原始 PR 作者 labAxiaoming 合并时间 2026-09-11 20:58 文件变更 1 提交数 1 评论 4 代码增减 +3 / -1

执行摘要

Cosmos3-Edge 内层语言模型显式传入文本架构

目标是让 Cosmos3-Edge 能在 vLLM 里真正完成引擎初始化。顶层多模态 checkpoint 配置带有合法 architectures,但嵌套的 text_config(model_type 为 cosmos3_edge_text)既没有 architectures,也不在 Transformers 的 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 中,with_hf_config(text_config) 无法推断内层 causal-LM 架构,重建出的 ModelConfig.architectures 变成空列表。PR body 给出两条由此触发的失败链:CPU 初始化时 CpuPlatform.check_and_update_config 读取 model_config.use_mla,最终在 ModelConfig._get_transformers_backend_cls -> self.architectures[0] 上抛 IndexError;PP > 1 时 ModelConfig.verify_with_parallel_config -> ModelRegistry.is_pp_supported_model([]) -> ModelRegistry.inspect_model_cls([]) 抛 ValueError: No model architectures are specified。关联的 #43271 / #43272 描述的 Qwen3-VL 家族在 --pipeline-parallel-size > 1 下崩溃是同一根因,PR body 特别指出 CUDA PP=1 之所以“看起来正常”,只是因为它绕开了这两条校验路径,内层配置其实一直是坏的。

值得精读,但不是因为代码量:改动只有 3 行,真正有信息量的是 PR body 里的根因表与两条失败链(CPU 的 use_mla -> self.architectures[0] IndexError、PP 的 is_pp_supported_model -> inspect_model_cls ValueError)。建议关注三个设计点:一是把修复放在模型实现侧而不是 ModelConfig._get_transformers_backend_cls() 加通用兜底的取舍论证;二是显式声明 NemotronHForCausalLM 与内层实际类 Cosmos3EdgeForCausalLM 的对应关系;三是内层 hf_config 从顶层多模态配置切换到 text_config 带来的字段来源变化。此外,hmellor 对 MODEL_MAPPING_NAMES 与 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 的区分是理解 vLLM 配置推断边界的好材料。后续建议补一个断言内层 architectures 非空的单元测试或小模型 CPU 初始化用例,把这层契约固化下来。

讨论亮点

本 PR 的讨论量很小(4 条 issue 评论、0 条行内 review 评论、2 条 review)。最有价值的一次交锋是 maintainer hmellor 对 PR 描述的纠正:他强调 cosmos3_edge_text、qwen3_vl_text 这类 model_type 并不是“缺失映射”,它们只是不是 causal LM,因此不在 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 里,但都存在于 MODEL_MAPPING_NAMES——这直接限定了 with_hf_config 推断失败的真正原因,避免把根因误判成 Transformers 覆盖不全。第二个决策点是范围拆分:hmellor 表示会把原始的 Qwen3-VL 修复合入、Cosmos3-Edge 在本 PR 单独处理,这使得本 PR 从三模型修复缩水成单文件改动,也让标题与最终 diff 出现范围偏差。第三点是对 CI 失败的定性:作者指出失败的 test_dummy_maverick 用例与本 PR 无关,并引用 #56423 的同款失败,maintainer 随后直接 APPROVED 且未写评论,说明该定性被接受。claude[bot] 的 review 只是声明 fork PR 不跑自动评审,没有实质内容。

实现拆解

  1. 唯一的变更入口:改动落在 vllm/model_executor/models/cosmos3_edge.py 的 Cosmos3EdgeForConditionalGeneration.__init__(+3/-1)。原 PR 同时包含 qwen3_vl.py 与 qwen3_vl_moe.py 的同类修改,maintainer hmellor 在讨论中决定“先合并原始的 Qwen3 VL 修复,Cosmos3 在这里单独做”,最终范围的收缩使本 PR 变成一个单文件、单行语义的配置契约修复。

  2. 内层语言模型的配置契约改造:构造 Cosmos3EdgeForCausalLM 时,参数由 vllm_config=vllm_config 改为 vllm_config=vllm_config.with_hf_config(config.text_config, architectures=["NemotronHForCausalLM"])。这一步同时完成两件事:一是把内层 LM 的 hf_config 从顶层多模态配置切换到嵌套的 text_config;二是显式声明架构名,避免 with_hf_config 依赖 Transformers 的 causal-LM 映射去反推 cosmos3_edge_text 这种自定义 model_type。

  3. 修复两条初始化崩溃路径:架构列表非空后,CPU 侧 CpuPlatform.check_and_update_config -> model_config.use_mla -> using_transformers_backend -> _get_transformers_backend_cls 不再越界访问 self.architectures[0];PP > 1 侧 ModelConfig.verify_with_parallel_config -> ModelRegistry.is_pp_supported_model / inspect_model_cls 也能拿到可解析的模型类,不再抛 “No model architectures are specified”。同一份内层 ModelConfig 还会被其他 model-registry 消费方读取,所以修复的是整条配置重建链,而非某一个调用点。

  4. 为什么选模型侧修复而不是通用兜底:PR body 明确论证“只有模型实现才能可靠识别自定义嵌套配置对应的 vLLM 架构”,在 ModelConfig._get_transformers_backend_cls() 里加空列表保护只能盖住 CPU 的那个 IndexError,无法为 PP 能力校验与其他 registry 消费方提供架构名。已有的同类先例是 qwen3_omni_moe_thinker.py 传 ["Qwen3MoeForCausalLM"]、qwen3_asr.py 传 ["Qwen3ForCausalLM"]。

  5. 测试与配套改动缺位:本 PR 没有新增或修改任何测试文件、配置或文档。验证完全依赖作者在本地 checkpoint 上的人工初始化:CPU PP=1、CPU PP=2 配置校验、CUDA PP=1(作为回归检查)、CUDA PP=2(另一台 CUDA 机器)全部通过;PR body 还审计了 Step3-VL-10B 与 MOSS-Transcribe-Diarize,确认它们不受影响。CI 上出现的 test_dummy_maverick 失败被作者判定为与本改动无关(另一 PR #56423 同样失败)。

文件 模块 状态 重要度
vllm/model_executor/models/cosmos3_edge.py 多模态模型 modified 5.68

关键符号

Cosmos3EdgeForConditionalGeneration.__init__ VllmConfig.with_hf_config ModelConfig._get_transformers_backend_cls ModelRegistry.inspect_model_cls

关键源码片段

vllm/model_executor/models/cosmos3_edge.py data-contract

本 PR 唯一改动文件。在 Cosmos3EdgeForConditionalGeneration.__init__ 中把内层语言模型的 vllm_config 从顶层多模态配置改为带显式 architectures 的 text_config,直接决定 CPU 与 PP > 1 两条初始化路径能否走通。

# vllm/model_executor/models/cosmos3_edge.py(改动后的核心实现,已重新整理)
class Cosmos3EdgeForConditionalGeneration(nn.Module, SupportsMultiModal):
    def __init__(self, *, vllm_config: VllmConfig, prefix: str = "model"):
        super().__init__()
​
        # 顶层多模态配置:vision_config / projector_config 与嵌套的 text_config 都挂在这里
        config = vllm_config.model_config.hf_config
        quant_config = vllm_config.quant_config
        multimodal_config = vllm_config.model_config.multimodal_config
        assert multimodal_config is not None
​
        self.config = config
        self.multimodal_config = multimodal_config
        self.use_data_parallel = multimodal_config.mm_encoder_tp_mode == "data"
​
        # 视觉塔只吃 vision / projector 子配置,与内层语言模型的配置保持解耦
        with self._mark_tower_model(vllm_config, {"image", "video"}):
            self.visual = Cosmos3EdgeVisionModel(
                vision_config=config.vision_config,
                projector_config=config.projector_config,
                quant_config=quant_config,
                prefix=maybe_prefix(prefix, "visual"),
            )
​
        with self._mark_language_model(vllm_config):
            # 本次唯一改动:内层语言模型显式使用 text_config + architectures
            # 1) text_config 的 model_type 是自定义的 cosmos3_edge_text,
            # 不在 Transformers 的 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 中,
            # with_hf_config 无法反推架构,内层 ModelConfig.architectures 会是 [];
            # 2) 空 architectures 会让 CPU 路径的 use_mla -> _get_transformers_backend_cls
            # 访问 self.architectures[0] 时抛 IndexError,
            # 也会让 PP > 1 的 is_pp_supported_model / inspect_model_cls 抛
            # ValueError: No model architectures are specified;
            # 3) 显式声明 NemotronHForCausalLM(Cosmos3-Edge 文本骨干架构)后,
            # 内层 ModelConfig 才能被 PP 校验、registry 查询和 backend 解析正常消费。
            self.language_model = Cosmos3EdgeForCausalLM(
                vllm_config=vllm_config.with_hf_config(
                    config.text_config, architectures=["NemotronHForCausalLM"]
                ),
                prefix=maybe_prefix(prefix, "language_model"),
            )
​
        # 语言模型的中间张量构造入口上抛,供 PP / capture 相关逻辑复用
        self.make_empty_intermediate_tensors = (
            self.language_model.make_empty_intermediate_tensors
        )

评论区精华

PR 描述中关于 model_type 缺失映射的表述被 maintainer 纠正 documentation

hmellor 直接指出描述有误:“The explanation in this PR's description is incorrect though. The `model_types` you claim are missing are not in the causal LM mapping because they are not causal LMs. They are all present in `MODEL_MAPPING_NAMES`.”

结论:根因表述收敛为:cosmos3_edge_text 等自定义 model_type 不在 Transformers 的 MODEL_FOR_CAUSAL_LM_MAPPING_NAMES 中,因此 with_hf_config 无法推断 causal-LM 架构;并不是这些 model_type 完全缺失映射。 · 已解决

修复范围拆分:Qwen3-VL 归入 #43272,Cosmos3-Edge 留在本 PR 设计

hmellor 表示:“I'm going to merge the original for Qwen3 VL and we could do Cosmos3 separately here.”本 PR 原有的 qwen3_vl.py / qwen3_vl_moe.py 改动因此被移出。

结论:最终 diff 收缩为 cosmos3_edge.py 单文件 +3/-1,但标题仍保留 Qwen3-VL 字样,范围与描述出现偏差。 · 已解决

CI 失败 test_dummy_maverick 是否由本改动引入 测试

作者回应:“The failing test is `test_dummy_maverick[2-True-False-meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8-4-4-2]`, while this PR only touches Cosmos3-Edge-related code. https://github.com/vllm-project/vllm/pull/56423 report the same fail”。

结论:判定为与本 PR 无关的既有失败,未阻塞合并;maintainer 随后 APPROVED 且未附加评论。 · 已解决

模型侧修复 vs 在 ModelConfig 中加通用空列表兜底 设计

PR body 的 “Why This Is a Model-Side Fix” 小节论证:“A generic empty-list guard in `ModelConfig._get_transformers_backend_cls()` would avoid one CPU `IndexError`, but it would not provide the architecture required by PP capability validation or other model-registry consumers.”

结论:选择在模型实现侧显式传 architectures,因为只有模型知道自定义嵌套配置对应的 vLLM 架构,且 PP 校验与 model registry 同样依赖架构名;这也与 qwen3_omni_moe_thinker.py、qwen3_asr.py 的既有约定一致。 · 已解决

风险与影响

  1. 无自动化回归保护:改动没有配套测试,内层 ModelConfig.architectures 必须非空这一契约没有被任何用例锁住。PR body 自己承认 CUDA PP=1 在修复前也能通过,说明默认路径不会暴露问题,未来 Cosmos3-Edge 的 text_config 或 with_hf_config 行为变化时,同类崩溃仍只能在 CPU 或 PP > 1 场景下被人肉发现。

  2. 声明架构与实际类名不一致:内层实例化的是 Cosmos3EdgeForCausalLM,但 architectures 里写的是 NemotronHForCausalLM。凡是按 architectures 做分支的消费者(PP 支持性校验、registry 查询、transformers backend 解析,乃至 hybrid/Mamba 相关配置推断)都会按 NemotronH 的语义处理,需要确认这与 Cosmos3EdgeForCausalLM 的真实继承与缓存行为一致,否则可能引入难以定位的配置偏差。

  3. 配置来源切换的副作用:改动前 Cosmos3-Edge 直接把顶层多模态 vllm_config 传给内层 LM,改动后传给内层的是嵌套 text_config。除 architectures 之外,内层 LM 看到的 vocab_size、hidden_size 等字段来源也随之变化;若 Cosmos3EdgeForCausalLM 或其父类曾隐式依赖顶层多模态配置里的字段,行为会出现差异。PR 的验证只覆盖初始化成功,没有提供修复前后的输出质量对比。

  4. 范围与描述不一致带来的评审风险:标题仍写着 Qwen3-VL,但实际 diff 只碰 Cosmos3-Edge,后续检索历史(bisect、回溯回归)时容易误判影响面。

  5. CI 可信度:合并前存在 test_dummy_maverick 失败,虽然被判定为无关,但本 PR 自身没有对应的正向 CI 覆盖,等于把“修复生效”完全交给了作者的人工验证结论。

对用户而言,Cosmos3-Edge 在 CPU(含 pipeline_parallel_size=1)与 PP > 1 的多机部署场景此前会在引擎初始化阶段直接崩溃,修复后这些配置可用;Qwen3-VL / Qwen3-VL-MoE 的同类用户已在 #43272 中获得同等修复。对系统而言,改动位于模型构造边界,只影响该模型族的 ModelConfig 重建结果,不触及调度器、KV cache、注意力后端或内核路径,单卡 CUDA 推理基本无回退风险。对团队而言,本次修复把“嵌套 text_config 必须显式声明 architectures”这一约定再次固化(qwen3_omni_moe_thinker.py、qwen3_asr.py 已有先例),可以直接作为新增多模态模型或接入新 checkpoint 时的检查项;考虑到仓库近期多模态与模型接入类 PR 密集,这项约定的复用价值高于这次改动本身的代码量。

缺少自动化测试 嵌套 text_config 契约变更 声明架构与实际类可能不一致 描述范围与最终 diff 不一致

关联 Issue

#43272 [Bugfix] Qwen3-VL(-MoE): pass architectures to with_hf_config for pipeline parallelism

完整报告

参与讨论