diff --git a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx index 81288815..6662e0f2 100644 --- a/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx +++ b/skyvern-frontend/src/routes/tasks/create/SavedTaskForm.tsx @@ -87,6 +87,7 @@ function createTaskTemplateRequestObject(values: SavedTaskFormValues) { webhook_callback_url: values.webhookCallbackUrl, proxy_location: values.proxyLocation, workflow_definition: { + version: 2, parameters: [ { parameter_type: "workflow", diff --git a/skyvern-frontend/src/routes/tasks/create/SavedTasks.tsx b/skyvern-frontend/src/routes/tasks/create/SavedTasks.tsx index 72347260..d4feeb73 100644 --- a/skyvern-frontend/src/routes/tasks/create/SavedTasks.tsx +++ b/skyvern-frontend/src/routes/tasks/create/SavedTasks.tsx @@ -30,6 +30,7 @@ function createEmptyTaskTemplate() { webhook_callback_url: null, proxy_location: "RESIDENTIAL", workflow_definition: { + version: 2, parameters: [ { parameter_type: "workflow", diff --git a/skyvern-frontend/src/routes/tasks/list/TaskActions.tsx b/skyvern-frontend/src/routes/tasks/list/TaskActions.tsx index 86b1d968..cdfd0ced 100644 --- a/skyvern-frontend/src/routes/tasks/list/TaskActions.tsx +++ b/skyvern-frontend/src/routes/tasks/list/TaskActions.tsx @@ -54,6 +54,7 @@ function createTaskTemplateRequestObject( webhook_callback_url: task.request.webhook_callback_url, proxy_location: task.request.proxy_location, workflow_definition: { + version: 2, parameters: [ { parameter_type: "workflow", diff --git a/skyvern-frontend/src/routes/workflows/Workflows.tsx b/skyvern-frontend/src/routes/workflows/Workflows.tsx index eff44f17..1d6b0e8f 100644 --- a/skyvern-frontend/src/routes/workflows/Workflows.tsx +++ b/skyvern-frontend/src/routes/workflows/Workflows.tsx @@ -65,6 +65,7 @@ const emptyWorkflowRequest: WorkflowCreateYAMLRequest = { ai_fallback: true, run_with: "agent", workflow_definition: { + version: 2, blocks: [], parameters: [], }, diff --git a/skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx b/skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx index 902e909b..1241b220 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowsPageBanner.tsx @@ -8,6 +8,7 @@ const emptyWorkflowRequest: WorkflowCreateYAMLRequest = { title: "New Workflow", description: "", workflow_definition: { + version: 2, blocks: [], parameters: [], }, diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx index 4957f654..1f172feb 100644 --- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx @@ -84,6 +84,7 @@ import { getWorkflowBlocks, getWorkflowSettings, layout, + upgradeWorkflowDefinitionToVersionTwo, } from "./workflowEditorUtils"; import { getWorkflowErrors } from "./workflowEditorUtils"; import { toast } from "@/components/ui/use-toast"; @@ -360,6 +361,11 @@ function FlowRenderer({ const constructSaveData = useCallback((): WorkflowSaveData => { const blocks = getWorkflowBlocks(nodes, edges); + const { blocks: upgradedBlocks, version: workflowDefinitionVersion } = + upgradeWorkflowDefinitionToVersionTwo( + blocks, + workflow.workflow_definition.version, + ); const settings = getWorkflowSettings(nodes); const parametersInYAMLConvertibleJSON = convertToParametersYAML(parameters); const filteredParameters = workflow.workflow_definition.parameters.filter( @@ -377,7 +383,7 @@ function FlowRenderer({ // if there is an email node, we need to add the email aws secret parameters const emailAwsSecretParameters = getAdditionalParametersForEmailBlock( - blocks, + upgradedBlocks, overallParameters, ); @@ -387,7 +393,8 @@ function FlowRenderer({ ...parametersInYAMLConvertibleJSON, ...emailAwsSecretParameters, ], - blocks, + blocks: upgradedBlocks, + workflowDefinitionVersion, title, settings, workflow, diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 2739b253..1bb9912e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -1908,6 +1908,43 @@ function convertParametersToParameterYAML( .filter(Boolean); } +function clone(objectToClone: T): T { + return JSON.parse(JSON.stringify(objectToClone)); +} + +function assignSequentialNextBlockLabels(blocks: Array): void { + if (!blocks || blocks.length === 0) { + return; + } + + for (let index = 0; index < blocks.length; index++) { + const block = blocks[index]!; + const nextBlock = + index < blocks.length - 1 ? blocks[index + 1]! : undefined; + block.next_block_label = nextBlock ? nextBlock.label : null; + + if (block.block_type === "for_loop") { + const loopBlock = block as ForLoopBlockYAML; + assignSequentialNextBlockLabels(loopBlock.loop_blocks); + } + } +} + +export function upgradeWorkflowDefinitionToVersionTwo( + blocks: Array, + currentVersion?: number | null, +): { blocks: Array; version: number } { + const clonedBlocks = clone(blocks); + const baseVersion = currentVersion ?? 1; + + if (baseVersion <= 2) { + assignSequentialNextBlockLabels(clonedBlocks); + return { blocks: clonedBlocks, version: 2 }; + } + + return { blocks: clonedBlocks, version: baseVersion }; +} + function convertBlocksToBlockYAML( blocks: Array, ): Array { @@ -1915,6 +1952,7 @@ function convertBlocksToBlockYAML( const base = { label: block.label, continue_on_failure: block.continue_on_failure, + next_block_label: block.next_block_label, }; switch (block.block_type) { case "task": { @@ -2216,6 +2254,7 @@ function convertBlocksToBlockYAML( } function convert(workflow: WorkflowApiResponse): WorkflowCreateYAMLRequest { + const workflowDefinitionVersion = workflow.workflow_definition.version ?? 1; const userParameters = workflow.workflow_definition.parameters.filter( (parameter) => parameter.parameter_type !== WorkflowParameterTypes.Output, ); @@ -2230,6 +2269,7 @@ function convert(workflow: WorkflowApiResponse): WorkflowCreateYAMLRequest { max_screenshot_scrolls: workflow.max_screenshot_scrolls, extra_http_headers: workflow.extra_http_headers, workflow_definition: { + version: workflowDefinitionVersion, parameters: convertParametersToParameterYAML(userParameters), blocks: convertBlocksToBlockYAML(workflow.workflow_definition.blocks), }, diff --git a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts index 87f94f36..ae9f47f3 100644 --- a/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts +++ b/skyvern-frontend/src/routes/workflows/types/workflowTypes.ts @@ -288,6 +288,7 @@ export type WorkflowBlockBase = { output_parameter: OutputParameter; continue_on_failure: boolean; model: WorkflowModel | null; + next_block_label?: string | null; }; export type TaskBlock = WorkflowBlockBase & { @@ -522,6 +523,7 @@ export type HttpRequestBlock = WorkflowBlockBase & { }; export type WorkflowDefinition = { + version?: number | null; parameters: Array; blocks: Array; }; diff --git a/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts b/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts index e0c7db3e..457f630a 100644 --- a/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts +++ b/skyvern-frontend/src/routes/workflows/types/workflowYamlTypes.ts @@ -24,6 +24,7 @@ export type WorkflowCreateYAMLRequest = { }; export type WorkflowDefinitionYAML = { + version?: number | null; parameters: Array; blocks: Array; }; @@ -144,6 +145,7 @@ export type BlockYAMLBase = { block_type: WorkflowBlockType; label: string; continue_on_failure?: boolean; + next_block_label?: string | null; }; export type TaskBlockYAML = BlockYAMLBase & { diff --git a/skyvern-frontend/src/store/WorkflowHasChangesStore.ts b/skyvern-frontend/src/store/WorkflowHasChangesStore.ts index cf0d2b68..e8db0eef 100644 --- a/skyvern-frontend/src/store/WorkflowHasChangesStore.ts +++ b/skyvern-frontend/src/store/WorkflowHasChangesStore.ts @@ -19,6 +19,7 @@ import { WorkflowCreateYAMLRequest } from "@/routes/workflows/types/workflowYaml type SaveData = { parameters: Array; blocks: Array; + workflowDefinitionVersion: number; title: string; settings: WorkflowSettings; workflow: WorkflowApiResponse; @@ -139,6 +140,7 @@ const useWorkflowSave = (opts?: WorkflowSaveOpts) => { cache_key: normalizedKey, ai_fallback: saveData.settings.aiFallback ?? true, workflow_definition: { + version: saveData.workflowDefinitionVersion, parameters: saveData.parameters, blocks: saveData.blocks, },