fix:
1.工作流试运行时先保存
2.搜索引擎节点添加博查搜索
3.提取设置tinyflow的公共方法
4个文件已修改
2个文件已添加
319 ■■■■■ 已修改文件
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/config/BochaaiProps.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiWorkflowController.java 53 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiWorkflowFunction.java 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/utils/TinyFlowConfigService.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-starter/src/main/resources/application.yml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/workflowDesign/WorkflowDesign.tsx 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/config/BochaaiProps.java
New file
@@ -0,0 +1,19 @@
package tech.aiflowy.ai.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "node.bochaai")
public class BochaaiProps {
    private String apiKey;
    public String getApiKey() {
        return apiKey;
    }
    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }
}
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/controller/AiWorkflowController.java
@@ -19,6 +19,7 @@
import tech.aiflowy.ai.service.AiKnowledgeService;
import tech.aiflowy.ai.service.AiLlmService;
import tech.aiflowy.ai.service.AiWorkflowService;
import tech.aiflowy.ai.utils.TinyFlowConfigService;
import tech.aiflowy.common.domain.Result;
import tech.aiflowy.common.filestorage.FileStorageService;
import tech.aiflowy.common.util.SpringContextUtil;
@@ -45,10 +46,8 @@
    @Resource
    private AiKnowledgeService aiKnowledgeService;
    @Resource(name = "default")
    FileStorageService storageService;
    @Resource
    private ReaderManager readerManager;
    private TinyFlowConfigService tinyFlowConfigService;
    public AiWorkflowController(AiWorkflowService service, AiLlmService aiLlmService) {
        super(service);
@@ -70,6 +69,9 @@
        }
        Chain chain = tinyflow.toChain();
        if (chain == null) {
            return Result.fail(2, "节点配置错误,请检查! ");
        }
        List<Parameter> chainParameters = chain.getParameters();
        return Result.success("parameters", chainParameters);
    }
