From 77950e48c76f4a3b29d01831d43039caba29888a Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期二, 18 十一月 2025 14:12:42 +0800
Subject: [PATCH] 修改
---
app/components/workflow/run/tracing-panel.tsx | 247 ++++++++++++++++++++++++++++++++-----------------
1 files changed, 161 insertions(+), 86 deletions(-)
diff --git a/app/components/workflow/run/tracing-panel.tsx b/app/components/workflow/run/tracing-panel.tsx
index a6e9bf9..ad78971 100644
--- a/app/components/workflow/run/tracing-panel.tsx
+++ b/app/components/workflow/run/tracing-panel.tsx
@@ -12,27 +12,162 @@
RiMenu4Line,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
-import { useLogs } from './hooks'
import NodePanel from './node'
-import SpecialResultPanel from './special-result-panel'
-import type { NodeTracing } from '@/types/workflow'
-import formatNodeList from '@/app/components/workflow/run/utils/format-log'
+import {
+ BlockEnum,
+} from '@/app/components/workflow/types'
+import type { IterationDurationMap, NodeTracing } from '@/types/workflow'
type TracingPanelProps = {
list: NodeTracing[]
+ onShowIterationDetail?: (detail: NodeTracing[][], iterDurationMap: IterationDurationMap) => void
+ onShowRetryDetail?: (detail: NodeTracing[]) => void
className?: string
hideNodeInfo?: boolean
hideNodeProcessDetail?: boolean
}
+type TracingNodeProps = {
+ id: string
+ uniqueId: string
+ isParallel: boolean
+ data: NodeTracing | null
+ children: TracingNodeProps[]
+ parallelTitle?: string
+ branchTitle?: string
+ hideNodeInfo?: boolean
+ hideNodeProcessDetail?: boolean
+}
+
+function buildLogTree(nodes: NodeTracing[], t: (key: string) => string): TracingNodeProps[] {
+ const rootNodes: TracingNodeProps[] = []
+ const parallelStacks: { [key: string]: TracingNodeProps } = {}
+ const levelCounts: { [key: string]: number } = {}
+ const parallelChildCounts: { [key: string]: Set<string> } = {}
+ let uniqueIdCounter = 0
+ const getUniqueId = () => {
+ uniqueIdCounter++
+ return `unique-${uniqueIdCounter}`
+ }
+
+ const getParallelTitle = (parentId: string | null): string => {
+ const levelKey = parentId || 'root'
+ if (!levelCounts[levelKey])
+ levelCounts[levelKey] = 0
+
+ levelCounts[levelKey]++
+
+ const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : ''
+ const levelNumber = parentTitle ? parseInt(parentTitle.split('-')[1]) + 1 : 1
+ const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : ''
+ return `${t('workflow.common.parallel')}-${levelNumber}${letter}`
+ }
+
+ const getBranchTitle = (parentId: string | null, branchNum: number): string => {
+ const levelKey = parentId || 'root'
+ const parentTitle = parentId ? parallelStacks[parentId]?.parallelTitle : ''
+ const levelNumber = parentTitle ? parseInt(parentTitle.split('-')[1]) + 1 : 1
+ const letter = parallelChildCounts[levelKey]?.size > 1 ? String.fromCharCode(64 + levelCounts[levelKey]) : ''
+ const branchLetter = String.fromCharCode(64 + branchNum)
+ return `${t('workflow.common.branch')}-${levelNumber}${letter}-${branchLetter}`
+ }
+
+ // Count parallel children (for figuring out if we need to use letters)
+ for (const node of nodes) {
+ const parent_parallel_id = node.parent_parallel_id ?? node.execution_metadata?.parent_parallel_id ?? null
+ const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null
+
+ if (parallel_id) {
+ const parentKey = parent_parallel_id || 'root'
+ if (!parallelChildCounts[parentKey])
+ parallelChildCounts[parentKey] = new Set()
+
+ parallelChildCounts[parentKey].add(parallel_id)
+ }
+ }
+
+ for (const node of nodes) {
+ const parallel_id = node.parallel_id ?? node.execution_metadata?.parallel_id ?? null
+ const parent_parallel_id = node.parent_parallel_id ?? node.execution_metadata?.parent_parallel_id ?? null
+ const parallel_start_node_id = node.parallel_start_node_id ?? node.execution_metadata?.parallel_start_node_id ?? null
+ const parent_parallel_start_node_id = node.parent_parallel_start_node_id ?? node.execution_metadata?.parent_parallel_start_node_id ?? null
+
+ if (!parallel_id || node.node_type === BlockEnum.End) {
+ rootNodes.push({
+ id: node.id,
+ uniqueId: getUniqueId(),
+ isParallel: false,
+ data: node,
+ children: [],
+ })
+ }
+ else {
+ if (!parallelStacks[parallel_id]) {
+ const newParallelGroup: TracingNodeProps = {
+ id: parallel_id,
+ uniqueId: getUniqueId(),
+ isParallel: true,
+ data: null,
+ children: [],
+ parallelTitle: '',
+ }
+ parallelStacks[parallel_id] = newParallelGroup
+
+ if (parent_parallel_id && parallelStacks[parent_parallel_id]) {
+ const sameBranchIndex = parallelStacks[parent_parallel_id].children.findLastIndex(c =>
+ c.data?.execution_metadata?.parallel_start_node_id === parent_parallel_start_node_id || c.data?.parallel_start_node_id === parent_parallel_start_node_id,
+ )
+ parallelStacks[parent_parallel_id].children.splice(sameBranchIndex + 1, 0, newParallelGroup)
+ newParallelGroup.parallelTitle = getParallelTitle(parent_parallel_id)
+ }
+ else {
+ newParallelGroup.parallelTitle = getParallelTitle(parent_parallel_id)
+ rootNodes.push(newParallelGroup)
+ }
+ }
+ const branchTitle = parallel_start_node_id === node.node_id ? getBranchTitle(parent_parallel_id, parallelStacks[parallel_id].children.length + 1) : ''
+ if (branchTitle) {
+ parallelStacks[parallel_id].children.push({
+ id: node.id,
+ uniqueId: getUniqueId(),
+ isParallel: false,
+ data: node,
+ children: [],
+ branchTitle,
+ })
+ }
+ else {
+ let sameBranchIndex = parallelStacks[parallel_id].children.findLastIndex(c =>
+ c.data?.execution_metadata?.parallel_start_node_id === parallel_start_node_id || c.data?.parallel_start_node_id === parallel_start_node_id,
+ )
+ if (parallelStacks[parallel_id].children[sameBranchIndex + 1]?.isParallel)
+ sameBranchIndex++
+
+ parallelStacks[parallel_id].children.splice(sameBranchIndex + 1, 0, {
+ id: node.id,
+ uniqueId: getUniqueId(),
+ isParallel: false,
+ data: node,
+ children: [],
+ branchTitle,
+ })
+ }
+ }
+ }
+
+ return rootNodes
+}
+
const TracingPanel: FC<TracingPanelProps> = ({
list,
+ onShowIterationDetail,
+ onShowRetryDetail,
className,
hideNodeInfo = false,
hideNodeProcessDetail = false,
}) => {
const { t } = useTranslation()
- const treeNodes = formatNodeList(list, t)
+ const treeNodes = buildLogTree(list, t)
const [collapsedNodes, setCollapsedNodes] = useState<Set<string>>(new Set())
const [hoveredParallel, setHoveredParallel] = useState<string | null>(null)
@@ -68,47 +203,19 @@
}
}, [])
- const {
- showSpecialResultPanel,
-
- showRetryDetail,
- setShowRetryDetailFalse,
- retryResultList,
- handleShowRetryResultList,
-
- showIteratingDetail,
- setShowIteratingDetailFalse,
- iterationResultList,
- iterationResultDurationMap,
- handleShowIterationResultList,
-
- showLoopingDetail,
- setShowLoopingDetailFalse,
- loopResultList,
- loopResultDurationMap,
- loopResultVariableMap,
- handleShowLoopResultList,
-
- agentOrToolLogItemStack,
- agentOrToolLogListMap,
- handleShowAgentOrToolLog,
- } = useLogs()
-
- const renderNode = (node: NodeTracing) => {
- const isParallelFirstNode = !!node.parallelDetail?.isParallelStartNode
- if (isParallelFirstNode) {
- const parallelDetail = node.parallelDetail!
+ const renderNode = (node: TracingNodeProps) => {
+ if (node.isParallel) {
const isCollapsed = collapsedNodes.has(node.id)
const isHovered = hoveredParallel === node.id
return (
<div
- key={node.id}
- className="relative mb-2 ml-4"
+ key={node.uniqueId}
+ className="ml-4 mb-2 relative"
data-parallel-id={node.id}
onMouseEnter={() => handleParallelMouseEnter(node.id)}
onMouseLeave={handleParallelMouseLeave}
>
- <div className="mb-1 flex items-center">
+ <div className="flex items-center mb-1">
<button
onClick={() => toggleCollapse(node.id)}
className={cn(
@@ -116,22 +223,22 @@
isHovered ? 'rounded border-components-button-primary-border bg-components-button-primary-bg text-text-primary-on-surface' : 'text-text-secondary hover:text-text-primary',
)}
>
- {isHovered ? <RiArrowDownSLine className="h-3 w-3" /> : <RiMenu4Line className="h-3 w-3 text-text-tertiary" />}
+ {isHovered ? <RiArrowDownSLine className="w-3 h-3" /> : <RiMenu4Line className="w-3 h-3 text-text-tertiary" />}
</button>
- <div className="system-xs-semibold-uppercase flex items-center text-text-secondary">
- <span>{parallelDetail.parallelTitle}</span>
+ <div className="system-xs-semibold-uppercase text-text-secondary flex items-center">
+ <span>{node.parallelTitle}</span>
</div>
<div
- className="mx-2 h-px grow bg-divider-subtle"
+ className="mx-2 flex-grow h-px bg-divider-subtle"
style={{ background: 'linear-gradient(to right, rgba(16, 24, 40, 0.08), rgba(255, 255, 255, 0)' }}
></div>
</div>
- <div className={`relative pl-2 ${isCollapsed ? 'hidden' : ''}`}>
+ <div className={`pl-2 relative ${isCollapsed ? 'hidden' : ''}`}>
<div className={cn(
- 'absolute bottom-0 left-[5px] top-0 w-[2px]',
+ 'absolute top-0 bottom-0 left-[5px] w-[2px]',
isHovered ? 'bg-text-accent-secondary' : 'bg-divider-subtle',
)}></div>
- {parallelDetail.children!.map(renderNode)}
+ {node.children.map(renderNode)}
</div>
</div>
)
@@ -139,17 +246,16 @@
else {
const isHovered = hoveredParallel === node.id
return (
- <div key={node.id}>
- <div className={cn('system-2xs-medium-uppercase -mb-1.5 pl-4', isHovered ? 'text-text-tertiary' : 'text-text-quaternary')}>
- {node?.parallelDetail?.branchTitle}
+ <div key={node.uniqueId}>
+ <div className={cn('pl-4 -mb-1.5 system-2xs-medium-uppercase', isHovered ? 'text-text-tertiary' : 'text-text-quaternary')}>
+ {node.branchTitle}
</div>
<NodePanel
- nodeInfo={node!}
- allExecutions={list}
- onShowIterationDetail={handleShowIterationResultList}
- onShowLoopDetail={handleShowLoopResultList}
- onShowRetryDetail={handleShowRetryResultList}
- onShowAgentOrToolLog={handleShowAgentOrToolLog}
+ nodeInfo={node.data!}
+ onShowIterationDetail={onShowIterationDetail}
+ onShowRetryDetail={onShowRetryDetail}
+ justShowIterationNavArrow={true}
+ justShowRetryNavArrow={true}
hideInfo={hideNodeInfo}
hideProcessDetail={hideNodeProcessDetail}
/>
@@ -158,39 +264,8 @@
}
}
- if (showSpecialResultPanel) {
- return (
- <SpecialResultPanel
- showRetryDetail={showRetryDetail}
- setShowRetryDetailFalse={setShowRetryDetailFalse}
- retryResultList={retryResultList}
-
- showIteratingDetail={showIteratingDetail}
- setShowIteratingDetailFalse={setShowIteratingDetailFalse}
- iterationResultList={iterationResultList}
- iterationResultDurationMap={iterationResultDurationMap}
-
- showLoopingDetail={showLoopingDetail}
- setShowLoopingDetailFalse={setShowLoopingDetailFalse}
- loopResultList={loopResultList}
- loopResultDurationMap={loopResultDurationMap}
- loopResultVariableMap={loopResultVariableMap}
-
- agentOrToolLogItemStack={agentOrToolLogItemStack}
- agentOrToolLogListMap={agentOrToolLogListMap}
- handleShowAgentOrToolLog={handleShowAgentOrToolLog}
- />
- )
- }
-
return (
- <div
- className={cn('py-2', className)}
- onClick={(e) => {
- e.stopPropagation()
- e.nativeEvent.stopImmediatePropagation()
- }}
- >
+ <div className={cn(className || 'bg-components-panel-bg', 'py-2')}>
{treeNodes.map(renderNode)}
</div>
)
--
Gitblit v1.8.0