Skip to content

bug(polish): 润色阶段卡住时取消要等数十秒才生效,胶囊卡死需强制重启 #1000

Description

@MurphyLo

现象

配置为 qwen-audio-3.0-asr-flash(ASR)+ deepseek-v4-flash(润色)时,偶发润色阶段卡死:

  1. 转写完成、进入润色(LLM streaming)后,若对话模型接口迟迟不返回内容,语音输入胶囊会一直悬浮在屏幕上,不会消失。
  2. 此时点击胶囊上的关闭按钮(×)无效。
  3. 再按快捷键(Option)尝试开始新一轮语音输入也没有反应。
  4. 唯一的恢复方式是强制退出并重启 OpenLess App。

根因(已用本机日志 + 源码定位)

polish.rs::chat_completion_messages_streaming 的取消检测(should_cancel())只在 SSE 循环顶部轮询一次,真正等待网络数据的两处 await:

  • 建连/发送请求:send_with_transient_retry(request).await?
  • 逐帧读取:tokio::time::timeout(budget, response.chunk()).await

都不会被取消打断——一旦进了某一次 await,要么等到数据到达,要么等到 budget(首字最长 30s+、之后每帧最长 20s 空闲、硬顶 900s)自然超时,取消信号才会被看到。本机日志复现了这个时间差,例如:

2026-08-25T08:22:06.819608Z [INFO] [llm] polish_streaming provider=deepseek model=deepseek-v4-flash prior_turns=0 raw_chars=139
2026-08-25T08:22:49.972444Z [INFO] [coord] session cancelled (was Processing)
2026-08-25T08:23:22.154183Z [INFO] [llm] polish stream cancelled by caller after 0 deltas (0 chars); breaking SSE loop
2026-08-25T08:23:22.155346Z [ERROR][coord] streaming polish FAILED: invalid response: status 200, body: empty polish stream

用户在 08:22:49 点击取消,直到 08:23:22(32 秒后)取消才真正生效——这段时间里连接仍然保持打开、零字节返回。同日另一次实测延迟达 53.7 秒,还有一次用户连续点了 6 次取消都不生效,最终只能强制重启 App。

胶囊无法关闭 / Option 无响应是这个根因的下游表现:coordinator/dictation.rs::cancel_session()Processing 阶段刻意保持 phase=Processing(等 end_session 自然走完收尾),而 handle_pressed() 的状态机只在 Idle 阶段响应新一轮请求——只要润色卡住不放,phase 就一直卡在 Processing,胶囊和快捷键自然都失效。这两处本身逻辑没问题,是上游取消迟迟不生效在放大影响。

对照

同一类问题("进入 Processing 阶段后取消不会真正停下来")在 转写(ASR)阶段 已经在 #798 修复过:引入了 TranscribeRace + wait_for_processing_cancel(75ms 轮询)+ tokio::select!,让取消信号能打断在途的转写请求。但 #798 的修复范围只覆盖了 ASR 子阶段,没有推广到润色(LLM streaming)子阶段——这是一处遗留的修复范围缺口,不是有意为之的设计。

复现条件

  • 使用会偶发响应慢/挂起的对话模型接口(如反向代理、网络抖动、供应商侧限流静默丢包等)。
  • 润色阶段迟迟无首字或迟迟无新增字符。
  • 此时点击取消或按快捷键重新发起。

关联 PR:将提交一个最小化修复,用同款 tokio::select! 赛跑模式覆盖 chat_completion_messages_streaming 里的两处网络 await,取消检测粒度从"最长等一个 budget"收窄到"~75ms"。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions