mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
### What problem does this PR solve? Feat: Display agent operator call log #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -6,7 +6,11 @@ import {
|
||||
} from '@xyflow/react';
|
||||
import '@xyflow/react/dist/style.css';
|
||||
import { ChatSheet } from '../chat/chat-sheet';
|
||||
import { AgentInstanceContext } from '../context';
|
||||
import {
|
||||
AgentChatContext,
|
||||
AgentChatLogContext,
|
||||
AgentInstanceContext,
|
||||
} from '../context';
|
||||
import FormSheet from '../form-sheet/next';
|
||||
import {
|
||||
useHandleDrop,
|
||||
@ -16,6 +20,7 @@ import {
|
||||
} from '../hooks';
|
||||
import { useAddNode } from '../hooks/use-add-node';
|
||||
import { useBeforeDelete } from '../hooks/use-before-delete';
|
||||
import { useCacheChatLog } from '../hooks/use-cache-chat-log';
|
||||
import { useShowDrawer, useShowLogSheet } from '../hooks/use-show-drawer';
|
||||
import { LogSheet } from '../log-sheet';
|
||||
import RunSheet from '../run-sheet';
|
||||
@ -101,7 +106,12 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
hideDrawer,
|
||||
});
|
||||
|
||||
const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet();
|
||||
const { addEventList, setCurrentMessageId, currentEventListWithoutMessage } =
|
||||
useCacheChatLog();
|
||||
|
||||
const { showLogSheet, logSheetVisible, hideLogSheet } = useShowLogSheet({
|
||||
setCurrentMessageId,
|
||||
});
|
||||
|
||||
const { handleBeforeDelete } = useBeforeDelete();
|
||||
|
||||
@ -176,10 +186,13 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
</AgentInstanceContext.Provider>
|
||||
)}
|
||||
{chatVisible && (
|
||||
<ChatSheet
|
||||
visible={chatVisible}
|
||||
hideModal={hideRunOrChatDrawer}
|
||||
></ChatSheet>
|
||||
<AgentChatContext.Provider value={{ showLogSheet }}>
|
||||
<AgentChatLogContext.Provider
|
||||
value={{ addEventList, setCurrentMessageId }}
|
||||
>
|
||||
<ChatSheet hideModal={hideRunOrChatDrawer}></ChatSheet>
|
||||
</AgentChatLogContext.Provider>
|
||||
</AgentChatContext.Provider>
|
||||
)}
|
||||
{runVisible && (
|
||||
<RunSheet
|
||||
@ -188,7 +201,10 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) {
|
||||
></RunSheet>
|
||||
)}
|
||||
{logSheetVisible && (
|
||||
<LogSheet hideModal={hideLogSheet} showModal={showLogSheet}></LogSheet>
|
||||
<LogSheet
|
||||
hideModal={hideLogSheet}
|
||||
currentEventListWithoutMessage={currentEventListWithoutMessage}
|
||||
></LogSheet>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import MessageItem from '@/components/message-item';
|
||||
import { MessageType } from '@/constants/chat';
|
||||
import { useGetFileIcon } from '@/pages/chat/hooks';
|
||||
import { buildMessageItemReference } from '@/pages/chat/utils';
|
||||
@ -7,6 +6,7 @@ import { Spin } from 'antd';
|
||||
import { useSendNextMessage } from './hooks';
|
||||
|
||||
import MessageInput from '@/components/message-input';
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
import PdfDrawer from '@/components/pdf-drawer';
|
||||
import { useClickDrawer } from '@/components/pdf-drawer/hooks';
|
||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||
|
||||
@ -8,9 +8,16 @@ import { IModalProps } from '@/interfaces/common';
|
||||
import { cn } from '@/lib/utils';
|
||||
import AgentChatBox from './box';
|
||||
|
||||
export function ChatSheet({ visible, hideModal }: IModalProps<any>) {
|
||||
export function ChatSheet({ hideModal }: IModalProps<any>) {
|
||||
return (
|
||||
<Sheet open={visible} modal={false} onOpenChange={hideModal}>
|
||||
<Sheet
|
||||
open
|
||||
modal={false}
|
||||
onOpenChange={(open) => {
|
||||
console.log('🚀 ~ ChatSheet ~ open:', open);
|
||||
hideModal();
|
||||
}}
|
||||
>
|
||||
<SheetTitle className="hidden"></SheetTitle>
|
||||
<SheetContent className={cn('top-20 p-0')}>
|
||||
<SheetHeader>
|
||||
|
||||
@ -16,10 +16,11 @@ import api from '@/utils/api';
|
||||
import { message } from 'antd';
|
||||
import { get } from 'lodash';
|
||||
import trim from 'lodash/trim';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { useCallback, useContext, useEffect, useMemo } from 'react';
|
||||
import { useParams } from 'umi';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { BeginId } from '../constant';
|
||||
import { AgentChatLogContext } from '../context';
|
||||
import useGraphStore from '../store';
|
||||
import { receiveMessageError } from '../utils';
|
||||
|
||||
@ -86,6 +87,7 @@ export const useSendNextMessage = () => {
|
||||
const { id: agentId } = useParams();
|
||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||
const { refetch } = useFetchAgent();
|
||||
const { addEventList } = useContext(AgentChatLogContext);
|
||||
|
||||
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
||||
api.runCanvas,
|
||||
@ -160,6 +162,10 @@ export const useSendNextMessage = () => {
|
||||
}
|
||||
}, [addNewestAnswer, prologue]);
|
||||
|
||||
useEffect(() => {
|
||||
addEventList(answerList);
|
||||
}, [addEventList, answerList]);
|
||||
|
||||
return {
|
||||
handlePressEnter,
|
||||
handleInputChange,
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { RAGFlowNodeType } from '@/interfaces/database/flow';
|
||||
import { createContext } from 'react';
|
||||
import { useAddNode } from './hooks/use-add-node';
|
||||
import { useCacheChatLog } from './hooks/use-cache-chat-log';
|
||||
import { useShowLogSheet } from './hooks/use-show-drawer';
|
||||
|
||||
export const AgentFormContext = createContext<RAGFlowNodeType | undefined>(
|
||||
undefined,
|
||||
@ -14,3 +16,21 @@ type AgentInstanceContextType = Pick<
|
||||
export const AgentInstanceContext = createContext<AgentInstanceContextType>(
|
||||
{} as AgentInstanceContextType,
|
||||
);
|
||||
|
||||
type AgentChatContextType = Pick<
|
||||
ReturnType<typeof useShowLogSheet>,
|
||||
'showLogSheet'
|
||||
>;
|
||||
|
||||
export const AgentChatContext = createContext<AgentChatContextType>(
|
||||
{} as AgentChatContextType,
|
||||
);
|
||||
|
||||
type AgentChatLogContextType = Pick<
|
||||
ReturnType<typeof useCacheChatLog>,
|
||||
'addEventList' | 'setCurrentMessageId'
|
||||
>;
|
||||
|
||||
export const AgentChatLogContext = createContext<AgentChatLogContextType>(
|
||||
{} as AgentChatLogContextType,
|
||||
);
|
||||
|
||||
61
web/src/pages/agent/hooks/use-cache-chat-log.ts
Normal file
61
web/src/pages/agent/hooks/use-cache-chat-log.ts
Normal file
@ -0,0 +1,61 @@
|
||||
import { IEventList, MessageEventType } from '@/hooks/use-send-message';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
export const ExcludeTypes = [
|
||||
MessageEventType.Message,
|
||||
MessageEventType.MessageEnd,
|
||||
];
|
||||
|
||||
export function useCacheChatLog() {
|
||||
const [eventList, setEventList] = useState<IEventList>([]);
|
||||
const [currentMessageId, setCurrentMessageId] = useState('');
|
||||
|
||||
const filterEventListByMessageId = useCallback(
|
||||
(messageId: string) => {
|
||||
return eventList.filter((x) => x.message_id === messageId);
|
||||
},
|
||||
[eventList],
|
||||
);
|
||||
|
||||
const filterEventListByEventType = useCallback(
|
||||
(eventType: string) => {
|
||||
return eventList.filter((x) => x.event === eventType);
|
||||
},
|
||||
[eventList],
|
||||
);
|
||||
|
||||
const clearEventList = useCallback(() => {
|
||||
setEventList([]);
|
||||
}, []);
|
||||
|
||||
const addEventList = useCallback((events: IEventList) => {
|
||||
setEventList((list) => {
|
||||
const nextList = [...list];
|
||||
events.forEach((x) => {
|
||||
if (nextList.every((y) => y !== x)) {
|
||||
nextList.push(x);
|
||||
}
|
||||
});
|
||||
return nextList;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const currentEventListWithoutMessage = useMemo(() => {
|
||||
return eventList.filter(
|
||||
(x) =>
|
||||
x.message_id === currentMessageId &&
|
||||
ExcludeTypes.every((y) => y !== x.event),
|
||||
);
|
||||
}, [currentMessageId, eventList]);
|
||||
|
||||
return {
|
||||
eventList,
|
||||
currentEventListWithoutMessage,
|
||||
setEventList,
|
||||
clearEventList,
|
||||
addEventList,
|
||||
filterEventListByEventType,
|
||||
filterEventListByMessageId,
|
||||
setCurrentMessageId,
|
||||
};
|
||||
}
|
||||
@ -5,6 +5,7 @@ import { useCallback, useEffect } from 'react';
|
||||
import { Operator } from '../constant';
|
||||
import { BeginQuery } from '../interface';
|
||||
import useGraphStore from '../store';
|
||||
import { useCacheChatLog } from './use-cache-chat-log';
|
||||
import { useGetBeginNodeDataQuery } from './use-get-begin-query';
|
||||
import { useSaveGraph } from './use-save-graph';
|
||||
|
||||
@ -152,12 +153,22 @@ export function useShowDrawer({
|
||||
};
|
||||
}
|
||||
|
||||
export function useShowLogSheet() {
|
||||
export function useShowLogSheet({
|
||||
setCurrentMessageId,
|
||||
}: Pick<ReturnType<typeof useCacheChatLog>, 'setCurrentMessageId'>) {
|
||||
const { visible, showModal, hideModal } = useSetModalState();
|
||||
|
||||
const handleShow = useCallback(
|
||||
(messageId: string) => {
|
||||
setCurrentMessageId(messageId);
|
||||
showModal();
|
||||
},
|
||||
[setCurrentMessageId, showModal],
|
||||
);
|
||||
|
||||
return {
|
||||
logSheetVisible: visible,
|
||||
hideLogSheet: hideModal,
|
||||
showLogSheet: showModal,
|
||||
showLogSheet: handleShow,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,23 +1,116 @@
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@/components/ui/accordion';
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetDescription,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
} from '@/components/ui/sheet';
|
||||
import { INodeEvent, MessageEventType } from '@/hooks/use-send-message';
|
||||
import { IModalProps } from '@/interfaces/common';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { NotebookText } from 'lucide-react';
|
||||
import { useCallback, 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';
|
||||
|
||||
export function LogSheet({ hideModal }: IModalProps<any>) {
|
||||
type LogSheetProps = IModalProps<any> &
|
||||
Pick<ReturnType<typeof useCacheChatLog>, 'currentEventListWithoutMessage'>;
|
||||
|
||||
function JsonViewer({
|
||||
data,
|
||||
title,
|
||||
}: {
|
||||
data: Record<string, any>;
|
||||
title: string;
|
||||
}) {
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal}>
|
||||
<SheetContent>
|
||||
<section className="space-y-2">
|
||||
<div>{title}</div>
|
||||
<JsonView
|
||||
src={data}
|
||||
displaySize
|
||||
collapseStringsAfterLength={100000000000}
|
||||
className="w-full h-[200px] break-words overflow-auto p-2 bg-slate-800"
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function LogSheet({
|
||||
hideModal,
|
||||
currentEventListWithoutMessage,
|
||||
}: LogSheetProps) {
|
||||
const getNode = useGraphStore((state) => state.getNode);
|
||||
|
||||
const getNodeName = useCallback(
|
||||
(nodeId: string) => {
|
||||
return getNode(nodeId)?.data.name;
|
||||
},
|
||||
[getNode],
|
||||
);
|
||||
|
||||
const finishedNodeList = useMemo(() => {
|
||||
return currentEventListWithoutMessage.filter(
|
||||
(x) => x.event === MessageEventType.NodeFinished,
|
||||
) as INodeEvent[];
|
||||
}, [currentEventListWithoutMessage]);
|
||||
|
||||
return (
|
||||
<Sheet open onOpenChange={hideModal} modal={false}>
|
||||
<SheetContent className="top-20 right-96">
|
||||
<SheetHeader>
|
||||
<SheetTitle>Are you absolutely sure?</SheetTitle>
|
||||
<SheetDescription>
|
||||
This action cannot be undone. This will permanently delete your
|
||||
account and remove your data from our servers.
|
||||
</SheetDescription>
|
||||
<SheetTitle className="flex items-center gap-1">
|
||||
<NotebookText className="size-4" />
|
||||
Log
|
||||
</SheetTitle>
|
||||
</SheetHeader>
|
||||
<section className="max-h-[82vh] overflow-auto">
|
||||
{finishedNodeList.map((x, idx) => (
|
||||
<section key={idx}>
|
||||
<Accordion type="single" collapsible>
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{getNodeName(x.data?.component_id)}</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)}
|
||||
</span>
|
||||
<span
|
||||
className={cn(
|
||||
'border-background -end-1 -top-1 size-2 rounded-full border-2 bg-dot-green',
|
||||
{ 'text-dot-green': x.data.error === null },
|
||||
{ 'text-dot-red': x.data.error !== null },
|
||||
)}
|
||||
>
|
||||
<span className="sr-only">Online</span>
|
||||
</span>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="space-y-2">
|
||||
<JsonViewer
|
||||
data={x.data.inputs}
|
||||
title="Input"
|
||||
></JsonViewer>
|
||||
|
||||
<JsonViewer
|
||||
data={x.data.outputs}
|
||||
title="Output"
|
||||
></JsonViewer>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
</section>
|
||||
))}
|
||||
</section>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
);
|
||||
|
||||
@ -1,56 +0,0 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Form, useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useCallback, useState } from 'react';
|
||||
import DynamicCategorize from './agent/form/categorize-form/dynamic-categorize';
|
||||
|
||||
const formSchema = z.object({
|
||||
items: z
|
||||
.array(
|
||||
z
|
||||
.object({
|
||||
name: z.string().min(1, 'xxx').trim(),
|
||||
description: z.string().optional(),
|
||||
// examples: z
|
||||
// .array(
|
||||
// z.object({
|
||||
// value: z.string(),
|
||||
// }),
|
||||
// )
|
||||
// .optional(),
|
||||
})
|
||||
.optional(),
|
||||
)
|
||||
.optional(),
|
||||
});
|
||||
|
||||
export function Demo() {
|
||||
const [flag, setFlag] = useState(false);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
items: [],
|
||||
},
|
||||
});
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
form?.reset();
|
||||
}, [form]);
|
||||
|
||||
const handleSwitch = useCallback(() => {
|
||||
setFlag(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form {...form}>
|
||||
<DynamicCategorize></DynamicCategorize>
|
||||
</Form>
|
||||
<Button onClick={handleReset}>reset</Button>
|
||||
<Button onClick={handleSwitch}>switch</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user