Add continue on failure as passthrough and cache_actions property (#983)

This commit is contained in:
Shuchang Zheng
2024-10-15 13:41:54 -07:00
committed by GitHub
parent 575dd356fe
commit ec2217a3fe
12 changed files with 74 additions and 52 deletions

View File

@@ -1,9 +1,9 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";
export type CodeBlockNodeData = { export type CodeBlockNodeData = NodeBaseData & {
code: string; code: string;
editable: boolean; editable: boolean;
label: string;
}; };
export type CodeBlockNode = Node<CodeBlockNodeData, "codeBlock">; export type CodeBlockNode = Node<CodeBlockNodeData, "codeBlock">;
@@ -12,4 +12,5 @@ export const codeBlockNodeDefaultData: CodeBlockNodeData = {
editable: true, editable: true,
label: "", label: "",
code: `# To assign a value to the output of this block,\n# assign the value to the variable 'result'\n# The final value of 'result' will be used as the output of this block\n\nresult = 5`, code: `# To assign a value to the output of this block,\n# assign the value to the variable 'result'\n# The final value of 'result' will be used as the output of this block\n\nresult = 5`,
continueOnFailure: false,
} as const; } as const;

View File

@@ -1,10 +1,10 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { SKYVERN_DOWNLOAD_DIRECTORY } from "../../constants"; import { SKYVERN_DOWNLOAD_DIRECTORY } from "../../constants";
import { NodeBaseData } from "../types";
export type DownloadNodeData = { export type DownloadNodeData = NodeBaseData & {
url: string; url: string;
editable: boolean; editable: boolean;
label: string;
}; };
export type DownloadNode = Node<DownloadNodeData, "download">; export type DownloadNode = Node<DownloadNodeData, "download">;
@@ -13,6 +13,7 @@ export const downloadNodeDefaultData: DownloadNodeData = {
editable: true, editable: true,
label: "", label: "",
url: SKYVERN_DOWNLOAD_DIRECTORY, url: SKYVERN_DOWNLOAD_DIRECTORY,
continueOnFailure: false,
} as const; } as const;
export const helpTooltipContent = { export const helpTooltipContent = {

View File

@@ -1,9 +1,9 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";
export type FileParserNodeData = { export type FileParserNodeData = NodeBaseData & {
fileUrl: string; fileUrl: string;
editable: boolean; editable: boolean;
label: string;
}; };
export type FileParserNode = Node<FileParserNodeData, "fileParser">; export type FileParserNode = Node<FileParserNodeData, "fileParser">;
@@ -12,6 +12,7 @@ export const fileParserNodeDefaultData: FileParserNodeData = {
editable: true, editable: true,
label: "", label: "",
fileUrl: "", fileUrl: "",
continueOnFailure: false,
} as const; } as const;
export const helpTooltipContent = { export const helpTooltipContent = {

View File

@@ -1,9 +1,9 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";
export type LoopNodeData = { export type LoopNodeData = NodeBaseData & {
loopValue: string; loopValue: string;
editable: boolean; editable: boolean;
label: string;
}; };
export type LoopNode = Node<LoopNodeData, "loop">; export type LoopNode = Node<LoopNodeData, "loop">;
@@ -12,6 +12,7 @@ export const loopNodeDefaultData: LoopNodeData = {
editable: true, editable: true,
label: "", label: "",
loopValue: "", loopValue: "",
continueOnFailure: false,
} as const; } as const;
export function isLoopNode(node: Node): node is LoopNode { export function isLoopNode(node: Node): node is LoopNode {

View File

@@ -7,14 +7,14 @@ import {
SMTP_PORT_PARAMETER_KEY, SMTP_PORT_PARAMETER_KEY,
SMTP_USERNAME_PARAMETER_KEY, SMTP_USERNAME_PARAMETER_KEY,
} from "../../constants"; } from "../../constants";
import { NodeBaseData } from "../types";
export type SendEmailNodeData = { export type SendEmailNodeData = NodeBaseData & {
recipients: string; recipients: string;
subject: string; subject: string;
body: string; body: string;
fileAttachments: string; fileAttachments: string;
editable: boolean; editable: boolean;
label: string;
sender: string; sender: string;
smtpHostSecretParameterKey?: string; smtpHostSecretParameterKey?: string;
smtpPortSecretParameterKey?: string; smtpPortSecretParameterKey?: string;
@@ -36,6 +36,7 @@ export const sendEmailNodeDefaultData: SendEmailNodeData = {
smtpPortSecretParameterKey: SMTP_PORT_PARAMETER_KEY, smtpPortSecretParameterKey: SMTP_PORT_PARAMETER_KEY,
smtpUsernameSecretParameterKey: SMTP_USERNAME_PARAMETER_KEY, smtpUsernameSecretParameterKey: SMTP_USERNAME_PARAMETER_KEY,
smtpPasswordSecretParameterKey: SMTP_PASSWORD_PARAMETER_KEY, smtpPasswordSecretParameterKey: SMTP_PASSWORD_PARAMETER_KEY,
continueOnFailure: false,
} as const; } as const;
export const helpTooltipContent = { export const helpTooltipContent = {

View File

@@ -1,6 +1,7 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";
export type TaskNodeData = { export type TaskNodeData = NodeBaseData & {
url: string; url: string;
navigationGoal: string; navigationGoal: string;
dataExtractionGoal: string; dataExtractionGoal: string;
@@ -11,11 +12,10 @@ export type TaskNodeData = {
allowDownloads: boolean; allowDownloads: boolean;
downloadSuffix: string | null; downloadSuffix: string | null;
editable: boolean; editable: boolean;
label: string;
parameterKeys: Array<string>; parameterKeys: Array<string>;
totpVerificationUrl: string | null; totpVerificationUrl: string | null;
totpIdentifier: string | null; totpIdentifier: string | null;
continueOnFailure: boolean; cacheActions: boolean;
}; };
export type TaskNode = Node<TaskNodeData, "task">; export type TaskNode = Node<TaskNodeData, "task">;
@@ -38,6 +38,7 @@ export const taskNodeDefaultData: TaskNodeData = {
totpVerificationUrl: null, totpVerificationUrl: null,
totpIdentifier: null, totpIdentifier: null,
continueOnFailure: false, continueOnFailure: false,
cacheActions: false,
} as const; } as const;
export function isTaskNode(node: Node): node is TaskNode { export function isTaskNode(node: Node): node is TaskNode {

View File

@@ -1,6 +1,7 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { NodeBaseData } from "../types";
export type TextPromptNodeData = { export type TextPromptNodeData = NodeBaseData & {
prompt: string; prompt: string;
jsonSchema: string; jsonSchema: string;
editable: boolean; editable: boolean;
@@ -14,6 +15,7 @@ export const textPromptNodeDefaultData: TextPromptNodeData = {
label: "", label: "",
prompt: "", prompt: "",
jsonSchema: "null", jsonSchema: "null",
continueOnFailure: false,
} as const; } as const;
export const helpTooltipContent = { export const helpTooltipContent = {

View File

@@ -1,10 +1,10 @@
import type { Node } from "@xyflow/react"; import type { Node } from "@xyflow/react";
import { SKYVERN_DOWNLOAD_DIRECTORY } from "../../constants"; import { SKYVERN_DOWNLOAD_DIRECTORY } from "../../constants";
import { NodeBaseData } from "../types";
export type UploadNodeData = { export type UploadNodeData = NodeBaseData & {
path: string; path: string;
editable: boolean; editable: boolean;
label: string;
}; };
export type UploadNode = Node<UploadNodeData, "upload">; export type UploadNode = Node<UploadNodeData, "upload">;
@@ -13,6 +13,7 @@ export const uploadNodeDefaultData: UploadNodeData = {
editable: true, editable: true,
label: "", label: "",
path: SKYVERN_DOWNLOAD_DIRECTORY, path: SKYVERN_DOWNLOAD_DIRECTORY,
continueOnFailure: false,
} as const; } as const;
export const helpTooltipContent = { export const helpTooltipContent = {

View File

@@ -0,0 +1,4 @@
export type NodeBaseData = {
label: string;
continueOnFailure: boolean;
};

View File

@@ -1,7 +1,11 @@
import Dagre from "@dagrejs/dagre"; import Dagre from "@dagrejs/dagre";
import type { Node } from "@xyflow/react";
import { Edge } from "@xyflow/react"; import { Edge } from "@xyflow/react";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import type { import type {
AWSSecretParameter,
BitwardenSensitiveInformationParameter,
ContextParameter,
OutputParameter, OutputParameter,
Parameter, Parameter,
WorkflowApiResponse, WorkflowApiResponse,
@@ -33,6 +37,7 @@ import {
SMTP_USERNAME_AWS_KEY, SMTP_USERNAME_AWS_KEY,
SMTP_USERNAME_PARAMETER_KEY, SMTP_USERNAME_PARAMETER_KEY,
} from "./constants"; } from "./constants";
import { ParametersState } from "./FlowRenderer";
import { AppNode, nodeTypes } from "./nodes"; import { AppNode, nodeTypes } from "./nodes";
import { codeBlockNodeDefaultData } from "./nodes/CodeBlockNode/types"; import { codeBlockNodeDefaultData } from "./nodes/CodeBlockNode/types";
import { downloadNodeDefaultData } from "./nodes/DownloadNode/types"; import { downloadNodeDefaultData } from "./nodes/DownloadNode/types";
@@ -42,8 +47,8 @@ import { NodeAdderNode } from "./nodes/NodeAdderNode/types";
import { sendEmailNodeDefaultData } from "./nodes/SendEmailNode/types"; import { sendEmailNodeDefaultData } from "./nodes/SendEmailNode/types";
import { taskNodeDefaultData } from "./nodes/TaskNode/types"; import { taskNodeDefaultData } from "./nodes/TaskNode/types";
import { textPromptNodeDefaultData } from "./nodes/TextPromptNode/types"; import { textPromptNodeDefaultData } from "./nodes/TextPromptNode/types";
import { NodeBaseData } from "./nodes/types";
import { uploadNodeDefaultData } from "./nodes/UploadNode/types"; import { uploadNodeDefaultData } from "./nodes/UploadNode/types";
import type { Node } from "@xyflow/react";
export const NEW_NODE_LABEL_PREFIX = "block_"; export const NEW_NODE_LABEL_PREFIX = "block_";
@@ -126,6 +131,10 @@ function convertToNode(
position: { x: 0, y: 0 }, position: { x: 0, y: 0 },
connectable: false, connectable: false,
}; };
const commonData: NodeBaseData = {
label: block.label,
continueOnFailure: block.continue_on_failure,
};
switch (block.block_type) { switch (block.block_type) {
case "task": { case "task": {
return { return {
@@ -133,7 +142,7 @@ function convertToNode(
...common, ...common,
type: "task", type: "task",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
url: block.url ?? "", url: block.url ?? "",
navigationGoal: block.navigation_goal ?? "", navigationGoal: block.navigation_goal ?? "",
@@ -147,7 +156,7 @@ function convertToNode(
parameterKeys: block.parameters.map((p) => p.key), parameterKeys: block.parameters.map((p) => p.key),
totpIdentifier: block.totp_identifier ?? null, totpIdentifier: block.totp_identifier ?? null,
totpVerificationUrl: block.totp_verification_url ?? null, totpVerificationUrl: block.totp_verification_url ?? null,
continueOnFailure: block.continue_on_failure, cacheActions: block.cache_actions,
}, },
}; };
} }
@@ -157,7 +166,7 @@ function convertToNode(
...common, ...common,
type: "codeBlock", type: "codeBlock",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
code: block.code, code: block.code,
}, },
@@ -169,7 +178,7 @@ function convertToNode(
...common, ...common,
type: "sendEmail", type: "sendEmail",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
body: block.body, body: block.body,
fileAttachments: block.file_attachments.join(", "), fileAttachments: block.file_attachments.join(", "),
@@ -189,7 +198,7 @@ function convertToNode(
...common, ...common,
type: "textPrompt", type: "textPrompt",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
prompt: block.prompt, prompt: block.prompt,
jsonSchema: JSON.stringify(block.json_schema, null, 2), jsonSchema: JSON.stringify(block.json_schema, null, 2),
@@ -202,7 +211,7 @@ function convertToNode(
...common, ...common,
type: "loop", type: "loop",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
loopValue: block.loop_over.key, loopValue: block.loop_over.key,
}, },
@@ -214,7 +223,7 @@ function convertToNode(
...common, ...common,
type: "fileParser", type: "fileParser",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
fileUrl: block.file_url, fileUrl: block.file_url,
}, },
@@ -227,7 +236,7 @@ function convertToNode(
...common, ...common,
type: "download", type: "download",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
url: block.url, url: block.url,
}, },
@@ -240,7 +249,7 @@ function convertToNode(
...common, ...common,
type: "upload", type: "upload",
data: { data: {
label: block.label, ...commonData,
editable: true, editable: true,
path: block.path, path: block.path,
}, },
@@ -497,11 +506,15 @@ function JSONParseSafe(json: string): Record<string, unknown> | null {
function getWorkflowBlock( function getWorkflowBlock(
node: Exclude<AppNode, LoopNode | NodeAdderNode>, node: Exclude<AppNode, LoopNode | NodeAdderNode>,
): BlockYAML { ): BlockYAML {
const base = {
label: node.data.label,
continue_on_failure: node.data.continueOnFailure,
};
switch (node.type) { switch (node.type) {
case "task": { case "task": {
return { return {
...base,
block_type: "task", block_type: "task",
label: node.data.label,
url: node.data.url, url: node.data.url,
navigation_goal: node.data.navigationGoal, navigation_goal: node.data.navigationGoal,
data_extraction_goal: node.data.dataExtractionGoal, data_extraction_goal: node.data.dataExtractionGoal,
@@ -519,13 +532,13 @@ function getWorkflowBlock(
parameter_keys: node.data.parameterKeys, parameter_keys: node.data.parameterKeys,
totp_identifier: node.data.totpIdentifier, totp_identifier: node.data.totpIdentifier,
totp_verification_url: node.data.totpVerificationUrl, totp_verification_url: node.data.totpVerificationUrl,
continue_on_failure: node.data.continueOnFailure, cache_actions: node.data.cacheActions,
}; };
} }
case "sendEmail": { case "sendEmail": {
return { return {
...base,
block_type: "send_email", block_type: "send_email",
label: node.data.label,
body: node.data.body, body: node.data.body,
file_attachments: node.data.fileAttachments file_attachments: node.data.fileAttachments
.split(",") .split(",")
@@ -545,37 +558,37 @@ function getWorkflowBlock(
} }
case "codeBlock": { case "codeBlock": {
return { return {
...base,
block_type: "code", block_type: "code",
label: node.data.label,
code: node.data.code, code: node.data.code,
}; };
} }
case "download": { case "download": {
return { return {
...base,
block_type: "download_to_s3", block_type: "download_to_s3",
label: node.data.label,
url: node.data.url, url: node.data.url,
}; };
} }
case "upload": { case "upload": {
return { return {
...base,
block_type: "upload_to_s3", block_type: "upload_to_s3",
label: node.data.label,
path: node.data.path, path: node.data.path,
}; };
} }
case "fileParser": { case "fileParser": {
return { return {
...base,
block_type: "file_url_parser", block_type: "file_url_parser",
label: node.data.label,
file_url: node.data.fileUrl, file_url: node.data.fileUrl,
file_type: "csv", file_type: "csv",
}; };
} }
case "textPrompt": { case "textPrompt": {
return { return {
...base,
block_type: "text_prompt", block_type: "text_prompt",
label: node.data.label,
llm_key: "", llm_key: "",
prompt: node.data.prompt, prompt: node.data.prompt,
json_schema: JSONParseSafe(node.data.jsonSchema), json_schema: JSONParseSafe(node.data.jsonSchema),
@@ -630,13 +643,6 @@ function generateNodeLabel(existingLabels: Array<string>) {
throw new Error("Failed to generate a new node label"); throw new Error("Failed to generate a new node label");
} }
import type {
AWSSecretParameter,
BitwardenSensitiveInformationParameter,
ContextParameter,
} from "../types/workflowTypes";
import { ParametersState } from "./FlowRenderer";
/** /**
* If a parameter is not displayed in the editor, we should echo its value back when saved. * If a parameter is not displayed in the editor, we should echo its value back when saved.
*/ */
@@ -980,6 +986,7 @@ function convertBlocks(blocks: Array<WorkflowBlock>): Array<BlockYAML> {
parameter_keys: block.parameters.map((p) => p.key), parameter_keys: block.parameters.map((p) => p.key),
totp_identifier: block.totp_identifier, totp_identifier: block.totp_identifier,
totp_verification_url: block.totp_verification_url, totp_verification_url: block.totp_verification_url,
cache_actions: block.cache_actions,
}; };
return blockYaml; return blockYaml;
} }
@@ -1076,22 +1083,22 @@ function convert(workflow: WorkflowApiResponse): WorkflowCreateYAMLRequest {
} }
export { export {
convert,
convertEchoParameters,
createNode, createNode,
generateNodeData, generateNodeData,
getElements,
getWorkflowBlocks,
layout,
generateNodeLabel, generateNodeLabel,
convertEchoParameters,
getOutputParameterKey,
getUpdatedNodesAfterLabelUpdateForParameterKeys,
getAdditionalParametersForEmailBlock, getAdditionalParametersForEmailBlock,
getUniqueLabelForExistingNode, getAvailableOutputParameterKeys,
isOutputParameterKey,
getBlockNameOfOutputParameterKey, getBlockNameOfOutputParameterKey,
getDefaultValueForParameterType, getDefaultValueForParameterType,
getUpdatedParametersAfterLabelUpdateForSourceParameterKey, getElements,
getOutputParameterKey,
getPreviousNodeIds, getPreviousNodeIds,
getAvailableOutputParameterKeys, getUniqueLabelForExistingNode,
convert, getUpdatedNodesAfterLabelUpdateForParameterKeys,
getUpdatedParametersAfterLabelUpdateForSourceParameterKey,
getWorkflowBlocks,
isOutputParameterKey,
layout,
}; };

View File

@@ -137,6 +137,7 @@ export type TaskBlock = WorkflowBlockBase & {
download_suffix?: string | null; download_suffix?: string | null;
totp_verification_url?: string | null; totp_verification_url?: string | null;
totp_identifier?: string | null; totp_identifier?: string | null;
cache_actions: boolean;
}; };
export type ForLoopBlock = WorkflowBlockBase & { export type ForLoopBlock = WorkflowBlockBase & {

View File

@@ -110,6 +110,7 @@ export type TaskBlockYAML = BlockYAMLBase & {
download_suffix?: string | null; download_suffix?: string | null;
totp_verification_url?: string | null; totp_verification_url?: string | null;
totp_identifier?: string | null; totp_identifier?: string | null;
cache_actions: boolean;
}; };
export type CodeBlockYAML = BlockYAMLBase & { export type CodeBlockYAML = BlockYAMLBase & {