refact workflow run log

This commit is contained in:
zxhlyh 2024-12-27 16:17:44 +08:00
parent 23913b8640
commit e4cc8f7010
12 changed files with 193 additions and 400 deletions

View File

@ -6,8 +6,8 @@ import {
BlockEnum,
NodeRunningStatus,
} from '@/app/components/workflow/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { ErrorHandleTypeEnum } from '@/app/components/workflow/nodes/_base/components/error-handle/types'
import { useWorkflowStore } from '@/app/components/workflow/store'
export const useWorkflowNodeFinished = () => {
const store = useStoreApi()
@ -18,8 +18,6 @@ export const useWorkflowNodeFinished = () => {
const {
workflowRunningData,
setWorkflowRunningData,
iterParallelLogMap,
setIterParallelLogMap,
} = workflowStore.getState()
const {
getNodes,
@ -28,124 +26,45 @@ export const useWorkflowNodeFinished = () => {
setEdges,
} = store.getState()
const nodes = getNodes()
const nodeParentId = nodes.find(node => node.id === data.node_id)!.parentId
if (nodeParentId) {
if (!data.execution_metadata.parallel_mode_run_id) {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const currentIndex = draft.tracing!.findIndex(item => item.id === data.id)
if (currentIndex > -1) {
draft.tracing![currentIndex] = {
...draft.tracing![currentIndex],
...data,
}
}
}))
if (iterations && iterations.details) {
const iterationIndex = data.execution_metadata?.iteration_index || 0
if (!iterations.details[iterationIndex])
iterations.details[iterationIndex] = []
const currIteration = iterations.details[iterationIndex]
const nodeIndex = currIteration.findIndex(node =>
node.node_id === data.node_id && (
node.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || node.parallel_id === data.execution_metadata?.parallel_id),
)
if (nodeIndex !== -1) {
currIteration[nodeIndex] = {
...currIteration[nodeIndex],
...(currIteration[nodeIndex].retryDetail
? { retryDetail: currIteration[nodeIndex].retryDetail }
: {}),
...data,
} as any
}
else {
currIteration.push({
...data,
} as any)
}
}
}))
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!
currentNode.data._runningStatus = data.status
if (data.status === NodeRunningStatus.Exception) {
if (data.execution_metadata?.error_strategy === ErrorHandleTypeEnum.failBranch)
currentNode.data._runningBranchId = ErrorHandleTypeEnum.failBranch
}
else {
// open parallel mode
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const iterations = tracing.find(trace => trace.node_id === nodeParentId) // the iteration node
if (data.node_type === BlockEnum.IfElse)
currentNode.data._runningBranchId = data?.outputs?.selected_case_id
if (iterations && iterations.details) {
const iterRunID = data.execution_metadata?.parallel_mode_run_id
const currIteration = iterParallelLogMap.get(iterations.node_id)?.get(iterRunID)
const nodeIndex = currIteration?.findIndex(node =>
node.node_id === data.node_id && (
node?.parallel_run_id === data.execution_metadata?.parallel_mode_run_id),
)
if (currIteration) {
if (nodeIndex !== undefined && nodeIndex !== -1) {
currIteration[nodeIndex] = {
...currIteration[nodeIndex],
...data,
} as any
}
else {
currIteration.push({
...data,
} as any)
}
}
setIterParallelLogMap(iterParallelLogMap)
const iterLogMap = iterParallelLogMap.get(iterations.node_id)
if (iterLogMap)
iterations.details = Array.from(iterLogMap.values())
}
}))
if (data.node_type === BlockEnum.QuestionClassifier)
currentNode.data._runningBranchId = data?.outputs?.class_id
}
}
else {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const currentIndex = draft.tracing!.findIndex((trace) => {
if (!trace.execution_metadata?.parallel_id)
return trace.node_id === data.node_id
return trace.node_id === data.node_id && trace.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id
})
if (currentIndex > -1 && draft.tracing) {
draft.tracing[currentIndex] = {
...data,
...(draft.tracing[currentIndex].extras
? { extras: draft.tracing[currentIndex].extras }
: {}),
...(draft.tracing[currentIndex].retryDetail
? { retryDetail: draft.tracing[currentIndex].retryDetail }
: {}),
} as any
}
}))
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!
currentNode.data._runningStatus = data.status as any
if (data.status === NodeRunningStatus.Exception) {
if (data.execution_metadata.error_strategy === ErrorHandleTypeEnum.failBranch)
currentNode.data._runningBranchId = ErrorHandleTypeEnum.failBranch
}
else {
if (data.node_type === BlockEnum.IfElse)
currentNode.data._runningBranchId = data?.outputs?.selected_case_id
if (data.node_type === BlockEnum.QuestionClassifier)
currentNode.data._runningBranchId = data?.outputs?.class_id
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
incomeEdges.forEach((edge) => {
edge.data = {
...edge.data,
_targetRunningStatus: data.status as any,
}
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
incomeEdges.forEach((edge) => {
edge.data = {
...edge.data,
_targetRunningStatus: data.status as any,
}
})
})
setEdges(newEdges)
}
}, [workflowStore, store])
})
setEdges(newEdges)
}, [store, workflowStore])
return {
handleWorkflowNodeFinished,

View File

@ -3,7 +3,6 @@ import { useStoreApi } from 'reactflow'
import produce from 'immer'
import type { IterationFinishedResponse } from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { NodeRunningStatus } from '@/app/components/workflow/types'
import { DEFAULT_ITER_TIMES } from '@/app/components/workflow/constants'
export const useWorkflowNodeIterationFinished = () => {
@ -22,15 +21,14 @@ export const useWorkflowNodeIterationFinished = () => {
setNodes,
} = store.getState()
const nodes = getNodes()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const currIterationNode = tracing.find(trace => trace.node_id === data.node_id)
if (currIterationNode) {
Object.assign(currIterationNode, {
const currentIndex = draft.tracing!.findIndex(item => item.id === data.id)
if (currentIndex > -1) {
draft.tracing![currentIndex] = {
...draft.tracing![currentIndex],
...data,
status: NodeRunningStatus.Succeeded,
})
}
}
}))
setIterTimes(DEFAULT_ITER_TIMES)

