黑炭儿呀
2025-05-22 90fb6040154256e0c34a369207a90a3c3f3ed5c7
Merge remote-tracking branch 'origin/main'
3个文件已修改
1个文件已添加
217 ■■■■■ 已修改文件
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiWorkflowController.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/workflowDesign/SingleRun.tsx 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/workflowDesign/WorkflowDesign.tsx 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/workflowDesign/customNode/sqlNode.ts 4 ●●●● 补丁 | 查看 | 原始文档 | 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>
aiflowy-ui-react/src/pages/ai/workflowDesign/customNode/sqlNode.ts
@@ -1,9 +1,9 @@
export default {
    'sql-node': {
        title: 'sql查询',
        title: 'SQL 查询',
        group: 'base',
        description: '构建sql,查询结果',
        description: '通过 SQL,查询数据库',
        icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="rgba(37,99,235,1)"><path d="M5 12.5C5 12.8134 5.46101 13.3584 6.53047 13.8931C7.91405 14.5849 9.87677 15 12 15C14.1232 15 16.0859 14.5849 17.4695 13.8931C18.539 13.3584 19 12.8134 19 12.5V10.3287C17.35 11.3482 14.8273 12 12 12C9.17273 12 6.64996 11.3482 5 10.3287V12.5ZM19 15.3287C17.35 16.3482 14.8273 17 12 17C9.17273 17 6.64996 16.3482 5 15.3287V17.5C5 17.8134 5.46101 18.3584 6.53047 18.8931C7.91405 19.5849 9.87677 20 12 20C14.1232 20 16.0859 19.5849 17.4695 18.8931C18.539 18.3584 19 17.8134 19 17.5V15.3287ZM3 17.5V7.5C3 5.01472 7.02944 3 12 3C16.9706 3 21 5.01472 21 7.5V17.5C21 19.9853 16.9706 22 12 22C7.02944 22 3 19.9853 3 17.5ZM12 10C14.1232 10 16.0859 9.58492 17.4695 8.89313C18.539 8.3584 19 7.81342 19 7.5C19 7.18658 18.539 6.6416 17.4695 6.10687C16.0859 5.41508 14.1232 5 12 5C9.87677 5 7.91405 5.41508 6.53047 6.10687C5.46101 6.6416 5 7.18658 5 7.5C5 7.81342 5.46101 8.3584 6.53047 8.89313C7.91405 9.58492 9.87677 10 12 10Z"></path></svg>',
        sortNo: 803,
        parametersAddEnable: true,