Normalize ASR transcription endpoint and enforce streaming response cap
Addresses review feedback: ASR validation now recognizes OpenAI-style /chat/completions endpoints when building the /audio/transcriptions URL, and response-size protection is enforced while streaming the body instead of after buffering it all. Constraint: Keep ASR validation behavior provider-agnostic and minimal in scope Rejected: Heuristic URL rewrites beyond known suffixes | risk of breaking custom reverse proxies Confidence: high Scope-risk: narrow Directive: If changing reqwest features, keep Cargo.lock in sync and re-run tests Tested: cargo test -q
This commit is contained in:
1 parent
fd60f815c0
commit
df2d51e014
3 files changed
+33
-8
No files matched your search
Generated
+16
-1
@@ -4175,12 +4175,14 @@ dependencies = [
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams 0.4.2",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
]
|
||||
@@ -4220,7 +4222,7 @@ dependencies = [
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"wasm-streams 0.5.0",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
@@ -6147,6 +6149,19 @@ dependencies = [
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-streams"
|
||||
version = "0.5.0"
|
||||
|
||||
@@ -24,7 +24,7 @@ serde_json = "1"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.24", features = ["rustls-tls-native-roots"] }
|
||||
futures-util = "0.3"
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls"] }
|
||||
reqwest = { version = "0.12", default-features = false, features = ["json", "multipart", "rustls-tls", "stream"] }
|
||||
thiserror = "1"
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
|
||||
@@ -275,12 +275,15 @@ async fn validate_asr_transcription(config: &ProviderConfig, model: &str) -> Res
|
||||
return Err("providerResponseTooLarge".to_string());
|
||||
}
|
||||
}
|
||||
let body = response
|
||||
.bytes()
|
||||
.await
|
||||
.map_err(|_| "providerReadResponseFailed".to_string())?;
|
||||
if body.len() > MAX_ASR_VALIDATE_BODY_BYTES {
|
||||
return Err("providerResponseTooLarge".to_string());
|
||||
use futures_util::StreamExt;
|
||||
let mut body = Vec::<u8>::new();
|
||||
let mut stream = response.bytes_stream();
|
||||
while let Some(chunk) = stream.next().await {
|
||||
let chunk = chunk.map_err(|_| "providerReadResponseFailed".to_string())?;
|
||||
if body.len().saturating_add(chunk.len()) > MAX_ASR_VALIDATE_BODY_BYTES {
|
||||
return Err("providerResponseTooLarge".to_string());
|
||||
}
|
||||
body.extend_from_slice(&chunk);
|
||||
}
|
||||
let json: Value = serde_json::from_slice(&body).map_err(|_| "asrInvalidJson".to_string())?;
|
||||
if !json.is_object() || json.get("text").is_none() {
|
||||
@@ -303,6 +306,9 @@ fn asr_transcriptions_url(base_url: &str) -> Result<String, 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"))
|
||||
}
|
||||
|
||||
@@ -737,6 +743,10 @@ mod tests {
|
||||
asr_transcriptions_url("https://api.openai.com/v1").unwrap(),
|
||||
"https://api.openai.com/v1/audio/transcriptions"
|
||||
);
|
||||
assert_eq!(
|
||||
asr_transcriptions_url("https://api.openai.com/v1/chat/completions").unwrap(),
|
||||
"https://api.openai.com/v1/audio/transcriptions"
|
||||
);
|
||||
assert_eq!(
|
||||
asr_transcriptions_url("https://api.openai.com/v1/audio").unwrap(),
|
||||
"https://api.openai.com/v1/audio/transcriptions"
|
||||
|
||||
Reference in new issue
Block a user