fix: split Foundry prepare and transcription timeouts

This commit is contained in:
millionart committed 2026-05-06 23:06:38 +08:00
1 parent 4dc0246022
commit 0421ced5be
3 files changed
+36 -39

No files matched your search

@@ -62,7 +62,7 @@ impl FoundryLocalWhisperAsr {
self.language_hint.as_deref()
}
pub async fn transcribe(&self) -> Result<RawTranscript> {
pub async fn transcribe(&self, audio_timeout: std::time::Duration) -> Result<RawTranscript> {
let pcm = self.buffer.lock().clone();
if pcm.is_empty() {
return Ok(RawTranscript {
@@ -71,14 +71,18 @@ impl FoundryLocalWhisperAsr {
});
}
let result = self.transcribe_inner(&pcm).await;
let result = self.transcribe_inner(&pcm, audio_timeout).await;
if result.is_ok() {
self.buffer.lock().clear();
}
result
}
async fn transcribe_inner(&self, pcm: &[u8]) -> Result<RawTranscript> {
async fn transcribe_inner(
&self,
pcm: &[u8],
audio_timeout: std::time::Duration,
) -> Result<RawTranscript> {
let duration_ms = pcm_duration_ms(pcm);
#[cfg(not(target_os = "windows"))]
@@ -95,7 +99,12 @@ impl FoundryLocalWhisperAsr {
let wav_file = TempWavFile::create(pcm)?;
let text = self
.runtime
.transcribe_audio_file(&self.model_alias, self.language_hint(), wav_file.path())
.transcribe_audio_file(
&self.model_alias,
self.language_hint(),
wav_file.path(),
audio_timeout,
)
.await
.with_context(|| {
format!(
@@ -129,6 +129,7 @@ mod imp {
alias: &str,
language_hint: Option<&str>,
audio_path: &Path,
audio_timeout: std::time::Duration,
) -> Result<String> {
let _lifecycle = self.lifecycle.lock().await;
self.cancel_prepare.store(false, Ordering::SeqCst);
@@ -140,9 +141,14 @@ mod imp {
if let Some(language_hint) = normalized_language_hint(language_hint) {
client = client.language(language_hint);
}
let result = client
.transcribe(audio_path)
let result = tokio::time::timeout(audio_timeout, client.transcribe(audio_path))
.await
.with_context(|| {
format!(
"transcribe audio with Foundry model {alias} timed out after {} seconds",
audio_timeout.as_secs()
)
})?
.with_context(|| format!("transcribe audio with Foundry model {alias}"))?;
Ok(result.text)
}
@@ -448,6 +454,7 @@ impl FoundryLocalRuntime {
alias: &str,
_language_hint: Option<&str>,
_audio_path: &std::path::Path,
_audio_timeout: std::time::Duration,
) -> anyhow::Result<String> {
anyhow::bail!("Foundry Local Whisper is only available on Windows: {alias}");
}
+14 -33
View File
@@ -1716,13 +1716,15 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
#[cfg(target_os = "windows")]
ActiveAsr::FoundryLocalWhisper(local) => {
debug_assert!(!uses_global_timeout);
let timeout_duration = foundry_transcribe_timeout_duration();
match tokio::time::timeout(timeout_duration, local.transcribe()).await {
Ok(Ok(r)) => {
match local
.transcribe(foundry_audio_transcribe_timeout_duration())
.await
{
Ok(r) => {
schedule_foundry_local_asr_release(inner, current_session_id);
r
}
Ok(Err(e)) => {
Err(e) => {
log::error!("[coord] Foundry Local Whisper transcribe failed: {e:#}");
emit_capsule(
inner,
@@ -1737,26 +1739,6 @@ async fn end_session(inner: &Arc<Inner>) -> Result<(), String> {
schedule_capsule_idle(inner, CAPSULE_AUTO_HIDE_DELAY_MS);
return Err(e.to_string());
}
Err(_) => {
log::error!(
"[coord] Foundry Local Whisper timeout after {} seconds",
FOUNDRY_LOCAL_TRANSCRIBE_TIMEOUT_SECS
);
local.cancel();
schedule_foundry_local_asr_release(inner, current_session_id);
emit_capsule(
inner,
CapsuleState::Error,
0.0,
elapsed,
Some("本地识别超时".to_string()),
None,
);
restore_prepared_windows_ime_session(inner, current_session_id);
inner.state.lock().phase = SessionPhase::Idle;
schedule_capsule_idle(inner, CAPSULE_AUTO_HIDE_DELAY_MS);
return Err("foundry local timeout".to_string());
}
}
}
#[cfg(target_os = "macos")]
@@ -3256,11 +3238,13 @@ mod tests {
#[cfg(target_os = "windows")]
#[test]
fn foundry_transcribe_has_finite_extended_timeout() {
let timeout = foundry_transcribe_timeout_duration();
fn foundry_audio_transcribe_timeout_is_separate_from_prepare() {
let timeout = foundry_audio_transcribe_timeout_duration();
assert!(timeout > std::time::Duration::from_secs(COORDINATOR_GLOBAL_TIMEOUT_SECS));
assert!(timeout <= std::time::Duration::from_secs(15 * 60));
assert_eq!(
timeout,
std::time::Duration::from_secs(COORDINATOR_GLOBAL_TIMEOUT_SECS)
);
}
#[test]
@@ -3579,11 +3563,8 @@ const CAPSULE_AUTO_HIDE_DELAY_MS: u64 = 2000;
const COORDINATOR_GLOBAL_TIMEOUT_SECS: u64 = 15;
#[cfg(target_os = "windows")]
const FOUNDRY_LOCAL_TRANSCRIBE_TIMEOUT_SECS: u64 = 10 * 60;
#[cfg(target_os = "windows")]
fn foundry_transcribe_timeout_duration() -> std::time::Duration {
std::time::Duration::from_secs(FOUNDRY_LOCAL_TRANSCRIBE_TIMEOUT_SECS)
fn foundry_audio_transcribe_timeout_duration() -> std::time::Duration {
std::time::Duration::from_secs(COORDINATOR_GLOBAL_TIMEOUT_SECS)
}
/// begin_session 中各 await 之间的 cancel race 检查结果。