diff --git a/service-ai/service-ai-web/pom.xml b/service-ai/service-ai-web/pom.xml index ce3546c..300d3e9 100644 --- a/service-ai/service-ai-web/pom.xml +++ b/service-ai/service-ai-web/pom.xml @@ -89,7 +89,10 @@ com.yomahub liteflow-script-graaljs - ${liteflow.version} + + + com.yomahub + liteflow-script-python diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java index 12dbf00..372e252 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/controller/task/TaskTemplateController.java @@ -1,5 +1,6 @@ package com.lanyuanxiaoyao.service.ai.web.controller.task; +import cn.hutool.core.util.ObjectUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; @@ -41,6 +42,12 @@ public class TaskTemplateController extends SimpleControllerSupport getFlowGraph(@PathVariable("id") Long id) throws JsonProcessingException { + var template = flowTaskTemplateService.detailOrThrow(id); + return AmisResponse.responseSuccess(mapper.readValue(template.getFlowGraph(), Map.class)); + } + @PostMapping("update_flow_graph") public AmisResponse updateFlowGraph(@RequestBody UpdateGraphItem item) throws JsonProcessingException { flowTaskTemplateService.updateFlowGraph(item.getId(), mapper.writeValueAsString(item.getGraph())); @@ -49,8 +56,7 @@ public class TaskTemplateController extends SimpleControllerSupport saveItemMapper() { - var map = Mappers.getMapper(SaveItem.Mapper.class); - return item -> map.from(item, mapper); + return Mappers.getMapper(SaveItem.Mapper.class); } @Override @@ -69,15 +75,9 @@ public class TaskTemplateController extends SimpleControllerSupport inputSchema; @org.mapstruct.Mapper - public static abstract class Mapper { - public abstract FlowTaskTemplate from(SaveItem saveItem, @Context ObjectMapper mapper) throws Exception; - - protected String mapInputSchema(Map inputSchema, @Context ObjectMapper mapper) throws JsonProcessingException { - return mapper.writeValueAsString(inputSchema); - } + public interface Mapper extends SaveItemMapper { } } @@ -105,6 +105,9 @@ public class TaskTemplateController extends SimpleControllerSupport mapJson(String source, @Context ObjectMapper mapper) throws Exception { + if (ObjectUtil.isNull(source)) { + return null; + } return mapper.readValue(source, new TypeReference<>() { }); } diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/FlowHelper.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/FlowHelper.java index 24b914f..6fe87f0 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/FlowHelper.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/FlowHelper.java @@ -47,11 +47,6 @@ public class FlowHelper { throw new RuntimeException(StrUtil.format("Target node variable not found: {}.{}", targetNodeId, targetVariableName)); } return targetNodeData.get(targetVariableName); - } else if (context.getInput().containsKey(expression)) { - if (!context.getInput().containsKey(expression)) { - throw new RuntimeException(StrUtil.format("Target variable not found in input {}", expression)); - } - return context.getInput().get(expression); } return null; } diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/entity/FlowContext.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/entity/FlowContext.java index 2f6182e..e0cc7e7 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/entity/FlowContext.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/entity/FlowContext.java @@ -2,12 +2,10 @@ package com.lanyuanxiaoyao.service.ai.web.engine.entity; import lombok.Data; import org.eclipse.collections.api.factory.Maps; -import org.eclipse.collections.api.map.ImmutableMap; import org.eclipse.collections.api.map.MutableMap; @Data public class FlowContext { - private ImmutableMap input = Maps.immutable.empty(); private MutableMap> data = Maps.mutable.>empty().asSynchronized(); public MutableMap get(String key) { diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/node/InputNode.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/node/InputNode.java new file mode 100644 index 0000000..f0e1da5 --- /dev/null +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/engine/node/InputNode.java @@ -0,0 +1,16 @@ +package com.lanyuanxiaoyao.service.ai.web.engine.node; + +import com.lanyuanxiaoyao.service.ai.web.engine.FlowNodeRunner; +import lombok.extern.slf4j.Slf4j; + +/** + * @author lanyuanxiaoyao + * @version 20250711 + */ +@Slf4j +public class InputNode extends FlowNodeRunner { + @Override + public void run() { + // 入参相关的内容在启动流程的时候已经注入了 + } +} diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/entity/vo/FlowGraphVo.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/entity/vo/FlowGraphVo.java new file mode 100644 index 0000000..c9fb7d3 --- /dev/null +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/entity/vo/FlowGraphVo.java @@ -0,0 +1,14 @@ +package com.lanyuanxiaoyao.service.ai.web.entity.vo; + +import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowEdge; +import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowNode; +import lombok.Data; +import org.eclipse.collections.api.map.MutableMap; +import org.eclipse.collections.api.set.ImmutableSet; + +@Data +public class FlowGraphVo { + private ImmutableSet nodes; + private ImmutableSet edges; + private MutableMap> data; +} diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/feedback/FeedbackService.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/feedback/FeedbackService.java index 05c2fff..b223cee 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/feedback/FeedbackService.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/feedback/FeedbackService.java @@ -7,9 +7,7 @@ import com.lanyuanxiaoyao.service.ai.web.entity.context.FeedbackContext; import com.lanyuanxiaoyao.service.ai.web.repository.FeedbackRepository; import com.yomahub.liteflow.core.FlowExecutor; import java.util.List; -import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +22,7 @@ import org.springframework.transaction.annotation.Transactional; this.executor = executor; } - @Scheduled(initialDelay = 1, fixedDelay = 1, timeUnit = TimeUnit.MINUTES) + // @Scheduled(initialDelay = 1, fixedDelay = 1, timeUnit = TimeUnit.MINUTES) public void analysis() { List feedbacks = repository.findAll( builder -> builder diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskService.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskService.java index ad4927f..71901e1 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskService.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskService.java @@ -1,30 +1,29 @@ package com.lanyuanxiaoyao.service.ai.web.service.task; import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.lanyuanxiaoyao.service.ai.web.base.service.SimpleServiceSupport; import com.lanyuanxiaoyao.service.ai.web.engine.FlowExecutor; import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowContext; -import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowEdge; import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowGraph; -import com.lanyuanxiaoyao.service.ai.web.engine.entity.FlowNode; import com.lanyuanxiaoyao.service.ai.web.engine.node.CodeNode; +import com.lanyuanxiaoyao.service.ai.web.engine.node.InputNode; import com.lanyuanxiaoyao.service.ai.web.engine.node.LlmNode; import com.lanyuanxiaoyao.service.ai.web.engine.node.LoopNode; import com.lanyuanxiaoyao.service.ai.web.engine.node.OutputNode; import com.lanyuanxiaoyao.service.ai.web.engine.node.SwitchNode; import com.lanyuanxiaoyao.service.ai.web.engine.store.InMemoryFlowStore; import com.lanyuanxiaoyao.service.ai.web.entity.FlowTask; +import com.lanyuanxiaoyao.service.ai.web.entity.vo.FlowGraphVo; import com.lanyuanxiaoyao.service.ai.web.repository.FlowTaskRepository; import java.lang.reflect.InvocationTargetException; import java.util.Map; -import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.eclipse.collections.api.factory.Maps; -import org.eclipse.collections.api.map.MutableMap; -import org.eclipse.collections.api.set.ImmutableSet; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.stereotype.Service; @@ -42,7 +41,6 @@ public class FlowTaskService extends SimpleServiceSupport { var flowTask = detailOrThrow(id); var graphVo = mapper.readValue(flowTask.getTemplateFlowGraph(), FlowGraphVo.class); var flowGraph = new FlowGraph(IdUtil.fastUUID(), graphVo.getNodes(), graphVo.getEdges()); - log.info("Graph: {}", flowGraph); var store = new InMemoryFlowStore(); var executor = new FlowExecutor( @@ -52,19 +50,25 @@ public class FlowTaskService extends SimpleServiceSupport { "switch-node", SwitchNode.class, "code-node", CodeNode.class, "llm-node", LlmNode.class, + "input-node", InputNode.class, "output-node", OutputNode.class )) ); FlowContext context = new FlowContext(); - context.setInput(mapper.readValue(flowTask.getInput(), new TypeReference<>() {})); context.setData(graphVo.getData()); + + // 如果发现输入节点,将相关数据注入到对应节点的上下文中 + var inputNode = flowGraph.nodes() + .detectOptional(node -> StrUtil.equals(node.type(), "input-node") && ObjectUtil.isEmpty(node.parentId())) + .orElse(null); + if (ObjectUtil.isNotNull(inputNode)) { + if (!context.getData().containsKey(inputNode.id())) { + context.getData().put(inputNode.id(), Maps.mutable.empty()); + } + context.getData().get(inputNode.id()).putAll(mapper.readValue(flowTask.getInput(), new TypeReference<>() { + })); + } + executor.execute(flowGraph, context); } - - @Data - public static final class FlowGraphVo { - private ImmutableSet nodes; - private ImmutableSet edges; - private MutableMap> data; - } } diff --git a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskTemplateService.java b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskTemplateService.java index 41f6ab1..8711bc8 100644 --- a/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskTemplateService.java +++ b/service-ai/service-ai-web/src/main/java/com/lanyuanxiaoyao/service/ai/web/service/task/FlowTaskTemplateService.java @@ -1,22 +1,47 @@ package com.lanyuanxiaoyao.service.ai.web.service.task; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.lanyuanxiaoyao.service.ai.web.base.service.SimpleServiceSupport; import com.lanyuanxiaoyao.service.ai.web.entity.FlowTaskTemplate; +import com.lanyuanxiaoyao.service.ai.web.entity.vo.FlowGraphVo; import com.lanyuanxiaoyao.service.ai.web.repository.FlowTaskTemplateRepository; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Slf4j @Service public class FlowTaskTemplateService extends SimpleServiceSupport { - public FlowTaskTemplateService(FlowTaskTemplateRepository flowTaskTemplateRepository) { + private final ObjectMapper mapper; + + public FlowTaskTemplateService(FlowTaskTemplateRepository flowTaskTemplateRepository, Jackson2ObjectMapperBuilder builder) { super(flowTaskTemplateRepository); + this.mapper = builder.build(); } @Transactional(rollbackFor = Exception.class) - public void updateFlowGraph(Long id, String flowGraph) { + public void updateFlowGraph(Long id, String flowGraph) throws JsonProcessingException { var template = detailOrThrow(id); + + var graph = mapper.readValue(flowGraph, FlowGraphVo.class); + // 如果发现输入节点,就单独提取出来 + var inputNode = graph.getNodes() + .detectOptional(node -> StrUtil.equals(node.type(), "input-node") && ObjectUtil.isEmpty(node.parentId())) + .orElse(null); + if (ObjectUtil.isNotNull(inputNode)) { + var nodeId = inputNode.id(); + var nodeData = graph.getData().getOrDefault(nodeId, null); + if (ObjectUtil.isNotNull(nodeData) && nodeData.containsKey("inputs")) { + template.setInputSchema(mapper.writeValueAsString(nodeData.get("inputs"))); + } + } else { + template.setInputSchema("{}"); + } + template.setFlowGraph(flowGraph); save(template); } diff --git a/service-web/client/src/components/flow/FlowChecker.tsx b/service-web/client/src/components/flow/FlowChecker.tsx index 601d791..2681033 100644 --- a/service-web/client/src/components/flow/FlowChecker.tsx +++ b/service-web/client/src/components/flow/FlowChecker.tsx @@ -30,7 +30,7 @@ export const checkAddNode: (type: string, parentId: string | undefined, nodes: N throw typeNotFound(type) } for (const checker of nodeDefine.checkers.add) { - let checkResult = checker(type, parentId, {}, nodes, edges, undefined) + let checkResult = checker(type, parentId, nodes, edges, undefined) if (checkResult.error) { throw addNodeError(checkResult.message) } @@ -86,7 +86,7 @@ export const nodeTypeNotFound = () => new CheckError(302, '节点类型不存在 export const saveNodeError = (nodeId: string, reason?: string) => new CheckError(303, reason ?? `节点配置存在错误:${nodeId}`) // @ts-ignore -export const checkSave: (inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => void = (inputSchema, nodes, edges, data) => { +export const checkSave: (nodes: Node[], edges: Edge[], data: any) => void = (nodes, edges, data) => { if (isEmpty(nodes)) { throw atLeastOneNode() } @@ -102,7 +102,7 @@ export const checkSave: (inputSchema: Record>, nodes let nodeType = node.type! let nodeDefine = NodeRegistryMap[nodeType] for (let checker of nodeDefine.checkers.save) { - let checkResult = checker(node.id, node.parentId, inputSchema, nodes, edges, data) + let checkResult = checker(node.id, node.parentId, nodes, edges, data) if (checkResult.error) { throw saveNodeError(node.id, checkResult.message) } diff --git a/service-web/client/src/components/flow/FlowEditor.tsx b/service-web/client/src/components/flow/FlowEditor.tsx index c7db2fd..b12ee36 100644 --- a/service-web/client/src/components/flow/FlowEditor.tsx +++ b/service-web/client/src/components/flow/FlowEditor.tsx @@ -11,7 +11,6 @@ import AddNodeButton from './component/AddNodeButton.tsx' import {checkAddConnection, checkSave} from './FlowChecker.tsx' import {useNodeDrag} from './Helper.tsx' import {NodeRegistryMap} from './NodeRegistry.tsx' -import {useContextStore} from './store/ContextStore.ts' import {useDataStore} from './store/DataStore.ts' import {useFlowStore} from './store/FlowStore.ts' import {flowDotColor, type FlowEditorProps} from './types.ts' @@ -58,8 +57,6 @@ function FlowEditor(props: FlowEditorProps) { onConnect, } = useFlowStore() - const {inputSchema, setInputSchema} = useContextStore() - useEffect(() => { // language=JSON // let initialData = JSON.parse('{"nodes":[{"id":"TCxPixrdkI","type":"start-node","position":{"x":-256,"y":109.5},"data":{},"measured":{"width":256,"height":83},"selected":false,"dragging":false},{"id":"tGs78_ietp","type":"llm-node","position":{"x":108,"y":-2.5},"data":{},"measured":{"width":256,"height":105},"selected":false,"dragging":false},{"id":"OeZdaU7LpY","type":"llm-node","position":{"x":111,"y":196},"data":{},"measured":{"width":256,"height":105},"selected":false,"dragging":false},{"id":"LjfoCYZo-E","type":"knowledge-node","position":{"x":497.62196259607214,"y":-10.792497317791003},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"sQM_22GYB5","type":"end-node","position":{"x":874.3164534765615,"y":151.70316541496913},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"KpMH_xc3ZZ","type":"llm-node","position":{"x":529.6286840434341,"y":150.4721376669937},"data":{},"measured":{"width":256,"height":75},"selected":false,"dragging":false},{"id":"pOrR6EMVbe","type":"switch-node","position":{"x":110.33793030183864,"y":373.9551529987239},"data":{},"measured":{"width":256,"height":157},"selected":false,"dragging":false}],"edges":[{"source":"TCxPixrdkI","sourceHandle":"source","target":"tGs78_ietp","targetHandle":"target","id":"xy-edge__TCxPixrdkIsource-tGs78_ietptarget"},{"source":"TCxPixrdkI","sourceHandle":"source","target":"OeZdaU7LpY","targetHandle":"target","id":"xy-edge__TCxPixrdkIsource-OeZdaU7LpYtarget"},{"source":"tGs78_ietp","sourceHandle":"source","target":"LjfoCYZo-E","targetHandle":"target","id":"xy-edge__tGs78_ietpsource-LjfoCYZo-Etarget"},{"source":"LjfoCYZo-E","sourceHandle":"source","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__LjfoCYZo-Esource-KpMH_xc3ZZtarget"},{"source":"OeZdaU7LpY","sourceHandle":"source","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__OeZdaU7LpYsource-KpMH_xc3ZZtarget"},{"source":"KpMH_xc3ZZ","sourceHandle":"source","target":"sQM_22GYB5","targetHandle":"target","id":"xy-edge__KpMH_xc3ZZsource-sQM_22GYB5target"},{"source":"TCxPixrdkI","sourceHandle":"source","target":"pOrR6EMVbe","id":"xy-edge__TCxPixrdkIsource-pOrR6EMVbe"},{"source":"pOrR6EMVbe","sourceHandle":"3","target":"sQM_22GYB5","targetHandle":"target","id":"xy-edge__pOrR6EMVbe3-sQM_22GYB5target"},{"source":"pOrR6EMVbe","sourceHandle":"1","target":"KpMH_xc3ZZ","targetHandle":"target","id":"xy-edge__pOrR6EMVbe1-KpMH_xc3ZZtarget"}],"data":{"tGs78_ietp":{"model":"qwen3","outputs":{"text":{"type":"string"}},"systemPrompt":"你是个聪明人"},"OeZdaU7LpY":{"model":"qwen3","outputs":{"text":{"type":"string"}},"systemPrompt":"你也是个聪明人"}}}') @@ -72,7 +69,6 @@ function FlowEditor(props: FlowEditorProps) { setNodes(initialNodes) setEdges(initialEdges) - setInputSchema(props.inputSchema) }, [props.graphData]) const { @@ -127,7 +123,7 @@ function FlowEditor(props: FlowEditorProps) { if (commonInfo.debug) { console.info('Save', JSON.stringify({nodes, edges, data})) } - checkSave(inputSchema, nodes, edges, data) + checkSave(nodes, edges, data) props.onGraphDataChange({nodes, edges, data}) } catch (e) { // @ts-ignore diff --git a/service-web/client/src/components/flow/Helper.tsx b/service-web/client/src/components/flow/Helper.tsx index f8bd614..4e0caef 100644 --- a/service-web/client/src/components/flow/Helper.tsx +++ b/service-web/client/src/components/flow/Helper.tsx @@ -3,7 +3,6 @@ import type {Option} from 'amis/lib/Schema' import {contain, find, has, isEqual, max, min, unique} from 'licia' import {type DependencyList, type MouseEvent as ReactMouseEvent, useCallback, useRef} from 'react' import Queue from 'yocto-queue' -import {originTypeMap} from '../../pages/ai/task/InputSchema.tsx' import {useFlowStore} from './store/FlowStore.ts' import {type OutputVariable, type OutputVariableType} from './types.ts' @@ -21,17 +20,10 @@ export const getAllIncomerNodeById: (id: string, nodes: Node[], edges: Edge[]) = return unique(result, (a, b) => isEqual(a, b)) } -export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => OutputVariable[] = (id, inputSchema, nodes, edges, data) => { - let inputSchemaVariables: OutputVariable[] = Object.keys(inputSchema).map(key => ({ - group: '流程入参', - name: `${key}${inputSchema[key]?.label ? ` (${inputSchema[key].label})` : ''}`, - type: originTypeMap[inputSchema[key]?.type ?? ''], - variable: key, - })) - +export const getAllIncomerNodeOutputVariables: (id: string, nodes: Node[], edges: Edge[], data: any) => OutputVariable[] = (id, nodes, edges, data) => { let currentNode = find(nodes, n => isEqual(id, n.id)) if (!currentNode) { - return inputSchemaVariables + return [] } let incomerIds = getAllIncomerNodeById(id, nodes, edges) @@ -63,7 +55,6 @@ export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record< } return [ - ...inputSchemaVariables, ...(currentNode.parentId ? [ { group: '循环入参', @@ -82,9 +73,9 @@ export const getAllIncomerNodeOutputVariables: (id: string, inputSchema: Record< ] } -export const generateAllIncomerOutputVariablesFormOptions: (id: string, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any, targetTypes?: OutputVariableType[]) => Option[] = (id, inputSchema, nodes, edges, data, targetTypes) => { +export const generateAllIncomerOutputVariablesFormOptions: (id: string, nodes: Node[], edges: Edge[], data: any, targetTypes?: OutputVariableType[]) => Option[] = (id, nodes, edges, data, targetTypes) => { let optionMap: Record = {} - for (const item of getAllIncomerNodeOutputVariables(id, inputSchema, nodes, edges, data)) { + for (const item of getAllIncomerNodeOutputVariables(id, nodes, edges, data)) { if (targetTypes && !contain(targetTypes, item.type)) { continue } @@ -120,9 +111,9 @@ const numberDefaultOperator: string = 'equal' const arrayOperators: ConditionOperator[] = ['is_empty', 'is_not_empty'] const arrayDefaultOperator: string = 'is_empty' -export const generateAllIncomerOutputVariablesConditions: (id: string, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => Option[] = (id, inputSchema, nodes, edges, data) => { +export const generateAllIncomerOutputVariablesConditions: (id: string, nodes: Node[], edges: Edge[], data: any) => Option[] = (id, nodes, edges, data) => { let optionMap: Record = {} - for (const item of getAllIncomerNodeOutputVariables(id, inputSchema, nodes, edges, data)) { + for (const item of getAllIncomerNodeOutputVariables(id, nodes, edges, data)) { if (!optionMap[item.group]) { optionMap[item.group] = [] } diff --git a/service-web/client/src/components/flow/NodeRegistry.tsx b/service-web/client/src/components/flow/NodeRegistry.tsx index f0d71c1..b9b0025 100644 --- a/service-web/client/src/components/flow/NodeRegistry.tsx +++ b/service-web/client/src/components/flow/NodeRegistry.tsx @@ -11,12 +11,12 @@ import type {AddNodeChecker, NodeDefine, SaveNodeChecker} from './types.ts' import InputNode from './node/InputNode.tsx' const inputSingleVariableChecker: (field: string) => SaveNodeChecker = field => { - return (id, _parentId, inputSchema, nodes, edges, data) => { + return (id, _parentId, nodes, edges, data) => { let nodeData = data[id] ?? {} if (has(nodeData, field)) { let expression = nodeData?.[field] ?? '' if (!isEmpty(expression)) { - let outputVariables = new Set(getAllIncomerNodeOutputVariables(id, inputSchema, nodes, edges, data).map(i => i.variable)) + let outputVariables = new Set(getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => i.variable)) if (!outputVariables.has(expression)) { return { error: true, @@ -29,12 +29,12 @@ const inputSingleVariableChecker: (field: string) => SaveNodeChecker = field => } } -const inputMultiVariableChecker: SaveNodeChecker = (id, _parentId, inputSchema, nodes, edges, data) => { +const inputMultiVariableChecker: SaveNodeChecker = (id, _parentId, nodes, edges, data) => { let nodeData = data[id] ?? {} if (has(nodeData, 'inputs')) { let inputs = nodeData?.inputs ?? {} if (!isEmpty(inputs)) { - let outputVariables = new Set(getAllIncomerNodeOutputVariables(id, inputSchema, nodes, edges, data).map(i => i.variable)) + let outputVariables = new Set(getAllIncomerNodeOutputVariables(id, nodes, edges, data).map(i => i.variable)) for (const key of Object.keys(inputs)) { let variable = inputs[key]?.variable ?? '' if (!outputVariables.has(variable)) { @@ -49,7 +49,7 @@ const inputMultiVariableChecker: SaveNodeChecker = (id, _parentId, inputSchema, return {error: false} } -const noMoreThanOneNodeType: AddNodeChecker = (type, parentId, _inputSchema, nodes) => { +const noMoreThanOneNodeType: AddNodeChecker = (type, parentId, nodes) => { return { error: nodes.filter(n => isEqual(n.parentId, parentId) && isEqual(n.type, type)).length > 0, message: `同一个流程(子流程)中类型为 ${type} 的节点至多有一个` diff --git a/service-web/client/src/components/flow/node/AmisNode.tsx b/service-web/client/src/components/flow/node/AmisNode.tsx index d3a44f5..0d49894 100644 --- a/service-web/client/src/components/flow/node/AmisNode.tsx +++ b/service-web/client/src/components/flow/node/AmisNode.tsx @@ -12,7 +12,6 @@ import {type FormSchema, OutputVariableTypeMap} from '../types.ts' export function inputsFormColumns( nodeId: string, - inputSchema: Record>, nodes: Node[], edges: Edge[], data: any, @@ -38,7 +37,6 @@ export function inputsFormColumns( selectMode: 'group', options: generateAllIncomerOutputVariablesFormOptions( nodeId, - inputSchema, nodes, edges, data, diff --git a/service-web/client/src/components/flow/node/CodeNode.tsx b/service-web/client/src/components/flow/node/CodeNode.tsx index 028b37f..051501a 100644 --- a/service-web/client/src/components/flow/node/CodeNode.tsx +++ b/service-web/client/src/components/flow/node/CodeNode.tsx @@ -1,7 +1,6 @@ import type {NodeProps} from '@xyflow/react' import {Tag} from 'antd' import React, {useCallback, useMemo} from 'react' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {inputsFormColumns, nodeClassName, NormalNodeHandler, outputsFormColumns} from './AmisNode.tsx' @@ -16,13 +15,12 @@ const languageMap: Record = { const CodeNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData, getDataById} = useDataStore() - const {getInputSchema} = useContextStore() const nodeData = getDataById(props.id) const formSchema: () => FormSchema = useCallback(() => ({ columns: [ - ...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()), + ...inputsFormColumns(props.id, getNodes(), getEdges(), getData()), { type: 'divider', }, diff --git a/service-web/client/src/components/flow/node/KnowledgeNode.tsx b/service-web/client/src/components/flow/node/KnowledgeNode.tsx index c0d1d04..16d276a 100644 --- a/service-web/client/src/components/flow/node/KnowledgeNode.tsx +++ b/service-web/client/src/components/flow/node/KnowledgeNode.tsx @@ -1,7 +1,6 @@ import type {NodeProps} from '@xyflow/react' import React, {useCallback, useEffect} from 'react' import {commonInfo} from '../../../util/amis.tsx' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {inputsFormColumns, nodeClassName, NormalNodeHandler, outputsFormColumns} from './AmisNode.tsx' @@ -10,7 +9,6 @@ import type {FormSchema} from '../types.ts' const KnowledgeNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData, mergeDataById} = useDataStore() - const {getInputSchema} = useContextStore() useEffect(() => { mergeDataById( @@ -27,7 +25,7 @@ const KnowledgeNode = (props: NodeProps) => { const formSchema: () => FormSchema = useCallback(() => ({ columns: [ - ...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()), + ...inputsFormColumns(props.id, getNodes(), getEdges(), getData()), { type: 'divider', }, diff --git a/service-web/client/src/components/flow/node/LlmNode.tsx b/service-web/client/src/components/flow/node/LlmNode.tsx index 343dfd2..0807a62 100644 --- a/service-web/client/src/components/flow/node/LlmNode.tsx +++ b/service-web/client/src/components/flow/node/LlmNode.tsx @@ -1,7 +1,6 @@ import type {NodeProps} from '@xyflow/react' import {Tag} from 'antd' import React, {useCallback, useEffect, useMemo} from 'react' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {inputsFormColumns, nodeClassName, NormalNodeHandler, outputsFormColumns} from './AmisNode.tsx' @@ -15,7 +14,6 @@ const modelMap: Record = { const LlmNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData, mergeDataById, getDataById} = useDataStore() - const {getInputSchema} = useContextStore() const nodeData = getDataById(props.id) @@ -34,7 +32,7 @@ const LlmNode = (props: NodeProps) => { const formSchema: () => FormSchema = useCallback(() => ({ columns: [ - ...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()), + ...inputsFormColumns(props.id, getNodes(), getEdges(), getData()), { type: 'divider', }, diff --git a/service-web/client/src/components/flow/node/LoopNode.tsx b/service-web/client/src/components/flow/node/LoopNode.tsx index 4b3d7fd..6b2b22a 100644 --- a/service-web/client/src/components/flow/node/LoopNode.tsx +++ b/service-web/client/src/components/flow/node/LoopNode.tsx @@ -3,7 +3,6 @@ import {classnames} from 'amis' import React, {useCallback, useEffect, useMemo} from 'react' import AddNodeButton from '../component/AddNodeButton.tsx' import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import {flowBackgroundColor, flowDotColor, type FormSchema} from '../types.ts' @@ -12,7 +11,6 @@ import AmisNode, {nodeClassName, NormalNodeHandler, outputsFormColumns} from './ const LoopNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData, mergeDataById} = useDataStore() - const {getInputSchema} = useContextStore() useEffect(() => { mergeDataById( @@ -85,7 +83,6 @@ const LoopNode = (props: NodeProps) => { selectMode: 'group', options: generateAllIncomerOutputVariablesFormOptions( props.id, - getInputSchema(), getNodes(), getEdges(), getData(), @@ -101,7 +98,6 @@ const LoopNode = (props: NodeProps) => { selectMode: 'group', options: generateAllIncomerOutputVariablesFormOptions( props.id, - getInputSchema(), getNodes(), getEdges(), getData(), diff --git a/service-web/client/src/components/flow/node/OutputNode.tsx b/service-web/client/src/components/flow/node/OutputNode.tsx index 179073d..c1c1965 100644 --- a/service-web/client/src/components/flow/node/OutputNode.tsx +++ b/service-web/client/src/components/flow/node/OutputNode.tsx @@ -1,7 +1,6 @@ import type {NodeProps} from '@xyflow/react' import React, {useCallback} from 'react' import {generateAllIncomerOutputVariablesFormOptions} from '../Helper.tsx' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {EndNodeHandler, nodeClassName} from './AmisNode.tsx' @@ -10,7 +9,6 @@ import type {FormSchema} from '../types.ts' const OutputNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData} = useDataStore() - const {getInputSchema} = useContextStore() const formSchema: () => FormSchema = useCallback(() => ({ columns: [ @@ -22,7 +20,6 @@ const OutputNode = (props: NodeProps) => { selectMode: 'group', options: generateAllIncomerOutputVariablesFormOptions( props.id, - getInputSchema(), getNodes(), getEdges(), getData(), diff --git a/service-web/client/src/components/flow/node/SwitchNode.tsx b/service-web/client/src/components/flow/node/SwitchNode.tsx index a037e35..b2cabd3 100644 --- a/service-web/client/src/components/flow/node/SwitchNode.tsx +++ b/service-web/client/src/components/flow/node/SwitchNode.tsx @@ -4,7 +4,6 @@ import {Tag} from 'antd' import {contain, isEqual} from 'licia' import React, {useCallback, useMemo} from 'react' import {generateAllIncomerOutputVariablesConditions} from '../Helper.tsx' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {nodeClassName} from './AmisNode.tsx' @@ -13,7 +12,6 @@ import type {FormSchema} from '../types.ts' const SwitchNode = (props: NodeProps) => { const {getNodes, getEdges, removeEdges} = useFlowStore() const {getData, getDataById} = useDataStore() - const {getInputSchema} = useContextStore() const nodeData = getDataById(props.id) // @ts-ignore @@ -37,7 +35,6 @@ const SwitchNode = (props: NodeProps) => { showANDOR: true, fields: generateAllIncomerOutputVariablesConditions( props.id, - getInputSchema(), getNodes(), getEdges(), getData(), diff --git a/service-web/client/src/components/flow/node/TemplateNode.tsx b/service-web/client/src/components/flow/node/TemplateNode.tsx index d8005bb..661b34c 100644 --- a/service-web/client/src/components/flow/node/TemplateNode.tsx +++ b/service-web/client/src/components/flow/node/TemplateNode.tsx @@ -1,7 +1,6 @@ import type {NodeProps} from '@xyflow/react' import {Tag} from 'antd' import React, {useCallback, useEffect, useMemo} from 'react' -import {useContextStore} from '../store/ContextStore.ts' import {useDataStore} from '../store/DataStore.ts' import {useFlowStore} from '../store/FlowStore.ts' import AmisNode, {inputsFormColumns, nodeClassName, NormalNodeHandler, outputsFormColumns} from './AmisNode.tsx' @@ -17,7 +16,6 @@ const typeMap: Record = { const TemplateNode = (props: NodeProps) => { const {getNodes, getEdges} = useFlowStore() const {getData, getDataById, mergeDataById} = useDataStore() - const {getInputSchema} = useContextStore() const nodeData = getDataById(props.id) @@ -36,7 +34,7 @@ const TemplateNode = (props: NodeProps) => { const formSchema: () => FormSchema = useCallback(() => ({ columns: [ - ...inputsFormColumns(props.id, getInputSchema(), getNodes(), getEdges(), getData()), + ...inputsFormColumns(props.id, getNodes(), getEdges(), getData()), { type: 'divider', }, diff --git a/service-web/client/src/components/flow/store/ContextStore.ts b/service-web/client/src/components/flow/store/ContextStore.ts deleted file mode 100644 index 2cd3319..0000000 --- a/service-web/client/src/components/flow/store/ContextStore.ts +++ /dev/null @@ -1,11 +0,0 @@ -import {create} from 'zustand/react' - -export const useContextStore = create<{ - inputSchema: Record>, - getInputSchema: () => Record>, - setInputSchema: (inputSchema: Record>) => void, -}>((set, get) => ({ - inputSchema: {}, - getInputSchema: () => get().inputSchema, - setInputSchema: (inputSchema: Record>) => set({inputSchema}), -})) \ No newline at end of file diff --git a/service-web/client/src/components/flow/types.ts b/service-web/client/src/components/flow/types.ts index 5d3279b..20845eb 100644 --- a/service-web/client/src/components/flow/types.ts +++ b/service-web/client/src/components/flow/types.ts @@ -20,13 +20,12 @@ export type NodeError = { message?: string, } -export type AddNodeChecker = (type: string, parentId: string | undefined, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => NodeError -export type SaveNodeChecker = (id: string, parentId: string | undefined, inputSchema: Record>, nodes: Node[], edges: Edge[], data: any) => NodeError +export type AddNodeChecker = (type: string, parentId: string | undefined, nodes: Node[], edges: Edge[], data: any) => NodeError +export type SaveNodeChecker = (id: string, parentId: string | undefined, nodes: Node[], edges: Edge[], data: any) => NodeError export type GraphData = { nodes: Node[], edges: Edge[], data: any } export type FlowEditorProps = { - inputSchema: Record>, graphData: GraphData, onGraphDataChange: (graphData: GraphData) => void, } diff --git a/service-web/client/src/pages/ai/task/InputSchema.tsx b/service-web/client/src/pages/ai/task/InputSchema.tsx index 37c51f3..4c7092c 100644 --- a/service-web/client/src/pages/ai/task/InputSchema.tsx +++ b/service-web/client/src/pages/ai/task/InputSchema.tsx @@ -1,5 +1,4 @@ import type {Schema} from 'amis' -import type {OutputVariableType} from '../../../components/flow/types.ts' import {commonInfo, formInputFileStaticColumns} from '../../../util/amis.tsx' export const typeMap: Record = { @@ -9,13 +8,6 @@ export const typeMap: Record = { files: '文件', } -export const originTypeMap: Record = { - text: 'text', - textarea: 'text', - number: 'number', - files: 'array-text', -} - export type InputField = { type: string label: string diff --git a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx index 7cfe7cb..a3412dc 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateEdit.tsx @@ -1,9 +1,8 @@ -import {isEmpty, isEqual} from 'licia' +import {isEqual} from 'licia' import React from 'react' import {useNavigate, useParams} from 'react-router' import styled from 'styled-components' import {amisRender, commonInfo, horizontalFormOptions} from '../../../../util/amis.tsx' -import {generateInputForm, typeMap} from '../InputSchema.tsx' const TemplateEditDiv = styled.div` .antd-EditorControl { @@ -39,31 +38,6 @@ const FlowTaskTemplateEdit: React.FC = () => { wrapWithPanel: false, ...horizontalFormOptions(), onEvent: { - change: { - actions: [ - { - actionType: 'validate', - }, - { - actionType: 'custom', - // @ts-ignore - script: (context, doAction, event) => { - let data = event?.data ?? {} - let inputSchema = data.inputSchema ?? [] - if (!isEmpty(inputSchema) && isEmpty(data?.validateResult?.error ?? undefined)) { - doAction({ - actionType: 'setValue', - args: { - value: { - inputPreview: generateInputForm(inputSchema, '入参表单预览'), - }, - }, - }) - } - }, - }, - ], - }, submitSucc: { actions: [ { @@ -99,71 +73,6 @@ const FlowTaskTemplateEdit: React.FC = () => { maxLength: 500, showCounter: true, }, - { - type: 'group', - body: [ - { - type: 'wrapper', - size: 'none', - body: [ - { - type: 'input-kvs', - name: 'inputSchema', - label: '输入变量', - addButtonText: '新增入参', - draggable: false, - keyItem: { - label: '参数名称', - ...horizontalFormOptions(), - validations: { - isAlphanumeric: true, - }, - }, - valueItems: [ - { - ...horizontalFormOptions(), - type: 'input-text', - name: 'label', - required: true, - label: '中文名称', - clearValueOnEmpty: true, - clearable: true, - }, - { - ...horizontalFormOptions(), - type: 'input-text', - name: 'description', - label: '参数描述', - clearValueOnEmpty: true, - clearable: true, - }, - { - ...horizontalFormOptions(), - type: 'select', - name: 'type', - label: '参数类型', - required: true, - selectFirst: true, - options: Object.keys(typeMap).map(key => ({label: typeMap[key], value: key})), - }, - { - ...horizontalFormOptions(), - type: 'switch', - name: 'required', - label: '是否必填', - required: true, - value: true, - }, - ], - }, - ], - }, - { - type: 'amis', - name: 'inputPreview', - }, - ], - }, { type: 'button-toolbar', buttons: [ diff --git a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx index 6f6c147..6edf7de 100644 --- a/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx +++ b/service-web/client/src/pages/ai/task/template/FlowTaskTemplateFlowEdit.tsx @@ -13,7 +13,6 @@ const FlowTaskTemplateFlowEditDiv = styled.div` const FlowTaskTemplateFlowEdit: React.FC = () => { const navigate = useNavigate() const {template_id} = useParams() - const [inputSchema, setInputSchema] = useState>>({}) const [graphData, setGraphData] = useState({nodes: [], edges: [], data: {}}) useMount(async () => { @@ -23,14 +22,12 @@ const FlowTaskTemplateFlowEdit: React.FC = () => { headers: commonInfo.authorizationHeaders, }, ) - setInputSchema(data?.data?.inputSchema) setGraphData(data?.data?.flowGraph) }) return ( { await axios.post(