diff --git a/astrbot/dashboard/server.py b/astrbot/dashboard/server.py index 1a54b8411..d8c1a1dd9 100644 --- a/astrbot/dashboard/server.py +++ b/astrbot/dashboard/server.py @@ -71,6 +71,7 @@ class AstrBotDashboard: route, view_handler, methods, _ = api if route == f"/{subpath}" and request.method in methods: return await view_handler(*args, **kwargs) + return jsonify(Response().error("未找到该路由").__dict__) async def auth_middleware(self): if not request.path.startswith("/api"): diff --git a/dashboard/src/views/alkaid/KnowledgeBase.vue b/dashboard/src/views/alkaid/KnowledgeBase.vue index 78676e29a..0aef3a175 100644 --- a/dashboard/src/views/alkaid/KnowledgeBase.vue +++ b/dashboard/src/views/alkaid/KnowledgeBase.vue @@ -2,14 +2,21 @@
-
+

还没有安装知识库插件

+ + 立即安装 + +
+

还没有知识库,快创建一个吧!🙂

创建知识库
-

知识库列表

{{ kb.collection_name }}
{{ kb.count || 0 }} 条知识
+
+ + mdi-delete + +
+
+ Tips: 在聊天页面通过 /kb 指令了解如何使用! +
+ + @@ -96,13 +113,13 @@ mdi-close - + 上传文件 搜索内容 - + @@ -111,21 +128,14 @@

上传文件到知识库

支持 txt、pdf、word、excel 等多种格式

- -
- + +
+ mdi-cloud-upload

拖放文件到这里或点击上传

- +
@@ -136,71 +146,53 @@ mdi-close
- +
- + 上传到知识库
- +
- +
- +
- - - + + + - +

正在搜索...

- +

搜索结果

- mdi-file-document-outline - {{ result.metadata.source }} + mdi-file-document-outline + {{ + result.metadata.source }} - + 相关度: {{ Math.round(result.score * 100) }}%
@@ -208,7 +200,7 @@
- +
没有找到匹配的内容 @@ -222,6 +214,22 @@ + + + + 确认删除 + +

您确定要删除知识库 {{ deleteTarget.collection_name }} 吗?

+

此操作不可逆,所有知识库内容将被永久删除。

+
+ + + 取消 + 删除 + +
+
+ {{ snackbar.text }} @@ -236,6 +244,8 @@ export default { name: 'KnowledgeBase', data() { return { + installed: true, + installing: false, kbCollections: [], showCreateDialog: false, showEmojiPicker: false, @@ -287,13 +297,58 @@ export default { searchResults: [], searching: false, searchPerformed: false, - topK: 5 + topK: 5, + showDeleteDialog: false, + deleteTarget: { + collection_name: '' + }, + deleting: false } }, mounted() { - this.getKBCollections(); + this.checkPlugin(); }, methods: { + checkPlugin() { + axios.get('/api/plugin/get?name=astrbot_plugin_knowledge_base') + .then(response => { + if (response.data.status !== 'ok') { + this.showSnackbar('插件未安装或不可用', 'error'); + } + if (response.data.data.length > 0) { + this.installed = true; + this.getKBCollections(); + } else { + this.installed = false; + } + }) + .catch(error => { + console.error('Error checking plugin:', error); + this.showSnackbar('检查插件失败', 'error'); + }) + }, + + installPlugin() { + this.installing = true; + axios.post('/api/plugin/install', { + url: "https://github.com/soulter/astrbot_plugin_knowledge_base", + proxy: localStorage.getItem('selectedGitHubProxy') || "" + }) + .then(response => { + if (response.data.status === 'ok') { + this.checkPlugin(); + } else { + this.showSnackbar(response.data.message || '安装失败', 'error'); + } + }) + .catch(error => { + console.error('Error installing plugin:', error); + this.showSnackbar('安装插件失败', 'error'); + }).finally(() => { + this.installing = false; + }); + }, + getKBCollections() { axios.get('/api/plug/alkaid/kb/collections') .then(response => { @@ -353,7 +408,7 @@ export default { this.showContentDialog = true; this.resetContentDialog(); }, - + resetContentDialog() { this.activeTab = 'upload'; this.selectedFile = null; @@ -361,28 +416,28 @@ export default { this.searchResults = []; this.searchPerformed = false; }, - + triggerFileInput() { this.$refs.fileInput.click(); }, - + onFileSelected(event) { const files = event.target.files; if (files.length > 0) { this.selectedFile = files[0]; } }, - + onFileDrop(event) { const files = event.dataTransfer.files; if (files.length > 0) { this.selectedFile = files[0]; } }, - + getFileIcon(filename) { const extension = filename.split('.').pop().toLowerCase(); - + switch (extension) { case 'pdf': return 'mdi-file-pdf-box'; @@ -401,53 +456,53 @@ export default { return 'mdi-file-outline'; } }, - + uploadFile() { if (!this.selectedFile) { this.showSnackbar('请先选择文件', 'warning'); return; } - + this.uploading = true; - + const formData = new FormData(); formData.append('file', this.selectedFile); formData.append('collection_name', this.currentKB.collection_name); - + axios.post('/api/plug/alkaid/kb/collection/add_file', formData, { headers: { 'Content-Type': 'multipart/form-data' } }) - .then(response => { - if (response.data.status === 'ok') { - this.showSnackbar('文件上传成功'); - this.selectedFile = null; - - // 刷新知识库列表,获取更新的数量 - this.getKBCollections(); - } else { - this.showSnackbar(response.data.message || '上传失败', 'error'); - } - }) - .catch(error => { - console.error('Error uploading file:', error); - this.showSnackbar('文件上传失败', 'error'); - }) - .finally(() => { - this.uploading = false; - }); + .then(response => { + if (response.data.status === 'ok') { + this.showSnackbar('文件上传成功'); + this.selectedFile = null; + + // 刷新知识库列表,获取更新的数量 + this.getKBCollections(); + } else { + this.showSnackbar(response.data.message || '上传失败', 'error'); + } + }) + .catch(error => { + console.error('Error uploading file:', error); + this.showSnackbar('文件上传失败', 'error'); + }) + .finally(() => { + this.uploading = false; + }); }, - + searchKnowledgeBase() { if (!this.searchQuery.trim()) { this.showSnackbar('请输入搜索内容', 'warning'); return; } - + this.searching = true; this.searchPerformed = true; - + axios.get(`/api/plug/alkaid/kb/collection/search`, { params: { collection_name: this.currentKB.collection_name, @@ -455,26 +510,26 @@ export default { top_k: this.topK } }) - .then(response => { - if (response.data.status === 'ok') { - this.searchResults = response.data.data || []; - - if (this.searchResults.length === 0) { - this.showSnackbar('没有找到匹配的内容', 'info'); + .then(response => { + if (response.data.status === 'ok') { + this.searchResults = response.data.data || []; + + if (this.searchResults.length === 0) { + this.showSnackbar('没有找到匹配的内容', 'info'); + } + } else { + this.showSnackbar(response.data.message || '搜索失败', 'error'); + this.searchResults = []; } - } else { - this.showSnackbar(response.data.message || '搜索失败', 'error'); + }) + .catch(error => { + console.error('Error searching knowledge base:', error); + this.showSnackbar('搜索知识库失败', 'error'); this.searchResults = []; - } - }) - .catch(error => { - console.error('Error searching knowledge base:', error); - this.showSnackbar('搜索知识库失败', 'error'); - this.searchResults = []; - }) - .finally(() => { - this.searching = false; - }); + }) + .finally(() => { + this.searching = false; + }); }, showSnackbar(text, color = 'success') { @@ -486,7 +541,43 @@ export default { selectEmoji(emoji) { this.newKB.emoji = emoji; this.showEmojiPicker = false; - } + }, + + confirmDelete(kb) { + this.deleteTarget = kb; + this.showDeleteDialog = true; + }, + + deleteKnowledgeBase() { + if (!this.deleteTarget.collection_name) { + this.showSnackbar('删除目标不存在', 'error'); + return; + } + + this.deleting = true; + + axios.get('/api/plug/alkaid/kb/collection/delete', { + params: { + collection_name: this.deleteTarget.collection_name + } + }) + .then(response => { + if (response.data.status === 'ok') { + this.showSnackbar('知识库删除成功'); + this.getKBCollections(); // 刷新列表 + this.showDeleteDialog = false; + } else { + this.showSnackbar(response.data.message || '删除失败', 'error'); + } + }) + .catch(error => { + console.error('Error deleting knowledge base:', error); + this.showSnackbar('删除知识库失败', 'error'); + }) + .finally(() => { + this.deleting = false; + }); + }, } } @@ -502,7 +593,7 @@ export default { .kb-card { height: 280px; border-radius: 8px; - overflow: hidden; + overflow: hidden; position: relative; cursor: pointer; display: flex; @@ -638,4 +729,22 @@ export default { background-color: rgba(0, 0, 0, 0.02); border-radius: 4px; } + +.kb-actions { + position: absolute; + bottom: 10px; + right: 10px; + display: flex; + gap: 8px; + opacity: 0; + transition: opacity 0.2s ease; +} + +.kb-card { + position: relative; +} + +.kb-card:hover .kb-actions { + opacity: 1; +}