Prhub

#47741 [Rust Frontend] Add Seed-OSS tool parser

原始 PR 作者 ricky-chaoju 合并时间 2026-07-16 17:28 文件变更 7 提交数 5 评论 3 代码增减 +332 / -18

执行摘要

Rust 前端新增 Seed-OSS tool parser

Seed-OSS 模型的 tool-call 格式使用 <seed:tool_call> 标记,与 Qwen3-Coder 的 <tool_call> 不同,但内部结构完全相同。为避免重复实现,需要一种可配置的解析器工厂来支持不同标记,同时保持与 Python 前端一致的行为。PR body 明确指出:"I didn't add a new parser. I made the wrapper markers configurable on Qwen3CoderToolParser and added a thin SeedOssToolParser that points at it with the seed markers."

该 PR 设计清晰,薄包装模式值得参考。若需支持 Seed-OSS 的 guided decoding,需在后续 PR 中完善。建议阅读 seed_oss.rs 和 qwen_coder.rs 以理解配置复用模式。

讨论亮点

审阅中无实质性讨论。BugenZhao 直接批准。作者在评论中说明 CI 强制要求更新解析器名称断言,已通过第二笔提交修复。

实现拆解

  1. 参数化 Qwen3CoderToolParser:在 rust/src/parser/src/tool/qwen_coder.rs 中新增 QwenCoderConfig 结构体(包含 parser_name, tool_call_start, tool_call_end),将原有的硬编码常量作为默认配置。新增 with_config 构造函数,允许指定自定义包装标记。修改 parse_next_qwen_coder_eventfinish 中的检查逻辑,以使用配置中的标记。
  2. 实现 SeedOssToolParser:新建 rust/src/parser/src/tool/seed_oss.rs,定义 SeedOssToolParser 结构体,内部持有 Qwen3CoderToolParser 实例,通过 with_config 传入 SEED_OSS_CONFIG<seed:tool_call>/</seed:tool_call>)。所有 ToolParser trait 方法均委托给内部解析器。
  3. 注册解析器:在 rust/src/chat/src/parser/tool/mod.rs 中导入 SeedOssToolParser,添加 names::SEED_OSS 常量,注册解析器及其模型匹配模式 "seed-oss""seedoss",以便自动路由。
  4. 更新测试与快照:在 rust/src/chat/src/parser/tool/tests.rs 中添加模式匹配测试;在 rust/src/chat/src/lib.rsexpect_test 快照中补充 seed_oss 解析器名称;在 rust/src/chat/tests/roundtrip.rs 中同步更新。同时在 seed_oss.rs 内部添加单元测试(纯文本、普通工具标记拦截、完整工具提取、streaming 场景)。
文件 模块 状态 重要度
rust/src/parser/src/tool/seed_oss.rs 解析器 added 8.89
rust/src/parser/src/tool/qwen_coder.rs 解析器 modified 7.57
rust/src/chat/src/parser/tool/mod.rs 前端 modified 5.57
rust/src/chat/src/parser/tool/tests.rs 前端 modified 4.9
rust/src/chat/src/lib.rs 前端 modified 4.49
rust/src/parser/src/tool/mod.rs 解析器 modified 4.32
rust/src/chat/tests/roundtrip.rs 测试 modified 3.42

关键符号

SeedOssToolParser::new SeedOssToolParser::create SeedOssToolParser::parse_into SeedOssToolParser::finish Qwen3CoderToolParser::with_config Qwen3CoderToolParser::parse_into Qwen3CoderToolParser::finish

关键源码片段

rust/src/parser/src/tool/seed_oss.rs core-logic

新增核心文件,定义 SeedOssToolParser 结构体,通过配置复用 Qwen3CoderToolParser,并包含单元测试。

// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright contributors to the vLLM projectuse super::qwen_coder::{Qwen3CoderToolParser, QwenCoderConfig};
use crate::tool::{Result, Tool, ToolParser, ToolParserOutput};// 定义 Seed-OSS 专用的配置,使用 <seed:tool_call> 作为包装标记
const SEED_OSS_CONFIG: QwenCoderConfig = QwenCoderConfig {
    parser_name: "Seed-OSS",
    tool_call_start: "<seed:tool_call>",
    tool_call_end: "</seed:tool_call>",
};/// Seed-OSS 的 tool parser,内部分配给 Qwen3CoderToolParser
/// 内部 <function=...> / <parameter=...> 与 Qwen3-Coder 完全相同
pub struct SeedOssToolParser {
    inner: Qwen3CoderToolParser,
}impl SeedOssToolParser {
    // 使用 SEED_OSS_CONFIG 创建内部解析器
    fn new(tools: &[Tool]) -> Self {
        Self {
            inner: Qwen3CoderToolParser::with_config(tools, SEED_OSS_CONFIG),
        }
    }
}impl ToolParser for SeedOssToolParser {
    fn create(tools: &[Tool]) -> Result<Box<dyn ToolParser>> {
        Ok(Box::new(Self::new(tools)))
    }    // 所有解析方法直接委托给 inner
    fn parse_into(&mut self, chunk: &str, output: &mut ToolParserOutput) -> Result<()> {
        self.inner.parse_into(chunk, output)
    }    fn finish(&mut self) -> Result<ToolParserOutput> {
        self.inner.finish()
    }    fn reset(&mut self) -> String {
        self.inner.reset()
    }
}
rust/src/parser/src/tool/qwen_coder.rs core-logic

核心修改文件,引入 QwenCoderConfig 配置结构体,使工具调用标记可参数化,影响所有使用 Qwen 格式的解析器。

/// 模型专用配置,用于共享的 Qwen Coder 语法
/// 不同模型仅包装标记不同,内部 <function=> / <parameter=> 标签总是相同
#[derive(Debug, Clone, Copy)]
pub(crate) struct QwenCoderConfig {
    pub(crate) parser_name: &'static str,
    pub(crate) tool_call_start: &'static str,
    pub(crate) tool_call_end: &'static str,
}// Qwen 自身的默认配置
const QWEN_CODER_CONFIG: QwenCoderConfig = QwenCoderConfig {
    parser_name: "Qwen Coder",
    tool_call_start: TOOL_CALL_START, // "<tool_call>"
    tool_call_end: TOOL_CALL_END, // "</tool_call>"
};impl Qwen3CoderToolParser {
    // 对外公开的构造方法,接收任意配置
    pub(crate) fn with_config(tools: &[Tool], config: QwenCoderConfig) -> Self {
        Self {
            buffer: String::new(),
            mode: QwenCoderMode::Text,
            emitted_tool_count: 0,
            tool_parameters: ToolSchemas::from_tools(tools),
            config,
        }
    }
}
rust/src/chat/src/parser/tool/mod.rs configuration

注册 SeedOssToolParser 及其模型名称模式,使自动路由生效。

// 在 pub use 中添加 SeedOssToolParser
pub use vllm_parser::tool::{
    // ...
    Qwen3CoderToolParser,
    Qwen3XmlToolParser,
    SeedOssToolParser,
    ToolParser,
    ToolParserError,
};// 在 names 模块中添加常量
pub const SEED_OSS: &str = "seed_oss";// 在工厂注册中添加解析器和模式
factory
    .register_parser::<SeedOssToolParser>(names::SEED_OSS)
    .register_pattern("seed-oss", names::SEED_OSS)
    .register_pattern("seedoss", names::SEED_OSS);

评论区精华

CI 预提交强制更新解析器名称断言 测试

作者添加 seed_oss 后 CI 断言失败,需要更新 expect_test 快照。

结论:作者提交了第二笔提交更新快照,CI 通过。 · 已解决

风险与影响

风险较低。主要风险在于:QwenCoderConfig 的 tool_call_start 和 tool_call_end 可能被误用导致解析失败;Seed-OSS 的 guided decoding 未支持(structural_tag_model 为 None),与 Python 一致但可能影响部分用户;模型名称模式 'seed-oss' 和 'seedoss' 可能与其他模型冲突(可能性低)。

对用户而言,Seed-OSS 模型在 Rust 前端中可实现 tool-call 功能,与 Python 前端行为一致。对系统而言,新增解析器注册不影响现有行为。对开发团队而言,该模式(薄包装 + 配置复用)可推广到其他类似模型。

配置参数化误用 缺乏 guided decoding 支持 模型模式匹配冲突

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论