Prhub

#47857 [Model] Add LongCat-Flash-Lite (n-gram embedding)

原始 PR 作者 mgoin 合并时间 2026-07-10 22:17 文件变更 16 提交数 2 评论 6 代码增减 +630 / -12

执行摘要

添加 LongCat-Flash-Lite n-gram 嵌入变体模型

支持 n-gram 嵌入变体,是 LongCat-Flash 系列的扩展。PR body 说明:'Adds LongcatFlashNgramForCausalLM, the n-gram-embedding variant of LongCat-Flash, as a Model-Runner-V2 model. The n-gram input layer's per-request token history is isolated in a ModelState.' 之前尝试 #33611 采用了不同方式,本次通过 ModelState 隔离更干净。

该 PR 值得阅读,特别是 ModelState 隔离的架构模式、CUDA 自定义算子集成方式以及 fix 中的 guard 变量技巧。对于计划集成类似模型或需要理解 MRV2 的开发者有参考价值。

讨论亮点
  • Claude[bot] 指出 LongcatNgramModelState.add_request 中 n-gram 左上下文在 resume 路径(抢占、prefix-cache 命中、KV 转移)下可能使用空/截断数据,导致 CUDA 内核静默错误。该问题未被本次修复,列为已知问题。
  • LucasWilkinsonattn_utils.py 中通过硬编码模型名称判断双注意力模块表示担忧('it would be nice to remove model specific stuff from here'),但接受当前方案。mgoin 回应同意后续改进。
  • LucasWilkinson 赞扬了 ModelState 的使用('nice use of ModelState!')。
  • LucasWilkinson 提出 nit 建议:如果 remove_request 时填充 -1,可以消除 _req_id_to_index 映射。但非必选。

实现拆解

  1. 新增 vllm/model_executor/models/longcat_flash_ngram.py,定义 NgramEmbeddingFlashNgramModel,实现 n-gram 嵌入逻辑,利用 CUDA 内核(ngram_embedding_kernels.cu)计算 n-gram id,ModelState 类隔离每个请求的 token 历史。
  2. vllm/model_executor/models/config.py 中添加 LongcatFlashNgramForCausalLMConfig,设置默认编译模式为 NONE、CUDA Graph 模式为 FULL,避开 torch.compile 和 PIECEWISE 不兼容问题,并将其注册到 MODELS_CONFIG_MAP
  3. vllm/_custom_ops.py 和 CUDA 绑定层(ops.htorch_bindings.cpp)添加 ngram_compute_n_gram_ids 自定义算子,封装 CUDA 内核调用。
  4. vllm/model_executor/models/registry.py 中注册新模型以启用测试。
  5. 修改 vllm/v1/worker/gpu/attn_utils.py,支持双注意力模块的 KV 缓存绑定(通过 model_typelongcat_flashlongcat_flash_ngram 时设置 num_attn_module=2)。
  6. 修复 longcat_flash.pylongcat_flash_mtp.py 中 MLA 缩放的 double-bake bug:添加 _mla_q_lora_scaled 等 guard 变量避免重复缩放。
  7. 修改 longcat_flash_mtp.py 中使用 object.__setattr__ 绕过严格配置验证,并修复初始化填充语法错误。
  8. 修改 vllm/config/speculative.py 中与 MTP 相关的初始化逻辑。
  9. 调整测试工具 tests/models/utils.py 以支持新模型。
文件 模块 状态 重要度
vllm/model_executor/models/longcat_flash_ngram.py 模型实现 added 9.17
vllm/model_executor/models/config.py 模型配置 modified 7.26
vllm/_custom_ops.py 自定义算子 modified 6.51
vllm/model_executor/models/longcat_flash_mtp.py MTP 实现 modified 5.91
vllm/model_executor/models/longcat_flash.py 原始模型 modified 5.85
vllm/v1/worker/gpu/attn_utils.py 注意力工具 modified 5.11
csrc/libtorch_stable/ops.h CUDA 头文件 modified 4.81
csrc/libtorch_stable/ngram_embedding_kernels.cu CUDA 内核 added 5.69
vllm/model_executor/models/registry.py 模型注册 modified 4.49
csrc/libtorch_stable/torch_bindings.cpp PyTorch 绑定 modified 4.86
vllm/config/speculative.py 推测解码 modified 4.09
tests/models/utils.py 测试工具 modified 3.98

关键符号

LongcatFlashNgramForCausalLM NgramEmbedding.__init__ NgramEmbedding._init_ngram_embeddings NgramEmbedding.embed_batched ngram_compute_n_gram_ids LongcatFlashNgramForCausalLMConfig.verify_and_update_config LongcatNgramModelState.add_request

关键源码片段

vllm/model_executor/models/longcat_flash_ngram.py core-logic

新增文件,包含 n-gram 嵌入模型核心实现:NgramEmbedding、FlashNgramModel、LongcatNgramModelState 及 ModelState 集成,是 PR 的主要变更。

# longcat_flash_ngram.py - NgramEmbedding 部分实现class NgramEmbedding(nn.Module):
    """Token embedding fused with hashed n-gram embeddings.    TP-sharded: the k*(n-1) per-embedder tables are concatenated into one
    :class:`VocabParallelEmbedding` (oe_embedder) with per-embedder offsets,
    and the projections are stacked into one oe_projection applied with a
    single bmm. Hashing math is ported from the HF reference.
    """
​
    def __init__(self, config: FlashConfig, base_embeddings: nn.Module) -> None:
        super().__init__()
        self.config = config
        self.word_embeddings = base_embeddings
