跳转至

PDF 布局翻译:取消后的迟到结果覆盖新任务

范围与现象

整篇和单页布局翻译都可能在请求取消后继续返回最终结果,造成以下问题:

  • 停止后立即重试,旧任务把新任务状态改回 cancelled。
  • 清空译文或切换文档后,旧译文重新出现在阅读器中。
  • 旧任务的最终结果再次安排 sidecar 写入,干扰新任务的缓存更新。
  • 停止或切换页任务后,未完成块仍标记为 running,下一次点击只能再次停止而不是重试。

原因

进度回调已经检查 AbortSignal,但 await runLayoutRegionTranslate(...) 返回后仍会先持久化最终结果,再处理取消状态。取消只表示不再需要结果,并不保证底层请求立即结束,因此旧 runner 仍可能在新任务开始后返回。

停止操作此前也只修改任务级状态,没有同步释放块级的 running 状态。

修复

整篇和单页路径在最终结果写入前都验证两件事:请求未取消,并且对应 AbortController 仍属于当前任务。失去所有权的旧任务直接返回,不再更新 UI 或安排最终 sidecar 写入。

停止任务时立即把运行中的块恢复为 pending;启动新的单页任务时也释放其他页被中断的块。已完成译文继续保留,未完成块可以立即重试。

这套所有权检查与布局翻译可靠性层配合使用:部分完成保持 partial 状态,重试只处理缺失块,不覆盖已经成功的结果。

回归验证

  • test/pdf-layout-translate-lifecycle.test.ts:12 个场景覆盖整篇重启、单页替换、停止后立即重试、成功块保留、清空、切换文档、组件卸载、迟到进度/错误,以及当前任务正常完成和缓存写入。
  • test/pdf-layout-translate-reliable.test.ts:验证超长源文本拆分不会截断内容,并覆盖无空白长串的硬切分边界。

完整前端测试套件、TypeScript、Biome 和依赖边界检查均应作为回归门槛。Rust 翻译提供商侧另外检查 OpenAI-compatible 的不完整 finish_reason,避免把截断响应当作成功结果缓存。