执行摘要
此 PR 为 Rust 前端的 Kimi K2 模型保留模型原生的 tool call ID,替代之前统一生成的 call_<uuid> 格式,与 Python 端行为对齐。通过给 ToolParser trait 新增可选方法实现扩展,改动量小、向后兼容,已通过新增测试和 roundtrip 测试验证。
功能与动机
Kimi K2 模型在流式输出中通过 token 标记定义 tool call,并在 header 中包含原始 ID(如 functions.get_weather:0)。但 Rust 前端此前总是使用 call_<uuid> 替换,导致与 Python 端行为不一致,且 roundtrip 测试中的 tool_call_mix 用例无法通过。PR #44901 基于 output/mod.rs 中遗留的 TODO 注释,实现了对模型生成 ID 的保留。
实现拆解
- 扩展
ToolParser trait (lib.rs):添加 fn tool_call_id(&self, tool_index: usize) -> Option<&str> 方法,默认返回 None,为 parser 提供可选 hook。
- Kimi K2 parser 存储 ID (
kimi_k2.rs):在结构体新增 call_ids: BTreeMap<usize, String> 字段;在 apply_event 处理 ToolCallHeader 时,从原始 header 中提取 tool_call_id 并存入映射;实现 tool_call_id() 方法返回对应 ID;在 reset() 中清理映射。
- 输出流使用 parser ID (
tool.rs):修改 ToolState::process_tool_items 中生成 ToolCallStart 事件的逻辑:优先调用 self.parser.tool_call_id(),若返回 Some 则直接使用,否则 fallback 到 generate_tool_call_id()。
- 清理 TODO (
mod.rs):移除 generate_tool_call_id 函数旁的 TODO 注释。
- 启用 roundtrip 测试 (
roundtrip.rs):取消 kimi_k25 => [tool_call_mix] 的注释,该用例现在可以正确传递。
测试方面新增了三个测试:tool_stream_preserves_parser_provided_tool_call_id(验证 parser 提供的 ID 被保留)、tool_stream_generates_tool_call_id_when_parser_omits_one(验证 parser 未提供 ID 时 fallback 生效)、以及 kimi_k2_preserves_model_generated_tool_call_ids(验证 Kimi K2 parser 正确解析和暴露 ID)。
rust/src/tool-parser/src/kimi_k2.rs
Kimi K2 parser 存储模型生成的 tool call ID,并通过 tool_call_id() 暴露给输出流。
// 存储模型生成的 tool call ID ( 例如 "functions.get_weather:0")
pub struct KimiK2ToolParser {
buffer: String,
mode: KimiK2Mode,
active_tool_index: Option<usize>,
call_ids: BTreeMap<usize, String>, // 新增字段
}
impl ToolParser for KimiK2ToolParser {
fn tool_call_id(&self, tool_index: usize) -> Option<&str> {
self.call_ids.get(&tool_index).map(String::as_str)
}
}
// 在 apply_event 中处理 ToolCallHeader 时存储 ID
KimiK2Event::ToolCallHeader { tool_call_id, function_name, function_index } => {
let tool_index = function_index;
self.call_ids.insert(tool_index, tool_call_id); // 存储 ID
output.calls.push(ToolCallDelta {
tool_index,
name: Some(function_name),
arguments: String::new(),
});
}
评论区精华
BugenZhao: "This looks nice. Thanks! Would you also try uncommenting these lines to enable roundtrip tests for Kimi K2 as it's unblocked now?"
作者按照建议取消了 roundtrip 测试的注释,验证了功能完整性。该讨论已解决。
风险与影响
- 风险:极低。改动向后兼容(默认返回
None),仅影响 Kimi K2 parser。新增测试覆盖了核心路径和 roundtrip 场景。唯一潜在风险是 tool_call_id 返回空字符串或格式异常,但 Kimi K2 的 ID 格式固定且已在 Python 端成熟使用。
- 影响:Kimi K2 模型用户将获得与 Python 一致的 tool call ID。其他模型不受影响。团队清理了 TODO,测试覆盖更完整。影响范围限于 Rust 前端中 Kimi K2 模型。
关联脉络
本 PR 之前,Rust 前端已通过 #42892 等 PR 支持 Kimi K2 parser,但未保留模型原生 ID。遗留的 TODO 注释记录了此缺口。PR #44901 填补了该缺口,并依赖于 PR #44729(结构化输出)等 Rust 前端基础设施。整体而言,这是 Rust 前端不断对齐 Python 端功能演进的一个步骤。
参与讨论