From a430284aa21e3ae1f0d5654e55b2ad2852519cc2 Mon Sep 17 00:00:00 2001
From: wwf <yearningwang@iqtogether.com>
Date: 星期三, 04 六月 2025 15:17:49 +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