@@ -84,50 +86,7 @@
        Tinyflow tinyflow = workflow.toTinyflow();
        ReadDocService readService = readerManager.getReader();
        DocNodeParser docNodeParser = new DocNodeParser(readService);
        MakeFileNodeParser makeFileNodeParser = new MakeFileNodeParser(storageService);
        ChainParser chainParser = tinyflow.getChainParser();
        chainParser.addNodeParser(docNodeParser.getNodeName(),docNodeParser);
        chainParser.addNodeParser(makeFileNodeParser.getNodeName(),makeFileNodeParser);
        tinyflow.setLlmProvider(new LlmProvider() {
            @Override
            public Llm getLlm(Object id) {
                AiLlm aiLlm = aiLlmService.getById(new BigInteger(id.toString()));
                return aiLlm.toLlm();
            }
        });
        tinyflow.setKnowledgeProvider(new KnowledgeProvider() {
            @Override
            public Knowledge getKnowledge(Object o) {
                AiKnowledge aiKnowledge = aiKnowledgeService.getById(new BigInteger(o.toString()));
                return new Knowledge() {
                    @Override
                    public List<Document> search(String keyword, int limit, KnowledgeNode knowledgeNode, Chain chain) {
                        DocumentStore documentStore = aiKnowledge.toDocumentStore();
                        if (documentStore == null) {
                            return null;
                        }
                        AiLlm aiLlm = aiLlmService.getById(aiKnowledge.getVectorEmbedLlmId());
                        if (aiLlm == null) {
                            return null;
                        }
                        documentStore.setEmbeddingModel(aiLlm.toLlm());
                        SearchWrapper wrapper = new SearchWrapper();
                        wrapper.setMaxResults(Integer.valueOf(limit));
                        wrapper.setText(keyword);
                        StoreOptions options = StoreOptions.ofCollectionName(aiKnowledge.getVectorStoreCollection());
                        List<Document> results = documentStore.search(wrapper, options);
                        return results;
                    }
                };
            }
        });
        tinyFlowConfigService.setAll(tinyflow);
        Chain chain = tinyflow.toChain();
        chain.addEventListener(new ChainEventListener() {
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/entity/AiWorkflowFunction.java
@@ -1,22 +1,12 @@
package tech.aiflowy.ai.entity;
import com.agentsflex.core.document.Document;
import com.agentsflex.core.llm.Llm;
import com.agentsflex.core.store.DocumentStore;
import com.agentsflex.core.store.SearchWrapper;
import com.agentsflex.core.store.StoreOptions;
import dev.tinyflow.core.Tinyflow;
import dev.tinyflow.core.knowledge.Knowledge;
import dev.tinyflow.core.node.KnowledgeNode;
import dev.tinyflow.core.provider.KnowledgeProvider;
import dev.tinyflow.core.provider.LlmProvider;
import tech.aiflowy.ai.service.AiKnowledgeService;
import tech.aiflowy.ai.service.AiLlmService;
import tech.aiflowy.ai.service.AiWorkflowService;
import tech.aiflowy.common.util.SpringContextUtil;
import com.agentsflex.core.chain.Chain;
import com.agentsflex.core.chain.Parameter;
import com.agentsflex.core.llm.functions.BaseFunction;
import dev.tinyflow.core.Tinyflow;
import tech.aiflowy.ai.service.AiWorkflowService;
import tech.aiflowy.ai.utils.TinyFlowConfigService;
import tech.aiflowy.common.util.SpringContextUtil;
import java.math.BigInteger;
import java.util.Arrays;
@@ -71,8 +61,7 @@
        AiWorkflow workflow = service.getById(this.workflowId);
        if (workflow != null) {
            Tinyflow tinyflow = workflow.toTinyflow();
            setLlmProvider(tinyflow);
            setKnowledgeProvider(tinyflow);
            setTinyflow(tinyflow);
            Chain chain = tinyflow.toChain();
            return chain.executeForResult(argsMap);
        } else {
@@ -80,48 +69,9 @@
        }
    }
    private void setLlmProvider(Tinyflow tinyflow) {
        AiLlmService aiLlmService = SpringContextUtil.getBean(AiLlmService.class);
        tinyflow.setLlmProvider(new LlmProvider() {
            @Override
            public Llm getLlm(Object id) {
                AiLlm aiLlm = aiLlmService.getById(new BigInteger(id.toString()));
                return aiLlm.toLlm();
            }
        });
    }
    private void setKnowledgeProvider(Tinyflow tinyflow) {
        AiLlmService aiLlmService = SpringContextUtil.getBean(AiLlmService.class);
        AiKnowledgeService aiKnowledgeService = SpringContextUtil.getBean(AiKnowledgeService.class);
        tinyflow.setKnowledgeProvider(new KnowledgeProvider() {
            @Override
            public Knowledge getKnowledge(Object o) {
                AiKnowledge aiKnowledge = aiKnowledgeService.getById(new BigInteger(o.toString()));
                return new Knowledge() {
                    @Override
                    public List<Document> search(String keyword, int limit, KnowledgeNode knowledgeNode, Chain chain) {
                        DocumentStore documentStore = aiKnowledge.toDocumentStore();
                        if (documentStore == null) {
                            return null;
                        }
                        AiLlm aiLlm = aiLlmService.getById(aiKnowledge.getVectorEmbedLlmId());
                        if (aiLlm == null) {
                            return null;
                        }
                        documentStore.setEmbeddingModel(aiLlm.toLlm());
                        SearchWrapper wrapper = new SearchWrapper();
                        wrapper.setMaxResults(Integer.valueOf(limit));
                        wrapper.setText(keyword);
                        StoreOptions options = StoreOptions.ofCollectionName(aiKnowledge.getVectorStoreCollection());
                        List<Document> results = documentStore.search(wrapper, options);
                        return results;
                    }
                };
            }
        });
    private void setTinyflow(Tinyflow tinyflow) {
        TinyFlowConfigService service = SpringContextUtil.getBean(TinyFlowConfigService.class);
        service.setAll(tinyflow);
    }
    @Override
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/utils/TinyFlowConfigService.java
New file
@@ -0,0 +1,118 @@
package tech.aiflowy.ai.utils;
import com.agentsflex.core.chain.Chain;
import com.agentsflex.core.document.Document;
import com.agentsflex.core.llm.Llm;
import com.agentsflex.core.store.DocumentStore;
import com.agentsflex.core.store.SearchWrapper;
import com.agentsflex.core.store.StoreOptions;
import dev.tinyflow.core.Tinyflow;
import dev.tinyflow.core.knowledge.Knowledge;
import dev.tinyflow.core.node.KnowledgeNode;
import dev.tinyflow.core.parser.ChainParser;
import dev.tinyflow.core.provider.KnowledgeProvider;
import dev.tinyflow.core.provider.LlmProvider;
import dev.tinyflow.core.provider.SearchEngineProvider;
import dev.tinyflow.core.searchengine.SearchEngine;
import dev.tinyflow.core.searchengine.impl.BochaaiSearchEngineImpl;
import org.springframework.stereotype.Component;
import tech.aiflowy.ai.config.BochaaiProps;
import tech.aiflowy.ai.entity.AiKnowledge;
import tech.aiflowy.ai.entity.AiLlm;
import tech.aiflowy.ai.node.DocNodeParser;
import tech.aiflowy.ai.node.MakeFileNodeParser;
import tech.aiflowy.ai.node.ReadDocService;
import tech.aiflowy.ai.node.ReaderManager;
import tech.aiflowy.ai.service.AiKnowledgeService;
import tech.aiflowy.ai.service.AiLlmService;
import tech.aiflowy.common.filestorage.FileStorageService;
import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.List;
@Component
public class TinyFlowConfigService {
    @Resource
    private ReaderManager readerManager;
    @Resource(name = "default")
    private FileStorageService storageService;
    @Resource
    private AiLlmService aiLlmService;
    @Resource
    private BochaaiProps bochaaiProps;
    @Resource
    private AiKnowledgeService aiKnowledgeService;
    public void setAll(Tinyflow tinyflow) {
        setExtraNodeParser(tinyflow);
        setSearchEngineProvider(tinyflow);
        setLlmProvider(tinyflow);
        setKnowledgeProvider(tinyflow);
    }
    public void setExtraNodeParser(Tinyflow tinyflow) {
        ReadDocService readService = readerManager.getReader();
        // 文档解析
        DocNodeParser docNodeParser = new DocNodeParser(readService);
        // 文件生成
        MakeFileNodeParser makeFileNodeParser = new MakeFileNodeParser(storageService);
        ChainParser chainParser = tinyflow.getChainParser();
        chainParser.addNodeParser(docNodeParser.getNodeName(), docNodeParser);
        chainParser.addNodeParser(makeFileNodeParser.getNodeName(), makeFileNodeParser);
    }
    public void setSearchEngineProvider(Tinyflow tinyflow) {
        tinyflow.setSearchEngineProvider(new SearchEngineProvider() {
            @Override
            public SearchEngine getSearchEngine(Object id) {
                BochaaiSearchEngineImpl searchEngine = new BochaaiSearchEngineImpl();
                searchEngine.setApiKey(bochaaiProps.getApiKey());
                return searchEngine;
            }
        });
    }
    public void setLlmProvider(Tinyflow tinyflow) {
        tinyflow.setLlmProvider(new LlmProvider() {
            @Override
            public Llm getLlm(Object id) {
                AiLlm aiLlm = aiLlmService.getById(new BigInteger(id.toString()));
                return aiLlm.toLlm();
            }
        });
    }
    public void setKnowledgeProvider(Tinyflow tinyflow) {
        tinyflow.setKnowledgeProvider(new KnowledgeProvider() {
            @Override
            public Knowledge getKnowledge(Object id) {
                AiKnowledge aiKnowledge = aiKnowledgeService.getById(new BigInteger(id.toString()));
                return new Knowledge() {
                    @Override
                    public List<Document> search(String keyword, int limit, KnowledgeNode knowledgeNode, Chain chain) {
                        DocumentStore documentStore = aiKnowledge.toDocumentStore();
                        if (documentStore == null) {
                            return null;
                        }
                        AiLlm aiLlm = aiLlmService.getById(aiKnowledge.getVectorEmbedLlmId());
                        if (aiLlm == null) {
                            return null;
                        }
                        documentStore.setEmbeddingModel(aiLlm.toLlm());
                        SearchWrapper wrapper = new SearchWrapper();
                        wrapper.setMaxResults(Integer.valueOf(limit));
                        wrapper.setText(keyword);
                        StoreOptions options = StoreOptions.ofCollectionName(aiKnowledge.getVectorStoreCollection());
                        List<Document> results = documentStore.search(wrapper, options);
                        return results;
                    }
                };
            }
        });
    }
}
aiflowy-starter/src/main/resources/application.yml
@@ -38,6 +38,9 @@
  reader: 'defaultReader'
  gitee:
    appKey: 'xxx'
  # 搜索引擎节点 - 目前只支持博查搜索
  bochaai:
    apiKey: 'xxx'
jetcache:
  # 缓存类型,可选值:local/remote/both CacheConfig 类初始化
  cacheType: local
aiflowy-ui-react/src/pages/ai/workflowDesign/WorkflowDesign.tsx
@@ -16,11 +16,26 @@
    const {message} = App.useApp()
    const params = useParams();
    const {result: workflow, doGet: reGetWorkflow} = useDetail("aiWorkflow", params.id);
    const {result: workflow} = useDetail("aiWorkflow", params.id);
    const {doUpdate} = useUpdate("aiWorkflow");
    const {result: llms} = useGet('/api/v1/aiLlm/list')
    const {result: knowledge} = useGet('/api/v1/aiKnowledge/list')
    const [parameters, setParameters] = useState<any[]>()
    const [workflowData, setWorkflowData] = useState<any>({})
    const [saveLoading, setSaveLoading] = useState(false);
    const [runLoading, setRunLoading] = useState(false);
    // 添加 useEffect 监听 workflow 变化
    useEffect(() => {
        if (workflow?.data?.content) {
            try {
                setWorkflowData(JSON.parse(workflow.data.content))
            } catch (e) {
                setWorkflowData({})
            }
        }
    }, [workflow])
    const getOptions = (options: { id: any; title: any }[]): { value: any; label: any }[] => {
        if (options) {
            return options.map((item) => ({
@@ -35,7 +50,11 @@
    const provider = {
        llm: () => getOptions(llms?.data),
        knowledge: (): any => getOptions(knowledge?.data)
        knowledge: (): any => getOptions(knowledge?.data),
        searchEngine: (): any => [{
            value: 'bocha',
            label: '博查搜索',
        }]
    }
    const {setOptions} = useLayout();
    useEffect(() => {
@@ -47,15 +66,17 @@
    const saveHandler = () => {
        console.log("data: ", tinyflowRef.current!.getData())
        setSaveLoading(true)
        doUpdate({
            data: {
                id: params.id,
                content: tinyflowRef.current!.getData()
            }
        }).then(reGetWorkflow)
            .then(() => {
                message.success('保存成功')
            })
        }).then(() => {
            setSaveLoading(false)
            // console.log("res: ", res)
            message.success('保存成功')
        })
    }
    const onKeydown = (event: KeyboardEvent) => {
@@ -82,15 +103,24 @@
    const {doGet: getRunningParameters} = useGetManual("/api/v1/aiWorkflow/getRunningParameters");
    const {doPost: tryRunning} = usePostManual("/api/v1/aiWorkflow/tryRunning");
    const showRunningParameters = () => {
    const showRunningParameters = async () => {
        setRunLoading(true)
        await doUpdate({
            data: {
                id: params.id,
                content: tinyflowRef.current!.getData()
            }
        })
        getRunningParameters({
            params: {
                id: params.id,
            }
        }).then((resp) => {
            console.log(resp)
            setParameters(resp.data.parameters)
            showDrawer()
            if (resp.data.errorCode === 0) {
                showDrawer()
                setParameters(resp.data.parameters)
            }
            setRunLoading(false)
        })
    }
@@ -113,7 +143,9 @@
                variables: values
            }
        }).then((resp) => {
            message.success("成功")
            if (resp.data.errorCode === 0) {
                message.success("成功")
            }
            setSubmitLoading(false)
            setExecuteResult(JSON.stringify(resp.data))
        })
@@ -237,11 +269,11 @@
                            <FormOutlined/>
                        </div>
                        <div style={{display: "flex", gap: "10px"}}>
                            <Button type={"default"} onClick={showRunningParameters}> <SendOutlined/> 试运行</Button>
                            <Button type={"primary"} onClick={saveHandler}>保存 (Ctrl + s)</Button>
                            <Button type={"default"} loading={runLoading} onClick={showRunningParameters}> <SendOutlined/> 试运行</Button>
                            <Button type={"primary"} loading={saveLoading} onClick={saveHandler}>保存 (Ctrl + s)</Button>
                        </div>
                    </div>
                    <Tinyflow ref={tinyflowRef} data={JSON.parse(workflow?.data?.content || '{}')}
                    <Tinyflow ref={tinyflowRef} data={workflowData}
                              provider={provider}
                              // onChange={(data: any) => {
                              //     console.log(data)