View File

@ -10,8 +10,6 @@ export const useWorkflowNodeIterationNext = () => {
const handleWorkflowNodeIterationNext = useCallback((params: IterationNextResponse) => {
const {
workflowRunningData,
setWorkflowRunningData,
iterTimes,
setIterTimes,
} = workflowStore.getState()
@ -22,17 +20,6 @@ export const useWorkflowNodeIterationNext = () => {
setNodes,
} = store.getState()
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const iteration = draft.tracing!.find(trace => trace.node_id === data.node_id)
if (iteration) {
if (iteration.iterDurationMap && data.duration)
iteration.iterDurationMap[data.parallel_mode_run_id ?? `${data.index - 1}`] = data.duration
if (iteration.details!.length >= iteration.metadata.iterator_length!)
return
}
if (!data.parallel_mode_run_id)
iteration?.details!.push([])
}))
const nodes = getNodes()
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!

View File

@ -35,15 +35,13 @@ export const useWorkflowNodeIterationStarted = () => {
transform,
} = store.getState()
const nodes = getNodes()
setIterTimes(DEFAULT_ITER_TIMES)
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.tracing!.push({
...data,
status: NodeRunningStatus.Running,
details: [],
iterDurationMap: {},
} as any)
})
}))
setIterTimes(DEFAULT_ITER_TIMES)
const {
setViewport,

View File

@ -3,7 +3,6 @@ import { useStoreApi } from 'reactflow'
import produce from 'immer'
import type {
NodeFinishedResponse,
NodeTracing,
} from '@/types/workflow'
import { useWorkflowStore } from '@/app/components/workflow/store'
@ -16,8 +15,6 @@ export const useWorkflowNodeRetry = () => {
const {
workflowRunningData,
setWorkflowRunningData,
iterParallelLogMap,
setIterParallelLogMap,
} = workflowStore.getState()
const {
getNodes,
@ -25,65 +22,9 @@ export const useWorkflowNodeRetry = () => {
} = store.getState()
const nodes = getNodes()
const currentNode = nodes.find(node => node.id === data.node_id)!
const nodeParent = nodes.find(node => node.id === currentNode.parentId)
if (nodeParent) {
if (!data.execution_metadata.parallel_mode_run_id) {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const iteration = tracing.find(trace => trace.node_id === nodeParent.id)
if (iteration && iteration.details?.length) {
const currentNodeRetry = iteration.details[nodeParent.data._iterationIndex - 1]?.find(item => item.node_id === data.node_id)
if (currentNodeRetry) {
if (currentNodeRetry?.retryDetail)
currentNodeRetry?.retryDetail.push(data as NodeTracing)
else
currentNodeRetry.retryDetail = [data as NodeTracing]
}
}
}))
}
else {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const iteration = tracing.find(trace => trace.node_id === nodeParent.id)
if (iteration && iteration.details?.length) {
const iterRunID = data.execution_metadata?.parallel_mode_run_id
const currIteration = iterParallelLogMap.get(iteration.node_id)?.get(iterRunID)
const currentNodeRetry = currIteration?.find(item => item.node_id === data.node_id)
if (currentNodeRetry) {
if (currentNodeRetry?.retryDetail)
currentNodeRetry?.retryDetail.push(data as NodeTracing)
else
currentNodeRetry.retryDetail = [data as NodeTracing]
}
setIterParallelLogMap(iterParallelLogMap)
const iterLogMap = iterParallelLogMap.get(iteration.node_id)
if (iterLogMap)
iteration.details = Array.from(iterLogMap.values())
}
}))
}
}
else {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const currentRetryNodeIndex = tracing.findIndex(trace => trace.node_id === data.node_id)
if (currentRetryNodeIndex > -1) {
const currentRetryNode = tracing[currentRetryNodeIndex]
if (currentRetryNode.retryDetail)
draft.tracing![currentRetryNodeIndex].retryDetail!.push(data as NodeTracing)
else
draft.tracing![currentRetryNodeIndex].retryDetail = [data as NodeTracing]
}
}))
}
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.tracing!.push(data)
}))
const newNodes = produce(nodes, (draft) => {
const currentNode = draft.find(node => node.id === data.node_id)!

View File

@ -24,8 +24,6 @@ export const useWorkflowNodeStarted = () => {
const {
workflowRunningData,
setWorkflowRunningData,
iterParallelLogMap,
setIterParallelLogMap,
} = workflowStore.getState()
const {
getNodes,
@ -35,84 +33,54 @@ export const useWorkflowNodeStarted = () => {
transform,
} = store.getState()
const nodes = getNodes()
const node = nodes.find(node => node.id === data.node_id)
if (node?.parentId) {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
const tracing = draft.tracing!
const iterations = tracing.find(trace => trace.node_id === node?.parentId)
const currIteration = iterations?.details![node.data.iteration_index] || iterations?.details![iterations.details!.length - 1]
if (!data.parallel_run_id) {
currIteration?.push({
...data,
status: NodeRunningStatus.Running,
} as any)
}
else {
const nodeId = iterations?.node_id as string
if (!iterParallelLogMap.has(nodeId as string))
iterParallelLogMap.set(iterations?.node_id as string, new Map())
const currentIterLogMap = iterParallelLogMap.get(nodeId)!
if (!currentIterLogMap.has(data.parallel_run_id))
currentIterLogMap.set(data.parallel_run_id, [{ ...data, status: NodeRunningStatus.Running } as any])
else
currentIterLogMap.get(data.parallel_run_id)!.push({ ...data, status: NodeRunningStatus.Running } as any)
setIterParallelLogMap(iterParallelLogMap)
if (iterations)
iterations.details = Array.from(currentIterLogMap.values())
}
}))
}
else {
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.tracing!.push({
...data,
status: NodeRunningStatus.Running,
} as any)
}))
const {
setViewport,
} = reactflow
const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id)
const currentNode = nodes[currentNodeIndex]
const position = currentNode.position
const zoom = transform[2]
if (!currentNode.parentId) {
setViewport({
x: (containerParams.clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom,
y: (containerParams.clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom,
zoom: transform[2],
})
}
const newNodes = produce(nodes, (draft) => {
draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running
draft[currentNodeIndex].data._waitingRun = false
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
draft.tracing!.push({
...data,
status: NodeRunningStatus.Running,
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
}))
incomeEdges.forEach((edge) => {
const incomeNode = nodes.find(node => node.id === edge.source)!
if (
(!incomeNode.data._runningBranchId && edge.sourceHandle === 'source')
|| (incomeNode.data._runningBranchId && edge.sourceHandle === incomeNode.data._runningBranchId)
) {
edge.data = {
...edge.data,
_sourceRunningStatus: incomeNode.data._runningStatus,
_targetRunningStatus: NodeRunningStatus.Running,
_waitingRun: false,
}
const {
setViewport,
} = reactflow
const currentNodeIndex = nodes.findIndex(node => node.id === data.node_id)
const currentNode = nodes[currentNodeIndex]
const position = currentNode.position
const zoom = transform[2]
if (!currentNode.parentId) {
setViewport({
x: (containerParams.clientWidth - 400 - currentNode.width! * zoom) / 2 - position.x * zoom,
y: (containerParams.clientHeight - currentNode.height! * zoom) / 2 - position.y * zoom,
zoom: transform[2],
})
}
const newNodes = produce(nodes, (draft) => {
draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Running
draft[currentNodeIndex].data._waitingRun = false
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
const incomeEdges = draft.filter((edge) => {
return edge.target === data.node_id
})
incomeEdges.forEach((edge) => {
const incomeNode = nodes.find(node => node.id === edge.source)!
if (
(!incomeNode.data._runningBranchId && edge.sourceHandle === 'source')
|| (incomeNode.data._runningBranchId && edge.sourceHandle === incomeNode.data._runningBranchId)
) {
edge.data = {
...edge.data,
_sourceRunningStatus: incomeNode.data._runningStatus,
_targetRunningStatus: NodeRunningStatus.Running,
_waitingRun: false,
}
})
}
})
setEdges(newEdges)
}
})
setEdges(newEdges)
}, [workflowStore, store, reactflow])
return {

View File

@ -24,6 +24,7 @@ import Toast from '../../base/toast'
import InputsPanel from './inputs-panel'
import cn from '@/utils/classnames'
import Loading from '@/app/components/base/loading'
import formatNodeList from '@/app/components/workflow/run/utils/format-log'
const WorkflowPreview = () => {
const { t } = useTranslation()
@ -160,7 +161,7 @@ const WorkflowPreview = () => {
{currentTab === 'TRACING' && (
<TracingPanel
className='bg-background-section-burn'
list={workflowRunningData?.tracing || []}
list={formatNodeList(workflowRunningData?.tracing || [], t)}
/>
)}
{currentTab === 'TRACING' && !workflowRunningData?.tracing?.length && (

View File

@ -0,0 +1,54 @@
import { useState } from 'react'
import { RiMoreLine } from '@remixicon/react'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Button from '@/app/components/base/button'
type AgentLogNavMoreProps = {
options: { id: string; label: string }[]
}
const AgentLogNavMore = ({
options,
}: AgentLogNavMoreProps) => {
const [open, setOpen] = useState(false)
return (
<PortalToFollowElem
placement='bottom-start'
offset={{
mainAxis: 2,
crossAxis: -54,
}}
open={open}
onOpenChange={setOpen}
>
<PortalToFollowElemTrigger>
<Button
className='w-6 h-6'
variant='ghost-accent'
>
<RiMoreLine className='w-4 h-4' />
</Button>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent>
<div className='p-1 w-[136px] bg-components-panel-bg-blur border-[0.5px] border-components-panel-border rounded-xl shadow-lg'>
{
options.map(option => (
<div
key={option.id}
className='flex items-center px-2 h-8 rounded-lg system-md-regular text-text-secondary hover:bg-state-base-hover cursor-pointer'
>
{option.label}
</div>
))
}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default AgentLogNavMore

View File

@ -0,0 +1,39 @@
import { RiArrowLeftLine } from '@remixicon/react'
import AgentLogNavMore from './agent-log-nav-more'
import Button from '@/app/components/base/button'
const AgentLogNav = () => {
return (
<div className='flex items-center p-1 pr-3 h-8'>
<Button
className='shrink-0 px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => {}}
>
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
Agent
</Button>
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<Button
className='shrink-0 px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => {}}
>
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
Agent strategy
</Button>
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<AgentLogNavMore
options={[]}
/>
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<div className='flex items-center px-[5px] system-xs-medium-uppercase text-text-tertiary'>
Run Actions
</div>
</div>
)
}
export default AgentLogNav

View File

@ -1,6 +1,5 @@
import Button from '@/app/components/base/button'
import { RiArrowLeftLine } from '@remixicon/react'
import AgentLogItem from './agent-log-item'
import AgentLogNav from './agent-log-nav'
import type { AgentLogItemWithChildren } from '@/types/workflow'
type AgentResultPanelProps = {
@ -12,29 +11,7 @@ const AgentResultPanel = ({
}: AgentResultPanelProps) => {
return (
<div className='overflow-y-auto'>
<div className='flex items-center p-1 pr-3 h-8'>
<Button
className='shrink-0 px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => {}}
>
<RiArrowLeftLine className='mr-1 w-3.5 h-3.5' />
Back
</Button>
<div className='shrink-0 mx-0.5 system-xs-regular text-divider-deep'>/</div>
<div className='grow px-[5px] system-xs-medium-uppercase'>
Agent strategy
</div>
<Button
className='px-[5px]'
size='small'
variant='ghost-accent'
onClick={() => {}}
>
close
</Button>
</div>
<AgentLogNav />
{
<div className='p-2'>
{

View File

@ -5,7 +5,7 @@ import formatRetryNode from './retry'
import formatAgentNode from './agent'
const formatToTracingNodeList = (list: NodeTracing[], t: any) => {
const allItems = [...list].reverse()
const allItems = [...list].sort((a, b) => a.index - b.index)
/*
* First handle not change list structure node
* Because Handle struct node will put the node in different

View File

@ -147,18 +147,7 @@ export type NodeStartedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
id: string
node_id: string
iteration_id?: string
parallel_run_id?: string
node_type: string
index: number
predecessor_node_id?: string
inputs: any
created_at: number
extras?: any
}
data: NodeTracing
}
export type FileResponse = {
@ -176,120 +165,42 @@ export type NodeFinishedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
id: string
node_id: string
iteration_id?: string
node_type: string
index: number
predecessor_node_id?: string
inputs: any
process_data: any
outputs: any
status: string
error: string
elapsed_time: number
execution_metadata: {
total_tokens: number
total_price: number
currency: string
parallel_id?: string
parallel_start_node_id?: string
iteration_index?: number
iteration_id?: string
parallel_mode_run_id: string
error_strategy?: ErrorHandleTypeEnum
}
created_at: number
files?: FileResponse[]
retry_index?: number
}
data: NodeTracing
}
export type IterationStartedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
id: string
node_id: string
metadata: {
iterator_length: number
iteration_id: string
iteration_index: number
}
created_at: number
extras?: any
}
data: NodeTracing
}
export type IterationNextResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
id: string
node_id: string
index: number
output: any
extras?: any
created_at: number
parallel_mode_run_id: string
execution_metadata: {
parallel_id?: string
iteration_index: number
parallel_mode_run_id?: string
}
duration?: number
}
data: NodeTracing
}
export type IterationFinishedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
id: string
node_id: string
outputs: any
extras?: any
status: string
created_at: number
error: string
execution_metadata: {
parallel_id?: string
}
}
data: NodeTracing
}
export type ParallelBranchStartedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
parallel_id: string
parallel_start_node_id: string
parent_parallel_id: string
parent_parallel_start_node_id: string
iteration_id?: string
created_at: number
}
data: NodeTracing
}
export type ParallelBranchFinishedResponse = {
task_id: string
workflow_run_id: string
event: string
data: {
parallel_id: string
parallel_start_node_id: string
parent_parallel_id: string
parent_parallel_start_node_id: string
iteration_id?: string
status: string
created_at: number
error: string
}
data: NodeTracing
}
export type TextChunkResponse = {