diff --git a/REFACTOR.md b/REFACTOR.md index ae8cc1f7..226b5a2c 100644 --- a/REFACTOR.md +++ b/REFACTOR.md @@ -27,4 +27,6 @@ - [ ] Config spacy model 的 load - [x] 在工作区的文件编辑的时候,保存和取消的按钮应该是悬浮在编辑框的右上角,而不是在 header 上面 - [ ] default enable all build in tools / kbs / skills / mcps / subagents -- [ ] 链接 Notion 和 feishu 目前来看,都是支持的 \ No newline at end of file +- [ ] 链接 Notion 和 feishu 目前来看,都是支持的 +- [ ] 知识库的权限调整,修改为三个等级,全局共享、部门共享(选择多个部门,默认是自己部门且必须包含自己部门)、指定人可访问(选择多个用户,默认是仅自己,可以添加其他人)。UI 上也需要调整,三个卡片不再是等宽,而是选中的会宽一点,并展示描述以及选择按钮,未选中的则是默认宽度,仅显示标题。对于选中的卡片除了展示描述、按钮之外,还包括“X 个部门可访问”、“X 个用户可访问”的信息展示。全局的就看是所有用户可访问。所以等级的字段配置也要重新设计,不需要考虑兼容,所有知识库都会重新构建。 +- [ ] databaseinfo 的重构,在左侧展示那个 tab 标签吧,将文件管理(filetable)以及右侧的那些图谱、检索、检索配置、评估之类的,都列为不同的 tab,进入之后默认激活的是 filetable。这样页面布局就好的多。作恶侧边栏除了这些 tab 之外,顶部是和 Skill Detail 那里的 header 一样, \ No newline at end of file diff --git a/web/src/components/ToolCallingResult/ToolCallRenderer.vue b/web/src/components/ToolCallingResult/ToolCallRenderer.vue index 49bb749b..e5a5863e 100644 --- a/web/src/components/ToolCallingResult/ToolCallRenderer.vue +++ b/web/src/components/ToolCallingResult/ToolCallRenderer.vue @@ -23,6 +23,8 @@ import WebSearchTool from './tools/WebSearchTool.vue' import ListKbsTool from './tools/ListKbsTool.vue' import GetMindmapTool from './tools/GetMindmapTool.vue' import QueryKbTool from './tools/QueryKbTool.vue' +import FindKbDocumentTool from './tools/FindKbDocumentTool.vue' +import OpenKbDocumentTool from './tools/OpenKbDocumentTool.vue' import CalculatorTool from './tools/CalculatorTool.vue' import TodoListTool from './tools/TodoListTool.vue' import TaskTool from './tools/TaskTool.vue' @@ -65,6 +67,7 @@ const TOOL_RENDERERS = { cmd: ExecuteTool, edit_file: EditFileTool, execute: ExecuteTool, + find_kb_document: FindKbDocumentTool, get_mindmap: GetMindmapTool, glob: GlobTool, grep: GrepTool, @@ -74,6 +77,7 @@ const TOOL_RENDERERS = { mysql_describe_table: MysqlDescribeTableTool, mysql_list_tables: MysqlListTablesTool, mysql_query: MysqlQueryTool, + open_kb_document: OpenKbDocumentTool, query_kb: QueryKbTool, read_file: ReadFileTool, replace: EditFileTool, diff --git a/web/src/components/ToolCallingResult/index.js b/web/src/components/ToolCallingResult/index.js index 813b92dd..1f1532d7 100644 --- a/web/src/components/ToolCallingResult/index.js +++ b/web/src/components/ToolCallingResult/index.js @@ -7,6 +7,8 @@ export { default as WebSearchTool } from './tools/WebSearchTool.vue' export { default as ListKbsTool } from './tools/ListKbsTool.vue' export { default as GetMindmapTool } from './tools/GetMindmapTool.vue' export { default as QueryKbTool } from './tools/QueryKbTool.vue' +export { default as FindKbDocumentTool } from './tools/FindKbDocumentTool.vue' +export { default as OpenKbDocumentTool } from './tools/OpenKbDocumentTool.vue' export { default as CalculatorTool } from './tools/CalculatorTool.vue' export { default as TodoListTool } from './tools/TodoListTool.vue' export { default as ImageTool } from './tools/ImageTool.vue' diff --git a/web/src/components/ToolCallingResult/toolRegistry.js b/web/src/components/ToolCallingResult/toolRegistry.js index 499fc208..230498a0 100644 --- a/web/src/components/ToolCallingResult/toolRegistry.js +++ b/web/src/components/ToolCallingResult/toolRegistry.js @@ -24,6 +24,7 @@ export const TOOL_ICON_MAP = { cmd: Terminal, edit_file: FilePen, execute: Terminal, + find_kb_document: FolderSearch, get_mindmap: Network, glob: FolderSearch, grep: FolderSearch, @@ -33,6 +34,7 @@ export const TOOL_ICON_MAP = { mysql_describe_table: Database, mysql_list_tables: Database, mysql_query: Database, + open_kb_document: FileText, present_artifacts: FolderOutput, query_kb: BookOpen, read_file: FileText, diff --git a/web/src/components/ToolCallingResult/tools/FindKbDocumentTool.vue b/web/src/components/ToolCallingResult/tools/FindKbDocumentTool.vue new file mode 100644 index 00000000..42f3e236 --- /dev/null +++ b/web/src/components/ToolCallingResult/tools/FindKbDocumentTool.vue @@ -0,0 +1,88 @@ + + + + + Find + | + 知识库: {{ resourceName }} + | + 文件: {{ fileId }} + {{ patternsLabel }} + + + + + + {{ stringifyResult(resultContent) }} + + + + + + + diff --git a/web/src/components/ToolCallingResult/tools/KbDocumentPreview.vue b/web/src/components/ToolCallingResult/tools/KbDocumentPreview.vue new file mode 100644 index 00000000..f4da2bb8 --- /dev/null +++ b/web/src/components/ToolCallingResult/tools/KbDocumentPreview.vue @@ -0,0 +1,176 @@ + + + + {{ summaryText }} + file_id: {{ result.file_id }} + + + + + + {{ windowTitle(window, windowIndex) }} + + 命中 {{ window.matched_lines.length }} 行 + + + {{ line }} + + + + 暂无可预览内容 + + + + + + diff --git a/web/src/components/ToolCallingResult/tools/OpenKbDocumentTool.vue b/web/src/components/ToolCallingResult/tools/OpenKbDocumentTool.vue new file mode 100644 index 00000000..5fdfb683 --- /dev/null +++ b/web/src/components/ToolCallingResult/tools/OpenKbDocumentTool.vue @@ -0,0 +1,87 @@ + + + + + Open + | + 知识库: {{ resourceName }} + | + 文件: {{ fileId }} + {{ lineLabel }} + + + + + + {{ stringifyResult(resultContent) }} + + + + + + + diff --git a/web/src/components/ToolCallingResult/tools/QueryKbTool.vue b/web/src/components/ToolCallingResult/tools/QueryKbTool.vue index e1e67191..ba6e76e5 100644 --- a/web/src/components/ToolCallingResult/tools/QueryKbTool.vue +++ b/web/src/components/ToolCallingResult/tools/QueryKbTool.vue @@ -3,8 +3,8 @@ {{ operationLabel }} - | - 知识库: {{ kbName }} + | + 知识库: {{ resourceLabel }} | {{ queryText }} @@ -93,6 +93,7 @@ import { computed } from 'vue' import BaseToolCall from '../BaseToolCall.vue' import KbResultGroupedList from '@/components/sources/KbResultGroupedList.vue' +import { useDatabaseStore } from '@/stores/database' const props = defineProps({ toolCall: { @@ -101,6 +102,8 @@ const props = defineProps({ } }) +const databaseStore = useDatabaseStore() + const args = computed(() => { const value = props.toolCall.args || props.toolCall.function?.arguments if (!value) return {} @@ -116,7 +119,9 @@ const toolName = computed(() => props.toolCall.name || props.toolCall.function?. const operationLabel = computed(() => `${toolName.value} 搜索`) -const kbName = computed(() => args.value.kb_name || '') +const resourceLabel = computed( + () => args.value.kb_name || databaseStore.getDatabaseNameById(args.value.resource_id) +) const queryText = computed(() => args.value.query_text || '') const EMPTY_RESULT = Object.freeze({ @@ -133,7 +138,9 @@ const normalizeChunks = (payload) => { if (Array.isArray(payload)) return payload if (!payload || typeof payload !== 'object') return [] + if (Array.isArray(payload.results)) return payload.results if (Array.isArray(payload.chunks)) return payload.chunks + if (Array.isArray(payload.data?.results)) return payload.data.results if (Array.isArray(payload.data?.chunks)) return payload.data.chunks return [] diff --git a/web/src/components/sources/KbResultGroupedList.vue b/web/src/components/sources/KbResultGroupedList.vue index d6ace920..cc9def9e 100644 --- a/web/src/components/sources/KbResultGroupedList.vue +++ b/web/src/components/sources/KbResultGroupedList.vue @@ -97,7 +97,31 @@ const resolveChunks = (input) => { } const normalizedChunks = computed(() => - resolveChunks(props.chunks).filter((item) => item && typeof item === 'object' && item.content) + resolveChunks(props.chunks) + .filter((item) => item && typeof item === 'object' && item.content) + .map((item) => { + const metadata = item.metadata && typeof item.metadata === 'object' ? item.metadata : {} + const source = + metadata.source || + metadata.file_name || + metadata.filename || + metadata.title || + item.file_name || + item.filename || + item.file_id || + item.resource_id || + '未知来源' + + return { + ...item, + score: typeof item.score === 'number' ? item.score : metadata.score, + metadata: { + ...metadata, + source, + chunk_id: metadata.chunk_id || item.id + } + } + }) ) const fileGroupList = computed(() => { diff --git a/web/src/stores/database.js b/web/src/stores/database.js index d295cd00..1c5569de 100644 --- a/web/src/stores/database.js +++ b/web/src/stores/database.js @@ -527,6 +527,22 @@ export const useDatabaseStore = defineStore('database', () => { } } + function getDatabaseNameById(id) { + const normalizedId = String(id || '').trim() + if (!normalizedId) return '' + + const matchedDatabase = databases.value.find( + (item) => String(item.db_id || '').trim() === normalizedId + ) + if (matchedDatabase?.name) return matchedDatabase.name + + if (String(database.value?.db_id || '').trim() === normalizedId) { + return database.value?.name || '' + } + + return '' + } + return { databases, database, @@ -554,6 +570,7 @@ export const useDatabaseStore = defineStore('database', () => { startAutoRefresh, stopAutoRefresh, toggleAutoRefresh, - selectAllFailedFiles + selectAllFailedFiles, + getDatabaseNameById } }) diff --git a/web/src/utils/kb_utils.js b/web/src/utils/kb_utils.js index 8c57cceb..f1da27dd 100644 --- a/web/src/utils/kb_utils.js +++ b/web/src/utils/kb_utils.js @@ -16,7 +16,7 @@ export const brandIcons = { export const getKbTypeLabel = (type) => { const labels = { - milvus: 'CommonRAG', + milvus: 'Yuxi', dify: 'Dify', notion: 'Notion' } @@ -40,3 +40,24 @@ export const getKbTypeColor = (type) => { } return colors[type] || 'blue' } + +const READ_ONLY_KB_TYPES = new Set(['dify', 'notion']) + +export const isReadOnlyDatabase = (database, kbTypes = {}) => { + const kbType = (typeof database === 'string' ? database : database?.kb_type || 'milvus').toLowerCase() + + if (database?.supports_documents !== undefined) { + return database.supports_documents === false + } + if (kbTypes[kbType]?.supports_documents !== undefined) { + return kbTypes[kbType].supports_documents === false + } + return READ_ONLY_KB_TYPES.has(kbType) +} + +export const kbUtils = { + getKbTypeLabel, + getKbTypeIcon, + getKbTypeColor, + isReadOnlyDatabase +} diff --git a/web/src/views/DataBaseInfoView.vue b/web/src/views/DataBaseInfoView.vue index c6c6465f..1b178876 100644 --- a/web/src/views/DataBaseInfoView.vue +++ b/web/src/views/DataBaseInfoView.vue @@ -139,7 +139,7 @@
{{ line }}