Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions ui/src/api/API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,11 @@ API 对象和工作空间上下文,作为该抽屉的范围选择例外;用
`KnowledgeWorkflowDetail`。前端详情与保存协议的 `default_model_setting` 复用
`DefaultModelSettingPayload`;服务端需支持该字段的持久化与回传(当前仓库知识库后端尚未实现)。

`postKnowledgeWorkflowImport(knowledgeId, payload)` 使用 POST `/<knowledgeId>/upload_document`
执行已发布版本的文档导入,参数复用 `KnowledgeWorkflowDebugPayload`,结果为
`KnowledgeWorkflowAction`;调试仍调用独立的 `/debug`。文档页导入 Drawer 复用工作流的
数据源、知识库输入及结果 Action,执行详情沿用 `getKnowledgeWorkflowAction`。

### 智能体复制

`ApplicationDetail` 复用 `ApplicationFormPayload` 中的配置字段,并保留详情接口的 `model`
Expand Down Expand Up @@ -495,7 +500,8 @@ Admin 上传尚未创建的资源文件时,`sourceId` 可传 `undefined`,省
普通上传直接等待 `request`,需要进度时传入 `(percent, event)`;只有能获取上传总量时才回调
0–100 的百分比,100 表示请求体已上传,不代表服务端处理成功,完成状态以 `request` 为准。
取消时 Promise 仍拒绝,由调用方处理状态,请求层不弹出通用错误提示;loading 由调用方在
`finally` 中恢复。
`finally` 中恢复。Admin `FileApi.deleteFile(fileId)` 通过 DELETE `/oss/file/<fileId>` 删除已上传文件,
返回值沿用请求层统一解包,不使用 v2 的 `Result` 包装。

资源类型使用 `@/api/enums` 的 `FILE_SOURCE_TYPE` 与 `@/api/types` 的 `FileSourceType`,
包括知识库、智能体、工具、文档、对话及三种临时文件有效期。对话 Store 使用
Expand Down Expand Up @@ -534,6 +540,27 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展

### 知识库文档

文档地址及飞书导入也维护在 `workspace/knowledge/document.ts`:
`postWebDocument` 向 `/<knowledgeId>/document/web` 提交 `WebDocumentImportPayload`,包含
`source_url_list`、`selector` 与 `doc_strategy`。
`getLarkDocumentList` 沿用 POST `/lark/<knowledgeId>/<folderToken>/doc_list`,支持 `page_token`,
返回 `LarkDocumentList` 的 `files`、`has_more` 和 `next_page_token`。
`postImportLarkDocuments` 沿用 POST `/lark/<knowledgeId>/import` 的文档数组协议,
每项为 `LarkDocumentImportPayload`(名称、token、类型及处理策略)。飞书接口属于扩展端;
当前仓库未包含 v3 实现,`doc_strategy` 的持久化与执行需要扩展端支持,不能视为已联调。
上述跨 API 与 UI 的类型统一通过 `@/api/types` 导出。

文档上传复用 `workspace/knowledge/document.ts`:`postSplitDocuments` 向 `document/split`
发送多文件 FormData(重复 `file` 字段)与 JSON 字符串 `doc_strategy`,返回 `DocumentSplitResult[]`;
页面将解析结果的 `content` 转为 `paragraphs`,保留 `source_file_id` 与策略,再通过
`putBatchCreateDocuments` PUT `document/batch_create` 创建文档,与空白文档创建共用同一接口封装。
`postImportTableDocumentFiles` 和 `postImportQADocumentFiles` 分别向 `document/table` 和
`document/qa` 发送多文件 FormData,
由服务端解析并创建。`exportTableDocumentTemplate` 和 `exportQADocumentTemplate` 分别使用 GET
`/workspace/knowledge/document/table_template/export` 和 `/workspace/knowledge/document/template/export`,查询参数 `type` 为
`excel` / `csv`。上传解析与创建类型维护在 `types/document.ts`,统一通过 `@/api/types` 导入。
共享文档 API 不提供上传写入方法。

