From 50c232643dac6a582539d2d5eb4d39eb090b69a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Sun, 2 Aug 2026 02:21:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20provider=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81(11=20=E5=8E=82=E5=95=86=E9=A2=84=E8=AE=BE=20+=20modal?= =?UTF-8?q?ities=20=E4=BF=AE=E6=AD=A3=20+=20defaultModel=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=20+=20=E5=99=AA=E9=9F=B3=E8=BF=87=E6=BB=A4=20+=20UI?= =?UTF-8?q?=20=E6=8F=90=E7=A4=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit providerPresets 扩展 6 厂商(Qwen/Kimi/ERNIE/Spark/Doubao/Gemini)+ SenseNova base_url 修正; modalities 预设表补 SenseNova flash-lite vision;defaultModel 过时更新;sensenova-u1 图像生成 噪音过滤;ProviderPanel defaultModel UI 提示。 --- crates/df-ai/presets/models.json | 16 +++ crates/df-ai/src/model_fetch_helpers.rs | 28 ++++- crates/df-ai/src/model_probe.rs | 27 +++++ src/components/settings/ProviderPanel.vue | 6 + src/components/settings/providerPresets.ts | 124 +++++++++++++++++++-- src/i18n/en/settings.ts | 4 + src/i18n/zh-CN/settings.ts | 3 + 7 files changed, 196 insertions(+), 12 deletions(-) diff --git a/crates/df-ai/presets/models.json b/crates/df-ai/presets/models.json index 177e62e..771f17e 100644 --- a/crates/df-ai/presets/models.json +++ b/crates/df-ai/presets/models.json @@ -127,6 +127,22 @@ "weight": 65, "context_window": 128000 }, + { + "model_id": "sensenova-6.7-flash-lite", + "enabled": true, + "modalities": ["text", "vision"], + "capabilities": ["tool_use"], + "weight": 85, + "context_window": 262144 + }, + { + "model_id": "deepseek-v4-flash", + "enabled": true, + "modalities": ["text"], + "capabilities": ["tool_use"], + "weight": 80, + "context_window": 1048576 + }, { "model_id": "embedding-3", "enabled": true, diff --git a/crates/df-ai/src/model_fetch_helpers.rs b/crates/df-ai/src/model_fetch_helpers.rs index cdec90f..2051a9f 100644 --- a/crates/df-ai/src/model_fetch_helpers.rs +++ b/crates/df-ai/src/model_fetch_helpers.rs @@ -41,7 +41,7 @@ pub fn build_models_url(base_url: &str) -> String { /// 判断是否为非 chat 模型(应从列表中剔除)。 /// /// 规则(设计 §5.4 + 合理扩展,见 issues): -/// - 图片生成:dall-e / midjourney / stable-diffusion / imagen +/// - 图片生成:dall-e / midjourney / stable-diffusion / imagen / sensenova-u1 / infographic / image-generation /// - 语音:tts / whisper / audio / speech / voice(语音合成/识别) /// - 实时:realtime(OpenAI Realtime API 语音对话,非 chat completions) /// - 转写:transcribe / transcription @@ -50,6 +50,9 @@ pub fn build_models_url(base_url: &str) -> String { /// /// embedding 设计 §5.4 注释「保留(知识库需要)」— 此处遵循设计保留 embedding, /// 不在 `is_non_chat_model` 剔除(知识库 embedding 路由用得着)。 +/// +/// 注:`sensenova-u1` 用厂商前缀限定而非裸 `u1`,避免误伤未来含 `u1` 词素的 chat 模型 +/// (SenseNova U1 系列走 `/v1/images/generations` 端点,非 chat completions)。 pub fn is_non_chat_model(id: &str) -> bool { let id = id.to_lowercase(); // 图片生成 @@ -57,6 +60,11 @@ pub fn is_non_chat_model(id: &str) -> bool { || id.contains("midjourney") || id.contains("stable-diffusion") || id.contains("imagen") + // SenseNova U1 系列(图像生成,如 sensenova-u1-fast,走 /v1/images/generations 非 chat) + || id.contains("sensenova-u1") + // 通用图像生成命名(infographic 海报生成 / image-generation 直白命名) + || id.contains("infographic") + || id.contains("image-generation") // 语音(tts 合成 / whisper 识别 / 通用 audio / speech / voice) || id.contains("tts") || id.contains("whisper") @@ -226,6 +234,24 @@ mod tests { assert!(is_non_chat_model("stable-diffusion-xl")); } + #[test] + fn non_chat_sensenova_u1_image_filtered() { + // SenseNova U1 系列是图像生成模型(走 /v1/images/generations,非 chat completions), + // /v1/models 会返回但不应留为 chat 模型,否则用户选它对话会失败。 + assert!(is_non_chat_model("sensenova-u1-fast")); + assert!(is_non_chat_model("SenseNova-U1-Fast")); // 大小写无关 + assert!(is_non_chat_model("sensenova-u1-pro")); + // 通用图像生成命名也剔 + assert!(is_non_chat_model("infographic-v1")); + assert!(is_non_chat_model("image-generation-latest")); + // 对照组:同厂商的 chat 模型 sensenova-6.7-flash-lite 不应被误剔 + // (它是原生多模态 chat,见 model_probe.rs 预设表) + assert!( + !is_non_chat_model("sensenova-6.7-flash-lite"), + "sensenova-6.7-flash-lite 是 chat 模型,不应被当图像生成剔除" + ); + } + #[test] fn non_chat_speech_filtered() { assert!(is_non_chat_model("tts-1")); diff --git a/crates/df-ai/src/model_probe.rs b/crates/df-ai/src/model_probe.rs index d38ebd8..579b9e0 100644 --- a/crates/df-ai/src/model_probe.rs +++ b/crates/df-ai/src/model_probe.rs @@ -130,6 +130,33 @@ mod tests { ); } + // ── 预设表:已知多模态模型(命名无 vision 词素,靠预设显式标 vision) ── + // SenseNova flash-lite 原生多模态(商汤文档:支持图像输入理解), + // 但模型名不含 vision/vl/-v 词素,启发式 has_vision_token 无法识别 → + // 必须靠预设表显式标 vision,否则 modalities 漏 vision 致路由误判。 + #[test] + fn probe_preset_sensenova_flash_lite_has_vision() { + let m = probe("sensenova-6.7-flash-lite"); + assert_eq!(m.probe_source, Some(ProbeSource::PresetTable)); + assert!( + m.modalities.contains(&Modality::Vision), + "sensenova-6.7-flash-lite 应有 Vision(原生多模态): {:?}", + m.modalities + ); + assert!(m.modalities.contains(&Modality::Text)); + // context_window 应为预设的 256K + assert_eq!(m.context_window, 262144); + } + + #[test] + fn probe_preset_deepseek_v4_flash_text_only() { + // SenseNova 上的 deepseek-v4-flash:纯文本对话 + 1M 上下文,无 vision + let m = probe("deepseek-v4-flash"); + assert_eq!(m.probe_source, Some(ProbeSource::PresetTable)); + assert_eq!(m.modalities, vec![Modality::Text]); + assert_eq!(m.context_window, 1_048_576); + } + // ── 预设模糊匹配 ── #[test] diff --git a/src/components/settings/ProviderPanel.vue b/src/components/settings/ProviderPanel.vue index 1167fe3..f609d40 100644 --- a/src/components/settings/ProviderPanel.vue +++ b/src/components/settings/ProviderPanel.vue @@ -96,6 +96,9 @@