From 6ec51104c82eba0acaa7eea1131b8f327a05b259 Mon Sep 17 00:00:00 2001 From: YuanShiJiLoong <2450829984@qq.com> Date: Sun, 22 Feb 2026 19:27:42 +0900 Subject: [PATCH] feat: add rebuild all embeddings button for RAG initialization --- app/components/SettingsPanel.js | 38 +++++++++++++++++++++++++++++++++ app/lib/settings.js | 29 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/app/components/SettingsPanel.js b/app/components/SettingsPanel.js index f16da28..148cc70 100644 --- a/app/components/SettingsPanel.js +++ b/app/components/SettingsPanel.js @@ -17,6 +17,7 @@ import { getActiveWorkId, setActiveWorkId, getAllWorks, + rebuildAllEmbeddings, } from '../lib/settings'; import SettingsTree from './SettingsTree'; import { useI18n } from '../lib/useI18n'; @@ -486,6 +487,7 @@ function ApiConfigForm({ data, onChange }) { const [testStatus, setTestStatus] = useState(null); const [fetchedModels, setFetchedModels] = useState(null); const [fetchedEmbedModels, setFetchedEmbedModels] = useState(null); + const [rebuildStatus, setRebuildStatus] = useState(null); // null | 'loading' | {done, total, failed} const [savedProfiles, setSavedProfiles] = useState([]); const [profileName, setProfileName] = useState(''); const [showSaveInput, setShowSaveInput] = useState(false); @@ -557,6 +559,20 @@ function ApiConfigForm({ data, onChange }) { } catch { setFetchedEmbedModels(null); setTestStatus({ success: false, error: t('apiConfig.fetchEmbedModelsFailed') }); } }; + const handleRebuildEmbeddings = async () => { + setRebuildStatus({ done: 0, total: 0, failed: 0 }); + try { + const result = await rebuildAllEmbeddings((done, total, failed) => { + setRebuildStatus({ done, total, failed }); + }); + setRebuildStatus({ ...result, finished: true }); + setTimeout(() => setRebuildStatus(null), 5000); + } catch { + setRebuildStatus({ error: true }); + setTimeout(() => setRebuildStatus(null), 3000); + } + }; + const currentProvider = PROVIDERS.find(p => p.key === data.provider) || PROVIDERS[7]; const isCustom = data.provider === 'custom'; @@ -689,6 +705,28 @@ function ApiConfigForm({ data, onChange }) { )} + + {/* 重建向量按钮 */} +
+ + {rebuildStatus?.finished && ( + 0 ? 'var(--warning)' : 'var(--success)' }}> + ✓ 完成!{rebuildStatus.done - rebuildStatus.failed}/{rebuildStatus.total} 成功{rebuildStatus.failed > 0 ? `,${rebuildStatus.failed} 失败` : ''} + + )} + {rebuildStatus?.error && ( + 重建失败 + )} +
首次开启或更换嵌入模型后,需要重建向量才能使用 RAG 智能检索
+
)} diff --git a/app/lib/settings.js b/app/lib/settings.js index 38e347f..23c089a 100644 --- a/app/lib/settings.js +++ b/app/lib/settings.js @@ -565,6 +565,35 @@ export async function moveSettingsNode(id, newParentId) { return nodes[idx]; } +// 重新计算所有条目的 embedding (Async) +export async function rebuildAllEmbeddings(onProgress) { + const nodes = await getSettingsNodes(); + const { apiConfig } = getProjectSettings(); + const items = nodes.filter(n => n.type === 'item'); + let done = 0; + let failed = 0; + + for (const item of items) { + try { + const textToEmbed = extractTextForEmbedding(item); + const embedding = await getEmbedding(textToEmbed, apiConfig); + const idx = nodes.findIndex(n => n.id === item.id); + if (idx !== -1 && embedding) { + nodes[idx].embedding = embedding; + } else if (!embedding) { + failed++; + } + } catch { + failed++; + } + done++; + onProgress?.(done, items.length, failed); + } + + await saveSettingsNodes(nodes); + return { total: items.length, done, failed }; +} + // 获取指定分类下的所有 item 节点(递归) (Async) export async function getItemsByCategory(category) { const nodes = await getSettingsNodes();