import HightLightMarkdown from '@/components/highlight-markdown'; 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, isEmpty } 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; isShare?: boolean; }; export function JsonViewer({ data, title, }: { data: Record; title: string; }) { return (
{title}
); } export const typeMap = { begin: t('flow.logTimeline.begin'), agent: t('flow.logTimeline.agent'), retrieval: t('flow.logTimeline.retrieval'), message: t('flow.logTimeline.message'), awaitResponse: t('flow.logTimeline.awaitResponse'), switch: t('flow.logTimeline.switch'), iteration: t('flow.logTimeline.iteration'), categorize: t('flow.logTimeline.categorize'), code: t('flow.logTimeline.code'), textProcessing: t('flow.logTimeline.textProcessing'), tavilySearch: t('flow.logTimeline.tavilySearch'), tavilyExtract: t('flow.logTimeline.tavilyExtract'), exeSQL: t('flow.logTimeline.exeSQL'), google: t('flow.logTimeline.google'), duckDuckGo: t('flow.logTimeline.google'), wikipedia: t('flow.logTimeline.wikipedia'), googleScholar: t('flow.logTimeline.googleScholar'), arXiv: t('flow.logTimeline.googleScholar'), pubMed: t('flow.logTimeline.googleScholar'), gitHub: t('flow.logTimeline.gitHub'), email: t('flow.logTimeline.email'), httpRequest: t('flow.logTimeline.httpRequest'), wenCai: t('flow.logTimeline.wenCai'), yahooFinance: t('flow.logTimeline.yahooFinance'), }; export const toLowerCaseStringAndDeleteChar = ( str: string, char: string = '_', ) => str.toLowerCase().replace(/ /g, '').replaceAll(char, ''); 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, isShare, }: 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 ( <>
{!isShare && getNodeName(x.data?.component_name)} {isShare && typeMap[ toLowerCaseStringAndDeleteChar( nodeLabel, ) as keyof typeof typeMap ]} {x.data.elapsed_time?.toString().slice(0, 6)} Online
{!isShare && (
{!isShare && ( <> )}
)} {isShare && x.data?.thoughts && (
{x.data.thoughts || ''}
)}
{hasTrace(x.data.component_id) && ( )} ); })}
); };