Jon/add input parameters to code block (#2308)
This commit is contained in:
84
skyvern-frontend/src/components/WorkflowBlockInputSet.tsx
Normal file
84
skyvern-frontend/src/components/WorkflowBlockInputSet.tsx
Normal 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"> </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 };
|
||||
27
skyvern-frontend/src/components/workflow-block-input-set.css
Normal file
27
skyvern-frontend/src/components/workflow-block-input-set.css
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"]>;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user