diff --git a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx index 8c9b43916b..d1926da15e 100644 --- a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx @@ -5,6 +5,7 @@ import type { IterationDurationMap, NodeTracing, } from '@/types/workflow' +import { NodeRunningStatus } from '@/app/components/workflow/types' import { Iteration } from '@/app/components/base/icons/src/vender/workflow' type IterationLogTriggerProps = { @@ -54,6 +55,30 @@ const IterationLogTrigger = ({ structuredList = instanceKeys .map(key => filterNodesForInstance(key)) .filter(branchNodes => branchNodes.length > 0) + + // Also include failed iterations that might not be in duration map + if (allExecutions && nodeInfo.details?.length) { + const existingIterationIndices = new Set() + structuredList.forEach((iteration) => { + iteration.forEach((node) => { + if (node.execution_metadata?.iteration_index !== undefined) + existingIterationIndices.add(node.execution_metadata.iteration_index) + }) + }) + + // Find failed iterations that are not in the structured list + nodeInfo.details.forEach((iteration, index) => { + if (!existingIterationIndices.has(index) && iteration.some(node => node.status === NodeRunningStatus.Failed)) + structuredList.push(iteration) + }) + + // Sort by iteration index to maintain order + structuredList.sort((a, b) => { + const aIndex = a[0]?.execution_metadata?.iteration_index ?? 0 + const bIndex = b[0]?.execution_metadata?.iteration_index ?? 0 + return aIndex - bIndex + }) + } } else if (nodeInfo.details?.length) { structuredList = nodeInfo.details @@ -71,16 +96,36 @@ const IterationLogTrigger = ({ else if (nodeInfo.metadata?.iterator_length) displayIterationCount = nodeInfo.metadata.iterator_length - const getErrorCount = (details: NodeTracing[][] | undefined) => { + const getErrorCount = (details: NodeTracing[][] | undefined, iterationNodeMeta?: any) => { if (!details || details.length === 0) return 0 - return details.reduce((acc, iteration) => { - if (iteration.some(item => item.status === 'failed')) - acc++ - return acc - }, 0) + + // Use Set to track failed iteration indices to avoid duplicate counting + const failedIterationIndices = new Set() + + // Collect failed iteration indices from details + details.forEach((iteration, index) => { + if (iteration.some(item => item.status === NodeRunningStatus.Failed)) { + // Try to get iteration index from first node, fallback to array index + const iterationIndex = iteration[0]?.execution_metadata?.iteration_index ?? index + failedIterationIndices.add(iterationIndex) + } + }) + + // If allExecutions exists, check for additional failed iterations + if (iterationNodeMeta?.iteration_duration_map && allExecutions) { + // Find all failed iteration nodes + allExecutions.forEach((exec) => { + if (exec.execution_metadata?.iteration_id === nodeInfo.node_id + && exec.status === NodeRunningStatus.Failed + && exec.execution_metadata?.iteration_index !== undefined) + failedIterationIndices.add(exec.execution_metadata.iteration_index) + }) + } + + return failedIterationIndices.size } - const errorCount = getErrorCount(nodeInfo.details) + const errorCount = getErrorCount(nodeInfo.details, nodeInfo.execution_metadata) return (