Skip to content

Commit b85a2e2

Browse files
feat: workflow
1 parent 2ec619c commit b85a2e2

44 files changed

Lines changed: 1831 additions & 856 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎ui/src/api/API_README.md‎

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ src/api/
1111
│ │ └── types.ts # 认证 API 与认证 Store 共用类型
1212
│ ├── core/ # Admin 请求基础能力
1313
│ │ ├── request.ts # Axios 实例、HTTP 方法与统一响应解包
14-
│ │ └── types.ts # Admin 请求协议和 loading 类型
14+
│ │ └── types.ts # Admin 请求协议类型
1515
│ ├── system/ # 系统管理业务接口
1616
│ │ ├── chat-user/ # 对话用户、用户组及认证接口
1717
│ │ ├── settings/ # 登录认证、邮件与外观设置接口
@@ -61,7 +61,7 @@ src/api/
6161
- 一类资源的增删改查放在同一个最终资源文件中。调用方直接导入该文件,不为业务目录创建聚合
6262
入口,也不创建汇总所有业务接口的 `api.ts`。
6363
- 工具基础信息和工具工作流使用后端不同资源接口:`workspace/tool/tool.ts` 维护工具增删改查,
64-
`workspace/tool/workflow.ts` 维护工具工作流的加载、保存和发布。
64+
`workspace/tool/workflow.ts` 维护工具工作流的加载、保存、发布与调试。
6565
工具工作流保存请求的 `default_model_setting` 与详情响应统一复用 `DefaultModelSettingPayload`。
6666
- 每个业务接口函数必须添加简短的 JSDoc,说明接口的业务作用;注释应描述“获取什么”“保存什么”
6767
或“对哪个资源执行什么操作”,不重复参数类型、请求方法等代码已经清楚表达的信息。
@@ -103,16 +103,23 @@ Action、Drawer 或 Dialog。复用方直接使用 `typeof XxxApi` 约束完整
103103
共用类型 `WorkflowVersion` 和 `WorkflowVersionPayload` 位于 `types/workflow-version.ts`,
104104
通过 `@/api/types` 导出。`ButtonApplicationPublishHistory` 内部调用智能体版本 API,公共发布历史
105105
UI 组件只接收数据和事件,不接收 API 或推测其他工作流的接口地址。
106+
`workspace/tool/workflow-version.ts` 单独维护工具 `tool_version` 资源,提供
107+
`getWorkflowVersions(toolId)` 和 `putWorkflowVersion(toolId, versionId, payload)`,
108+
复用上述版本类型,由 `views/workflow/tool/ButtonPublishHistory.vue` 调用。
109+
工具版本接口同样尚未支持 `description`,且未返回版本的默认模型配置。
110+
`workspace/knowledge/workflow-version.ts` 维护知识库 `knowledge_version` 资源,提供同名查询与编辑方法,
111+
由知识库 `ButtonPublishHistory.vue` 调用;知识库版本也尚未支持更新说明和默认模型配置。
106112

107113
v3 编辑表单提交 `{ name, description }`,标题上限 64、更新说明上限 1000。
108114
当前仓库后端的版本编辑序列化器只处理 `name`,列表和详情也未返回 `description`;
109115
更新说明的持久化与回显需要后端补齐,前端不将智能体自身的 `desc` 当作版本更新说明。
110116

111117
### 智能体模板中心
112118

113-
`admin/tool-store.ts` 的 `getStoreApplicationList(query)` 查询智能体模板,直接返回
114-
`ApplicationStoreResponse`,不再返回 `unknown`。模板元数据使用 `ApplicationStoreTemplate`,
115-
与响应类型一起维护在 `api/types/application.ts`,经 `@/api/types` 导入。
119+
`admin/store.ts` 的 `getStoreApplicationList(query)` 查询智能体模板,直接返回
120+
`ApplicationStoreResponse`,不再返回 `unknown`。公共模板元数据为 `api/types/workflow-template.ts` 的 `WorkflowStoreTemplate`,
121+
智能体 `ApplicationStoreTemplate` 为其类型别名,响应结构仍由 `api/types/application.ts` 维护。
122+
类型统一经 `@/api/types` 导入;各资源业务入口整理响应字段,公共 UI 不调用接口。
116123
创建或覆盖工作流时通过已有智能体 API 提交 `work_flow_template`,成功后的刷新与导航由 View 负责。
117124

118125
### 模型选项查询
@@ -235,7 +242,8 @@ API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文
235242
- token 和平台公开档案由 `stores/auth.ts` 管理,语言由 `stores/user.ts` 管理;Router、Axios 等业务代码通过
236243
`stores/index.ts` 导出的 `useStore()` 按需访问 Store;401 响应统一清除 token 并跳转 Admin
237244
登录页。
238-
- loading 不作为 API 函数参数,由调用接口的页面或 Store 管理。
245+
- loading 不作为业务 API 或 Admin、Chat 底层请求封装的参数;JSON 请求、文件上传和下载均遵循此规则。
246+
由调用接口的页面、组件或 Store 在请求前开启 loading,并在 Promise 的 `finally` 中释放,确保成功和失败都恢复状态。
239247
- 流式 POST 请求使用 `postStream` 返回原始 `Response`,由业务组件按具体协议解析数据块;
240248
参数顺序为 `postStream(base, path, data?, config?)`,`config.signal` 用于取消请求。
241249
鉴权、语言请求头和错误状态仍由请求基础设施统一处理。
@@ -300,3 +308,27 @@ API 对象和工作空间上下文,作为该抽屉的范围选择例外;用
300308
`ApplicationDetail` 复用 `ApplicationFormPayload` 中的配置字段,并保留详情接口的 `model`
301309
和可空描述。复制通过 `getApplicationDetail` 获取完整配置,将 `model` 映射为 `model_id`,
302310
再调用 `postApplication` 创建副本;不使用卡片列表摘要作为复制数据。
311+
312+
### 工具工作流调试
313+
314+
`workspace/tool/workflow.ts` 的 `postToolWorkflowDebug(toolId, parameters)` 使用 Admin `postStream`
315+
请求 `/<toolId>/debug`,返回原始 SSE Response;输入参数来自工具基础节点,`chat_record_id` 用于识别
316+
执行记录,表单续跑沿用该 ID 并传入 `position`。`getToolWorkflowRecord(toolId, recordId)` 查询
317+
`/<toolId>/tool_record/<recordId>`,返回 `ToolWorkflowRecord` 的运行状态、输出及节点详情。
318+
工具调试读取服务端已保存工作流,画布页面在调试前保存未提交改动。
319+
320+
### 工具工作流模板中心
321+
322+
`admin/store.ts` 的 `getStoreToolWorkflowList(query)` 查询 `/workspace/store/tool_workflow_template`,
323+
返回 `ToolWorkflowStoreResponse`,其中 `apps` 复用 `WorkflowStoreTemplate[]`。
324+
`workspace/tool/workflow.ts` 的 `putToolWorkflow` 支持两种互斥载荷:保存 `work_flow` 与默认模型设置,
325+
或提交 `work_flow_template` 由服务端下载并覆盖当前工具工作流。模板覆盖后由 View 重新查询详情,
326+
同步默认模型设置、保存时间和图快照;确认取消或请求失败不关闭模板中心。
327+
328+
### 知识库工作流模板与导出
329+
330+
`admin/store.ts` 的 `getStoreKnowledgeList(query)` 查询 `/workspace/store/knowledge_template`,
331+
返回 `KnowledgeWorkflowStoreResponse`,其中 `apps` 使用公共 `WorkflowStoreTemplate[]`。
332+
`putKnowledgeWorkflow` 接受互斥的 `work_flow` 保存载荷或 `work_flow_template` 覆盖载荷,覆盖成功后由 View 重载详情。
333+
`exportKnowledgeWorkflow(knowledgeId, name)` 通过 GET `/<knowledgeId>/workflow/export` 下载 `.kbwf` 文件,
334+
只导出工作流,不调用包含文档的知识库包导出接口。

‎ui/src/api/admin/core/request.ts‎

Lines changed: 30 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import axios, { AxiosHeaders, type AxiosRequestConfig, type AxiosResponse, type InternalAxiosRequestConfig } from 'axios'
44
import router from '@/router/admin'
55
import { useStore } from '@/stores'
6-
import type { ApiResponse, LoadingTarget } from './types'
6+
import type { ApiResponse } from './types'
77
import type { Dict } from '@/api/types'
88
import { MsgError } from '@/utils/message'
99
import { ADMIN_API_BASE_PATH } from '@/api/constants'
@@ -30,28 +30,6 @@ function setRequestHeaders(config: InternalAxiosRequestConfig) {
3030
return config
3131
}
3232

