Jon/add input parameters to code block (#2308)

This commit is contained in:
Shuchang Zheng
2025-05-08 07:26:03 -07:00
committed by GitHub
parent 869a94dbf4
commit 04d66ae06e
6 changed files with 146 additions and 2 deletions

View File

@@ -0,0 +1,84 @@
import { PlusIcon } from "@radix-ui/react-icons";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
import { useState, useEffect } from "react";
import { Cross2Icon } from "@radix-ui/react-icons";
import "./workflow-block-input-set.css";
import { useWorkflowParametersState } from "@/routes/workflows/editor/useWorkflowParametersState";
type Props = {
onChange: (parameterKeys: Set<string>) => void;
nodeId: string;
values: Set<string>;
};
function WorkflowBlockInputSet(props: Props) {
const { nodeId, onChange, values } = props;
const [parameterKeys, setParameterKeys] = useState<Set<string>>(values);
const hasKeys = parameterKeys.size > 0;
const [workflowParameters] = useWorkflowParametersState();
const availableParameterKeys = new Set(
workflowParameters.map((parameter) => parameter.key),
);
useEffect(() => {
onChange(new Set(parameterKeys));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [parameterKeys]);
return (
<div className="workflow-block-input-set relative rounded-md border border-input">
<div className="ze-set">
{hasKeys ? (
Array.from(parameterKeys).map((parameterKey) => {
const missing = !availableParameterKeys.has(parameterKey);
return (
<div
key={parameterKey}
className={`parameter-key flex items-center gap-2 ${missing ? "missing" : ""}`}
>
<span>{parameterKey}</span>
<Cross2Icon
className="size-4 cursor-pointer"
onClick={() => {
setParameterKeys((prev) => {
const newSet = new Set(prev);
newSet.delete(parameterKey);
return newSet;
});
}}
/>
</div>
);
})
) : (
<span className="flex items-center gap-2 text-slate-400">&nbsp;</span>
)}
</div>
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
<Popover>
<PopoverTrigger asChild>
<div className="rounded p-1 hover:bg-muted">
<PlusIcon className="size-4" />
</div>
</PopoverTrigger>
<PopoverContent className="w-[22rem]">
<WorkflowBlockParameterSelect
nodeId={nodeId}
onAdd={(parameterKey) => {
setParameterKeys((prev) => {
const newSet = new Set(prev);
newSet.add(parameterKey);
return newSet;
});
}}
/>
</PopoverContent>
</Popover>
</div>
</div>
);
}
export { WorkflowBlockInputSet };

View File

@@ -0,0 +1,27 @@
.workflow-block-input-set {
font-size: 0.8rem;
width: 100%;
padding: 0.5rem;
padding-right: 2.25rem;
padding-left: 0.75rem;
}
.workflow-block-input-set .ze-set {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
align-items: center;
justify-content: flex-start;
}
.workflow-block-input-set .parameter-key {
background-color: rgba(255, 255, 255, 0.05);
padding: 0.25rem;
padding-left: 0.35rem;
border-radius: 0.25rem;
}
.workflow-block-input-set .parameter-key.missing {
background-color: rgba(255, 0, 0, 0.1);
color: red;
}

View File

@@ -1,4 +1,5 @@
import { Label } from "@/components/ui/label";
import { WorkflowBlockInputSet } from "@/components/WorkflowBlockInputSet";
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
@@ -19,6 +20,7 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
});
const [inputs, setInputs] = useState({
code: data.code,
parameterKeys: data.parameterKeys,
});
return (
@@ -61,6 +63,20 @@ function CodeBlockNode({ id, data }: NodeProps<CodeBlockNode>) {
}}
/>
</div>
<div className="space-y-2">
<Label className="text-xs text-slate-300">Input Parameters</Label>
<WorkflowBlockInputSet
nodeId={id}
onChange={(parameterKeys) => {
setInputs({
...inputs,
parameterKeys: Array.from(parameterKeys),
});
updateNodeData(id, { parameterKeys: Array.from(parameterKeys) });
}}
values={new Set(inputs.parameterKeys ?? [])}
/>
</div>
<div className="space-y-2">
<Label className="text-xs text-slate-300">Code Input</Label>
<CodeEditor

View File

@@ -3,13 +3,26 @@ import { NodeBaseData } from "../types";
export type CodeBlockNodeData = NodeBaseData & {
code: string;
parameterKeys: Array<string> | null;
};
export type CodeBlockNode = Node<CodeBlockNodeData, "codeBlock">;
const codeLead = `
# This feature is currently in private beta. Please reach out to
# founders@skyvern.com to get access.
#
# Any parameter you've added to the "Input Parameters" list is available in
# global scope, by the same name.
#
# Any top-level variable you create is assigned to the output of this block.
# e.g., if you've written 'x = 5', then 'x' is included in the block output.
`;
export const codeBlockNodeDefaultData: CodeBlockNodeData = {
editable: true,
label: "",
code: `# This feature is currently in private beta. Please reach out to founders@skyvern.com to get access\n# All variables will be assigned to the output of this block.\n# Like 'x = 5', 'x' will be assigned to the output of this block.\n\n`,
code: codeLead,
continueOnFailure: false,
parameterKeys: null,
} as const;

View File

@@ -6,7 +6,8 @@ import { AddNodeProps } from "../FlowRenderer";
import { WorkflowBlockNode } from "../nodes";
import { WorkflowBlockIcon } from "../nodes/WorkflowBlockIcon";
const enableCodeBlock = import.meta.env.VITE_ENABLE_CODE_BLOCK === "true";
const enableCodeBlock =
import.meta.env.VITE_ENABLE_CODE_BLOCK?.toLowerCase() === "true";
const nodeLibraryItems: Array<{
nodeType: NonNullable<WorkflowBlockNode["type"]>;

View File

@@ -384,6 +384,7 @@ function convertToNode(
data: {
...commonData,
code: block.code,
parameterKeys: block.parameters.map((p) => p.key),
},
};
}
@@ -1140,6 +1141,7 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML {
return {
...base,
block_type: "code",
parameter_keys: node.data.parameterKeys,
code: node.data.code,
};
}
@@ -1842,6 +1844,7 @@ function convertBlocksToBlockYAML(
...base,
block_type: "code",
code: block.code,
parameter_keys: block.parameters.map((p) => p.key),
};
return blockYaml;
}