From 65d4b5243b4e8beb7d969fe02c5525f5eda2b906 Mon Sep 17 00:00:00 2001 From: baiqing Date: Sat, 2 May 2026 10:46:03 +0800 Subject: [PATCH] =?UTF-8?q?fix(qa):=20=E5=8A=A0=20QA=20recorder=20runtime?= =?UTF-8?q?=20error=20monitor=20(closes=20#168)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QA 录音中麦克风设备消失 / cpal 内部 panic 时,QA 流程之前**没有**对应 runtime error 监听器(recorder_errors 用 _ 弃用),导致: - QA 浮窗一直显示"录音中" - 用户按 Option 第二次没反应 - 必须按 Esc 才能脱困 修: - 新增 spawn_qa_recorder_error_monitor,镜像 dictation 现有 spawn_recorder_error_monitor - begin_qa_session Recorder::start 成功后启动监听器 - 用 qa_state.session_id 守卫 stale 事件 - 收到 error → finish_qa_with_error("录音设备异常: ..") → emit 错误胶囊 + 浮窗错误状态 测试: - cargo check ✅ --- openless-all/app/src-tauri/src/coordinator.rs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/openless-all/app/src-tauri/src/coordinator.rs b/openless-all/app/src-tauri/src/coordinator.rs index 98c25487..ad3e16f8 100644 --- a/openless-all/app/src-tauri/src/coordinator.rs +++ b/openless-all/app/src-tauri/src/coordinator.rs @@ -1039,6 +1039,33 @@ fn spawn_recorder_error_monitor(inner: &Arc, rx: mpsc::Receiver, rx: mpsc::Receiver) { + let captured_session_id = inner.qa_state.lock().session_id; + let inner = Arc::clone(inner); + std::thread::Builder::new() + .name("openless-qa-recorder-error-monitor".into()) + .spawn(move || { + if let Ok(err) = rx.recv() { + let current_session_id = inner.qa_state.lock().session_id; + if captured_session_id != current_session_id { + log::warn!( + "[coord] QA recorder error from stale session {} dropped (current={}, err={})", + captured_session_id, + current_session_id, + err + ); + return; + } + log::error!("[coord] QA recorder runtime error: {err}"); + finish_qa_with_error(&inner, format!("录音设备异常: {err}")); + } + }) + .ok(); +} + fn abort_recording_with_error(inner: &Arc, message: String) { let elapsed = { let mut state = inner.state.lock(); @@ -1754,8 +1781,11 @@ async fn begin_qa_session(inner: &Arc) -> Result<(), String> { }); match Recorder::start(consumer, level_handler) { - Ok((rec, _runtime_errors)) => { + Ok((rec, runtime_errors)) => { *inner.qa_recorder.lock() = Some(rec); + // QA 也跟主听写一样监听 cpal runtime error。设备中途消失 / panic 时 + // 不能让 QA 永远卡在 Recording 没反馈。详见 issue #168。 + spawn_qa_recorder_error_monitor(inner, runtime_errors); } Err(e) => { log::error!("[coord] QA recorder start failed: {e}");