From 0126b0c6611de7431176674a5207bbf606b36aa6 Mon Sep 17 00:00:00 2001 From: let5sne Date: Fri, 3 Jul 2026 02:08:08 +0800 Subject: [PATCH] feat(aihr): add siliconflow embedding provider --- .../aihr/service/AihrModelSeedService.java | 6 ++- frontend/src/views/system/model/index.vue | 44 ++++++++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java index 1874b72f..f9c5def4 100644 --- a/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java +++ b/backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrModelSeedService.java @@ -59,7 +59,8 @@ public class AihrModelSeedService { new ProviderResponse(-2L, "DeepSeek", "deepseek", "DeepSeek 官方 API", "https://api.deepseek.com", "0", 1, "ruoyi-ai", false), new ProviderResponse(-3L, "智谱 AI", "zhipu", "智谱 AI 大模型服务", "https://open.bigmodel.cn", "0", 2, "ruoyi-ai", false), new ProviderResponse(-4L, "阿里云百炼", "qianwen", "阿里云百炼大模型服务", "https://dashscope.aliyuncs.com/compatible-mode/v1", "0", 3, "ruoyi-ai", false), - new ProviderResponse(-5L, "Ollama", "ollama", "本地 Ollama 服务", "http://127.0.0.1:11434/v1", "0", 4, "ruoyi-ai", false) + new ProviderResponse(-5L, "Ollama", "ollama", "本地 Ollama 服务", "http://127.0.0.1:11434/v1", "0", 4, "ruoyi-ai", false), + new ProviderResponse(-6L, "硅基流动", "siliconflow", "硅基流动 OpenAI 兼容接口", "https://api.siliconflow.cn/v1", "0", 5, "seed", false) ); } @@ -67,7 +68,8 @@ public class AihrModelSeedService { return List.of( new ConfigResponse(-1L, "chat", DEFAULT_MODEL, DEFAULT_PROVIDER, "Demo 对话与 RAG 生成模型", null, "Y", null, true, false, false), new ConfigResponse(-2L, "vector", "embedding-3", "zhipu", "后续用于知识片段向量化", 2048, "N", null, false, false, false), - new ConfigResponse(-3L, "rerank", "rerank", "zhipu", "后续用于召回片段重排序", null, "N", null, false, false, false) + new ConfigResponse(-3L, "rerank", "rerank", "zhipu", "后续用于召回片段重排序", null, "N", null, false, false, false), + new ConfigResponse(-4L, "vector", "BAAI/bge-m3", "siliconflow", "硅基流动 bge-m3,用于知识片段向量化", 1024, "N", null, false, false, false) ); } diff --git a/frontend/src/views/system/model/index.vue b/frontend/src/views/system/model/index.vue index 33681986..43a3f055 100644 --- a/frontend/src/views/system/model/index.vue +++ b/frontend/src/views/system/model/index.vue @@ -298,6 +298,18 @@ const seedProviders: ProviderView[] = [ source: 'seed', configured: false, logo: 'OL' + }, + { + id: -6, + providerName: '硅基流动', + providerCode: 'siliconflow', + providerDesc: '硅基流动 OpenAI 兼容接口', + apiHost: 'https://api.siliconflow.cn/v1', + status: '0', + sortOrder: 5, + source: 'seed', + configured: false, + logo: 'SF' } ]; @@ -350,6 +362,19 @@ const seedModels: ModelConfig[] = [ enabled: false, apiKeyConfigured: false, configured: false + }, + { + id: -5, + category: 'vector', + modelName: 'BAAI/bge-m3', + providerCode: 'siliconflow', + modelDescribe: '硅基流动 bge-m3,用于知识片段向量化', + modelDimension: 1024, + modelShow: 'N', + apiHost: '', + enabled: false, + apiKeyConfigured: false, + configured: false } ]; @@ -421,13 +446,23 @@ const providerTag = (provider: ProviderView) => { const normalizeProviders = (items: ModelProvider[]): ProviderView[] => { if (!items.length) return seedProviders; const seedMap = new Map(seedProviders.map((item) => [item.providerCode, item])); - return items.map((item) => { + const rows = items.map((item) => { const seed = seedMap.get(item.providerCode); return { ...item, logo: seed?.logo ?? item.providerName.slice(0, 2).toUpperCase() }; }); + const existingCodes = new Set(rows.map((item) => item.providerCode)); + return [...rows, ...seedProviders.filter((item) => !existingCodes.has(item.providerCode))]; +}; + +const modelKey = (item: ModelConfig) => `${item.category}:${item.providerCode}:${item.modelName}`; + +const normalizeModels = (items: ModelConfig[]): ModelConfig[] => { + if (!items.length) return seedModels; + const existingKeys = new Set(items.map(modelKey)); + return [...items, ...seedModels.filter((item) => !existingKeys.has(modelKey(item)))]; }; const loadData = async (preferredProviderCode = selectedProviderCode.value) => { @@ -435,7 +470,7 @@ const loadData = async (preferredProviderCode = selectedProviderCode.value) => { try { const [providerResult, configResult] = await Promise.all([getModelProviders(), getModelConfigs()]); providers.value = normalizeProviders(providerResult.data ?? []); - modelRows.value = configResult.data?.length ? configResult.data : seedModels; + modelRows.value = normalizeModels(configResult.data ?? []); selectedProviderCode.value = providers.value.find((item) => item.providerCode === preferredProviderCode)?.providerCode ?? providers.value[0]?.providerCode ?? 'deepseek'; } catch { @@ -751,6 +786,11 @@ onMounted(loadData); color: #334155; } +.logo-siliconflow { + background: #f3e8ff; + color: #6d28d9; +} + .provider-text { min-width: 0;