From 7b39491a17a75d86981ef3c3511c3633c286d85d Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Wed, 20 May 2026 20:49:20 +0800 Subject: [PATCH] Fix Zhipu ASR endpoint handling --- openless-all/app/src-tauri/src/asr/whisper.rs | 44 ++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/openless-all/app/src-tauri/src/asr/whisper.rs b/openless-all/app/src-tauri/src/asr/whisper.rs index 6659e4c5..528ecf76 100644 --- a/openless-all/app/src-tauri/src/asr/whisper.rs +++ b/openless-all/app/src-tauri/src/asr/whisper.rs @@ -101,8 +101,7 @@ impl WhisperBatchASR { .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]])) .collect(); let wav = encode_wav_16k_mono(&samples); - let base_url = self.base_url.trim_end_matches('/'); - let url = format!("{}/audio/transcriptions", base_url); + let url = transcription_url(&self.base_url)?; let wav_part = reqwest::multipart::Part::bytes(wav) .file_name("audio.wav") @@ -173,6 +172,23 @@ fn split_pcm_by_duration(pcm: &[u8], max_chunk_duration_ms: Option) -> Vec< pcm.chunks(bytes_per_chunk).collect() } +fn transcription_url(base_url: &str) -> Result { + let parsed = reqwest::Url::parse(base_url.trim()).context("parse Whisper base URL")?; + let mut url = parsed.clone(); + let path = parsed.path().trim_end_matches('/'); + let next_path = if path.ends_with("/audio/transcriptions") { + path.to_string() + } else if path.ends_with("/audio") { + format!("{path}/transcriptions") + } else if let Some(prefix) = path.strip_suffix("/chat/completions") { + format!("{prefix}/audio/transcriptions") + } else { + format!("{path}/audio/transcriptions") + }; + url.set_path(&next_path); + Ok(url.to_string()) +} + fn join_transcript_chunks(chunks: &[String]) -> String { let mut joined = String::new(); for chunk in chunks.iter().map(|chunk| chunk.trim()) { @@ -462,6 +478,29 @@ mod tests { assert_eq!(split_pcm_by_duration(&pcm, Some(0)), vec![pcm.as_slice()]); } + #[test] + fn transcription_url_accepts_base_audio_or_full_endpoint() { + assert_eq!( + transcription_url("https://open.bigmodel.cn/api/paas/v4").unwrap(), + "https://open.bigmodel.cn/api/paas/v4/audio/transcriptions" + ); + assert_eq!( + transcription_url("https://open.bigmodel.cn/api/paas/v4/audio").unwrap(), + "https://open.bigmodel.cn/api/paas/v4/audio/transcriptions" + ); + assert_eq!( + transcription_url("https://open.bigmodel.cn/api/paas/v4/audio/transcriptions").unwrap(), + "https://open.bigmodel.cn/api/paas/v4/audio/transcriptions" + ); + assert_eq!( + transcription_url( + "https://open.bigmodel.cn/api/paas/v4/audio/transcriptions?api-version=2026-01-01" + ) + .unwrap(), + "https://open.bigmodel.cn/api/paas/v4/audio/transcriptions?api-version=2026-01-01" + ); + } + #[test] fn join_transcript_chunks_skips_empty_chunks() { let chunks = vec![" hello ".to_string(), "".to_string(), "world".to_string()]; @@ -571,6 +610,7 @@ mod tests { Err(err) => panic!("accept ASR test request failed: {err}"), } }; + stream.set_nonblocking(false).unwrap(); stream .set_read_timeout(Some(Duration::from_secs(5))) .unwrap();