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)