`putDocument(knowledgeId, documentId, data)` 统一通过 PUT `/<documentId>` 更新名称、启用状态和单项设置,
不按更新字段重复封装同一接口。载荷 `DocumentUpdatePayload` 仅包含可更新字段,按需传入
`name`、`is_active`、`hit_handling_method`、`directly_return_similarity`、`meta`,返回更新后的
Expand Down Expand Up @@ -567,8 +594,9 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展

`workspace/knowledge/document.ts` 维护文档分页查询,路径为
`/workspace/<workspaceId>/knowledge/<knowledgeId>/document/<currentPage>/<pageSize>`。
`putQuickCreateDocuments(knowledgeId, documents)` 通过 PUT `document/batch_create` 创建空白文档,
载荷为 `DocumentQuickCreatePayload[]`(仅含 `name`),返回 `DocumentItem[]`;名称上限 128 字符。
`putBatchCreateDocuments(knowledgeId, documents)` 统一通过 PUT `document/batch_create` 批量创建文档,
空白创建提交 `DocumentQuickCreatePayload[]`(仅含 `name`),解析后创建提交 `DocumentImportPayload[]`,
返回 `DocumentItem[]`;名称上限 128 字符。不按创建场景重复封装同一接口。
共享文档 API 不增加创建接口;文档页监听公共 MkQuickCreate 的 `create` 事件,管理创建请求和提交状态。
分页使用 `ParamsPage` / `ResponsePage<DocumentItem>`,支持 `name`、`create_user`、`status`、`task_type`、`is_active`、`hit_handling_method` 和 `tags` 筛选;
文件任务使用 `DOCUMENT_TASK_STATE` 的字符状态及 `DOCUMENT_TASK_TYPE` 的任务位置,
Expand Down
7 changes: 5 additions & 2 deletions ui/src/api/admin/file.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { AxiosProgressEvent } from 'axios'
import { postUpload } from './core/request'
import { postUpload, del } from './core/request'
import type { FileSourceType } from '@/api/types'

/** 上传资源文件,支持可选的进度回调与取消操作。 */
Expand All @@ -16,4 +16,7 @@ const postUploadFile = (
return postUpload<string>('/oss/file', formData, onProgress)
}

export default { postUploadFile }
/** 删除已上传的资源文件。 */
const deleteFile = (fileId: string) => del(`/oss/file/${fileId}`)

export default { postUploadFile, deleteFile }
64 changes: 60 additions & 4 deletions ui/src/api/admin/workspace/knowledge/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import type { ParamsPage, ResponsePage } from '../../core/types'
import type {
Dict,
DocumentGeneratePayload,
DocumentImportPayload,
DocumentSplitResult,
DocumentStrategy,
DocumentItem,
DocumentQuickCreatePayload,
DocumentSettingPayload,
DocumentTaskState,
DocumentTaskType,
DocumentUpdatePayload,
WebDocumentImportPayload,
LarkDocumentList,
LarkDocumentImportPayload,
} from '@/api/types'
import { getWorkspaceId } from '@/utils/resource-context'

Expand All @@ -18,9 +24,51 @@ const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowl
const getDocumentPage = (knowledgeId: string, page: ParamsPage, query?: Dict<unknown>) =>
get<ResponsePage<DocumentItem>>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query)

/** 批量创建仅包含名称的空白文档。 */
const putQuickCreateDocuments = (knowledgeId: string, documents: DocumentQuickCreatePayload[]) =>
put<DocumentQuickCreatePayload[], DocumentItem[]>(`${getPrefix(knowledgeId)}/batch_create`, documents)
/** 批量创建空白文档或导入text文档。 */
const putBatchCreateDocuments = (knowledgeId: string, documents: DocumentQuickCreatePayload[] | DocumentImportPayload[]) =>
put<DocumentQuickCreatePayload[] | DocumentImportPayload[], DocumentItem[]>(`${getPrefix(knowledgeId)}/batch_create`, documents)

