fix: guard Foundry release and cancel stale transcripts
This commit is contained in:
1 parent
2290a6cbee
commit
a54ca77e9b
2 files changed
+52
-3
No files matched your search
@@ -6,6 +6,7 @@ use std::fs::{self, OpenOptions};
|
||||
use std::io::Write;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -28,6 +29,7 @@ pub struct FoundryLocalWhisperAsr {
|
||||
model_alias: String,
|
||||
language_hint: Option<String>,
|
||||
buffer: Mutex<Vec<u8>>,
|
||||
cancel_generation: AtomicU64,
|
||||
}
|
||||
|
||||
impl FoundryLocalWhisperAsr {
|
||||
@@ -42,6 +44,7 @@ impl FoundryLocalWhisperAsr {
|
||||
model_alias,
|
||||
language_hint: normalize_language_hint(language_hint),
|
||||
buffer: Mutex::new(Vec::new()),
|
||||
cancel_generation: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +54,7 @@ impl FoundryLocalWhisperAsr {
|
||||
model_alias,
|
||||
language_hint: normalize_language_hint(language_hint),
|
||||
buffer: Mutex::new(Vec::new()),
|
||||
cancel_generation: AtomicU64::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +67,7 @@ impl FoundryLocalWhisperAsr {
|
||||
}
|
||||
|
||||
pub async fn transcribe(&self, audio_timeout: std::time::Duration) -> Result<RawTranscript> {
|
||||
let cancel_generation = self.cancel_generation.load(Ordering::SeqCst);
|
||||
let pcm = self.buffer.lock().clone();
|
||||
if pcm.is_empty() {
|
||||
return Ok(RawTranscript {
|
||||
@@ -72,6 +77,9 @@ impl FoundryLocalWhisperAsr {
|
||||
}
|
||||
|
||||
let result = self.transcribe_inner(&pcm, audio_timeout).await;
|
||||
if self.cancel_generation.load(Ordering::SeqCst) != cancel_generation {
|
||||
anyhow::bail!("Foundry Local Whisper transcription cancelled");
|
||||
}
|
||||
if result.is_ok() {
|
||||
self.buffer.lock().clear();
|
||||
}
|
||||
@@ -121,6 +129,7 @@ impl FoundryLocalWhisperAsr {
|
||||
}
|
||||
|
||||
pub fn cancel(&self) {
|
||||
self.cancel_generation.fetch_add(1, Ordering::SeqCst);
|
||||
self.buffer.lock().clear();
|
||||
}
|
||||
}
|
||||
@@ -298,6 +307,12 @@ mod tests {
|
||||
provider.cancel();
|
||||
|
||||
assert!(provider.buffer.lock().is_empty());
|
||||
assert_eq!(
|
||||
provider
|
||||
.cancel_generation
|
||||
.load(std::sync::atomic::Ordering::SeqCst),
|
||||
1
|
||||
);
|
||||
assert_eq!(provider.model_alias(), "whisper-small");
|
||||
assert_eq!(provider.language_hint(), Some("zh"));
|
||||
}
|
||||
|
||||
@@ -2522,6 +2522,15 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
|
||||
r
|
||||
}
|
||||
Err(e) => {
|
||||
if inner.state.lock().cancelled {
|
||||
log::info!(
|
||||
"[coord] Foundry Local Whisper transcribe cancelled — discarding transcript"
|
||||
);
|
||||
schedule_foundry_local_asr_release(inner, current_session_id);
|
||||
restore_prepared_windows_ime_session(inner, current_session_id);
|
||||
set_phase_idle_if_session_matches(inner, current_session_id);
|
||||
return Ok(());
|
||||
}
|
||||
log::error!("[coord] Foundry Local Whisper transcribe failed: {e:#}");
|
||||
emit_capsule(
|
||||
inner,
|
||||
@@ -3179,6 +3188,11 @@ fn foundry_local_asr_release_keep_secs(inner: &Arc<Inner>) -> u32 {
|
||||
inner.prefs.get().foundry_local_asr_keep_loaded_secs
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn foundry_release_session_is_current(inner: &Arc<Inner>, session_id: u64) -> bool {
|
||||
inner.state.lock().session_id == session_id
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn schedule_foundry_local_asr_release(inner: &Arc<Inner>, session_id: u64) {
|
||||
let keep_secs = foundry_local_asr_release_keep_secs(inner);
|
||||
@@ -3187,9 +3201,9 @@ fn schedule_foundry_local_asr_release(inner: &Arc<Inner>, session_id: u64) {
|
||||
tauri::async_runtime::spawn(async move {
|
||||
if keep_secs > 0 {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(keep_secs as u64)).await;
|
||||
if inner.state.lock().session_id != session_id {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if !foundry_release_session_is_current(&inner, session_id) {
|
||||
return;
|
||||
}
|
||||
if let Err(error) = runtime.release_now().await {
|
||||
log::warn!("[foundry-asr] scheduled release failed: {error:#}");
|
||||
@@ -4064,6 +4078,26 @@ mod tests {
|
||||
assert_eq!(foundry_local_asr_release_keep_secs(&coordinator.inner), 7);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn foundry_release_guard_rejects_stale_session() {
|
||||
let runtime = Arc::new(crate::asr::local::FoundryLocalRuntime::new());
|
||||
let coordinator = Coordinator::new_with_foundry_runtime(runtime);
|
||||
let old_session_id = coordinator.inner.state.lock().session_id;
|
||||
|
||||
assert!(foundry_release_session_is_current(
|
||||
&coordinator.inner,
|
||||
old_session_id
|
||||
));
|
||||
|
||||
coordinator.inner.state.lock().session_id = old_session_id.wrapping_add(1);
|
||||
|
||||
assert!(!foundry_release_session_is_current(
|
||||
&coordinator.inner,
|
||||
old_session_id
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_ark_endpoint_rejects_blank_key_without_custom_endpoint() {
|
||||
assert_eq!(
|
||||
|
||||
Reference in new issue
Block a user