执行摘要
本 PR 对 vLLM Rust 前端的解析流水线进行了重构,统一了 tool parser 和 reasoning parser 的接口,用 UnifiedParser trait 和 CombinedParser 适配器消除两阶段分离,保持行为不变。这是未来支持混合块式输出的基础,不影响用户可见接口。
功能与动机
在现有架构中,reasoning 解析和 tool 解析是独立的两个流处理阶段,导致处理混合输出时需要复杂的协调。PR body 描述了这种笨拙,例如对于类似 <channel_thinking>...<end> <channel_tool>...</end> 的块式输出,现有分工难以自然处理。统一解析器接口允许未来实现真正的统一解析器,简化输出处理。
实现拆解
- 定义
UnifiedParser trait 及相关类型(UnifiedParserOutput、UnifiedParserEvent),统一定义解析输出事件:Text、Reasoning、ToolCall。
- 实现
CombinedParser 作为适配器,同时持有可选的 ReasoningParser 和 ToolParser,parse_into 方法先经 reasoning parser 拆分,再将非 reasoning 部分传给 tool parser。
- 在 chat 层新增
UnifiedParserState 替代原有的 ReasoningState 和 ToolState,直接驱动 UnifiedParser。
- 删除旧的
reasoning.rs 和 tool.rs 模块。
- 将工具解析器源代码从
rust/src/tool-parser/ 搬迁到 rust/src/parser/src/tool/,保持 Python 绑定兼容。
- 更新测试:新增
combined.rs 单元测试,适配 chat output 流式测试。
rust/src/parser/src/unified/combined.rs
CombinedParser 实现 UnifiedParser,合并两个现有解析器,是核心适配器。
//! 组合解析器适配器,包装 reasoning 和 tool 解析器。
/// 新建组合解析器,可指定 reasoning 和 tool 解析器(均为可选)。
pub fn new(
reasoning: Option<Box<dyn ReasoningParser>>,
tool: Option<Box<dyn ToolParser>>,
) -> Self {
Self { reasoning, tool }
}
/// 将一段解码后的文本增量送入组合解析器。
///
/// 先经 reasoning 解析器拆分,再将非 reasoning 部分送入 tool 解析器。
fn parse_into(&mut self, delta: &str, output: &mut UnifiedParserOutput) -> Result<()> {
// 如果没有配置 reasoning 解析器,直接走 tool 解析器
let Some(reasoning) = self.reasoning.as_mut() else {
return self.parse_tool(delta, output);
};
// reasoning 解析器将 delta 分为 reasoning 和 content 两部分
let reasoning_delta = reasoning.push(delta)?;
if let Some(reasoning) = reasoning_delta.reasoning {
output.push_reasoning(reasoning);
}
if let Some(content) = reasoning_delta.content {
// tool 解析器只处理非 reasoning 文本
self.parse_tool(&content, output)?;
}
Ok(())
}
评论区精华
- njhill 指出
combined.rs 中错误时会丢失部分输出:"Does it matter that we'll lose partial output here on errors?" → BugenZhao 认领并发起修复:"Good catch! We should defer throwing the error after calling output.append_tool_output." 该问题已在后续 commit 中解决。
- chatgpt-codex-connector 发现
append_tool_output 总是将 normal_text 放在事件列表前面,可能破坏 tool-call 增量顺序(P1)。BugenZhao 回应 "Will be covered in #46584",作为已知问题留待后续处理。
风险与影响
风险
- 事件顺序:
append_tool_output 的简单实现可能导致 tool-call 事件顺序错误(已识别未修复)。
- 错误恢复:两个解析器失败时的部分输出处理逻辑较为复杂,需要额外测试覆盖。
- 文件搬迁:导入路径和 Cargo 配置必须完全匹配,否则构建失败。
影响
- 用户:无行为变更,Python 导入路径兼容。
- 系统:架构更清晰,为未来支持块式输出(如同时包含 reasoning 和 tool)打下基础。
- 团队:维护负担降低,新解析策略只需实现
UnifiedParser trait。
关联脉络
本 PR 是 git-spice 管理的一个 stack 的底层 PR,上游还有 #46584 和 #46602 两个后续 PR 负责处理事件顺序问题并进一步扩展能力。此外,近期 parser 相关的重构 PR(如 #46314)都指向将解析逻辑向统一接口迁移的趋势。
参与讨论