diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx index 72b85d36..7740d233 100644 --- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx @@ -30,6 +30,7 @@ import "./reactFlowOverrideStyles.css"; import { createNode, generateNodeLabel, + getOutputParameterKey, getWorkflowBlocks, layout, } from "./workflowEditorUtils"; @@ -228,6 +229,7 @@ function FlowRenderer({ if (!node) { return; } + const deletedNodeLabel = node.data.label; const newNodes = nodes.filter((node) => node.id !== id); const newEdges = edges.flatMap((edge) => { if (edge.source === id) { @@ -257,7 +259,24 @@ function FlowRenderer({ return; } - doLayout(newNodes, newEdges); + // if any node was using the output parameter of the deleted node, remove it from their parameter keys + const newNodesWithUpdatedParameters = newNodes.map((node) => { + if (node.type === "task") { + return { + ...node, + data: { + ...node.data, + parameterKeys: node.data.parameterKeys.filter( + (parameter) => + parameter !== getOutputParameterKey(deletedNodeLabel), + ), + }, + }; + } + return node; + }); + + doLayout(newNodesWithUpdatedParameters, newEdges); } return ( diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx index 326743e4..9249c923 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx @@ -17,7 +17,15 @@ import { import { Switch } from "@/components/ui/switch"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { ListBulletIcon, MixerVerticalIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Edge, + Handle, + NodeProps, + Position, + useEdges, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; @@ -25,12 +33,49 @@ import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch"; import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel"; import type { TaskNode, TaskNodeDisplayMode } from "./types"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { AppNode } from ".."; +import { getOutputParameterKey } from "../../workflowEditorUtils"; + +function getPreviousNodeIds( + nodes: Array, + edges: Array, + target: string, +): Array { + const nodeIds: string[] = []; + const node = nodes.find((node) => node.id === target); + if (!node) { + return nodeIds; + } + let current = edges.find((edge) => edge.target === target); + if (current) { + while (current) { + nodeIds.push(current.source); + current = edges.find((edge) => edge.target === current!.source); + } + } + if (!node.parentId) { + return nodeIds; + } + return [...nodeIds, ...getPreviousNodeIds(nodes, edges, node.parentId)]; +} function TaskNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const [displayMode, setDisplayMode] = useState("basic"); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); + const nodes = useNodes(); + const edges = useEdges(); + const previousNodeIds = getPreviousNodeIds(nodes, edges, id); + const previousNodes = nodes.filter((node) => + previousNodeIds.includes(node.id), + ); + const labels = previousNodes + .filter((node) => node.type !== "nodeAdder") + .map((node) => node.data.label); + const outputParameterKeys = labels.map((label) => + getOutputParameterKey(label), + ); const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ @@ -387,6 +432,7 @@ function TaskNode({ id, data }: NodeProps) { { updateNodeData(id, { parameterKeys }); diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNodeParametersPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNodeParametersPanel.tsx index 79b13428..fe63711e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNodeParametersPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNodeParametersPanel.tsx @@ -2,13 +2,20 @@ import { Checkbox } from "@/components/ui/checkbox"; import { useWorkflowParametersState } from "../../useWorkflowParametersState"; type Props = { + availableOutputParameters: Array; parameters: Array; onParametersChange: (parameters: Array) => void; }; -function TaskNodeParametersPanel({ parameters, onParametersChange }: Props) { +function TaskNodeParametersPanel({ + availableOutputParameters, + parameters, + onParametersChange, +}: Props) { const [workflowParameters] = useWorkflowParametersState(); - + const keys = workflowParameters + .map((parameter) => parameter.key) + .concat(availableOutputParameters); return (
@@ -18,27 +25,25 @@ function TaskNodeParametersPanel({ parameters, onParametersChange }: Props) {
- {workflowParameters.map((workflowParameter) => { + {keys.map((key) => { return (
{ if (checked) { - onParametersChange([...parameters, workflowParameter.key]); + onParametersChange([...parameters, key]); } else { onParametersChange( - parameters.filter( - (parameter) => parameter !== workflowParameter.key, - ), + parameters.filter((parameterKey) => parameterKey !== key), ); } }} /> - {workflowParameter.key} + {key}
); })} diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 576520f4..23bf78a4 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -616,6 +616,10 @@ function convertEchoParameters( }); } +function getOutputParameterKey(label: string) { + return label + "_output"; +} + export { createNode, generateNodeData, @@ -624,4 +628,5 @@ export { layout, generateNodeLabel, convertEchoParameters, + getOutputParameterKey, };