diff --git a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.spec.ts b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.spec.ts index 8a783c9644..f4d78b62f2 100644 --- a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.spec.ts @@ -22,7 +22,76 @@ describe('graphToLogStruct', () => { ], }) }) + test('iteration nodes', () => { + expect(graphToLogStruct('start -> (iteration, 1, [2, 3])')).toEqual([ + { + id: 'start', + node_id: 'start', + title: 'start', + execution_metadata: {}, + status: 'succeeded', + }, + { + id: '1', + node_id: '1', + title: '1', + execution_metadata: {}, + status: 'succeeded', + node_type: 'iteration', + }, + { + id: '2', + node_id: '2', + title: '2', + execution_metadata: { iteration_id: '1', iteration_index: 0 }, + status: 'succeeded', + }, + { + id: '3', + node_id: '3', + title: '3', + execution_metadata: { iteration_id: '1', iteration_index: 1 }, + status: 'succeeded', + }, + ]) + }) test('retry nodes', () => { - console.log(graphToLogStruct('start -> (retry, 1, 3)')) + expect(graphToLogStruct('start -> (retry, 1, 3)')).toEqual([ + { + id: 'start', + node_id: 'start', + title: 'start', + execution_metadata: {}, + status: 'succeeded', + }, + { + id: '1', + node_id: '1', + title: '1', + execution_metadata: {}, + status: 'succeeded', + }, + { + id: '1', + node_id: '1', + title: '1', + execution_metadata: {}, + status: 'retry', + }, + { + id: '1', + node_id: '1', + title: '1', + execution_metadata: {}, + status: 'retry', + }, + { + id: '1', + node_id: '1', + title: '1', + execution_metadata: {}, + status: 'retry', + }, + ]) }) }) diff --git a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts index faa16d53c6..d7092f0642 100644 --- a/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts +++ b/web/app/components/workflow/run/utils/format-log/graph-to-log-struct.ts @@ -2,18 +2,27 @@ const STEP_SPLIT = '->' const toNodeData = (step: string, info: Record = {}): any => { const [nodeId, title] = step.split('@') - const data = { + const data: Record = { id: nodeId, node_id: nodeId, title: title || nodeId, execution_metadata: {}, status: 'succeeded', } - // const executionMetadata = data.execution_metadata - const { isRetry } = info + + const executionMetadata = data.execution_metadata + const { isRetry, isIteration, inIterationInfo } = info if (isRetry) data.status = 'retry' + if (isIteration) + data.node_type = 'iteration' + + if (inIterationInfo) { + executionMetadata.iteration_id = inIterationInfo.iterationId + executionMetadata.iteration_index = inIterationInfo.iterationIndex + } + return data } @@ -30,6 +39,21 @@ const toRetryNodeData = ({ return res } +const toIterationNodeData = ({ + nodeId, + children, +}: { + nodeId: string, + children: number[], +}) => { + const res = [toNodeData(nodeId, { isIteration: true })] + // TODO: handle inner node structure + for (let i = 0; i < children.length; i++) + res.push(toNodeData(`${children[i]}`, { inIterationInfo: { iterationId: nodeId, iterationIndex: i } })) + + return res +} + type NodeStructure = { node: string; params: Array; @@ -43,6 +67,7 @@ export function parseNodeString(input: string): NodeStructure { const parts: Array = [] let current = '' let depth = 0 + let inArrayDepth = 0 for (let i = 0; i < input.length; i++) { const char = input[i] @@ -52,7 +77,14 @@ export function parseNodeString(input: string): NodeStructure { else if (char === ')') depth-- - if (char === ',' && depth === 0) { + if (char === '[') + inArrayDepth++ + else if (char === ']') + inArrayDepth-- + + const isInArray = inArrayDepth > 0 + + if (char === ',' && depth === 0 && !isInArray) { parts.push(current.trim()) current = '' } @@ -97,6 +129,12 @@ const toNodes = (input: string): any[] => { const { node, params } = parseNodeString(step) switch (node) { + case 'iteration': + res.push(...toIterationNodeData({ + nodeId: params[0] as string, + children: JSON.parse(params[1] as string) as number[], + })) + break case 'retry': res.push(...toRetryNodeData({ nodeId: params[0] as string, diff --git a/web/app/components/workflow/run/utils/format-log/iteration/data.ts b/web/app/components/workflow/run/utils/format-log/iteration/data.ts deleted file mode 100644 index 0d08cd5350..0000000000 --- a/web/app/components/workflow/run/utils/format-log/iteration/data.ts +++ /dev/null @@ -1,190 +0,0 @@ -export const simpleIterationData = (() => { - // start -> code(output: [1, 2, 3]) -> iteration(output: ['aaa', 'aaa', 'aaa']) -> end(output: ['aaa', 'aaa', 'aaa']) - const startNode = { - id: '36c9860a-39e6-4107-b750-655b07895f47', - index: 1, - predecessor_node_id: null, - node_id: '1735023354069', - node_type: 'start', - title: 'Start', - inputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - process_data: null, - outputs: { - 'sys.files': [], - 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', - 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', - 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', - 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.011458, - execution_metadata: null, - extras: {}, - created_by_end_user: null, - finished_at: 1735023510, - } - - const outputArrayNode = { - id: 'a3105c5d-ff9e-44ea-9f4c-ab428958af20', - index: 2, - predecessor_node_id: '1735023354069', - node_id: '1735023361224', - node_type: 'code', - title: 'Code', - inputs: null, - process_data: null, - outputs: { - result: [ - 1, - 2, - 3, - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.103333, - execution_metadata: null, - extras: {}, - finished_at: 1735023511, - } - - const iterationNode = { - id: 'a823134d-9f1a-45a4-8977-db838d076316', - index: 3, - predecessor_node_id: '1735023361224', - node_id: '1735023391914', - node_type: 'iteration', - title: 'Iteration', - inputs: null, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - - } - - const iterations = [ - { - id: 'a84a22d8-0f08-4006-bee2-fa7a7aef0420', - index: 4, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.112688, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 0, - }, - extras: {}, - created_at: 1735023511, - finished_at: 1735023511, - }, - { - id: 'ff71d773-a916-4513-960f-d7dcc4fadd86', - index: 5, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.126034, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 1, - }, - extras: {}, - created_at: 1735023511, - finished_at: 1735023511, - }, - { - id: 'd91c3ef9-0162-4013-9272-d4cc7fb1f188', - index: 6, - predecessor_node_id: '1735023391914start', - node_id: '1735023409906', - node_type: 'code', - title: 'Code 2', - inputs: null, - process_data: null, - outputs: { - result: 'aaa', - }, - status: 'succeeded', - error: null, - elapsed_time: 0.122716, - execution_metadata: { - iteration_id: '1735023391914', - iteration_index: 2, - }, - extras: {}, - created_at: 1735023511, - finished_at: 1735023511, - }, - ] - - const endNode = { - id: 'e6ad6560-1aa3-43f3-89e3-e5287c9ea272', - index: 7, - predecessor_node_id: '1735023391914', - node_id: '1735023417757', - node_type: 'end', - title: 'End', - inputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - process_data: null, - outputs: { - output: [ - 'aaa', - 'aaa', - 'aaa', - ], - }, - status: 'succeeded', - error: null, - elapsed_time: 0.017552, - execution_metadata: null, - extras: {}, - finished_at: 1735023511, - } - - return { - in: [startNode, outputArrayNode, iterationNode, ...iterations, endNode], - expect: [startNode, outputArrayNode, { - ...iterationNode, - details: [ - [iterations[0]], - [iterations[1]], - [iterations[2]], - ], - }, endNode], - } -})() diff --git a/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts b/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts index 77b776f12c..4c49c41420 100644 --- a/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts +++ b/web/app/components/workflow/run/utils/format-log/iteration/index.spec.ts @@ -1,11 +1,23 @@ import format from '.' -import { simpleIterationData } from './data' +import graphToLogStruct from '../graph-to-log-struct' describe('iteration', () => { + const list = graphToLogStruct('start -> (iteration, 1, [2, 3])') + const [startNode, iterationNode, ...iterations] = graphToLogStruct('start -> (iteration, 1, [2, 3])') + const result = format(list as any, () => { }) test('result should have no nodes in iteration node', () => { - expect(format(simpleIterationData.in as any).find(item => !!(item as any).execution_metadata?.iteration_id)).toBeUndefined() + expect((result as any).find((item: any) => !!item.execution_metadata?.iteration_id)).toBeUndefined() }) test('iteration should put nodes in details', () => { - expect(format(simpleIterationData.in as any)).toEqual(simpleIterationData.expect) + expect(result as any).toEqual([ + startNode, + { + ...iterationNode, + details: [ + [iterations[0]], + [iterations[1]], + ], + }, + ]) }) })