import { Timeline, TimelineContent, TimelineHeader, TimelineIndicator, TimelineItem, TimelineSeparator, } from '@/components/originui/timeline'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from '@/components/ui/accordion'; import { useFetchMessageTrace } from '@/hooks/use-agent-request'; import { INodeData, INodeEvent, MessageEventType, } from '@/hooks/use-send-message'; import { ITraceData } from '@/interfaces/database/agent'; import { cn } from '@/lib/utils'; import { t } from 'i18next'; import { get } from 'lodash'; import { useCallback, useEffect, useMemo, useState } from 'react'; import JsonView from 'react18-json-view'; import { Operator } from '../constant'; import { useCacheChatLog } from '../hooks/use-cache-chat-log'; import OperatorIcon from '../operator-icon'; import ToolTimelineItem from './toolTimelineItem'; type LogFlowTimelineProps = Pick< ReturnType, 'currentEventListWithoutMessage' | 'currentMessageId' > & { canvasId?: string; sendLoading: boolean }; export function JsonViewer({ data, title, }: { data: Record; title: string; }) { return (
{title}
); } function getInputsOrOutputs( nodeEventList: INodeData[], field: 'inputs' | 'outputs', ) { const inputsOrOutputs = nodeEventList.map((x) => get(x, field, {})); if (inputsOrOutputs.length < 2) { return inputsOrOutputs[0] || {}; } return inputsOrOutputs; } export const WorkFlowTimeline = ({ currentEventListWithoutMessage, currentMessageId, canvasId, sendLoading, }: LogFlowTimelineProps) => { // const getNode = useGraphStore((state) => state.getNode); const [isStopFetchTrace, setISStopFetchTrace] = useState(false); const { data: traceData, setMessageId } = useFetchMessageTrace( isStopFetchTrace, canvasId, ); useEffect(() => { setMessageId(currentMessageId); }, [currentMessageId, setMessageId]); const getNodeName = (nodeId: string) => { if ('begin' === nodeId) return t('flow.begin'); return nodeId; }; useEffect(() => { setISStopFetchTrace(!sendLoading); }, [sendLoading]); const startedNodeList = useMemo(() => { const finish = currentEventListWithoutMessage?.some( (item) => item.event === MessageEventType.WorkflowFinished, ); setISStopFetchTrace(finish || !sendLoading); const duplicateList = currentEventListWithoutMessage?.filter( (x) => x.event === MessageEventType.NodeStarted, ) as INodeEvent[]; // Remove duplicate nodes return duplicateList?.reduce>((pre, cur) => { if (pre.every((x) => x.data.component_id !== cur.data.component_id)) { pre.push(cur); } return pre; }, []); }, [currentEventListWithoutMessage, sendLoading]); const hasTrace = useCallback( (componentId: string) => { if (Array.isArray(traceData)) { return traceData?.some((x) => x.component_id === componentId); } return false; }, [traceData], ); const filterTrace = useCallback( (componentId: string) => { const trace = traceData ?.filter((x) => x.component_id === componentId) .reduce((pre, cur) => { pre.push(...cur.trace); return pre; }, []); return Array.isArray(trace) ? trace : [{}]; }, [traceData], ); const filterFinishedNodeList = useCallback( (componentId: string) => { const nodeEventList = currentEventListWithoutMessage .filter( (x) => x.event === MessageEventType.NodeFinished && (x.data as INodeData)?.component_id === componentId, ) .map((x) => x.data); return nodeEventList; }, [currentEventListWithoutMessage], ); return ( {startedNodeList?.map((x, idx) => { const nodeDataList = filterFinishedNodeList(x.data.component_id); const finishNodeIds = nodeDataList.map( (x: INodeData) => x.component_id, ); const inputs = getInputsOrOutputs(nodeDataList, 'inputs'); const outputs = getInputsOrOutputs(nodeDataList, 'outputs'); const nodeLabel = x.data.component_type; return ( <>
{getNodeName(x.data?.component_name)} {x.data.elapsed_time?.toString().slice(0, 6)} Online
{hasTrace(x.data.component_id) && ( )} ); })}
); };