From 3e1e9084220c96558c1802c5ab770dcd5bdd5ed6 Mon Sep 17 00:00:00 2001 From: balibabu Date: Tue, 8 Jul 2025 09:27:56 +0800 Subject: [PATCH] Feat: Get the running log of each message through the trace interface #3221 (#8711) ### What problem does this PR solve? Feat: Get the running log of each message through the trace interface #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/hooks/use-agent-request.ts | 33 ++++++++++- web/src/interfaces/database/agent.ts | 5 ++ web/src/pages/agent/canvas/index.tsx | 2 + .../pages/agent/hooks/use-cache-chat-log.ts | 1 + web/src/pages/agent/log-sheet/index.tsx | 59 +++++++++++++------ web/src/services/flow-service.ts | 5 ++ web/src/utils/api.ts | 1 + 7 files changed, 88 insertions(+), 18 deletions(-) diff --git a/web/src/hooks/use-agent-request.ts b/web/src/hooks/use-agent-request.ts index cf0996537..9570625dc 100644 --- a/web/src/hooks/use-agent-request.ts +++ b/web/src/hooks/use-agent-request.ts @@ -1,4 +1,5 @@ import { AgentGlobals } from '@/constants/agent'; +import { ITraceData } from '@/interfaces/database/agent'; import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; import i18n from '@/locales/config'; import { BeginId } from '@/pages/agent/constant'; @@ -9,7 +10,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useDebounce } from 'ahooks'; import { message } from 'antd'; import { get, set } from 'lodash'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'umi'; import { v4 as uuid } from 'uuid'; @@ -27,6 +28,7 @@ export const enum AgentApiAction { SetAgent = 'setAgent', FetchAgentTemplates = 'fetchAgentTemplates', UploadCanvasFile = 'uploadCanvasFile', + Trace = 'trace', } export const EmptyDsl = { @@ -300,3 +302,32 @@ export const useUploadCanvasFile = () => { return { data, loading, uploadCanvasFile: mutateAsync }; }; + +export const useFetchMessageTrace = () => { + const { id } = useParams(); + const [messageId, setMessageId] = useState(''); + + const { + data, + isFetching: loading, + refetch, + } = useQuery({ + queryKey: [AgentApiAction.Trace, id, messageId], + refetchOnReconnect: false, + refetchOnMount: false, + refetchOnWindowFocus: false, + gcTime: 0, + enabled: !!id && !!messageId, + refetchInterval: 2000, + queryFn: async () => { + const { data } = await flowService.trace({ + canvas_id: id, + message_id: messageId, + }); + + return data?.data ?? []; + }, + }); + + return { data, loading, refetch, setMessageId }; +}; diff --git a/web/src/interfaces/database/agent.ts b/web/src/interfaces/database/agent.ts index 60f6957ab..ca954c050 100644 --- a/web/src/interfaces/database/agent.ts +++ b/web/src/interfaces/database/agent.ts @@ -217,3 +217,8 @@ export interface IGraph { nodes: RAGFlowNodeType[]; edges: Edge[]; } + +export interface ITraceData { + component_id: string; + trace: Array>; +} diff --git a/web/src/pages/agent/canvas/index.tsx b/web/src/pages/agent/canvas/index.tsx index 9972f9385..829ed30ec 100644 --- a/web/src/pages/agent/canvas/index.tsx +++ b/web/src/pages/agent/canvas/index.tsx @@ -115,6 +115,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { setCurrentMessageId, currentEventListWithoutMessage, clearEventList, + currentMessageId, } = useCacheChatLog(); const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({ @@ -221,6 +222,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { )} diff --git a/web/src/pages/agent/hooks/use-cache-chat-log.ts b/web/src/pages/agent/hooks/use-cache-chat-log.ts index 9b4f92bc5..899d7e76a 100644 --- a/web/src/pages/agent/hooks/use-cache-chat-log.ts +++ b/web/src/pages/agent/hooks/use-cache-chat-log.ts @@ -57,5 +57,6 @@ export function useCacheChatLog() { filterEventListByEventType, filterEventListByMessageId, setCurrentMessageId, + currentMessageId, }; } diff --git a/web/src/pages/agent/log-sheet/index.tsx b/web/src/pages/agent/log-sheet/index.tsx index 0e2903b2e..6e4ac1927 100644 --- a/web/src/pages/agent/log-sheet/index.tsx +++ b/web/src/pages/agent/log-sheet/index.tsx @@ -18,23 +18,24 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; -import { - ILogData, - ILogEvent, - MessageEventType, -} from '@/hooks/use-send-message'; +import { useFetchMessageTrace } from '@/hooks/use-agent-request'; +import { ILogEvent, MessageEventType } from '@/hooks/use-send-message'; import { IModalProps } from '@/interfaces/common'; +import { ITraceData } from '@/interfaces/database/agent'; import { cn } from '@/lib/utils'; import { isEmpty } from 'lodash'; import { BellElectric, NotebookText } from 'lucide-react'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; import JsonView from 'react18-json-view'; import 'react18-json-view/src/style.css'; import { useCacheChatLog } from '../hooks/use-cache-chat-log'; import useGraphStore from '../store'; type LogSheetProps = IModalProps & - Pick, 'currentEventListWithoutMessage'>; + Pick< + ReturnType, + 'currentEventListWithoutMessage' | 'currentMessageId' + >; function JsonViewer({ data, @@ -78,9 +79,16 @@ type EventWithIndex = { startNodeIdx: number } & ILogEvent; export function LogSheet({ hideModal, currentEventListWithoutMessage, + currentMessageId, }: LogSheetProps) { const getNode = useGraphStore((state) => state.getNode); + const { data: traceData, setMessageId } = useFetchMessageTrace(); + + useEffect(() => { + setMessageId(currentMessageId); + }, [currentMessageId, setMessageId]); + const getNodeName = useCallback( (nodeId: string) => { return getNode(nodeId)?.data.name; @@ -88,6 +96,28 @@ export function LogSheet({ [getNode], ); + const hasTrace = useCallback( + (componentId: string) => { + if (Array.isArray(traceData)) { + return traceData?.some((x) => x.component_id === componentId); + } + }, + [traceData], + ); + + const filterTrace = useCallback( + (componentId: string) => { + return traceData + ?.filter((x) => x.component_id === componentId) + .reduce((pre, cur) => { + pre.push(...cur.trace); + + return pre; + }, []); + }, + [traceData], + ); + // Look up to find the nearest start component id and concatenate the finish and log data into one const finishedNodeList = useMemo(() => { return currentEventListWithoutMessage.filter( @@ -109,19 +139,14 @@ export function LogSheet({ const item = pre.find((x) => x.startNodeIdx === startNodeIdx); - const { logs = {}, inputs = {}, outputs = {} } = cur.data; + const { inputs = {}, outputs = {} } = cur.data; if (item) { - const { - inputs: inputList, - outputs: outputList, - logs: logList, - } = item.data; + const { inputs: inputList, outputs: outputList } = item.data; item.data = { ...item.data, inputs: concatData(inputList, inputs), outputs: concatData(outputList, outputs), - logs: concatData(logList, logs), }; } else { pre.push({ @@ -195,10 +220,10 @@ export function LogSheet({ title="Input" > - {isEmpty((x.data as ILogData)?.logs) || ( + {hasTrace(x.data.component_id) && ( )} diff --git a/web/src/services/flow-service.ts b/web/src/services/flow-service.ts index 846d8ec74..4b2df89c6 100644 --- a/web/src/services/flow-service.ts +++ b/web/src/services/flow-service.ts @@ -19,6 +19,7 @@ const { listCanvasTeam, settingCanvas, uploadCanvasFile, + trace, } = api; const methods = { @@ -86,6 +87,10 @@ const methods = { url: uploadCanvasFile, method: 'post', }, + trace: { + url: trace, + method: 'get', + }, } as const; const flowService = registerServer(methods, request); diff --git a/web/src/utils/api.ts b/web/src/utils/api.ts index 817cf46ae..2a51a2ad2 100644 --- a/web/src/utils/api.ts +++ b/web/src/utils/api.ts @@ -144,6 +144,7 @@ export default { getInputElements: `${api_host}/canvas/input_elements`, debug: `${api_host}/canvas/debug`, uploadCanvasFile: `${api_host}/canvas/upload`, + trace: `${api_host}/canvas/trace`, // mcp server getMcpServerList: `${api_host}/mcp_server/list`,