​
        self.m = config.ngram_vocab_size_ratio * config.vocab_size
        self.k = config.emb_split_num
        self.n = config.emb_neighbor_num
        self.pad_id = config.pad_token_id
        self.eos_token_id = config.eos_token_id
        self._dtype = _config_dtype(config)
​
        self._init_ngram_embeddings()
​
    def _init_ngram_embeddings(self) -> None:
        self.num_embedders = self.k * (self.n - 1)
        oe_dim = self.config.hidden_size // self.num_embedders
        self.oe_dim = oe_dim
​
        # 每个 embedder 的 table 大小 = m + i*2 + 1,偏移量累加
        sizes = [int(self.m + i * 2 + 1) for i in range(self.num_embedders)]
        offsets = [0]
        for s in sizes:
            offsets.append(offsets[-1] + s)
        self._offsets = offsets
        self._sizes = sizes
​
        # All embedder tables concatenated into one VocabParallelEmbedding
        self.oe_embedder = VocabParallelEmbedding(
            offsets[-1], oe_dim, params_dtype=self._dtype
        )
        # Stacked projections
        self.oe_projection = nn.Parameter(
            torch.empty(
                self.num_embedders, oe_dim, self.config.hidden_size, dtype=self._dtype
            ),
            requires_grad=False,
        )
​
        # Precomputed tables for CUDA kernel
        vocab = self.config.vocab_size
        ne_weights = torch.zeros(self.n - 1, self.k, self.n, dtype=torch.int32)
        ne_mods = torch.zeros(self.n - 1, self.k, dtype=torch.int32)
        for i in range(self.n - 1):
            for j in range(self.k):
                mod = int(self.m + 2 * (i * self.k + j) + 1)
                ne_mods[i, j] = mod
                for delta in range(self.n):
                    ne_weights[i, j, delta] = pow(vocab, delta, mod)
        self.register_buffer("ne_weights", ne_weights, persistent=False)
        self.register_buffer("ne_mods", ne_mods, persistent=False)
        self.register_buffer(
            "exclusive_sizes",
            torch.tensor(offsets, dtype=torch.int32),
            persistent=False,
        )
vllm/model_executor/models/config.py data-contract

新增 LongcatFlashNgramForCausalLMConfig 配置类,设置默认编译模式避免不兼容问题,并注册到 MODELS_CONFIG_MAP。同时修复了 LlamaBidirectionalConfig 中 pooling_type 默认值的潜在 bug。

# vllm/model_executor/models/config.py - 新增配置类class LongcatFlashNgramForCausalLMConfig(VerifyAndUpdateConfig):
    @staticmethod
    def verify_and_update_config(vllm_config: "VllmConfig") -> None:
        # LongCat-Flash-Lite 的 zero-expert MoE 在 torch.compile 下触发数据依赖 assert,
        # n-gram inputs_embeds 只适配了 FULL CUDAGraph(PIECEWISE 会中断)。
        # 因此默认关闭编译且使用 FULL CUDAGraph,除非用户明确指定。
        from vllm.config.compilation import CompilationMode, CUDAGraphMode
​
        compilation_config = vllm_config.compilation_config
        if compilation_config.mode is None:
            compilation_config.mode = CompilationMode.NONE
        if compilation_config.cudagraph_mode is None:
            compilation_config.cudagraph_mode = CUDAGraphMode.FULL

评论区精华

Resume 路径 n-gram 上下文安全性 正确性

claude[bot] 指出在 preemption、prefix-cache 命中、KV-connector 转移后,`add_request` 中使用的 `prompt_token_ids` 可能不足,导致 n-gram 上下文为空或截断,CUDA 内核静默读取错误数据。

结论:问题已记录但未修复,作为已知问题留给后续 PR。 · unresolved

attn_utils 中模型特定代码的抽象 设计

LucasWilkinson 评论 'it would be nice to remove model specific stuff from here',指通过硬编码 model_type 判断双注意力模块。mgoin 回应同意但当前遵循已有模式。

结论:接受当前方式,后续考虑改进。 · 已解决

ModelState 实现赞扬 style

LucasWilkinson 称赞了 ModelState 的使用 ('nice use of ModelState!')。

结论:设计得到认可。 · trivial

_req_id_to_index 映射优化 设计

LucasWilkinson 指出如果在 remove_request 时填充 -1 而不是在 add_request 时填充,可以消除 `_req_id_to_index` 映射。

结论:非强制优化,可后续考虑。 · 已解决

风险与影响

  • Resume 路径数据错误:claude[bot] 指出的 n-gram 上下文截断问题未修复,可能导致静默推理错误,影响需要 resume 的场景(如 preemption、KV 转移)。
  • CUDAGraph 兼容性限制:模型强制禁用 torch.compile 并使用 FULL CUDAGraph,无法使用 PIECEWISE 模式,限制了在某些输入长度下的优化收益。
  • Zero-expert MoE Dynamo 不兼容:torch.compile 因数据依赖 assert 失败,可能需要额外工作支持。
  • 模型特定代码侵入attn_utils.py 中硬编码的模型类型判断降低了通用性。
  • 用户:新模型可直接通过 vllm serve 部署,无需手动配置编译或 CUDAGraph 参数。验证支持 GSM8K 5-shot ~84% 正确率,0% 无效输出。
  • 系统:新增约 500 行源码(Python + CUDA),编译依赖 CUDA 工具链。模型注册和配置修改影响全局模型加载路径。
  • 团队:为后续 MRV2 模型集成提供了参考模式,特别是 ModelState 隔离和自定义算子集成。
resume 路径未修复 CUDAGraph 兼容限制 模型特定逻辑侵入 torch.compile 不兼容

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论