From 525346188be62e68d64bc9b06c491a18e81ad41b Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 7 Jul 2026 12:57:11 +0800 Subject: [PATCH] fix(proxy): close media fallback gaps for Volcano GLM 5.2 image 400s Fixes #5025. The rectifier's media fallback missed Volcano Coding Plan's GLM 5.2 on both paths: - Preventive: known_text_only_model had glm-5.1 but not glm-5.2, so image blocks were forwarded verbatim. Added glm-5.2 as an exact tail match (not a prefix, to avoid stripping images from a future glm-5.2v multimodal variant following Zhipu's 4v/5v naming). - Reactive: the upstream error "Model only support text input" never mentions image/media, so the mentions_image gate rejected it before hints ran; and the existing "only supports text" hint misses the gateway's missing third-person "s". Added a self-evident phrase list ("only support text" / "only supports text") that asserts a modality rejection on its own and bypasses the image-mention gate. Includes regression tests using the verbatim #5025 error body and the glm-5.2[1M] mapped-model form, plus a glm-5.2v negative assertion. --- src-tauri/src/proxy/media_sanitizer.rs | 43 +++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src-tauri/src/proxy/media_sanitizer.rs b/src-tauri/src/proxy/media_sanitizer.rs index 95b2aa305..880b18fa4 100644 --- a/src-tauri/src/proxy/media_sanitizer.rs +++ b/src-tauri/src/proxy/media_sanitizer.rs @@ -63,6 +63,19 @@ pub fn is_unsupported_image_error(error: &ProxyError) -> bool { let message = extract_error_text(body); let message = message.to_ascii_lowercase(); + + // 自证性表述:这类短语本身就断言了"仅接受文本",属于模态拒绝,无需再要求 + // 错误提到 image/media 等字样——火山方舟等网关的报错是 + // "Model only support text input",全程不出现 image(issue #5025)。 + // 国产网关的英文常缺三单 s,因此带 s / 不带 s 两种形式都要列。 + const TEXT_ONLY_SELF_EVIDENT_HINTS: &[&str] = &["only support text", "only supports text"]; + if TEXT_ONLY_SELF_EVIDENT_HINTS + .iter() + .any(|hint| message.contains(hint)) + { + return true; + } + let mentions_image = message.contains("image") || message.contains("vision") || message.contains("multimodal") @@ -83,7 +96,6 @@ pub fn is_unsupported_image_error(error: &ProxyError) -> bool { "doesn't support", "do not support", "don't support", - "only supports text", "text only", "text-only", "invalid content type", @@ -250,6 +262,9 @@ fn known_text_only_model(model: &str) -> bool { "deepseek-v4-flash", "deepseek-v4-pro", "glm-5.1", + // 精确匹配而非 TAIL_PREFIXES:智谱视觉版沿用 4v/5v 命名(glm-5.2v), + // 前缀匹配会误剥未来多模态变体的图片。 + "glm-5.2", "kat-coder", "kat-coder-pro", "kat-coder-pro v1", @@ -727,6 +742,32 @@ mod tests { assert!(is_unsupported_image_error(&error)); } + #[test] + fn detects_text_only_errors_without_image_mention() { + // 火山方舟真实报错(issue #5025):不含 image/media 等字样,且英文缺 + // 三单 s——旧逻辑的 mentions_image 门与 "only supports text" 提示都拦不住。 + let error = ProxyError::UpstreamError { + status: 400, + body: Some( + r#"{"error":{"message":"Model only support text input Request id: 021783"}}"# + .to_string(), + ), + }; + + assert!(is_unsupported_image_error(&error)); + } + + #[test] + fn glm_52_is_classified_text_only() { + // issue #5025:火山 Coding Plan 的 GLM 5.2 是纯文本端点, + // 映射链 glm-5.2[1M] 归一化后尾部为 glm-5.2。 + assert!(known_text_only_model("glm-5.2")); + assert!(known_text_only_model("GLM-5.2[1M]")); + assert!(known_text_only_model("zai-org/GLM-5.2")); + // 未来视觉版(智谱 4v/5v 命名惯例)不能被误判为纯文本。 + assert!(!known_text_only_model("glm-5.2v")); + } + #[test] fn ignores_non_image_errors() { let error = ProxyError::UpstreamError {