Files
supervision-test-frontend-repo/src/views/super/airag/aiflow/api/api.ts
T
2026-04-09 15:09:34 +08:00

296 lines
8.2 KiB
TypeScript

import {defHttp} from "@/utils/http/axios";
import {useMessage} from '/@/hooks/web/useMessage';
import dayjs from "dayjs";
enum Api {
// 运行流程(需要保存)
run = '/airag/flow/run',
// 调试流程(不需要保存)
debug = '/airag/flow/debug',
// invoke = '/airag/flow/invoke',
// 获取流程列表
list = '/airag/flow/list',
// 添加流程
add = '/airag/flow/add',
// 编辑流程(不包含design数据)
edit = '/airag/flow/edit',
// 仅保存流程设计
designSave = '/airag/flow/design/save',
// 通过id删除流程
deleteById = '/airag/flow/delete',
// 批量删除流程
deleteBatch = '/airag/flow/deleteBatch',
// 获取子流程列表(包括入参)
subflowList = '/airag/flow/subflowList',
// 根据ID获取单个子流程
querySubflowById = '/airag/flow/querySubflowById',
}
const {createMessage: $message} = useMessage();
// 运行流程超时时间(5分钟)
const runTimeout = 5 * 60 * 1000;
/**
* 阻塞式运行流程
* @param flowId
* @param inputParams
*/
export async function blockRun(flowId: string, inputParams: Recordable) {
return defHttp.post({
url: Api.run,
params: {
flowId: flowId,
inputParams: inputParams,
responseMode: 'blocking'
},
timeout: runTimeout,
});
}
/**
* 流式运行流程
// * @param flowId NY5LzSY2VW1BSthYSnJArCFqbgwtZqSuyPQ/OD1n1twWJGU2RN/wkzf+kBVO5DztN85Ca9keeuaRAiwcatr8N0M15+Wv2SmRw82lMwawE2naX5tpJMpkxrUhUUcjnC+BSBL4+PV2JUXBFW8/oOG8HLqYvmPxoP7MMBhMi9D7lRY=
* @param flowRecord
* @param inputParams
*/
// export function createStreamRun(flowId: string, inputParams: Recordable) {
export function createStreamRun(/*flowId: string, */ flowRecord: Recordable, inputParams: Recordable) {
type RunEventType = 'FLOW_STARTED' | 'NODE_STARTED' | 'NODE_FINISHED' | 'FLOW_FINISHED' | 'MESSAGE';
const logColor = (text: string, color: string) => [`%c${text}`, `color: ${color};`];
const lcInfo = () => logColor('[INFO]', '#2196F3');
const lcWarn = () => logColor('[WARN]', '#FFC107');
const lcError = () => logColor('[ERRO]', '#F44336');
const colorTextRun = logColor('[stream-run]', '#999999');
const debugStreamRun = (lcFn: Fn, ...args: any[]) => {
const colorTextType: string[] = lcFn();
const colorText = [
`${colorTextRun[0]} ${colorTextType[0]}`,
colorTextRun[1], colorTextType[1],
];
const dateText = dayjs().format('HH:mm:ss.SSS');
console.debug(...colorText, `[${dateText}]`, ...args);
};
// 记录解析失败的数据
let failChunkText = '';
// 发送请求
async function send() {
const readableStream = await defHttp.post({
url: Api.debug,
params: {
// flowId: flowId,
flow: flowRecord,
inputParams: inputParams,
responseMode: 'streaming',
},
adapter: 'fetch',
responseType: 'stream',
timeout: runTimeout,
}, {
isTransformResponse: false,
});
const reader = readableStream.getReader();
const decoder = new TextDecoder();
failChunkText = '';
while (true) {
const {done, value} = await reader.read();
if (done) {
break;
}
const chunkText = decoder.decode(value, {stream: true});
try {
debugStreamRun(lcInfo, `收到 chunkText:`, {chunkText});
handleChunkText(chunkText);
} catch (error) {
console.error('Error parsing update:', error);
}
}
}
/**
* 处理 chunkText
* @param chunkText
*/
function handleChunkText(chunkText: string) {
if (!chunkText) {
debugStreamRun(lcError, 'chunkText 为空:', {chunkText});
return;
}
// 如果包含解析失败的数据,则合并再解析
const hasFailChunkText = failChunkText.length > 0;
if (hasFailChunkText) {
chunkText = failChunkText + chunkText;
debugStreamRun(lcInfo, '合并解析失败的数据:', {chunkText});
failChunkText = '';
}
let hasFailChunk = false;
const chunks = chunkText.split('\n').flatMap((chunk: string) => {
chunk = chunk ? chunk.trim() : '';
if (!chunk) {
return []
}
if (chunk.startsWith('data:')) {
chunk = chunk.slice(5);
}
if (!chunk) {
debugStreamRun(lcError, 'chunk 为空:', {chunk, chunkText});
return [];
}
try {
return [
JSON.parse(chunk)
];
} catch (e) {
hasFailChunk = true;
debugStreamRun(lcError, 'chunk 解析失败:', {chunk, chunkText});
console.error(e);
}
return [];
});
if (hasFailChunk) {
// 解析失败是因为数据量太大,一次性传输的 chunk 不完整,需要合并再解析
// 记录解析失败的数据
failChunkText += chunkText;
}
chunks.forEach(handleChunkData);
}
const cbMap = new Map<RunEventType, Fn>();
const chunkHandled = new Set<string>();
function handleChunkData(data: {
data: Recordable,
event: RunEventType,
flowId?: string,
requestId: string,
success?: boolean,
message?: string,
}) {
//update-begin---author:wangshuai---date:2025-03-25---for:【QQYUN-11724】调试流程时,如果直接失败,调试界面会卡主---
if (data.success == false) {
let cb = cbMap.get("FLOW_FINISHED");
if (typeof cb === 'function') {
cb(data);
return;
}
}
//update-end---author:wangshuai---date:2025-03-25---for:【QQYUN-11724】调试流程时,如果直接失败,调试界面会卡主---
const key = `${data.event}-${data.data?.id || data.data?.fromNodeId || ''}`;
if (chunkHandled.has(key)) {
debugStreamRun(lcWarn, 'chunk 重复执行:', {key, data});
return;
}
chunkHandled.add(key);
const cb = cbMap.get(data.event);
if (typeof cb === 'function') {
debugStreamRun(lcInfo, ` ------ 处理 ${data.event} 事件:`, {key, data});
cb(data.data, data);
} else {
debugStreamRun(lcWarn, `${data.event} 事件对应的回调不存在:`, {key, data});
}
}
function setCB(type: RunEventType, cb: Fn) {
cbMap.set(type, cb);
}
return {
run: send,
onFlowStarted: (cb: Fn) => setCB('FLOW_STARTED', cb),
onFlowFinished: (cb: Fn) => setCB('FLOW_FINISHED', cb),
onNodeStarted: (cb: Fn) => setCB('NODE_STARTED', cb),
onNodeFinished: (cb: Fn) => setCB('NODE_FINISHED', cb),
onMessage: (cb: Fn) => setCB('MESSAGE', cb),
}
}
export async function getProcessList(params?: any) {
return defHttp.get({url: Api.list, params});
}
export async function addProcess(data: Recordable, opt?: Recordable) {
const silent = opt?.silent ?? false;
return defHttp.post({url: Api.add, params: data}, {
successMessageMode: silent ? 'none' : 'success',
});
}
export async function updateProcess(data: Recordable) {
return defHttp.put({url: Api.edit, params: data});
}
// 发布流程
export async function releaseProcess(flowId: string, un = false) {
const msg = un ? '取消发布' : '发布'
const res = await defHttp.put({
url: Api.edit, params: {
id: flowId,
status: un ? 'enable' : 'release',
}
}, {successMessageMode: 'none', isTransformResponse: false});
if (res.success) {
$message.success(`${msg}成功`)
return true;
}
$message.warn(res.message || `${msg}失败`)
return false;
}
export async function updateDesign(data: Recordable, opt?: Recordable) {
const silent = opt?.silent ?? false;
return defHttp.put({
url: Api.designSave, params: {
id: data.id,
name: data.name,
chain: data.chain,
design: data.design,
}
}, {
successMessageMode: silent ? 'none' : 'success',
});
}
export async function deleteProcess(id: string) {
return defHttp.delete({
url: Api.deleteById,
data: {id}
}, {
joinParamsToUrl: true
});
}
export async function deleteBatchProcess(idList: string[]) {
return defHttp.delete({
url: Api.deleteBatch,
data: {
ids: idList.join(',')
}
}, {
joinParamsToUrl: true
});
}
export async function querySubflowList(
params: Recordable,
) {
return defHttp.get({
url: Api.subflowList,
params: params,
});
}
export async function querySubflowById(subflowId: string) {
return defHttp.get({
url: Api.querySubflowById,
params: {subflowId}
});
}