Prhub

#28691 Add LFM2.5 embedding model support

原始 PR 作者 vincentzed 合并时间 2026-07-29 19:58 文件变更 2 提交数 7 评论 6 代码增减 +164 / -4

执行摘要

添加 LFM2.5 双向嵌入模型支持

LiquidAI/LFM2.5-Embedding-350M 是一个密集双编码器,其 HuggingFace checkpoint 暴露 Lfm2BidirectionalModel,但 SGLang 之前只支持因果 LFM2 路径。此 PR 添加最小模型支持,使 /v1/embeddings 能够通过现有的 prefill-only 嵌入流程返回模型的 CLS 池化归一化向量。

值得精读,特别是 AttentionType.ENCODER_ONLY 的引入以及 is_generation_model 注册机制,是 SGLang 嵌入模型集成的标准路径。可作为添加类似 bidirectional encoder 模型的模板。

讨论亮点

PR review 仅含一条 approve 评论('LGTM, very clean'),内部讨论集中在应依赖架构自动检测而非 --is-embedding 参数。作者最终选择注册非生成架构列表,避免了用户额外配置。

实现拆解

实现分为以下步骤:

  1. 修改 Lfm2Attention 添加 attn_type 参数,使注意力层可切换为 ENCODER_ONLY(无因果掩码)。
  2. 新增 Lfm2BidirectionalShortConv 类,继承 Lfm2ShortConv,重写 forward 为 same-padding 非因果卷积,支持非等长 batch 的 padding/unpadding 操作。
  3. 修改 Lfm2DecoderLayer,通过 bidirectional 标志控制传递 AttentionType.ENCODER_ONLY 或 DECODER。
  4. 新增 Lfm2BidirectionalModel 类,继承 Lfm2ForCausalLM,重写 get_num_kv_cache_layers 返回 0,添加 Pooler 实现 CLS 池化 + L2 归一化,复用原权重加载逻辑。
  5. 在 model_config.py 的 is_generation_model 中注册 Lfm2BidirectionalModel,使其自动识别为 embedding 模型,无需命令行参数。
文件 模块 状态 重要度
python/sglang/srt/models/lfm2.py 模型层 modified 8.78
python/sglang/srt/configs/model_config.py 配置 modified 3.99

关键符号

Lfm2BidirectionalShortConv.forward Lfm2BidirectionalModel.__init__ Lfm2BidirectionalModel.forward Lfm2BidirectionalModel.get_num_kv_cache_layers Lfm2BidirectionalModel.load_weights

关键源码片段

python/sglang/srt/models/lfm2.py core-logic

核心模型文件,新增 Lfm2BidirectionalModel 类及其组件,是主要变更

# Lfm2BidirectionalShortConv 使用 same padding 的卷积代替 causal 卷积
class Lfm2BidirectionalShortConv(Lfm2ShortConv):
    # 与父类的 causal 卷积不同,此处使用 F.conv1d 的 padding 参数实现非因果效果
    def forward(self, hidden_states, forward_batch):
        if forward_batch.forward_mode.is_idle():
            return hidden_states
​
        proj, _ = self.in_proj(hidden_states)
        B_gate, C_gate, x = proj.chunk(3, dim=-1)
        Bx = B_gate * x
​
        seq_lens = forward_batch.extend_seq_lens_cpu
        if seq_lens is None:
            seq_lens = forward_batch.extend_seq_lens.detach().cpu().tolist()
        max_len = max(seq_lens)
​
        padded = Bx.new_zeros((len(seq_lens), max_len, Bx.shape[-1]))
        offset = 0
        for batch_idx, seq_len in enumerate(seq_lens):
            seq_len = int(seq_len)
            end = offset + seq_len
            padded[batch_idx, :seq_len] = Bx[offset:end]
            offset = end
​
        # 使用 same padding,保证输出长度与输入长度一致(自动截断或填充)
        conv_out = F.conv1d(
            padded.transpose(1, 2),
            weight=self.conv_weight.unsqueeze(1),
            bias=self.conv_bias,
            padding=self.conv_kernel // 2,
            groups=self.hidden_size_per_partition,
        )
        if conv_out.shape[-1] > max_len:
            conv_out = conv_out[..., :max_len]
        elif conv_out.shape[-1] < max_len:
            conv_out = F.pad(conv_out, (0, max_len - conv_out.shape[-1]))
        conv_out = conv_out.transpose(1, 2)
​
        unpadded = Bx.new_empty(Bx.shape)
        offset = 0
        for batch_idx, seq_len in enumerate(seq_lens):
            seq_len = int(seq_len)
            end = offset + seq_len
            unpadded[offset:end] = conv_out[batch_idx, :seq_len]
            offset = end
​
        output, _ = self.out_proj(C_gate * unpadded)
        return output

评论区精华

自动检测模型类型 设计

选择在 is_generation_model 中注册架构而非依赖用户 --is-embedding 参数,简化用户使用。

结论:采用注册方式,合并 PR。 · 已解决

风险与影响

主要风险:修改了共享的 LFM2 模型文件(lfm2.py),可能影响原有因果 LFM2 模型的加载与推理行为。但新增代码均封装在新类或条件分支中,原有路径不受影响。缺少专用单元测试,回归依赖手动验证。另外,非因果短卷积中的 padding 逻辑要求正确计算长度,若扩展序列长度与假设不符可能导致越界。

对用户:可直接使用 LFM2.5 嵌入模型,无需手动指定 --is-embedding,降低使用门槛。对系统:新增架构注册,不影响已有模型。对团队:为后续嵌入模型集成提供了参考模式。影响程度中等偏小。

共享文件修改 缺少单元测试 padding 边界处理

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论