mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### 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)
This commit is contained in:
@ -1,4 +1,5 @@
|
|||||||
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';
|
||||||
@ -9,7 +10,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|||||||
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';
|
||||||
@ -27,6 +28,7 @@ export const enum AgentApiAction {
|
|||||||
SetAgent = 'setAgent',
|
SetAgent = 'setAgent',
|
||||||
FetchAgentTemplates = 'fetchAgentTemplates',
|
FetchAgentTemplates = 'fetchAgentTemplates',
|
||||||
UploadCanvasFile = 'uploadCanvasFile',
|
UploadCanvasFile = 'uploadCanvasFile',
|
||||||
|
Trace = 'trace',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EmptyDsl = {
|
export const EmptyDsl = {
|
||||||
@ -300,3 +302,32 @@ export const useUploadCanvasFile = () => {
|
|||||||
|
|
||||||
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 };
|
||||||
|
};
|
||||||
|
|||||||
@ -217,3 +217,8 @@ export interface IGraph {
|
|||||||
nodes: RAGFlowNodeType[];
|
nodes: RAGFlowNodeType[];
|
||||||
edges: Edge[];
|
edges: Edge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ITraceData {
|
||||||
|
component_id: string;
|
||||||
|
trace: Array<Record<string, any>>;
|
||||||
|
}
|
||||||
|
|||||||
@ -115,6 +115,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
setCurrentMessageId,
|
setCurrentMessageId,
|
||||||
currentEventListWithoutMessage,
|
currentEventListWithoutMessage,
|
||||||
clearEventList,
|
clearEventList,
|
||||||
|
currentMessageId,
|
||||||
} = useCacheChatLog();
|
} = useCacheChatLog();
|
||||||
|
|
||||||
const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({
|
const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({
|
||||||
@ -221,6 +222,7 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
<LogSheet
|
<LogSheet
|
||||||
hideModal={hideLogSheet}
|
hideModal={hideLogSheet}
|
||||||
currentEventListWithoutMessage={currentEventListWithoutMessage}
|
currentEventListWithoutMessage={currentEventListWithoutMessage}
|
||||||
|
currentMessageId={currentMessageId}
|
||||||
></LogSheet>
|
></LogSheet>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -57,5 +57,6 @@ export function useCacheChatLog() {
|
|||||||
filterEventListByEventType,
|
filterEventListByEventType,
|
||||||
filterEventListByMessageId,
|
filterEventListByMessageId,
|
||||||
setCurrentMessageId,
|
setCurrentMessageId,
|
||||||
|
currentMessageId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,23 +18,24 @@ import {
|
|||||||
SheetHeader,
|
SheetHeader,
|
||||||
SheetTitle,
|
SheetTitle,
|
||||||
} from '@/components/ui/sheet';
|
} from '@/components/ui/sheet';
|
||||||
import {
|
import { useFetchMessageTrace } from '@/hooks/use-agent-request';
|
||||||
ILogData,
|
import { ILogEvent, MessageEventType } from '@/hooks/use-send-message';
|
||||||
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,
|
||||||
@ -78,9 +79,16 @@ type EventWithIndex = { startNodeIdx: number } & ILogEvent;
|
|||||||
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;
|
||||||
@ -88,6 +96,28 @@ export function LogSheet({
|
|||||||
[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(
|
||||||
@ -109,19 +139,14 @@ export function LogSheet({
|
|||||||
|
|
||||||
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 {
|
const { inputs: inputList, outputs: outputList } = item.data;
|
||||||
inputs: inputList,
|
|
||||||
outputs: outputList,
|
|
||||||
logs: logList,
|
|
||||||
} = 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({
|
||||||
@ -195,10 +220,10 @@ export function LogSheet({
|
|||||||
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}
|
data={filterTrace(x.data.component_id) ?? {}}
|
||||||
title={'Logs'}
|
title={'Trace'}
|
||||||
></JsonViewer>
|
></JsonViewer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const {
|
|||||||
listCanvasTeam,
|
listCanvasTeam,
|
||||||
settingCanvas,
|
settingCanvas,
|
||||||
uploadCanvasFile,
|
uploadCanvasFile,
|
||||||
|
trace,
|
||||||
} = api;
|
} = api;
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
@ -86,6 +87,10 @@ 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);
|
||||||
|
|||||||
@ -144,6 +144,7 @@ export default {
|
|||||||
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`,
|
||||||
|
|||||||
Reference in New Issue
Block a user