浏览代码

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)
tags/v0.20.0
balibabu 3 个月前
父节点
当前提交
3e1e908422
没有帐户链接到提交者的电子邮件

+ 32
- 1
web/src/hooks/use-agent-request.ts 查看文件

import { AgentGlobals } from '@/constants/agent'; import { AgentGlobals } from '@/constants/agent';
import { ITraceData } from '@/interfaces/database/agent';
import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow'; import { DSL, IFlow, IFlowTemplate } from '@/interfaces/database/flow';
import i18n from '@/locales/config'; import i18n from '@/locales/config';
import { BeginId } from '@/pages/agent/constant'; import { BeginId } from '@/pages/agent/constant';
import { useDebounce } from 'ahooks'; import { useDebounce } from 'ahooks';
import { message } from 'antd'; import { message } from 'antd';
import { get, set } from 'lodash'; import { get, set } from 'lodash';
import { useCallback } from 'react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useParams } from 'umi'; import { useParams } from 'umi';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
SetAgent = 'setAgent', SetAgent = 'setAgent',
FetchAgentTemplates = 'fetchAgentTemplates', FetchAgentTemplates = 'fetchAgentTemplates',
UploadCanvasFile = 'uploadCanvasFile', UploadCanvasFile = 'uploadCanvasFile',
Trace = 'trace',
} }


export const EmptyDsl = { export const EmptyDsl = {


return { data, loading, uploadCanvasFile: mutateAsync }; return { data, loading, uploadCanvasFile: mutateAsync };
}; };

export const useFetchMessageTrace = () => {
const { id } = useParams();
const [messageId, setMessageId] = useState('');

const {
data,
isFetching: loading,
refetch,
} = useQuery<ITraceData[]>({
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 };
};

+ 5
- 0
web/src/interfaces/database/agent.ts 查看文件

nodes: RAGFlowNodeType[]; nodes: RAGFlowNodeType[];
edges: Edge[]; edges: Edge[];
} }

export interface ITraceData {
component_id: string;
trace: Array<Record<string, any>>;
}

+ 2
- 0
web/src/pages/agent/canvas/index.tsx 查看文件

setCurrentMessageId, setCurrentMessageId,
currentEventListWithoutMessage, currentEventListWithoutMessage,
clearEventList, clearEventList,
currentMessageId,
} = useCacheChatLog(); } = useCacheChatLog();


const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({ const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({
<LogSheet <LogSheet
hideModal={hideLogSheet} hideModal={hideLogSheet}
currentEventListWithoutMessage={currentEventListWithoutMessage} currentEventListWithoutMessage={currentEventListWithoutMessage}
currentMessageId={currentMessageId}
></LogSheet> ></LogSheet>
)} )}
</div> </div>

+ 1
- 0
web/src/pages/agent/hooks/use-cache-chat-log.ts 查看文件

filterEventListByEventType, filterEventListByEventType,
filterEventListByMessageId, filterEventListByMessageId,
setCurrentMessageId, setCurrentMessageId,
currentMessageId,
}; };
} }

+ 42
- 17
web/src/pages/agent/log-sheet/index.tsx 查看文件

SheetHeader, SheetHeader,
SheetTitle, SheetTitle,
} from '@/components/ui/sheet'; } 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 { IModalProps } from '@/interfaces/common';
import { ITraceData } from '@/interfaces/database/agent';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { BellElectric, NotebookText } from 'lucide-react'; import { BellElectric, NotebookText } from 'lucide-react';
import { useCallback, useMemo } from 'react';
import { useCallback, useEffect, useMemo } from 'react';
import JsonView from 'react18-json-view'; import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css'; import 'react18-json-view/src/style.css';
import { useCacheChatLog } from '../hooks/use-cache-chat-log'; import { useCacheChatLog } from '../hooks/use-cache-chat-log';
import useGraphStore from '../store'; import useGraphStore from '../store';


type LogSheetProps = IModalProps<any> & type LogSheetProps = IModalProps<any> &
Pick<ReturnType<typeof useCacheChatLog>, 'currentEventListWithoutMessage'>;
Pick<
ReturnType<typeof useCacheChatLog>,
'currentEventListWithoutMessage' | 'currentMessageId'
>;


function JsonViewer({ function JsonViewer({
data, data,
export function LogSheet({ export function LogSheet({
hideModal, hideModal,
currentEventListWithoutMessage, currentEventListWithoutMessage,
currentMessageId,
}: LogSheetProps) { }: LogSheetProps) {
const getNode = useGraphStore((state) => state.getNode); const getNode = useGraphStore((state) => state.getNode);


const { data: traceData, setMessageId } = useFetchMessageTrace();

useEffect(() => {
setMessageId(currentMessageId);
}, [currentMessageId, setMessageId]);

const getNodeName = useCallback( const getNodeName = useCallback(
(nodeId: string) => { (nodeId: string) => {
return getNode(nodeId)?.data.name; return getNode(nodeId)?.data.name;
[getNode], [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<ITraceData['trace']>((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 // Look up to find the nearest start component id and concatenate the finish and log data into one
const finishedNodeList = useMemo(() => { const finishedNodeList = useMemo(() => {
return currentEventListWithoutMessage.filter( return currentEventListWithoutMessage.filter(


const item = pre.find((x) => x.startNodeIdx === startNodeIdx); const item = pre.find((x) => x.startNodeIdx === startNodeIdx);


const { logs = {}, inputs = {}, outputs = {} } = cur.data;
const { inputs = {}, outputs = {} } = cur.data;
if (item) { if (item) {
const {
inputs: inputList,
outputs: outputList,
logs: logList,
} = item.data;
const { inputs: inputList, outputs: outputList } = item.data;


item.data = { item.data = {
...item.data, ...item.data,
inputs: concatData(inputList, inputs), inputs: concatData(inputList, inputs),
outputs: concatData(outputList, outputs), outputs: concatData(outputList, outputs),
logs: concatData(logList, logs),
}; };
} else { } else {
pre.push({ pre.push({
title="Input" title="Input"
></JsonViewer> ></JsonViewer>


{isEmpty((x.data as ILogData)?.logs) || (
{hasTrace(x.data.component_id) && (
<JsonViewer <JsonViewer
data={(x.data as ILogData)?.logs}
title={'Logs'}
data={filterTrace(x.data.component_id) ?? {}}
title={'Trace'}
></JsonViewer> ></JsonViewer>
)} )}



+ 5
- 0
web/src/services/flow-service.ts 查看文件

listCanvasTeam, listCanvasTeam,
settingCanvas, settingCanvas,
uploadCanvasFile, uploadCanvasFile,
trace,
} = api; } = api;


const methods = { const methods = {
url: uploadCanvasFile, url: uploadCanvasFile,
method: 'post', method: 'post',
}, },
trace: {
url: trace,
method: 'get',
},
} as const; } as const;


const flowService = registerServer<keyof typeof methods>(methods, request); const flowService = registerServer<keyof typeof methods>(methods, request);

+ 1
- 0
web/src/utils/api.ts 查看文件

getInputElements: `${api_host}/canvas/input_elements`, getInputElements: `${api_host}/canvas/input_elements`,
debug: `${api_host}/canvas/debug`, debug: `${api_host}/canvas/debug`,
uploadCanvasFile: `${api_host}/canvas/upload`, uploadCanvasFile: `${api_host}/canvas/upload`,
trace: `${api_host}/canvas/trace`,


// mcp server // mcp server
getMcpServerList: `${api_host}/mcp_server/list`, getMcpServerList: `${api_host}/mcp_server/list`,

正在加载...
取消
保存