From cb2eb8e9ce7ec48e7e600365a177fd3ec0deb7d8 Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Mon, 4 May 2026 21:35:14 +0800 Subject: [PATCH] Preserve query parameters when building ASR transcription validation URL ASR validation URL normalization now edits the parsed URL path instead of string-appending to the raw endpoint, so endpoints with query parameters (e.g. api-version) remain valid after adding /audio/transcriptions. Constraint: Keep endpoint normalization minimal and avoid changing query/fragment semantics Rejected: Drop query parameters during validation | would break Azure-style OpenAI-compatible deployments Confidence: high Scope-risk: narrow Tested: cargo test -q --- openless-all/app/src-tauri/src/commands.rs | 32 ++++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/openless-all/app/src-tauri/src/commands.rs b/openless-all/app/src-tauri/src/commands.rs index e5c9c00c..3fef6292 100644 --- a/openless-all/app/src-tauri/src/commands.rs +++ b/openless-all/app/src-tauri/src/commands.rs @@ -293,23 +293,27 @@ async fn validate_asr_transcription(config: &ProviderConfig, model: &str) -> Res } fn asr_transcriptions_url(base_url: &str) -> Result { - let trimmed = base_url.trim().trim_end_matches('/'); - let parsed = reqwest::Url::parse(trimmed).map_err(|_| "endpointInvalid".to_string())?; + let parsed = reqwest::Url::parse(base_url.trim()).map_err(|_| "endpointInvalid".to_string())?; let host = parsed.host_str().unwrap_or_default(); let localhost = host.eq_ignore_ascii_case("localhost") || host == "127.0.0.1"; if parsed.scheme() != "https" && !localhost { return Err("endpointMustUseHttps".to_string()); } - if trimmed.ends_with("/audio/transcriptions") { - return Ok(trimmed.to_string()); - } - if trimmed.ends_with("/audio") { - return Ok(format!("{trimmed}/transcriptions")); - } - if let Some(prefix) = trimmed.strip_suffix("/chat/completions") { - return Ok(format!("{prefix}/audio/transcriptions")); - } - Ok(format!("{trimmed}/audio/transcriptions")) + + // Work on the URL path only so we don't corrupt query parameters. + 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 encode_wav_16k_mono_silence(duration_ms: u32) -> Vec { @@ -755,6 +759,10 @@ mod tests { asr_transcriptions_url("https://api.openai.com/v1/audio/transcriptions").unwrap(), "https://api.openai.com/v1/audio/transcriptions" ); + assert_eq!( + asr_transcriptions_url("https://api.openai.com/v1?api-version=2024-12-01").unwrap(), + "https://api.openai.com/v1/audio/transcriptions?api-version=2024-12-01" + ); } #[test]