From c93c264c5a805dc20eb77bdff5279b769ab6f087 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 24 Dec 2024 16:07:50 +0800 Subject: [PATCH] feat: format to tracing --- web/app/components/workflow/run/index.tsx | 115 +---- .../run/utils/format-to-tracing-node-list.ts | 105 ++++ .../format-to-tracting-node-list.spec.ts | 8 + .../workflow/run/utils/spec-test-data.ts | 481 ++++++++++++++++++ 4 files changed, 595 insertions(+), 114 deletions(-) create mode 100644 web/app/components/workflow/run/utils/format-to-tracing-node-list.ts create mode 100644 web/app/components/workflow/run/utils/format-to-tracting-node-list.spec.ts create mode 100644 web/app/components/workflow/run/utils/spec-test-data.ts diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index 8b0319cabe..464698144c 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -4,7 +4,6 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' import { useBoolean } from 'ahooks' -import { BlockEnum } from '../types' import OutputPanel from './output-panel' import ResultPanel from './result-panel' import TracingPanel from './tracing-panel' @@ -17,7 +16,7 @@ import { fetchRunDetail, fetchTracingList } from '@/service/log' import type { IterationDurationMap, NodeTracing } from '@/types/workflow' import type { WorkflowRunDetailResponse } from '@/models/log' import { useStore as useAppStore } from '@/app/components/app/store' - +import formatNodeList from './utils/format-to-tracing-node-list' export type RunProps = { hideResult?: boolean activeTab?: 'RESULT' | 'DETAIL' | 'TRACING' @@ -60,118 +59,6 @@ const RunPanel: FC = ({ hideResult, activeTab = 'RESULT', runID, getRe } }, [notify, getResultCallback]) - const formatNodeList = useCallback((list: NodeTracing[]) => { - const allItems = [...list].reverse() - const result: NodeTracing[] = [] - const nodeGroupMap = new Map>() - - const processIterationNode = (item: NodeTracing) => { - result.push({ - ...item, - details: [], - }) - } - - const updateParallelModeGroup = (runId: string, item: NodeTracing, iterationNode: NodeTracing) => { - if (!nodeGroupMap.has(iterationNode.node_id)) - nodeGroupMap.set(iterationNode.node_id, new Map()) - - const groupMap = nodeGroupMap.get(iterationNode.node_id)! - - if (!groupMap.has(runId)) { - groupMap.set(runId, [item]) - } - else { - if (item.status === 'retry') { - const retryNode = groupMap.get(runId)!.find(node => node.node_id === item.node_id) - - if (retryNode) { - if (retryNode?.retryDetail) - retryNode.retryDetail.push(item) - else - retryNode.retryDetail = [item] - } - } - else { - groupMap.get(runId)!.push(item) - } - } - - if (item.status === 'failed') { - iterationNode.status = 'failed' - iterationNode.error = item.error - } - - iterationNode.details = Array.from(groupMap.values()) - } - const updateSequentialModeGroup = (index: number, item: NodeTracing, iterationNode: NodeTracing) => { - const { details } = iterationNode - if (details) { - if (!details[index]) { - details[index] = [item] - } - else { - if (item.status === 'retry') { - const retryNode = details[index].find(node => node.node_id === item.node_id) - - if (retryNode) { - if (retryNode?.retryDetail) - retryNode.retryDetail.push(item) - else - retryNode.retryDetail = [item] - } - } - else { - details[index].push(item) - } - } - } - - if (item.status === 'failed') { - iterationNode.status = 'failed' - iterationNode.error = item.error - } - } - const processNonIterationNode = (item: NodeTracing) => { - const { execution_metadata } = item - if (!execution_metadata?.iteration_id) { - if (item.status === 'retry') { - const retryNode = result.find(node => node.node_id === item.node_id) - - if (retryNode) { - if (retryNode?.retryDetail) - retryNode.retryDetail.push(item) - else - retryNode.retryDetail = [item] - } - - return - } - result.push(item) - return - } - - const iterationNode = result.find(node => node.node_id === execution_metadata.iteration_id) - if (!iterationNode || !Array.isArray(iterationNode.details)) - return - - const { parallel_mode_run_id, iteration_index = 0 } = execution_metadata - - if (parallel_mode_run_id) - updateParallelModeGroup(parallel_mode_run_id, item, iterationNode) - else - updateSequentialModeGroup(iteration_index, item, iterationNode) - } - - allItems.forEach((item) => { - item.node_type === BlockEnum.Iteration - ? processIterationNode(item) - : processNonIterationNode(item) - }) - - return result - }, []) - const getTracingList = useCallback(async (appID: string, runID: string) => { try { const { data: nodeList } = await fetchTracingList({ diff --git a/web/app/components/workflow/run/utils/format-to-tracing-node-list.ts b/web/app/components/workflow/run/utils/format-to-tracing-node-list.ts new file mode 100644 index 0000000000..305cf0d04d --- /dev/null +++ b/web/app/components/workflow/run/utils/format-to-tracing-node-list.ts @@ -0,0 +1,105 @@ +import type { NodeTracing } from '@/types/workflow' +import { BlockEnum } from '../../types' + +type IterationNodeId = string +type RunIndex = string +type IterationGroupMap = Map> + +const processIterationNode = (item: NodeTracing) => { + return { + ...item, + details: [], // to add the sub nodes in the iteration + } +} + +const updateParallelModeGroup = (nodeGroupMap: IterationGroupMap, runIndex: string, item: NodeTracing, iterationNode: NodeTracing) => { + if (!nodeGroupMap.has(iterationNode.node_id)) + nodeGroupMap.set(iterationNode.node_id, new Map()) + + const groupMap = nodeGroupMap.get(iterationNode.node_id)! + + if (!groupMap.has(runIndex)) + groupMap.set(runIndex, [item]) + + else + groupMap.get(runIndex)!.push(item) + + if (item.status === 'failed') { + iterationNode.status = 'failed' + iterationNode.error = item.error + } + + iterationNode.details = Array.from(groupMap.values()) +} + +const updateSequentialModeGroup = (runIndex: number, item: NodeTracing, iterationNode: NodeTracing) => { + const { details } = iterationNode + if (details) { + if (!details[runIndex]) + details[runIndex] = [item] + else + details[runIndex].push(item) + } + + if (item.status === 'failed') { + iterationNode.status = 'failed' + iterationNode.error = item.error + } +} + +const addRetryDetail = (result: NodeTracing[], item: NodeTracing) => { + const retryNode = result.find(node => node.node_id === item.node_id) + + if (retryNode) { + if (retryNode?.retryDetail) + retryNode.retryDetail.push(item) + else + retryNode.retryDetail = [item] + } +} + +const processNonIterationNode = (result: NodeTracing[], nodeGroupMap: IterationGroupMap, item: NodeTracing) => { + const { execution_metadata } = item + if (!execution_metadata?.iteration_id) { + if (item.status === 'retry') { + addRetryDetail(result, item) + return + } + result.push(item) + return + } + + const parentIterationNode = result.find(node => node.node_id === execution_metadata.iteration_id) + const isInIteration = !!parentIterationNode && Array.isArray(parentIterationNode.details) + if (!isInIteration) + return + + // the parallel in the iteration in mode. + const { parallel_mode_run_id, iteration_index = 0 } = execution_metadata + const isInParallel = !!parallel_mode_run_id + + if (isInParallel) + updateParallelModeGroup(nodeGroupMap, parallel_mode_run_id, item, parentIterationNode) + else + updateSequentialModeGroup(iteration_index, item, parentIterationNode) +} + +// list => tree. Put the iteration node's children into the details field. +const formatToTracingNodeList = (list: NodeTracing[]) => { + const allItems = [...list].reverse() + const result: NodeTracing[] = [] + const iterationGroupMap = new Map>() + + allItems.forEach((item) => { + item.node_type === BlockEnum.Iteration + ? result.push(processIterationNode(item)) + : processNonIterationNode(result, iterationGroupMap, item) + }) + + console.log(allItems) + console.log(result) + + return result +} + +export default formatToTracingNodeList diff --git a/web/app/components/workflow/run/utils/format-to-tracting-node-list.spec.ts b/web/app/components/workflow/run/utils/format-to-tracting-node-list.spec.ts new file mode 100644 index 0000000000..e62770f9cf --- /dev/null +++ b/web/app/components/workflow/run/utils/format-to-tracting-node-list.spec.ts @@ -0,0 +1,8 @@ +import formatToTracingNodeList from './format-to-tracing-node-list' +import { simpleIterationData } from './spec-test-data' + +describe('format api data to tracing panel data', () => { + test('iteration should put nodes in details', () => { + expect(formatToTracingNodeList(simpleIterationData.in)).toEqual(simpleIterationData.out) + }) +}) diff --git a/web/app/components/workflow/run/utils/spec-test-data.ts b/web/app/components/workflow/run/utils/spec-test-data.ts new file mode 100644 index 0000000000..17c85ab48c --- /dev/null +++ b/web/app/components/workflow/run/utils/spec-test-data.ts @@ -0,0 +1,481 @@ +export const simpleIterationData = { + // start -> code(output: [1, 2, 3]) -> iteration(output: ['aaa', 'aaa', 'aaa']) -> end(output: ['aaa', 'aaa', 'aaa']) + in: [ + { + id: '36c9860a-39e6-4107-b750-655b07895f47', + index: 1, + predecessor_node_id: null, + node_id: '1735023354069', + node_type: 'start', + title: 'Start', + inputs: { + 'sys.files': [], + 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', + 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', + 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', + }, + process_data: null, + outputs: { + 'sys.files': [], + 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', + 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', + 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.011458, + execution_metadata: null, + extras: {}, + created_at: 1735023510, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023510, + }, + { + id: 'a3105c5d-ff9e-44ea-9f4c-ab428958af20', + index: 2, + predecessor_node_id: '1735023354069', + node_id: '1735023361224', + node_type: 'code', + title: 'Code', + inputs: null, + process_data: null, + outputs: { + result: [ + 1, + 2, + 3, + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.103333, + execution_metadata: null, + extras: {}, + created_at: 1735023510, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'a823134d-9f1a-45a4-8977-db838d076316', + index: 3, + predecessor_node_id: '1735023361224', + node_id: '1735023391914', + node_type: 'iteration', + title: 'Iteration', + inputs: null, + process_data: null, + outputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.408383, + execution_metadata: { + iteration_duration_map: { + 0: 0.118153, + 1: 0.135956, + 2: 0.128251, + }, + total_tokens: 0, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'a84a22d8-0f08-4006-bee2-fa7a7aef0420', + index: 4, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.112688, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 0, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'ff71d773-a916-4513-960f-d7dcc4fadd86', + index: 5, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.126034, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 1, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'd91c3ef9-0162-4013-9272-d4cc7fb1f188', + index: 6, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.122716, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 2, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'e6ad6560-1aa3-43f3-89e3-e5287c9ea272', + index: 7, + predecessor_node_id: '1735023391914', + node_id: '1735023417757', + node_type: 'end', + title: 'End', + inputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + process_data: null, + outputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.017552, + execution_metadata: null, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + ], + output: [ + { + id: '36c9860a-39e6-4107-b750-655b07895f47', + index: 1, + predecessor_node_id: null, + node_id: '1735023354069', + node_type: 'start', + title: 'Start', + inputs: { + 'sys.files': [], + 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', + 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', + 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', + }, + process_data: null, + outputs: { + 'sys.files': [], + 'sys.user_id': '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + 'sys.app_id': '8a5e87f8-6433-40f4-a67a-4be78a558dc7', + 'sys.workflow_id': 'bb5e2b89-40ac-45c9-9ccb-4f2cd926e080', + 'sys.workflow_run_id': '76adf675-a7d3-4cc1-9282-ed7ecfe4f65d', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.011458, + execution_metadata: null, + extras: {}, + created_at: 1735023510, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023510, + }, + { + id: 'a3105c5d-ff9e-44ea-9f4c-ab428958af20', + index: 2, + predecessor_node_id: '1735023354069', + node_id: '1735023361224', + node_type: 'code', + title: 'Code', + inputs: null, + process_data: null, + outputs: { + result: [ + 1, + 2, + 3, + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.103333, + execution_metadata: null, + extras: {}, + created_at: 1735023510, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + { + id: 'a823134d-9f1a-45a4-8977-db838d076316', + index: 3, + predecessor_node_id: '1735023361224', + node_id: '1735023391914', + node_type: 'iteration', + title: 'Iteration', + inputs: null, + process_data: null, + outputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.408383, + execution_metadata: { + iteration_duration_map: { + 0: 0.118153, + 1: 0.135956, + 2: 0.128251, + }, + total_tokens: 0, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + details: [ + [ + { + id: 'a84a22d8-0f08-4006-bee2-fa7a7aef0420', + index: 4, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.112688, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 0, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + ], + [ + { + id: 'ff71d773-a916-4513-960f-d7dcc4fadd86', + index: 5, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.126034, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 1, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + ], + [ + { + id: 'd91c3ef9-0162-4013-9272-d4cc7fb1f188', + index: 6, + predecessor_node_id: '1735023391914start', + node_id: '1735023409906', + node_type: 'code', + title: 'Code 2', + inputs: null, + process_data: null, + outputs: { + result: 'aaa', + }, + status: 'succeeded', + error: null, + elapsed_time: 0.122716, + execution_metadata: { + iteration_id: '1735023391914', + iteration_index: 2, + }, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + ], + ], + }, + { + id: 'e6ad6560-1aa3-43f3-89e3-e5287c9ea272', + index: 7, + predecessor_node_id: '1735023391914', + node_id: '1735023417757', + node_type: 'end', + title: 'End', + inputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + process_data: null, + outputs: { + output: [ + 'aaa', + 'aaa', + 'aaa', + ], + }, + status: 'succeeded', + error: null, + elapsed_time: 0.017552, + execution_metadata: null, + extras: {}, + created_at: 1735023511, + created_by_role: 'account', + created_by_account: { + id: '5ee03762-1d1a-46e8-ba0b-5f419a77da96', + name: 'Joel', + email: 'iamjoel007@gmail.com', + }, + created_by_end_user: null, + finished_at: 1735023511, + }, + ], +}