feat: make iteration

This commit is contained in:
Joel 2025-01-03 15:40:49 +08:00
parent fbf9984d85
commit 5fdfba6b00
4 changed files with 127 additions and 198 deletions

View File

@ -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',
},
])
})
})

View File

@ -2,18 +2,27 @@ const STEP_SPLIT = '->'
const toNodeData = (step: string, info: Record<string, any> = {}): any => {
const [nodeId, title] = step.split('@')
const data = {
const data: Record<string, any> = {
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<string | NodeStructure>;
@ -43,6 +67,7 @@ export function parseNodeString(input: string): NodeStructure {
const parts: Array<string | NodeStructure> = []
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,

View File

@ -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],
}
})()

View File

@ -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]],
],
},
])
})
})