/** 按处理策略解析文本文件,返回分段与原文件关联信息。 */
const postSplitDocuments = (knowledgeId: string, files: File[], strategy: DocumentStrategy) => {
const data = new FormData()
files.forEach((file) => data.append('file', file))
data.append('doc_strategy', JSON.stringify(strategy))
return post<FormData, DocumentSplitResult[]>(`${getPrefix(knowledgeId)}/split`, data)
}

/** 导入表格文件,每个 sheet 由服务端创建独立文档。 */
const postImportTableDocumentFiles = (knowledgeId: string, files: File[]) => {
const data = new FormData()
files.forEach((file) => data.append('file', file))
return post<FormData, DocumentItem[]>(`${getPrefix(knowledgeId)}/table`, data)
}

/** 导入 QA 问答对文件。 */
const postImportQADocumentFiles = (knowledgeId: string, files: File[]) => {
const data = new FormData()
files.forEach((file) => data.append('file', file))
return post<FormData, DocumentItem[]>(`${getPrefix(knowledgeId)}/qa`, data)
}

/** 按地址导入 Web 文档并应用文档处理策略。 */
const postWebDocument = (knowledgeId: string, payload: WebDocumentImportPayload) =>
post<WebDocumentImportPayload, boolean>(`${getPrefix(knowledgeId)}/web`, payload)

/** 查询飞书文件夹中的文件,支持分页加载。 */
const getLarkDocumentList = (knowledgeId: string, folderToken: string, query: { page_token?: string } = {}) =>
post<{ page_token?: string }, LarkDocumentList>(`/workspace/${getWorkspaceId()}/knowledge/lark/${knowledgeId}/${folderToken}/doc_list`, query)

/** 导入所选飞书文档,沿用飞书扩展接口。 */
const postImportLarkDocuments = (knowledgeId: string, documents: LarkDocumentImportPayload[]) =>
post<LarkDocumentImportPayload[], boolean>(`/workspace/${getWorkspaceId()}/knowledge/lark/${knowledgeId}/import`, documents)

/** 下载表格模板。 */
const exportTableDocumentTemplate = (format: 'excel' | 'csv') =>
getExportFile(`${format}-template.${format === 'excel' ? 'xlsx' : 'csv'}`, '/workspace/knowledge/document/table_template/export', { type: format })

/** 下载 QA 模板。 */
const exportQADocumentTemplate = (format: 'excel' | 'csv') =>
getExportFile(`${format}-template.${format === 'excel' ? 'xlsx' : 'csv'}`, '/workspace/knowledge/document/template/export', { type: format })

