Skip to content

Commit 8897633

Browse files
committed
feat(workflow): add human input node formatting to retain latest status
1 parent 8a20a59 commit 8897633

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

web/app/components/workflow/run/index.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import type { FC } from 'react'
33
import type { WorkflowRunDetailResponse } from '@/models/log'
44
import type { NodeTracing } from '@/types/workflow'
5-
import * as React from 'react'
65
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
76
import { useTranslation } from 'react-i18next'
87
import { useContext } from 'use-context-selector'
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { NodeTracing } from '@/types/workflow'
2+
import { BlockEnum } from '@/app/components/workflow/types'
3+
4+
/**
5+
* Format human-input nodes to ensure only the latest status is kept for each node.
6+
* Human-input nodes can have multiple log entries as their status changes
7+
* (e.g., running -> paused -> succeeded/failed).
8+
* This function keeps only the entry with the latest index for each unique node_id.
9+
*/
10+
const formatHumanInputNode = (list: NodeTracing[]): NodeTracing[] => {
11+
// Group human-input nodes by node_id
12+
const humanInputNodeMap = new Map<string, NodeTracing>()
13+
14+
// Track which node_ids are human-input type
15+
const humanInputNodeIds = new Set<string>()
16+
17+
// First pass: identify human-input nodes and keep the one with the highest index
18+
list.forEach((item) => {
19+
if (item.node_type === BlockEnum.HumanInput) {
20+
humanInputNodeIds.add(item.node_id)
21+
22+
const existingNode = humanInputNodeMap.get(item.node_id)
23+
if (!existingNode || item.index > existingNode.index) {
24+
humanInputNodeMap.set(item.node_id, item)
25+
}
26+
}
27+
})
28+
29+
// If no human-input nodes, return the list as is
30+
if (humanInputNodeIds.size === 0)
31+
return list
32+
33+
// Second pass: filter the list to remove duplicate human-input nodes
34+
// and keep only the latest one for each node_id
35+
const result: NodeTracing[] = []
36+
const addedHumanInputNodeIds = new Set<string>()
37+
38+
list.forEach((item) => {
39+
if (item.node_type === BlockEnum.HumanInput) {
40+
// Only add the human-input node with the highest index
41+
if (!addedHumanInputNodeIds.has(item.node_id)) {
42+
const latestNode = humanInputNodeMap.get(item.node_id)
43+
if (latestNode) {
44+
result.push(latestNode)
45+
addedHumanInputNodeIds.add(item.node_id)
46+
}
47+
}
48+
// Skip duplicate human-input nodes
49+
}
50+
else {
51+
// Keep all non-human-input nodes
52+
result.push(item)
53+
}
54+
})
55+
56+
return result
57+
}
58+
59+
export default formatHumanInputNode

web/app/components/workflow/run/utils/format-log/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { NodeTracing } from '@/types/workflow'
22
import { cloneDeep } from 'es-toolkit/object'
33
import { BlockEnum } from '../../../types'
44
import formatAgentNode from './agent'
5+
import formatHumanInputNode from './human-input'
56
import { addChildrenToIterationNode } from './iteration'
67
import { addChildrenToLoopNode } from './loop'
78
import formatParallelNode from './parallel'
@@ -83,7 +84,8 @@ const formatToTracingNodeList = (list: NodeTracing[], t: any) => {
8384
* Because Handle struct node will put the node in different
8485
*/
8586
const formattedAgentList = formatAgentNode(allItems)
86-
const formattedRetryList = formatRetryNode(formattedAgentList) // retry one node
87+
const formattedHumanInputList = formatHumanInputNode(formattedAgentList) // Keep only latest status for human-input nodes
88+
const formattedRetryList = formatRetryNode(formattedHumanInputList) // retry one node
8789
// would change the structure of the list. Iteration and parallel can include each other.
8890
const formattedLoopAndIterationList = formatIterationAndLoopNode(formattedRetryList, t)
8991
const formattedParallelList = formatParallelNode(formattedLoopAndIterationList, t)

0 commit comments

Comments
 (0)