33-
function startLoading(loading?: LoadingTarget) {
34-
if (!loading) {
35-
return
36-
}
37-
if ('start' in loading) {
38-
loading.start()
39-
return
40-
}
41-
loading.value = true
42-
}
43-
44-
function finishLoading(loading?: LoadingTarget) {
45-
if (!loading) {
46-
return
47-
}
48-
if ('done' in loading) {
49-
loading.done()
50-
return
51-
}
52-
loading.value = false
53-
}
54-
5533
function extractFilename(contentDisposition?: string) {
5634
if (!contentDisposition) {
5735
return undefined
@@ -93,7 +71,6 @@ async function getResponseErrorMessage(error: unknown) {
9371
return responseData?.message
9472
}
9573

96-
9774
async function downloadExportResponse(response: AxiosResponse<Blob>, fileName: string, mimeType = 'application/octet-stream') {
9875
if (response.data.type.includes('application/json')) {
9976
const text = await response.data.text()
@@ -165,81 +142,49 @@ request.interceptors.response.use(
165142
)
166143

167144
/**
168-
* 统一解包标准 API 响应,并同步可选的 loading 状态。
145+
* 统一解包标准 API 响应。
169146
*/
170-
export async function promise<T>(requestPromise: Promise<AxiosResponse<ApiResponse<T>>>, loading?: LoadingTarget) {
171-
startLoading(loading)
172-
try {
173-
const response = await requestPromise
174-
return response.data.data
175-
} finally {
176-
finishLoading(loading)
177-
}
147+
export async function promise<T>(requestPromise: Promise<AxiosResponse<ApiResponse<T>>>) {
148+
const response = await requestPromise
149+
return response.data.data
178150
}
179151

180152
/** 发送 GET 请求。 */
181-
export function get<T = unknown>(url: string, params?: Dict<unknown>, loading?: LoadingTarget, timeout?: number) {
182-
return promise<T>(request.get<ApiResponse<T>>(url, { params, timeout }), loading)
153+
export function get<T = unknown>(url: string, params?: Dict<unknown>, timeout?: number) {
154+
return promise<T>(request.get<ApiResponse<T>>(url, { params, timeout }))
183155
}
184156

185157
/** 发送 POST 请求。 */
186-
export function post<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, loading?: LoadingTarget, timeout?: number) {
187-
return promise<T>(request.post<ApiResponse<T>>(url, data, { params, timeout }), loading)
158+
export function post<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, timeout?: number) {
159+
return promise<T>(request.post<ApiResponse<T>>(url, data, { params, timeout }))
188160
}
189161

190162
/** 发送 GET 请求并将 Blob 响应下载为文件。 */
191-
export async function getExportFile(fileName: string, url: string, params?: Dict<unknown>, loading?: LoadingTarget): Promise<boolean> {
192-
startLoading(loading)
193-
try {
194-
const response = await request.get<Blob>(url, { params, responseType: 'blob', skipGlobalErrorMessage: true } as ExportRequestConfig)
163+
export async function getExportFile(fileName: string, url: string, params?: Dict<unknown>): Promise<boolean> {
164+
const response = await request.get<Blob>(url, { params, responseType: 'blob', skipGlobalErrorMessage: true } as ExportRequestConfig)
195165

196-
return downloadExportResponse(response, fileName)
197-
} finally {
198-
finishLoading(loading)
199-
}
166+
return downloadExportResponse(response, fileName)
200167
}
201168

202169
/** 发送 POST 请求并将 Blob 响应下载为 Excel 文件。 */
203-
export async function postExportExcel<TData = unknown>(
204-
fileName: string,
205-
url: string,
206-
params?: Dict<unknown>,
207-
data?: TData,
208-
loading?: LoadingTarget,
209-
): Promise<boolean> {
210-
startLoading(loading)
211-
try {
212-
const response = await request.post<Blob>(url, data, { params, responseType: 'blob', skipGlobalErrorMessage: true } as ExportRequestConfig)
170+
export async function postExportExcel<TData = unknown>(fileName: string, url: string, params?: Dict<unknown>, data?: TData): Promise<boolean> {
171+
const response = await request.post<Blob>(url, data, { params, responseType: 'blob', skipGlobalErrorMessage: true } as ExportRequestConfig)
213172

214-
return downloadExportResponse(response, fileName, 'application/vnd.ms-excel')
215-
} finally {
216-
finishLoading(loading)
217-
}
173+
return downloadExportResponse(response, fileName, 'application/vnd.ms-excel')
218174
}
219175

220176
/** 发送指定方法的 Blob 请求并触发浏览器下载。 */
221-
export async function downloadRequest(
222-
url: string,
223-
method: string,
224-
data?: unknown,
225-
params?: Dict<unknown>,
226-
loading?: LoadingTarget,
227-
): Promise<boolean> {
228-
startLoading(loading)
229-
try {
230-
const response = await request.request<Blob>({
231-
url,
232-
method,
233-
data,
234-
params,
235-
responseType: 'blob',
236-
skipGlobalErrorMessage: true,
237-
} as ExportRequestConfig)
238-
239-
return downloadExportResponse(response, 'download')
240-
} finally {
241-
finishLoading(loading)
242-
}
177+
export async function downloadRequest(url: string, method: string, data?: unknown, params?: Dict<unknown>): Promise<boolean> {
178+
const response = await request.request<Blob>({
179+
url,
180+
method,
181+
data,
182+
params,
183+
responseType: 'blob',
184+
skipGlobalErrorMessage: true,
185+
} as ExportRequestConfig)
186+
187+
return downloadExportResponse(response, 'download')
243188
}
244189

245190
/** 发送 POST 请求并返回可逐块读取的原始响应。 */
@@ -260,13 +205,13 @@ export function postStream(base: string, path: string, data?: unknown): Promise<
260205
}
261206

262207
/** 发送 PUT 请求。 */
263-
export function put<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, loading?: LoadingTarget, timeout?: number) {
264-
return promise<T>(request.put<ApiResponse<T>>(url, data, { params, timeout }), loading)
208+
export function put<TData = unknown, T = unknown>(url: string, data?: TData, params?: Dict<unknown>, timeout?: number) {
209+
return promise<T>(request.put<ApiResponse<T>>(url, data, { params, timeout }))
265210
}
266211

267212
/** 发送 DELETE 请求。 */
268-
export function del<TData = unknown, T = unknown>(url: string, params?: Dict<unknown>, data?: TData, loading?: LoadingTarget, timeout?: number) {
269-
return promise<T>(request.delete<ApiResponse<T>>(url, { params, data, timeout }), loading)
213+
export function del<TData = unknown, T = unknown>(url: string, params?: Dict<unknown>, data?: TData, timeout?: number) {
214+
return promise<T>(request.delete<ApiResponse<T>>(url, { params, data, timeout }))
270215
}
271216

272217
export default request

‎ui/src/api/admin/core/types.ts‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
/** Admin 请求基础设施内部使用的协议和 loading 类型。 */
1+
/** Admin 请求基础设施内部使用的协议类型。 */
22

33
export interface ApiResponse<T> {
44
code: number
55
message: string
66
data: T
77
}
88

9-
interface LoadingRef {
10-
value: boolean
11-
}
12-
13-
interface LoadingProgress {
14-
start(): void
15-
done(): void
16-
}
17-
18-
export type LoadingTarget = LoadingRef | LoadingProgress
19-
209
export interface ResponsePage<T> {
2110
total: number
2211
records: T[]

‎ui/src/api/admin/store.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { get } from './core/request'
2-
import type { ApplicationStoreResponse, Dict, ToolItem, ToolStoreResponse } from '@/api/types'
2+
import type {
3+
ApplicationStoreResponse,
4+
KnowledgeWorkflowStoreResponse,
5+
Dict,
6+
ToolItem,
7+
ToolStoreResponse,
8+
ToolWorkflowStoreResponse,
9+
} from '@/api/types'
310

411
/** 获取系统内置工具。 */
512
const getInternalToolList = (query?: Dict<unknown>) => {
@@ -18,12 +25,12 @@ const getStoreApplicationList = (query?: Dict<unknown>) => {
1825

1926
/** 获取知识库模板商店列表。 */
2027
const getStoreKnowledgeList = (query?: Dict<unknown>) => {
21-
return get<unknown>('/workspace/store/knowledge_template', query)
28+
return get<KnowledgeWorkflowStoreResponse>('/workspace/store/knowledge_template', query)
2229
}
2330

2431
/** 获取工作流工具模板商店列表。 */
2532
const getStoreToolWorkflowList = (query?: Dict<unknown>) => {
26-
return get<unknown>('/workspace/store/tool_workflow_template', query)
33+
return get<ToolWorkflowStoreResponse>('/workspace/store/tool_workflow_template', query)
2734
}
2835

2936
export default { getInternalToolList, getStoreApplicationList, getStoreKnowledgeList, getStoreToolList, getStoreToolWorkflowList }
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { get, put } from '../../core/request'
2+
import type { WorkflowVersion, WorkflowVersionPayload } from '@/api/types'
3+
import { getWorkspaceId } from '@/utils/resource-context'
4+
5+
const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/knowledge_version`
6+
7+
/** 获取知识库发布历史,按发布时间倒序返回完整版本快照。 */
8+
const getWorkflowVersions = (knowledgeId: string) => get<WorkflowVersion[]>(getPrefix(knowledgeId))
9+
10+
/** 修改知识库历史版本标题和更新说明,更新说明需要服务端支持 description 字段。 */
11+
const putWorkflowVersion = (knowledgeId: string, versionId: string, data: WorkflowVersionPayload) =>
12+
put<WorkflowVersionPayload, WorkflowVersion>(`${getPrefix(knowledgeId)}/${versionId}`, data)
13+
14+
export default { getWorkflowVersions, putWorkflowVersion }

0 commit comments

Comments
 (0)