/** 更新文档名称、启用状态或设置,返回保存后的文档。 */
const putDocument = (knowledgeId: string, documentId: string, data: DocumentUpdatePayload) =>
Expand Down Expand Up @@ -113,7 +161,15 @@ const postReplaceDocumentSource = (knowledgeId: string, documentId: string, file

export default {
getDocumentPage,
putQuickCreateDocuments,
putBatchCreateDocuments,
postSplitDocuments,
postImportTableDocumentFiles,
postImportQADocumentFiles,
postWebDocument,
getLarkDocumentList,
postImportLarkDocuments,
exportTableDocumentTemplate,
exportQADocumentTemplate,
putDocument,
deleteDocument,
putBatchDeleteDocuments,
Expand Down
2 changes: 2 additions & 0 deletions ui/src/api/admin/workspace/knowledge/knowledge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ const getKnowledgeMcpConfig = (knowledgeId: string): Promise<string> =>
const putKnowledgeKeywordIndex = (knowledgeId: string) => put<undefined, void>(`${getPrefix()}/${knowledgeId}/tokenize`)

/** 获取知识库按标签名称分组的标签选项。 */

// TODO
const getKnowledgeTags = (knowledgeId: string) => get<KnowledgeTagGroup[]>(`${getPrefix()}/${knowledgeId}/tags`)

export default {
Expand Down
5 changes: 5 additions & 0 deletions ui/src/api/admin/workspace/knowledge/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const postKnowledgeWorkflowDebug = (knowledgeId: string, payload: KnowledgeWorkf
return post<KnowledgeWorkflowDebugPayload, KnowledgeWorkflowAction>(`${getPrefix()}/${knowledgeId}/debug`, payload)
}

/** 使用已发布知识库工作流执行正式文档导入。 */
const postKnowledgeWorkflowImport = (knowledgeId: string, payload: KnowledgeWorkflowDebugPayload) =>
post<KnowledgeWorkflowDebugPayload, KnowledgeWorkflowAction>(`${getPrefix()}/${knowledgeId}/upload_document`, payload)

/** 获取知识库工作流执行详情,供调试轮询和执行记录共用。 */
const getKnowledgeWorkflowAction = (knowledgeId: string, actionId: string) => {
return get<KnowledgeWorkflowAction>(`${getPrefix()}/${knowledgeId}/action/${actionId}`)
Expand Down Expand Up @@ -76,6 +80,7 @@ export default {
putKnowledgeWorkflowPublish,
getKnowledgeWorkflowFormList,
postKnowledgeWorkflowDebug,
postKnowledgeWorkflowImport,
getKnowledgeWorkflowAction,
postCancelKnowledgeWorkflowAction,
}
46 changes: 45 additions & 1 deletion ui/src/api/types/document.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { DOCUMENT_HIT_HANDLING, DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE } from '@/api/enums'
import type { KnowledgeGeneratePayload, KnowledgeType } from './knowledge'
import type { DocumentStrategy, KnowledgeGeneratePayload, KnowledgeType } from './knowledge'
export type DocumentHitHandling = (typeof DOCUMENT_HIT_HANDLING)[keyof typeof DOCUMENT_HIT_HANDLING]
export type DocumentTaskState = (typeof DOCUMENT_TASK_STATE)[keyof typeof DOCUMENT_TASK_STATE]
export type DocumentTaskType = (typeof DOCUMENT_TASK_TYPE)[keyof typeof DOCUMENT_TASK_TYPE]
Expand Down Expand Up @@ -52,3 +52,47 @@ export interface DocumentItem {

/** 单个文档的局部更新字段。 */
export type DocumentUpdatePayload = Partial<Pick<DocumentItem, 'name' | 'is_active' | 'hit_handling_method' | 'directly_return_similarity' | 'meta'>>

/** 文本解析结果及批量创建文档的载荷。 */
export interface DocumentImportParagraph {
title?: string | null
content: string
problem_list?: { content: string }[]
}

export interface DocumentImportPayload {
name: string
source_file_id?: string | null
doc_strategy: DocumentStrategy
paragraphs: DocumentImportParagraph[]
}

export interface DocumentSplitResult extends Omit<DocumentImportPayload, 'paragraphs'> {
content: DocumentImportParagraph[]
}

/** Web 地址导入及处理策略。 */
export interface WebDocumentImportPayload {
source_url_list: string[]
selector: string
doc_strategy: DocumentStrategy
}

/** 飞书文件树节点,已导入文件不可重复选择。 */
export interface LarkDocumentNode {
name: string
token: string
type: string
is_exist: boolean
}

export interface LarkDocumentList {
files: LarkDocumentNode[]
has_more?: boolean
next_page_token?: string
}

/** 保留 v2 飞书数组协议,每个文件附带处理策略。 */
export interface LarkDocumentImportPayload extends Pick<LarkDocumentNode, 'name' | 'token' | 'type'> {
doc_strategy: DocumentStrategy
}
4 changes: 4 additions & 0 deletions ui/src/assets/file-type/file-qa-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions ui/src/assets/file-type/file-table-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 31 additions & 5 deletions ui/src/components/COMPONENT_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -672,11 +672,37 @@ Logo 数据统一从 Theme Store 获取。

### MkDragUpload

组合拖拽选择区和已选文件卡片,通过 `v-model` 管理 Element Plus `UploadUserFile[]`。`accept`
直接传给上传控件;`dragText`、`selectText`、`tipText` 和 `replaceText` 可替换展示文案。组件只负责
文件选择与展示,使用方通过 `change` 执行校验和上传,通过 `remove` 清理业务数据;`download`
作用域插槽提供当前文件,由使用方按业务需要放置下载按钮。组件暴露 `clearFiles()`,用于请求失败
或表单重置时清空上传控件内部状态。
`mk-drag-upload/index.vue` 为统一入口,根据 `multiple` 选择 `SingleFileUpload.vue` 或
`MultipleFileUpload.vue`,转发 Props、事件、插槽和公开方法。单文件的卡片、更换及整体清理逻辑
放在 `SingleFileUpload.vue`,多文件的常驻拖拽区、文件列表及逐项删除逻辑放在
`MultipleFileUpload.vue`,不在一个实现中混合两种模式的状态分支。
两种模式均通过 `v-model` 管理兼容 Element Plus `UploadUserFile[]` 的 `DragUploadFile[]`,
类型定义位于 `mk-drag-upload/types.ts`,可携带 `file_id`、`errMsg` 和 `canRetry`。
`multiple` 默认为 `false`,单文件模式选择后展示默认卡片和更换入口;传入 `multiple` 后支持
多文件选择,拖拽区保持显示。`accept`、`limit` 直接传给上传控件,多文件模式内部也校验文件格式、
空文件和重复文件;超出数量时提示并发出 `exceed`。`sizeLimit` 为多文件的单文件大小上限(MB),
默认 100;超限保留 `fail` 卡片和大小错误提示,不允许重试。格式不支持、空文件及重复选择移除;
目录中的 `.DS_Store` 格式不支持时静默移除。
`dragText`、`selectText`、`tipText` 和 `replaceText` 可替换默认文案,格式或限制说明通过
`tipText` 展示。文件列表由各模式组件直接展示,多文件模式展示
已选数量或上传完成汇总和两列 `el-card.small` 文件卡片;`download` 作用域插槽为文件卡片提供下载操作。
多文件列表使用 Element Plus 的 `ready`、`uploading`、`success`、`fail` 状态展示进度和失败原因,
可重试失败文件、其他失败文件、上传中文件依次置顶。尚未上传的文件显示已选择,不计入完成数。
多文件组件通过 `inject('upload')` 获取调用方提供的 `DragUploadHandler`:
`(file, onProgress) => { request: Promise<string>, abort }`。选择文件后先发出 `change`,等待
调用方列表回写并完成组件校验,再上传保留的 `ready` 文件;进度回调更新 `percentage`,请求成功后
从响应地址提取 `file_id` 并标记 `success`,网络失败标记 `fail` 并允许重试。100% 表示传输完成,成功状态以请求响应为准。
单个和全部重试仅对 `fail` 且 `canRetry` 的文件生效,有注入接口时直接重新上传;未注入时
保持文件选择模式,重试逐文件发出 `retry(file)`。删除、清空和卸载时取消仍在进行的上传。
使用方可通过 `change` 增加业务校验,通过 `remove` 清理业务数据。具体接口由页面
`provide<DragUploadHandler>('upload', handler)` 提供,不写入公共组件。
多文件组件移除 `success` 且具有 `file_id` 的文件时,直接调用 Admin `FileApi.deleteFile(file_id)`
删除服务端文件;`remove` 事件仍用于通知调用方清理业务数据。
数量限制使用 `limit`,不通过展示插槽控制行为。
组件暴露 `clearFiles()` 清空控件状态、`handleRemove(file)` 移除文件和响应式 `uploadingCount` 上传中数量。
多文件组件的默认选择文案内提供“选择文件夹”,内部维护隐藏的目录选择 input,选择后逐项
加入上传控件并触发相同的 `change` 流程;等待调用方校验回写后再计算数量限制,超限发出
`exceed`。多文件通用校验由组件负责,具体请求由业务页面提供。

### PythonCodeEditor

Expand Down
Loading
Loading