Prhub

#47289 [Rust Frontend] Recover buffered text from incomplete tool calls at EOS

原始 PR 作者 reidliu41 合并时间 2026-07-03 11:45 文件变更 1 提交数 2 评论 2 代码增减 +61 / -9

执行摘要

恢复 Gemma4 工具调用流结束时被截断的缓冲文本

当 Gemma4 工具调用流在闭合标记之前被截断时,Gemma4UnifiedParser::finish() 返回不完整工具调用错误,而原始工具调用文本仍缓冲在 parser 内部。统一流包装器之前仅关闭 parser 状态而不恢复该缓冲区,导致客户端只能收到 Done 和空 assistant 消息。此 PR 修复此问题,使客户端能获取截断的原始工具调用文本。

此 PR 修复了一个影响工具调用流完整性的问题,实现简洁,测试完善,值得阅读。

讨论亮点

该 PR 没有实质性的 review 讨论。BugenZhao 在创建 PR 后调用 @codex review,AI 工具 Codex 未发现重大问题。之后 BugenZhao 直接批准合并。

实现拆解

  1. 修改 unified.rs 中的 finish() 方法(第 108-117 行):在 parser.finish() 返回 Err(error) 时,除了设置 parser_failed = true 和清除 open_call_index 外,新增调用 self.parser.reset() 获取缓冲文本,并使用 push_text_delta 将其作为普通文本 delta 添加到事件列表。
  2. 更新测试用例(第 609-619 行):将原测试 unified_stream_finish_error_closes_parser_without_reset_text 重命名为 unified_stream_finish_error_recovers_buffered_text,并修改断言期望结果:除了 Done 事件外,还期望一个包含缓冲文本的 TextDelta 事件。
  3. 新增集成测试 unified_stream_recovers_incomplete_gemma4_tool_call_at_eos(第 623-680 行):创建真正的 Gemma4UnifiedParser,模拟一段不完整的工具调用 token 序列,验证在流结束时,未被闭合的工具调用文本被完整恢复为普通文本 delta。
  4. 调整导入:在测试模块中添加 std::sync::Arc、Tool、Gemma4UnifiedParser 和 TestTokenizer 的导入,以支持新的集成测试。
文件 模块 状态 重要度
rust/src/chat/src/output/default/unified.rs Rust 前端 modified 7.99

关键符号

finish

关键源码片段

rust/src/chat/src/output/default/unified.rs core-logic

核心变更文件:修改了 `finish()` 方法从 parser 恢复缓冲文本,并新增和修改了测试用例。

// rust/src/chat/src/output/default/unified.rs
// 在 finish() 的错误处理分支中,调用 parser.reset()
// 获取因工具调用截断而残留的缓冲文本,并作为普通文本 delta 发出
fn finish(&mut self) -> Result<Vec<AssistantEvent>> {
    let mut events = Vec::new();
​
    if self.parser_failed {
        return Ok(events);
    }
​
    match self.parser.finish() {
        Ok(output) => self.process_parser_output(output, &mut events)?,
        Err(error) => {
            warn!(
                error = %error.as_report(),
                "unified parser finish failed; closing open parser state"
            );
            self.parser_failed = true;
            self.open_call_index = None;
            // 关键修复:重置 parser 并恢复缓冲文本,而不是忽略它
            let recovered = self.parser.reset();
            push_text_delta(&mut events, AssistantBlockKind::Text, recovered);
        }
    }
​
    Ok(events)
}

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

风险较低。变更集中在 finish() 方法的错误处理分支,仅在 parser 失败时执行,不影响正常流程。新增的测试覆盖了通用错误恢复和 Gemma4 特定的不完整工具调用场景,确保功能正确。

影响范围:仅影响 Rust 前端统一流包装器中 finish() 的错误处理路径。对于使用工具调用的 Gemma4 模型,当流被意外截断时,客户端现在能收到截断前的缓冲文本,而非空消息。这改善了用户体验,使不完整工具调用的反馈更友好。

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论