diff --git a/dashboard/src/i18n/locales/en-US/features/session-management.json b/dashboard/src/i18n/locales/en-US/features/session-management.json index 84ddc0cdb..ee4880d1e 100644 --- a/dashboard/src/i18n/locales/en-US/features/session-management.json +++ b/dashboard/src/i18n/locales/en-US/features/session-management.json @@ -30,6 +30,7 @@ "ttsProvider": "TTS Provider", "llmStatus": "LLM Status", "ttsStatus": "TTS Status", + "knowledgeBase": "Knowledge Base", "pluginManagement": "Plugin Management", "actions": "Actions" } @@ -67,6 +68,31 @@ "fullSessionId": "Full Session ID", "hint": "Custom names help you easily identify sessions. The small information icon (!) will show the actual UMO when hovering." }, + "knowledgeBase": { + "title": "Knowledge Base Configuration", + "configure": "Configure", + "selectKB": "Select Knowledge Bases", + "selectMultiple": "You can select multiple knowledge bases", + "noKBAvailable": "No knowledge bases available", + "noKBDesc": "No knowledge bases have been created yet", + "createKB": "Create Knowledge Base", + "advancedSettings": "Advanced Settings", + "topK": "Result Count", + "topKHint": "Number of results to retrieve from knowledge base", + "enableRerank": "Enable Reranking", + "enableRerankHint": "Use reranking model to improve retrieval quality", + "clearConfig": "Clear Configuration", + "save": "Save", + "cancel": "Cancel", + "loading": "Loading knowledge base configuration...", + "description": "Configure knowledge bases for this session. The session will use configured knowledge bases to enhance conversation context.", + "saveSuccess": "Knowledge base configuration saved successfully", + "saveFailed": "Failed to save knowledge base configuration", + "loadFailed": "Failed to load knowledge base configuration", + "clearSuccess": "Knowledge base configuration cleared", + "clearFailed": "Failed to clear knowledge base configuration", + "clearConfirm": "Are you sure you want to clear the knowledge base configuration for this session?" + }, "deleteConfirm": { "message": "Are you sure you want to delete session {sessionName}?", "warning": "This action will permanently delete all chat history and preference settings for this session (except for data linked via plugins), and this cannot be undone. Continue?" diff --git a/dashboard/src/i18n/locales/zh-CN/features/session-management.json b/dashboard/src/i18n/locales/zh-CN/features/session-management.json index 6e93ef76f..e59c7a5f2 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/session-management.json +++ b/dashboard/src/i18n/locales/zh-CN/features/session-management.json @@ -30,6 +30,7 @@ "ttsProvider": "语音合成模型", "llmStatus": "启用 LLM", "ttsStatus": "启用 TTS", + "knowledgeBase": "知识库配置", "pluginManagement": "插件管理", "actions": "操作" } @@ -67,6 +68,31 @@ "fullSessionId": "完整会话ID", "hint": "自定义名称帮助您轻松识别会话。当设置了自定义名称时,会显示一个小感叹号标识(!),鼠标悬停时会显示实际的UMO。" }, + "knowledgeBase": { + "title": "知识库配置", + "configure": "配置", + "selectKB": "选择知识库", + "selectMultiple": "可以选择多个知识库", + "noKBAvailable": "暂无可用的知识库", + "noKBDesc": "目前没有创建任何知识库", + "createKB": "创建知识库", + "advancedSettings": "高级配置", + "topK": "返回结果数量", + "topKHint": "从知识库检索的结果数量", + "enableRerank": "启用重排序", + "enableRerankHint": "使用重排序模型提高检索质量", + "clearConfig": "清除配置", + "save": "保存", + "cancel": "取消", + "loading": "加载知识库配置中...", + "description": "为此会话配置使用的知识库。会话将使用配置的知识库来增强对话上下文。", + "saveSuccess": "知识库配置保存成功", + "saveFailed": "保存知识库配置失败", + "loadFailed": "加载知识库配置失败", + "clearSuccess": "知识库配置已清除", + "clearFailed": "清除知识库配置失败", + "clearConfirm": "确定要清除此会话的知识库配置吗?" + }, "deleteConfirm": { "message": "确定要删除会话 {sessionName} 吗?", "warning": "此操作将永久删除本次会话的「全部对话记录」与「偏好设置」(插件对会话的关联数据除外),且无法恢复。确认继续?" diff --git a/dashboard/src/views/SessionManagementPage.vue b/dashboard/src/views/SessionManagementPage.vue index a77cfd872..543b0f6f7 100644 --- a/dashboard/src/views/SessionManagementPage.vue +++ b/dashboard/src/views/SessionManagementPage.vue @@ -142,6 +142,14 @@ + + + + {{ tm('knowledgeBase.configure') }} + + + + + + + + {{ tm('knowledgeBase.title') }} - {{ selectedSessionForKB.session_name }} + + + mdi-close + + + + + + + {{ tm('knowledgeBase.description') }} + + + + + + + {{ item.raw.emoji }} + {{ item.raw.kb_name }} + + + + + + {{ item.raw.emoji }} + + {{ item.raw.kb_name }} + + {{ item.raw.description || tm('knowledgeBase.noKBDesc') }} - {{ item.raw.doc_count }} {{ tm('list.documents', { count: item.raw.doc_count }) }} + + + + + + + + + + mdi-cog + {{ tm('knowledgeBase.advancedSettings') }} + + + + + + + + + + + + + + + + mdi-database-off + {{ tm('knowledgeBase.noKBAvailable') }} + + {{ tm('knowledgeBase.createKB') }} + + + + + + + + {{ tm('knowledgeBase.loading') }} + + + + + + + {{ tm('knowledgeBase.clearConfig') }} + + + + {{ tm('knowledgeBase.cancel') }} + + + {{ tm('knowledgeBase.save') }} + + + + + {{ snackbarText }} @@ -399,6 +524,18 @@ export default { newSessionName: '', nameEditLoading: false, + // 知识库管理 + kbDialog: false, + selectedSessionForKB: null, + sessionKBConfig: { + kb_ids: [], + top_k: 5, + enable_rerank: true + }, + availableKBs: [], + loadingKBConfig: false, + savingKBConfig: false, + // 提示信息 snackbar: false, snackbarText: '', @@ -432,6 +569,7 @@ export default { { title: this.tm('table.headers.ttsProvider'), key: 'tts_provider', sortable: false, minWidth: '200px' }, { title: this.tm('table.headers.llmStatus'), key: 'llm_enabled', sortable: false, minWidth: '120px' }, { title: this.tm('table.headers.ttsStatus'), key: 'tts_enabled', sortable: false, minWidth: '120px' }, + { title: this.tm('table.headers.knowledgeBase'), key: 'knowledge_base', sortable: false, minWidth: '150px' }, { title: this.tm('table.headers.pluginManagement'), key: 'plugins', sortable: false, minWidth: '120px' }, { title: this.tm('table.headers.actions'), key: 'actions', sortable: false, minWidth: '100px' }, ] @@ -503,7 +641,8 @@ export default { ...session, updating: false, // 添加更新状态标志 loadingPlugins: false, // 添加插件加载状态标志 - deleting: false // 添加删除状态标志 + deleting: false, // 添加删除状态标志 + loadingKB: false // 添加知识库加载状态标志 })); this.availablePersonas = data.available_personas; this.availableChatProviders = data.available_chat_providers; @@ -964,6 +1103,110 @@ export default { this.currentPage = 1; // 重置到第一页 this.loadSessions(); }, + + // 知识库配置相关方法 + async openKBManager(session) { + this.selectedSessionForKB = session; + this.kbDialog = true; + this.loadingKBConfig = true; + + try { + // 加载可用的知识库列表 + const kbListResponse = await axios.get('/api/kb/list'); + if (kbListResponse.data.status === 'ok') { + this.availableKBs = kbListResponse.data.data.items; + } + + // 加载当前会话的知识库配置 + const configResponse = await axios.get('/api/kb/session/config/get', { + params: { session_id: session.session_id } + }); + + if (configResponse.data.status === 'ok') { + const config = configResponse.data.data; + this.sessionKBConfig = { + kb_ids: config.kb_ids || [], + top_k: config.top_k || 5, + enable_rerank: config.enable_rerank !== false + }; + } else { + // 如果没有配置,使用默认值 + this.sessionKBConfig = { + kb_ids: [], + top_k: 5, + enable_rerank: true + }; + } + } catch (error) { + console.error('加载知识库配置失败:', error); + this.showError(this.tm('knowledgeBase.loadFailed')); + } finally { + this.loadingKBConfig = false; + } + }, + + async saveKBConfig() { + if (!this.selectedSessionForKB) return; + + this.savingKBConfig = true; + try { + const response = await axios.post('/api/kb/session/config/set', { + scope: 'session', + scope_id: this.selectedSessionForKB.session_id, + kb_ids: this.sessionKBConfig.kb_ids, + top_k: this.sessionKBConfig.top_k, + enable_rerank: this.sessionKBConfig.enable_rerank + }); + + if (response.data.status === 'ok') { + this.showSuccess(this.tm('knowledgeBase.saveSuccess')); + this.kbDialog = false; + } else { + this.showError(response.data.message || this.tm('knowledgeBase.saveFailed')); + } + } catch (error) { + console.error('保存知识库配置失败:', error); + this.showError(error.response?.data?.message || this.tm('knowledgeBase.saveFailed')); + } finally { + this.savingKBConfig = false; + } + }, + + async clearKBConfig() { + if (!this.selectedSessionForKB) return; + + if (!confirm(this.tm('knowledgeBase.clearConfirm'))) { + return; + } + + this.savingKBConfig = true; + try { + const response = await axios.post('/api/kb/session/config/delete', { + scope: 'session', + scope_id: this.selectedSessionForKB.session_id + }); + + if (response.data.status === 'ok') { + this.showSuccess(this.tm('knowledgeBase.clearSuccess')); + this.sessionKBConfig = { + kb_ids: [], + top_k: 5, + enable_rerank: true + }; + } else { + this.showError(response.data.message || this.tm('knowledgeBase.clearFailed')); + } + } catch (error) { + console.error('清除知识库配置失败:', error); + this.showError(error.response?.data?.message || this.tm('knowledgeBase.clearFailed')); + } finally { + this.savingKBConfig = false; + } + }, + + goToKBPage() { + this.$router.push('/knowledge-base'); + }, }, } diff --git a/dashboard/src/views/knowledge-base/KBList.vue b/dashboard/src/views/knowledge-base/KBList.vue index 0177bff14..9a2c0756c 100644 --- a/dashboard/src/views/knowledge-base/KBList.vue +++ b/dashboard/src/views/knowledge-base/KBList.vue @@ -152,6 +152,7 @@ :label="t('create.embeddingModelLabel')" variant="outlined" class="mb-4" + @update:model-value="handleEmbeddingProviderChange" > @@ -165,6 +166,10 @@ + + 注意: 修改嵌入模型会导致现有的向量数据失效,建议重新上传文档。不同的嵌入模型生成的向量不兼容,可能导致检索结果不准确。 + + {{ snackbar.text }} + + + + + + mdi-alert + 确认修改嵌入模型 + + + + 警告: 修改嵌入模型将导致以下影响: + + + 现有的向量数据将失效 + 检索功能可能无法正常工作 + 建议删除现有文档后重新上传 + 不同嵌入模型生成的向量不兼容 + + + 您确定要将嵌入模型从 {{ originalEmbeddingProvider }} 修改为 {{ pendingEmbeddingProvider }} 吗? + + + + + + 取消 + + + 确认修改 + + + + @@ -291,6 +329,10 @@ const deleting = ref(false) const kbList = ref([]) const embeddingProviders = ref([]) const rerankProviders = ref([]) +const originalEmbeddingProvider = ref(null) +const showEmbeddingWarning = ref(false) +const embeddingChangeDialog = ref(false) +const pendingEmbeddingProvider = ref(null) // 对话框 const showCreateDialog = ref(false) @@ -386,6 +428,7 @@ const navigateToDetail = (kbId: string) => { // 编辑知识库 const editKB = (kb: any) => { editingKB.value = kb + originalEmbeddingProvider.value = kb.embedding_provider_id formData.value = { kb_name: kb.kb_name, description: kb.description || '', @@ -396,6 +439,39 @@ const editKB = (kb: any) => { showCreateDialog.value = true } +// 处理 embedding provider 变更 +const handleEmbeddingProviderChange = (newValue: string | null) => { + // 检测是否修改了embedding provider + if (newValue && originalEmbeddingProvider.value && newValue !== originalEmbeddingProvider.value) { + // 显示二次确认对话框 + showEmbeddingWarning.value = true + pendingEmbeddingProvider.value = newValue + embeddingChangeDialog.value = true + } else { + showEmbeddingWarning.value = false + } +} + +// 确认修改 embedding provider +const confirmEmbeddingChange = () => { + if (pendingEmbeddingProvider.value) { + formData.value.embedding_provider_id = pendingEmbeddingProvider.value + // 更新原始值,这样下次比较时不会重复弹窗 + originalEmbeddingProvider.value = pendingEmbeddingProvider.value + } + embeddingChangeDialog.value = false + showEmbeddingWarning.value = true +} + +// 取消修改 embedding provider +const cancelEmbeddingChange = () => { + // 恢复到原始值 + formData.value.embedding_provider_id = originalEmbeddingProvider.value + embeddingChangeDialog.value = false + showEmbeddingWarning.value = false + pendingEmbeddingProvider.value = null +} + // 确认删除 const confirmDelete = (kb: any) => { deleteTarget.value = kb @@ -481,6 +557,9 @@ const submitForm = async () => { const closeCreateDialog = () => { showCreateDialog.value = false editingKB.value = null + originalEmbeddingProvider.value = null + showEmbeddingWarning.value = false + pendingEmbeddingProvider.value = null formData.value = { kb_name: '', description: '', diff --git a/dashboard/src/views/knowledge-base/components/SettingsTab.vue b/dashboard/src/views/knowledge-base/components/SettingsTab.vue index a3c52d8f0..7eba48d02 100644 --- a/dashboard/src/views/knowledge-base/components/SettingsTab.vue +++ b/dashboard/src/views/knowledge-base/components/SettingsTab.vue @@ -86,9 +86,7 @@ :label="t('settings.embeddingProvider')" variant="outlined" density="comfortable" - disabled - hint="嵌入模型创建后不可修改" - persistent-hint + @update:model-value="handleEmbeddingProviderChange" /> @@ -108,6 +106,10 @@ {{ t('settings.tips') }} + + + 注意: 修改嵌入模型会导致现有的向量数据失效,建议重新上传文档。不同的嵌入模型生成的向量不兼容,可能导致检索结果不准确。 + @@ -131,6 +133,39 @@ {{ snackbar.text }} + + + + + + mdi-alert + 确认修改嵌入模型 + + + + 警告: 修改嵌入模型将导致以下影响: + + + 现有的向量数据将失效 + 检索功能可能无法正常工作 + 建议删除现有文档后重新上传 + 不同嵌入模型生成的向量不兼容 + + + 您确定要将嵌入模型从 {{ originalEmbeddingProvider }} 修改为 {{ pendingEmbeddingProvider }} 吗? + + + + + + 取消 + + + 确认修改 + + + + @@ -152,6 +187,10 @@ const saving = ref(false) const formRef = ref() const embeddingProviders = ref([]) const rerankProviders = ref([]) +const originalEmbeddingProvider = ref('') +const showEmbeddingWarning = ref(false) +const embeddingChangeDialog = ref(false) +const pendingEmbeddingProvider = ref('') const snackbar = ref({ show: false, @@ -190,6 +229,8 @@ watch(() => props.kb, (kb) => { embedding_provider_id: kb.embedding_provider_id || '', rerank_provider_id: kb.rerank_provider_id || '' } + // 保存原始的embedding provider + originalEmbeddingProvider.value = kb.embedding_provider_id || '' } }, { immediate: true }) @@ -212,6 +253,33 @@ const loadProviders = async () => { } } +// 处理embedding provider变更 +const handleEmbeddingProviderChange = (newValue: string) => { + if (newValue && newValue !== originalEmbeddingProvider.value) { + // 显示警告并需要确认 + showEmbeddingWarning.value = true + pendingEmbeddingProvider.value = newValue + embeddingChangeDialog.value = true + } else { + showEmbeddingWarning.value = false + } +} + +// 确认修改embedding provider +const confirmEmbeddingChange = () => { + formData.value.embedding_provider_id = pendingEmbeddingProvider.value + embeddingChangeDialog.value = false + showEmbeddingWarning.value = true +} + +// 取消修改embedding provider +const cancelEmbeddingChange = () => { + formData.value.embedding_provider_id = originalEmbeddingProvider.value + embeddingChangeDialog.value = false + showEmbeddingWarning.value = false + pendingEmbeddingProvider.value = '' +} + // 保存设置 const saveSettings = async () => { const { valid } = await formRef.value.validate()
{{ tm('knowledgeBase.noKBAvailable') }}