| aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiWorkflowController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| aiflowy-ui-react/src/pages/ai/workflowDesign/SingleRun.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| aiflowy-ui-react/src/pages/ai/workflowDesign/WorkflowDesign.tsx | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiWorkflowController.java
@@ -1,9 +1,13 @@ package tech.aiflowy.ai.controller; import cn.dev33.satoken.annotation.SaIgnore; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.StrUtil; import com.agentsflex.core.chain.*; import com.alibaba.fastjson.JSONObject; import dev.tinyflow.core.Tinyflow; import dev.tinyflow.core.parser.NodeParser; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -20,6 +24,7 @@ import java.io.InputStream; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -98,6 +103,7 @@ } }); chain.addOutputListener(new ChainOutputListener() { @Override public void onOutput(Chain chain, ChainNode node, Object outputMessage) { @@ -130,4 +136,68 @@ apiKeyService.checkApiKey(apiKey); return tryRunning(id, variables); } @PostMapping("/singleRun") public Result singleRun( @JsonBody(value = "id", required = true) BigInteger id, @JsonBody(value = "node", required = true) Map<String, Object> node, @JsonBody("variables") Map<String, Object> variables) { AiWorkflow workflow = service.getById(id); if (workflow == null) { return Result.fail(1, "工作流不存在"); } List<ChainNode> nodes = new ArrayList<>(); Tinyflow tinyflow = workflow.toTinyflow(); Chain fullChain = tinyflow.toChain(); if (fullChain != null) { nodes = fullChain.getNodes(); } Map<String, NodeParser> map = tinyflow.getChainParser().getNodeParserMap(); NodeParser parser = map.get(node.get("type").toString()); if (parser == null) { return Result.fail(1, "节点类型不存在"); } ChainNode currentNode = parser.parse(new JSONObject(node), tinyflow); if (currentNode == null) { return Result.fail(1, "节点不存在"); } currentNode.setInwardEdges(null); currentNode.setOutwardEdges(null); fixParamType(nodes, currentNode); Chain chain = new Chain(); chain.addNode(currentNode); Map<String, Object> res = chain.executeForResult(variables); return Result.success(res); } /** * 修正引用类的值类型 */ private void fixParamType(List<ChainNode> allNodes, ChainNode currentNode) { List<Parameter> currentParams = currentNode.getParameters(); if (CollectionUtil.isEmpty(currentParams)) { return; } for (Parameter parameter : currentParams) { RefType refType = parameter.getRefType(); if (refType.equals(RefType.REF)) { parameter.setRefType(RefType.INPUT); String ref = parameter.getRef(); if (StrUtil.isNotEmpty(ref)) { for (ChainNode node : allNodes) { List<Parameter> parameters = node.getParameters(); if (parameters != null) { for (Parameter nodeParameter : parameters) { String nodeAttr = node.getId() + "." + nodeParameter.getName(); if (ref.equals(nodeAttr)) { parameter.setDataType(nodeParameter.getDataType()); } } } } } } } } } aiflowy-ui-react/src/pages/ai/workflowDesign/SingleRun.tsx
New file @@ -0,0 +1,96 @@ import React, {forwardRef, useImperativeHandle, useState} from "react"; import {Button, Empty, Form, message, Spin} from "antd"; import {SendOutlined} from "@ant-design/icons"; import {usePostManual} from "../../../hooks/useApis.ts"; import JsonView from "react18-json-view"; import {DynamicItem} from "../../../libs/workflowUtil.tsx"; export type SingleRunProps = { ref?: any, workflowId: any, node: any } export const SingleRun: React.FC<SingleRunProps> = forwardRef(({workflowId, node}, ref) => { const {doPost: doSingleRun} = usePostManual("/api/v1/aiWorkflow/singleRun") const [form] = Form.useForm(); const [submitLoading, setSubmitLoading] = useState(false) const [executeResult, setExecuteResult] = useState<any>("") const onFinish = (values: any) => { const params = { id: workflowId, node: node, variables: values } setSubmitLoading(true) doSingleRun({ data: params }).then(res => { setExecuteResult(res.data.data) setSubmitLoading(false) if (res.data.errorCode == 0) { message.success("成功") } else { message.error(res.data.message) } }) }; const onFinishFailed = (errorInfo: any) => { console.log('Failed:', errorInfo); }; useImperativeHandle(ref, () => ({ resetForm: () => { form.resetFields() setExecuteResult("") } })); return ( <> <Spin spinning={submitLoading}> <Form form={form} name="basic" labelCol={{span: 5}} wrapperCol={{span: 16}} onFinish={onFinish} onFinishFailed={onFinishFailed} autoComplete="off" > {node.data?.parameters?.map((item: any) => { let text = item.description ? item.description : item.name return ( <Form.Item key={item.id} label={text} name={item.name} rules={[{required: true, message: '请输入' + text}]} > {DynamicItem(item, form, item.name)} </Form.Item> ) })} <Form.Item wrapperCol={{offset: 5, span: 18}}> <Button disabled={submitLoading} loading={submitLoading} type="primary" htmlType="submit"> <SendOutlined/> 开始运行 </Button> </Form.Item> </Form> <div style={{marginBottom: "10px"}}>执行结果:</div> <div style={{ padding: "20px", backgroundColor: "#fafafa", textAlign: "center", wordWrap: "break-word", }} > {executeResult ? <JsonView src={executeResult}/> : <Empty description={'暂无内容'} image={Empty.PRESENTED_IMAGE_SIMPLE}/> } </div> </Spin> </> ) }) aiflowy-ui-react/src/pages/ai/workflowDesign/WorkflowDesign.tsx
@@ -10,6 +10,7 @@ import customNode from './customNode/index.ts' import {PluginNode} from './customNode/pluginNode.ts' import {PluginTools} from "../botDesign/PluginTools.tsx"; import {SingleRun} from "./SingleRun.tsx"; export const WorkflowDesign = () => { @@ -132,6 +133,10 @@ } const [open, setOpen] = useState(false); const [singleRunOpen, setSingleRunOpen] = useState(false); const singleRunRef = useRef<any>(null) const [currentNode, setCurrentNode] = useState<any>(null) const showDrawer = () => { setOpen(true); @@ -139,6 +144,11 @@ const onClose = () => { setOpen(false); }; const onSingleRunClose = () => { setSingleRunOpen(false); singleRunRef.current.resetForm() }; const onFinish = (values: any) => { @@ -306,6 +316,17 @@ </Drawer> <Drawer width={640} title="请输入参数" placement="right" closable={false} onClose={onSingleRunClose} open={singleRunOpen} > <SingleRun ref={singleRunRef} workflowId={params.id} node={currentNode} /> </Drawer> <div style={{height: 'calc(100vh - 50px)', display: "flex"}} className={"agentsflow"}> <div style={{flexGrow: 1}}> <div style={{ @@ -335,16 +356,22 @@ <Spin spinning={pageLoading} > <Tinyflow ref={tinyflowRef} data={workflowData} provider={provider} // onChange={(data: any) => { // console.log(data) // setWorkflow({ // ...workflow, // data: { // ...workflow?.data, // content: JSON.stringify(data) // } // }) // }} onNodeExecute={async (node) => { if ("loopNode" === node.type) { message.warning("暂不支持") return } setCurrentNode(node) setPageLoading(true) await doUpdate({ data: { id: params.id, content: tinyflowRef.current!.getData() } }) setPageLoading(false) setSingleRunOpen(true) }} style={{height: 'calc(100vh - 10px)'}} customNodes={customNodes}/> </Spin> : <div style={{padding: '20px'}}><Skeleton active /></div>