mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 20:17:29 +08:00
feat: make iteration
This commit is contained in:
parent
fbf9984d85
commit
5fdfba6b00
@ -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', () => {
|
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',
|
||||||
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,18 +2,27 @@ const STEP_SPLIT = '->'
|
|||||||
|
|
||||||
const toNodeData = (step: string, info: Record<string, any> = {}): any => {
|
const toNodeData = (step: string, info: Record<string, any> = {}): any => {
|
||||||
const [nodeId, title] = step.split('@')
|
const [nodeId, title] = step.split('@')
|
||||||
const data = {
|
const data: Record<string, any> = {
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
node_id: nodeId,
|
node_id: nodeId,
|
||||||
title: title || nodeId,
|
title: title || nodeId,
|
||||||
execution_metadata: {},
|
execution_metadata: {},
|
||||||
status: 'succeeded',
|
status: 'succeeded',
|
||||||
}
|
}
|
||||||
// const executionMetadata = data.execution_metadata
|
|
||||||
const { isRetry } = info
|
const executionMetadata = data.execution_metadata
|
||||||
|
const { isRetry, isIteration, inIterationInfo } = info
|
||||||
if (isRetry)
|
if (isRetry)
|
||||||
data.status = 'retry'
|
data.status = 'retry'
|
||||||
|
|
||||||
|
if (isIteration)
|
||||||
|
data.node_type = 'iteration'
|
||||||
|
|
||||||
|
if (inIterationInfo) {
|
||||||
|
executionMetadata.iteration_id = inIterationInfo.iterationId
|
||||||
|
executionMetadata.iteration_index = inIterationInfo.iterationIndex
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -30,6 +39,21 @@ const toRetryNodeData = ({
|
|||||||
return res
|
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 = {
|
type NodeStructure = {
|
||||||
node: string;
|
node: string;
|
||||||
params: Array<string | NodeStructure>;
|
params: Array<string | NodeStructure>;
|
||||||
@ -43,6 +67,7 @@ export function parseNodeString(input: string): NodeStructure {
|
|||||||
const parts: Array<string | NodeStructure> = []
|
const parts: Array<string | NodeStructure> = []
|
||||||
let current = ''
|
let current = ''
|
||||||
let depth = 0
|
let depth = 0
|
||||||
|
let inArrayDepth = 0
|
||||||
|
|
||||||
for (let i = 0; i < input.length; i++) {
|
for (let i = 0; i < input.length; i++) {
|
||||||
const char = input[i]
|
const char = input[i]
|
||||||
@ -52,7 +77,14 @@ export function parseNodeString(input: string): NodeStructure {
|
|||||||
else if (char === ')')
|
else if (char === ')')
|
||||||
depth--
|
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())
|
parts.push(current.trim())
|
||||||
current = ''
|
current = ''
|
||||||
}
|
}
|
||||||
@ -97,6 +129,12 @@ const toNodes = (input: string): any[] => {
|
|||||||
|
|
||||||
const { node, params } = parseNodeString(step)
|
const { node, params } = parseNodeString(step)
|
||||||
switch (node) {
|
switch (node) {
|
||||||
|
case 'iteration':
|
||||||
|
res.push(...toIterationNodeData({
|
||||||
|
nodeId: params[0] as string,
|
||||||
|
children: JSON.parse(params[1] as string) as number[],
|
||||||
|
}))
|
||||||
|
break
|
||||||
case 'retry':
|
case 'retry':
|
||||||
res.push(...toRetryNodeData({
|
res.push(...toRetryNodeData({
|
||||||
nodeId: params[0] as string,
|
nodeId: params[0] as string,
|
||||||
|
|||||||
@ -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],
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
@ -1,11 +1,23 @@
|
|||||||
import format from '.'
|
import format from '.'
|
||||||
import { simpleIterationData } from './data'
|
import graphToLogStruct from '../graph-to-log-struct'
|
||||||
|
|
||||||
describe('iteration', () => {
|
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', () => {
|
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', () => {
|
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]],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user