Skip to content

[qa] 划词追问浮窗多处取消 race:Esc 关不掉流 / 烧 token / 跨会话漏字 / loading 帧丢失 #161

@appergb

Description

@appergb

现象 / Symptom

划词追问(QA)浮窗在 LLM 流式输出过程中,关闭 / 取消 / 重开 流程存在多个观察可见的异常:

  1. Esc 没用:流式生成中按 Esc,浮窗界面消失,但 LLM 在后端仍持续生成(也仍在烧 token / 计费)。
  2. 跨会话漏字:取消后立刻重新打开浮窗 + 按 Option 开始第二轮提问,旧会话的 LLM chunk 仍会被推到新窗口,新气泡里"幽灵地"出现上一轮的剩余答案。
  3. 过渡帧丢失:从"录音"到"思考中"的中间过渡("识别中"/"loading"那一帧)UI 没渲染——用户体验上是"录音状态突然跳到完整答案",中间"识别中"的反馈空了几百毫秒到几秒。

复现

  1. 选段长文,Cmd+Shift+; 弹浮窗
  2. 按 Option,说几句长问题让 LLM 答 30+ 秒
  3. 流式刚开始(前 1-3 个气泡 chunk 已出)→ 按 Esc 关浮窗
  4. 立刻 Cmd+Shift+; 重开浮窗 → Option 录音问新问题
  5. 观察:新会话气泡里会冒出旧 LLM 的尾段;后端日志会看到旧 LLM 流持续发完整篇

根因

问题 文件:行 解释
Esc 走错路由 coordinator.rs:744 handle_window_hotkey_event 前端 QA 浮窗的 Esc → 后端无条件 cancel_session(inner)(dictation 取消),从不查 qa_state.panel_visible
SSE 流无 cancel hook polish.rs:286 chat_completion_history_streaming 流式 loop while response.chunk().await 不查 qa_state.cancelled,没 tokio::select! abort signal;仅在 loop 结束后才有一次 cancelled 检查
on_delta 无 session_id 守卫 coordinator.rs:1916 on_delta 闭包仅捕获 Arc<Inner>,emit 时不验当前 qa_state.session_id 是否 == 启动时的;旧会话 chunk 漏进新会话
loading kind 后端发但前端不处理 coordinator.rs:1815{kind:"loading"},但 QaPanel.tsx:51 switch 5 个 case 都不匹配;types.ts:105 QaStateKind 联合也没 'loading'

影响

  • token 浪费:用户取消的流式仍计费到完整长度
  • 气泡污染:新会话出现旧字
  • 过渡反馈丢失:录音→思考中那一帧用户看不到反馈,体感"中断"
  • 平台:三平台(macOS/Windows/Linux)都受影响(流式逻辑跨平台)

建议 fix

  1. Esc 路由:在 handle_window_hotkey_event Esc 分支先查 panel_visible → 是 → cancel_qa_session + close_qa_panel;否 → 现行 dictation cancel
  2. SSE 取消:注入 Arc<AtomicBool>tokio_util::sync::CancellationToken,loop 每帧检查;cancel_qa_session flip 它
  3. session_id 守卫:on_delta 闭包捕获 let captured_id = inner.qa_state.lock().session_id;,每次 emit 前比对当前 id,不等就跳过
  4. loading 帧:后端去掉 loading 帧(直接发 thinking),或前端 QaPanel.tsx switch 加 case 'loading': setStatus('thinking'); break; + types.ts QaStateKind'loading'

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: highHigh priority

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions