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 等多种格式
-
-
-
+
+
-
+
@@ -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;
+}