Prhub

#43481 [Rust Frontend] Add InternLM2 tool parser

原始 PR 作者 willamhou 合并时间 2026-06-01 16:58 文件变更 10 提交数 2 评论 9 代码增减 +445 / -14

执行摘要

新增 InternLM2 模型的 Rust 工具调用解析器

PR 标题为 [Rust Frontend] Add InternLM2 tool parser ,目的将 Python 中的 InternLM2 工具解析器同步移植到 Rust 端,使 vllm-chat 能够自动识别 InternLM2 系列模型的工具调用格式,实现功能覆盖和对齐。

建议阅读本 PR 以学习如何系统地将 Python 工具解析器移植到 Rust,特别关注共享配置的演进式扩展、有意行为差异的文档化方法,以及如何使用 winnow 组合子实现灵活的 JSON 字段解析。

讨论亮点

Review 中的核心讨论有两个:

  • BugenZhao 询问Is it possible to use a for loop here for any number of candidates? ,针对 json_arguments_key 中硬编码的候选键遍历方式。作者 willamhou 采纳建议,改为循环附加每个候选键的 Expected 上下文,使错误信息可枚举所有合法键。
  • BugenZhao 进一步确认跳过 prelude 文本是否为故意行为:作者解释这是 Python 非流式路径的遗留代码,流式路径无等价处理,且模型格式保证直接输出 JSON ,因此决定不实现,保持与其他 JSON 解析器一致。

实现拆解

  1. 新增 Internlm2ToolParser 结构体:在 rust/src/tool-parser/src/json/internlm2.rs 中定义 INTERNLM2_CONFIG 常量,配置起始标记 <|action_start|><|plugin|> 、结束标记 <|action_end|> ,并设置参数键候选为 ["parameters", "arguments"] 。实现 ToolParser trait 的核心方法( newcreatepreserve_special_tokensparse_intofinishresetbuild_tool_call ),其中 preserve_special_tokens 返回 true 以匹配 Python 默认行为。
  2. 扩展共享配置 JsonToolCallConfig :在 rust/src/tool-parser/src/json/mod.rs 中将 arguments_key 字段类型从 &'static str 改为 &'static [&'static str] ,以支持 InternLM2 同时接受 parametersarguments 。新增 json_arguments_key 解析函数,使用 winnowverify 检查 JSON 字符串键是否在候选列表中,并利用 add_context 循环附加每个候选键的 Expected 上下文,使错误信息更清晰(例如 expected parameters, expected arguments )。
  3. 更新现有四个 JSON 解析器:将 HermesToolParserLlama3JsonToolParserMistralToolParserQwen3XmlToolParserarguments_key 配置从字符串改为单元素数组(如 &["arguments"] ),保持行为完全不变。
  4. vllm-chat 解析器工厂中注册:在 rust/src/chat/src/parser/tool/mod.rs 中添加 INTERNLM 名称常量(对应 Python 的 --tool-call-parser internlm ),注册 Internlm2ToolParser ,并添加模型模式匹配 "internlm2" ,确保 internlm2-chat-*internlm2_5-* 自动路由,同时排除 internlm-chat-* ( v1 )、 internlm3-*Intern-S1
  5. 添加全面的测试覆盖:在 rust/src/chat/src/parser/tool/tests.rs 中验证正反模型名称路由;在 internlm2.rs 内部添加覆盖完整解析、流式增量、多块提取、边界情况(如参数键回退、空白处理、截断错误)等 12 个测试用例,确保与 Python 参考实现的行为一致性,并明确文档中已知未对齐的差异。
  6. 模块导出:在 rust/src/tool-parser/src/lib.rs 中添加 Internlm2ToolParser 的公开导出。
文件 模块 状态 重要度
rust/src/tool-parser/src/json/internlm2.rs 工具解析器 added 8.98
rust/src/tool-parser/src/json/mod.rs 工具解析器 modified 6.83
rust/src/chat/src/parser/tool/mod.rs 聊天引擎 modified 5.32
rust/src/chat/src/parser/tool/tests.rs 聊天引擎 modified 5.12
rust/src/tool-parser/src/lib.rs 工具解析器 modified 4.16

关键符号

json_arguments_key Internlm2ToolParser::new Internlm2ToolParser::create Internlm2ToolParser::preserve_special_tokens Internlm2ToolParser::parse_into Internlm2ToolParser::finish Internlm2ToolParser::reset Internlm2ToolParser::build_tool_call

关键源码片段

rust/src/tool-parser/src/json/internlm2.rs dependency-wiring

新增 InternLM2 工具解析器的核心实现,定义了配置和解析逻辑。

use super::{JsonToolCallConfig, JsonToolCallParser, JsonToolCallWhitespace};
use crate::{Result, Tool, ToolParser, ToolParserOutput};/*
 * InternLM2 工具调用配置
 * 起始标记 : `<|action_start|><|plugin|>`
 * 结束标记 : `<|action_end|>`
 * 参数键同时接受 `parameters` 或 `arguments`,优先使用第一个遇到的
 */
const INTERNLM2_CONFIG: JsonToolCallConfig = JsonToolCallConfig {
    parser_name: "InternLM2",
    start_marker: "<|action_start|><|plugin|>",
    end_marker: "<|action_end|>",
    marker_whitespace: JsonToolCallWhitespace::Optional,
    delimiter: None,
    name_key: "name",
    arguments_key: &["parameters", "arguments"],
};/// InternLM2 特殊 token 包裹的 JSON 工具调用解析器
pub struct Internlm2ToolParser {
    inner: JsonToolCallParser,
}impl Internlm2ToolParser {
    fn new(_tools: &[Tool]) -> Self {
        Self {
            inner: JsonToolCallParser::new(INTERNLM2_CONFIG),
        }
    }
}impl ToolParser for Internlm2ToolParser {
    fn create(tools: &[Tool]) -> Result<Box<dyn ToolParser>> {
        Ok(Box::new(Self::new(tools)))
    }    // 保留 `<|action_start|>` 等特殊 token,对应 Python 的
    // `adjust_request(skip_special_tokens=False)`
    fn preserve_special_tokens(&self) -> bool {
        true
    }
    // parse_into, finish 等方法委托 inner 解析器处理
}
rust/src/tool-parser/src/json/mod.rs core-logic

共享 JSON 工具解析核心的修改,是关键架构变更点。

/// 解析 JSON 对象键,接受 `candidates` 候选列表中的任意一个。
/// 使用 `winnow` 的 `verify` 先解析完整 JSON 字符串,再检查是否在候选列表中。
/// 若失败,通过循环为每个候选键添加 `Expected` 上下文,使错误信息枚举所有合法键。
fn json_arguments_key(
    input: &mut JsonToolInput<'_>,
    candidates: &'static [&'static str],
) -> ModalResult<()> {
    let start = input.checkpoint(); // 记录输入位置用于错误上下文
    json_str
        .verify(|key: &String| candidates.contains(&key.as_str()))
        .void()
        .parse_next(input)
        .map_err(|err| {
            // 为每个候选键添加 Expected 上下文
            err.map(|context_error| {
                candidates.iter().fold(context_error, |context_error, candidate| {
                    context_error.add_context(
                        &*input,
                        &start,
                        StrContext::Expected(StrContextValue::StringLiteral(candidate)),
                    )
                })
            })
        })
}

评论区精华

使用 for 循环改进 json_arguments_key 错误上下文 设计

BugenZhao 询问是否可以对任意数量的候选键使用 for 循环,willamhou 采纳建议并实现,同时改进为在循环中附加 Expected 上下文,使错误信息枚举所有合法键。

结论:已采纳并实现,PR 中代码已包含循环实现。 · 已解决

跳过 InternLM2 prelude 文本是否为必要 question

chatgpt-codex-connector[bot] 提出 P2 建议需要跳过 `<|plugin|>` 后的非空白文本。BugenZhao 随后询问这种容忍行为是故意还是偶然,作者 willamhou 解释这是 Python 非流式路径的遗留行为,模型格式总是直接输出 `<|plugin|>{...`,因此决定不实现,保持与其他 JSON 解析器一致。

结论:决定不实现,故意偏离 Python 参考实现,并在文档中记录。 · 已解决

风险与影响

  1. 核心配置类型变更arguments_key 从字符串改为切片引用,虽已确认所有使用处均更新,但若后续新增解析器时遗漏此约定,可能导致编译错误或逻辑错误。
  2. 与 Python 参考行为差异:已知有三项差异(参数值类型限制、未知键硬错误、字段顺序要求),文档中已记录但未修复。若实际模型触发这些差异,可能导致请求失败或结果不符。
  3. 模型路由准确性:模式匹配 "internlm2" 可能过于宽泛(如匹配包含 internlm2 的其他模型),但已通过测试排除负面案例,风险可控。

用户:使用 InternLM2 模型且启用 Rust 前端 vllm-chat 的用户将自动获得工具调用支持,行为与 Python 解析器略有不同(并行调用、更严格的 JSON 结构检查)。
系统:新增约 350 行代码,模块化良好,共享核心扩展不影响现有解析器;编译时间和二进制体积略有增加。
团队:缩小 Rust 端与 Python 端的工具解析器差距,推动 Rust 前端功能完善。

核心配置类型变更 与 Python 参考行为差异 模型路由准确性

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论