Skip to content

Commit a5adfd3

Browse files
authored
Make output parameters available (#825)
1 parent acd167f commit a5adfd3

File tree

4 files changed

+87
-12
lines changed

4 files changed

+87
-12
lines changed

skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import "./reactFlowOverrideStyles.css";
3030
import {
3131
createNode,
3232
generateNodeLabel,
33+
getOutputParameterKey,
3334
getWorkflowBlocks,
3435
layout,
3536
} from "./workflowEditorUtils";
@@ -228,6 +229,7 @@ function FlowRenderer({
228229
if (!node) {
229230
return;
230231
}
232+
const deletedNodeLabel = node.data.label;
231233
const newNodes = nodes.filter((node) => node.id !== id);
232234
const newEdges = edges.flatMap((edge) => {
233235
if (edge.source === id) {
@@ -257,7 +259,24 @@ function FlowRenderer({
257259
return;
258260
}
259261

260-
doLayout(newNodes, newEdges);
262+
// if any node was using the output parameter of the deleted node, remove it from their parameter keys
263+
const newNodesWithUpdatedParameters = newNodes.map((node) => {
264+
if (node.type === "task") {
265+
return {
266+
...node,
267+
data: {
268+
...node.data,
269+
parameterKeys: node.data.parameterKeys.filter(
270+
(parameter) =>
271+
parameter !== getOutputParameterKey(deletedNodeLabel),
272+
),
273+
},
274+
};
275+
}
276+
return node;
277+
});
278+
279+
doLayout(newNodesWithUpdatedParameters, newEdges);
261280
}
262281

263282
return (

skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx

+47-1
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,65 @@ import {
1717
import { Switch } from "@/components/ui/switch";
1818
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
1919
import { ListBulletIcon, MixerVerticalIcon } from "@radix-ui/react-icons";
20-
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
20+
import {
21+
Edge,
22+
Handle,
23+
NodeProps,
24+
Position,
25+
useEdges,
26+
useNodes,
27+
useReactFlow,
28+
} from "@xyflow/react";
2129
import { useState } from "react";
2230
import { EditableNodeTitle } from "../components/EditableNodeTitle";
2331
import { NodeActionMenu } from "../NodeActionMenu";
2432
import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch";
2533
import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel";
2634
import type { TaskNode, TaskNodeDisplayMode } from "./types";
2735
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
36+
import { AppNode } from "..";
37+
import { getOutputParameterKey } from "../../workflowEditorUtils";
38+
39+
function getPreviousNodeIds(
40+
nodes: Array<AppNode>,
41+
edges: Array<Edge>,
42+
target: string,
43+
): Array<string> {
44+
const nodeIds: string[] = [];
45+
const node = nodes.find((node) => node.id === target);
46+
if (!node) {
47+
return nodeIds;
48+
}
49+
let current = edges.find((edge) => edge.target === target);
50+
if (current) {
51+
while (current) {
52+
nodeIds.push(current.source);
53+
current = edges.find((edge) => edge.target === current!.source);
54+
}
55+
}
56+
if (!node.parentId) {
57+
return nodeIds;
58+
}
59+
return [...nodeIds, ...getPreviousNodeIds(nodes, edges, node.parentId)];
60+
}
2861

2962
function TaskNode({ id, data }: NodeProps<TaskNode>) {
3063
const { updateNodeData } = useReactFlow();
3164
const [displayMode, setDisplayMode] = useState<TaskNodeDisplayMode>("basic");
3265
const { editable } = data;
3366
const deleteNodeCallback = useDeleteNodeCallback();
67+
const nodes = useNodes<AppNode>();
68+
const edges = useEdges();
69+
const previousNodeIds = getPreviousNodeIds(nodes, edges, id);
70+
const previousNodes = nodes.filter((node) =>
71+
previousNodeIds.includes(node.id),
72+
);
73+
const labels = previousNodes
74+
.filter((node) => node.type !== "nodeAdder")
75+
.map((node) => node.data.label);
76+
const outputParameterKeys = labels.map((label) =>
77+
getOutputParameterKey(label),
78+
);
3479

3580
const [label, setLabel] = useState(data.label);
3681
const [inputs, setInputs] = useState({
@@ -387,6 +432,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
387432
</PopoverTrigger>
388433
<PopoverContent className="w-72">
389434
<TaskNodeParametersPanel
435+
availableOutputParameters={outputParameterKeys}
390436
parameters={data.parameterKeys}
391437
onParametersChange={(parameterKeys) => {
392438
updateNodeData(id, { parameterKeys });

skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNodeParametersPanel.tsx

+15-10
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@ import { Checkbox } from "@/components/ui/checkbox";
22
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
33

44
type Props = {
5+
availableOutputParameters: Array<string>;
56
parameters: Array<string>;
67
onParametersChange: (parameters: Array<string>) => void;
78
};
89

9-
function TaskNodeParametersPanel({ parameters, onParametersChange }: Props) {
10+
function TaskNodeParametersPanel({
11+
availableOutputParameters,
12+
parameters,
13+
onParametersChange,
14+
}: Props) {
1015
const [workflowParameters] = useWorkflowParametersState();
11-
16+
const keys = workflowParameters
17+
.map((parameter) => parameter.key)
18+
.concat(availableOutputParameters);
1219
return (
1320
<div className="space-y-4">
1421
<header className="space-y-1">
@@ -18,27 +25,25 @@ function TaskNodeParametersPanel({ parameters, onParametersChange }: Props) {
1825
</span>
1926
</header>
2027
<div className="space-y-2">
21-
{workflowParameters.map((workflowParameter) => {
28+
{keys.map((key) => {
2229
return (
2330
<div
24-
key={workflowParameter.key}
31+
key={key}
2532
className="flex items-center gap-2 rounded-sm bg-slate-elevation1 px-3 py-2"
2633
>
2734
<Checkbox
28-
checked={parameters.includes(workflowParameter.key)}
35+
checked={parameters.includes(key)}
2936
onCheckedChange={(checked) => {
3037
if (checked) {
31-
onParametersChange([...parameters, workflowParameter.key]);
38+
onParametersChange([...parameters, key]);
3239
} else {
3340
onParametersChange(
34-
parameters.filter(
35-
(parameter) => parameter !== workflowParameter.key,
36-
),
41+
parameters.filter((parameterKey) => parameterKey !== key),
3742
);
3843
}
3944
}}
4045
/>
41-
<span className="text-xs">{workflowParameter.key}</span>
46+
<span className="text-xs">{key}</span>
4247
</div>
4348
);
4449
})}

skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,10 @@ function convertEchoParameters(
616616
});
617617
}
618618

619+
function getOutputParameterKey(label: string) {
620+
return label + "_output";
621+
}
622+
619623
export {
620624
createNode,
621625
generateNodeData,
@@ -624,4 +628,5 @@ export {
624628
layout,
625629
generateNodeLabel,
626630
convertEchoParameters,
631+
getOutputParameterKey,
627632
};

0 commit comments

Comments
 (0)