diff --git a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/BaseNode.java b/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/BaseNode.java deleted file mode 100644 index 576a06c..0000000 --- a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/BaseNode.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.lanyuanxiaoyao.service.ai.web.flow; - -import com.yomahub.liteflow.core.NodeComponent; - -/** - * @author lanyuanxiaoyao - * @version 20250625 - */ -public abstract class BaseNode extends NodeComponent { -} diff --git a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/EndNode.java b/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/EndNode.java deleted file mode 100644 index ef9cffa..0000000 --- a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/EndNode.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.lanyuanxiaoyao.service.ai.web.flow; - -/** - * @author lanyuanxiaoyao - * @version 20250625 - */ -public class EndNode extends BaseNode { - @Override - public void process() throws Exception { - - } -} diff --git a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LiteFlowService.java b/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LiteFlowService.java deleted file mode 100644 index 8307e55..0000000 --- a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LiteFlowService.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.lanyuanxiaoyao.service.ai.web.flow; - -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.yomahub.liteflow.builder.LiteFlowNodeBuilder; -import com.yomahub.liteflow.builder.el.ELBus; -import com.yomahub.liteflow.core.NodeComponent; -import com.yomahub.liteflow.enums.NodeTypeEnum; -import java.util.List; -import java.util.Map; -import lombok.Data; -import lombok.extern.slf4j.Slf4j; - -/** - * @author lanyuanxiaoyao - * @version 20250625 - */ -@Slf4j -public class LiteFlowService { - public LiteFlowService() { - createNode("start-amis-node", NodeTypeEnum.COMMON, StartNode.class); - createNode("end-amis-node", NodeTypeEnum.COMMON, EndNode.class); - createNode("llm-amis-node", NodeTypeEnum.COMMON, LlmNode.class); - } - - private static void createNode(String name, NodeTypeEnum type, Class clazz) { - LiteFlowNodeBuilder.createNode() - .setId(name) - .setName(name) - .setType(type) - .setClazz(clazz) - .build(); - } - - @Data - public static class FlowData { - private List nodes; - private List edges; - private Map data; - - @Data - public static class Node { - private String id; - private String type; - } - - @Data - public static class Edge { - private String id; - private String source; - private String target; - } - } - - public static void main(String[] args) throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - // language=JSON - String source = """ - { - "nodes": [ - { - "id": "BMFP3Eov94", - "type": "start-amis-node", - "position": { - "x": 10, - "y": 100 - }, - "data": {}, - "measured": { - "width": 256, - "height": 83 - }, - "selected": false - }, - { - "id": "PYK8LjduQ1", - "type": "end-amis-node", - "position": { - "x": 654, - "y": 332 - }, - "data": {}, - "measured": { - "width": 256, - "height": 83 - }, - "selected": false, - "dragging": false - }, - { - "id": "nCm-ij5I6o", - "type": "llm-amis-node", - "position": { - "x": 318, - "y": 208 - }, - "data": {}, - "measured": { - "width": 256, - "height": 83 - }, - "selected": true, - "dragging": false - } - ], - "edges": [ - { - "source": "BMFP3Eov94", - "target": "nCm-ij5I6o", - "id": "xy-edge__BMFP3Eov94-nCm-ij5I6o" - }, - { - "source": "nCm-ij5I6o", - "target": "PYK8LjduQ1", - "id": "xy-edge__nCm-ij5I6o-PYK8LjduQ1" - } - ], - "data": { - "BMFP3Eov94": { - "inputs": { - "name": { - "type": "text" - }, - "description": { - "type": "text", - "description": "文件描述" - } - } - }, - "nCm-ij5I6o": { - "model": "qwen3", - "outputs": { - "text": { - "type": "string" - } - }, - "systemPrompt": "你是个沙雕" - } - } - } - """; - FlowData root = mapper.readValue(StrUtil.trim(source), FlowData.class); - log.info("{}", root); - log.info( - "\n{}", - ELBus.ser( - "start-amis-node", - ELBus.ser("start-amis-node", "end-amis-node"), - "end-amis-node" - ).toEL(true) - ); - } -} diff --git a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LlmNode.java b/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LlmNode.java deleted file mode 100644 index 4dc9d70..0000000 --- a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/LlmNode.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.lanyuanxiaoyao.service.ai.web.flow; - -/** - * @author lanyuanxiaoyao - * @version 20250625 - */ -public class LlmNode extends BaseNode { - @Override - public void process() throws Exception { - - } -} diff --git a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/StartNode.java b/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/StartNode.java deleted file mode 100644 index 4d1a34f..0000000 --- a/service-ai/service-ai-web/src/test/java/com/lanyuanxiaoyao/service/ai/web/flow/StartNode.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.lanyuanxiaoyao.service.ai.web.flow; - -/** - * @author lanyuanxiaoyao - * @version 20250625 - */ -public class StartNode extends BaseNode { - @Override - public void process() throws Exception { - - } -} diff --git a/service-web/client/package.json b/service-web/client/package.json index 8395381..eaa7978 100644 --- a/service-web/client/package.json +++ b/service-web/client/package.json @@ -30,6 +30,7 @@ "react-markdown": "^10.1.0", "react-router": "^7.6.2", "styled-components": "^6.1.19", + "yocto-queue": "^1.2.1", "zustand": "^5.0.5" }, "devDependencies": { diff --git a/service-web/client/pnpm-lock.yaml b/service-web/client/pnpm-lock.yaml index 83648ca..d5a9534 100644 --- a/service-web/client/pnpm-lock.yaml +++ b/service-web/client/pnpm-lock.yaml @@ -71,6 +71,9 @@ importers: styled-components: specifier: ^6.1.19 version: 6.1.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + yocto-queue: + specifier: ^1.2.1 + version: 1.2.1 zustand: specifier: ^5.0.5 version: 5.0.5(@types/react@18.3.23)(react@18.3.1)(use-sync-external-store@1.5.0(react@18.3.1)) @@ -3480,6 +3483,10 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yocto-queue@1.2.1: + resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + engines: {node: '>=12.20'} + zip-stream@4.1.1: resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} engines: {node: '>= 10'} @@ -7500,6 +7507,8 @@ snapshots: yallist@4.0.0: optional: true + yocto-queue@1.2.1: {} + zip-stream@4.1.1: dependencies: archiver-utils: 3.0.4 diff --git a/service-web/client/src/pages/ai/flow/FlowEditor.tsx b/service-web/client/src/pages/ai/flow/FlowEditor.tsx index dc9f8aa..0c12140 100644 --- a/service-web/client/src/pages/ai/flow/FlowEditor.tsx +++ b/service-web/client/src/pages/ai/flow/FlowEditor.tsx @@ -4,7 +4,7 @@ import { BackgroundVariant, type Connection, Controls, - getIncomers, + type Edge, getOutgoers, MiniMap, type Node, @@ -26,6 +26,7 @@ import LlmNode from './node/LlmNode.tsx' import StartNode from './node/StartNode.tsx' import {useDataStore} from './store/DataStore.ts' import {useFlowStore} from './store/FlowStore.ts' +import Queue from 'yocto-queue' const FlowableDiv = styled.div` height: 100%; @@ -99,7 +100,7 @@ function FlowEditor() { ]) const [open, setOpen] = useState(false) - const {data, setData, getDataById, setDataById} = useDataStore() + const {setData, getDataById, setDataById} = useDataStore() const { nodes, getNodeById, @@ -220,21 +221,11 @@ function FlowEditor() { } else if (hasCycle(targetNode)) { throw new Error('禁止流程循环') } - - const hasShortcut = (node: Node, visited = new Set()) => { - if (visited.has(node.id)) return false - visited.add(node.id) - for (const incomer of getIncomers(node, nodes, edges)) { - if (isEqual(incomer.id, sourceNode?.id)) return true - if (hasShortcut(incomer, visited)) return true - } - } - console.log(getIncomers(targetNode, nodes, edges)) } useMount(() => { // language=JSON - let initialData = JSON.parse('{\n "nodes": [\n {\n "id": "BMFP3Eov94",\n "type": "start-amis-node",\n "position": {\n "x": 8,\n "y": 272\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "PYK8LjduQ1",\n "type": "end-amis-node",\n "position": {\n "x": 1439.5556937134281,\n "y": 282.2797340760818\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "nCm-ij5I6o",\n "type": "llm-amis-node",\n "position": {\n "x": 902.6781018665707,\n "y": 115.31234529524048\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "9RIg65O0YQ",\n "type": "knowledge-amis-node",\n "position": {\n "x": 338,\n "y": 287\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "2vTyjP0Gu9",\n "type": "code-amis-node",\n "position": {\n "x": 1086.6322978498904,\n "y": 371.3061114283591\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": true,\n "dragging": false\n },\n {\n "id": "s9VfZpvb4P",\n "type": "llm-amis-node",\n "position": {\n "x": 700.0944461714178,\n "y": 369.84258971430364\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "BMFP3Eov94",\n "target": "9RIg65O0YQ",\n "id": "xy-edge__BMFP3Eov94-9RIg65O0YQ"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "nCm-ij5I6o",\n "id": "xy-edge__9RIg65O0YQ-nCm-ij5I6o"\n },\n {\n "source": "nCm-ij5I6o",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__nCm-ij5I6o-PYK8LjduQ1"\n },\n {\n "source": "s9VfZpvb4P",\n "target": "2vTyjP0Gu9",\n "id": "xy-edge__s9VfZpvb4P-2vTyjP0Gu9"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "s9VfZpvb4P",\n "id": "xy-edge__9RIg65O0YQ-s9VfZpvb4P"\n },\n {\n "source": "2vTyjP0Gu9",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__2vTyjP0Gu9-PYK8LjduQ1"\n }\n ],\n "data": {\n "BMFP3Eov94": {\n "inputs": {\n "name": {\n "type": "text"\n },\n "description": {\n "type": "text",\n "description": "文件描述"\n }\n }\n },\n "nCm-ij5I6o": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个沙雕"\n },\n "9RIg65O0YQ": {\n "count": 3,\n "score": 0.75,\n "knowledgeId": 3585368238960640,\n "query": "hello world"\n },\n "2vTyjP0Gu9": {\n "type": "python",\n "content": "code=\'hello\'\\nprint(code)"\n },\n "s9VfZpvb4P": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n }\n }\n}') + let initialData = JSON.parse('{\n "nodes": [\n {\n "id": "BMFP3Eov94",\n "type": "start-amis-node",\n "position": {\n "x": 8,\n "y": 272\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "PYK8LjduQ1",\n "type": "end-amis-node",\n "position": {\n "x": 1439.5556937134281,\n "y": 282.2797340760818\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "nCm-ij5I6o",\n "type": "llm-amis-node",\n "position": {\n "x": 902.6781018665707,\n "y": 115.31234529524048\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "9RIg65O0YQ",\n "type": "knowledge-amis-node",\n "position": {\n "x": 338,\n "y": 287\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n },\n {\n "id": "2vTyjP0Gu9",\n "type": "code-amis-node",\n "position": {\n "x": 1086.6322978498904,\n "y": 371.3061114283591\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": true,\n "dragging": false\n },\n {\n "id": "s9VfZpvb4P",\n "type": "llm-amis-node",\n "position": {\n "x": 700.0944461714178,\n "y": 369.84258971430364\n },\n "data": {},\n "measured": {\n "width": 256,\n "height": 75\n },\n "selected": false,\n "dragging": false\n }\n ],\n "edges": [\n {\n "source": "BMFP3Eov94",\n "target": "9RIg65O0YQ",\n "id": "xy-edge__BMFP3Eov94-9RIg65O0YQ"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "nCm-ij5I6o",\n "id": "xy-edge__9RIg65O0YQ-nCm-ij5I6o"\n },\n {\n "source": "nCm-ij5I6o",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__nCm-ij5I6o-PYK8LjduQ1"\n },\n {\n "source": "s9VfZpvb4P",\n "target": "2vTyjP0Gu9",\n "id": "xy-edge__s9VfZpvb4P-2vTyjP0Gu9"\n },\n {\n "source": "9RIg65O0YQ",\n "target": "s9VfZpvb4P",\n "id": "xy-edge__9RIg65O0YQ-s9VfZpvb4P"\n },\n {\n "source": "2vTyjP0Gu9",\n "target": "PYK8LjduQ1",\n "id": "xy-edge__2vTyjP0Gu9-PYK8LjduQ1"\n }\n ],\n "data": {\n "BMFP3Eov94": {\n "inputs": {\n "name": {\n "type": "text"\n },\n "description": {\n "type": "text",\n "description": "文件描述"\n }\n }\n },\n "nCm-ij5I6o": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个沙雕"\n },\n "9RIg65O0YQ": {\n "count": 3,\n "score": 0.75,\n "knowledgeId": 3585368238960640,\n "query": "hello world"\n },\n "2vTyjP0Gu9": {\n "type": "python",\n "content": "code=\\"hello\\"\\nprint(code)"\n },\n "s9VfZpvb4P": {\n "model": "qwen3",\n "outputs": {\n "text": {\n "type": "string"\n }\n },\n "systemPrompt": "你是个聪明人"\n }\n }\n}') let initialNodes = initialData['nodes'] ?? [] let initialEdges = initialData['edges'] ?? [] @@ -287,8 +278,68 @@ function FlowEditor() {