mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-12-08 20:42:30 +08:00
Feat: Add log-detail page,Improve the style of chat boxes (#9119)
### What problem does this PR solve? Feat: Add log-detail page,Improve the style of chat boxes #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
@ -170,13 +170,15 @@ function MessageItem({
|
||||
></ReferenceDocumentList>
|
||||
)}
|
||||
{isAssistant && currentEventListWithoutMessageById && (
|
||||
<WorkFlowTimeline
|
||||
currentEventListWithoutMessage={currentEventListWithoutMessageById(
|
||||
item.id,
|
||||
)}
|
||||
currentMessageId={item.id}
|
||||
canvasId={conversationId}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<WorkFlowTimeline
|
||||
currentEventListWithoutMessage={currentEventListWithoutMessageById(
|
||||
item.id,
|
||||
)}
|
||||
currentMessageId={item.id}
|
||||
canvasId={conversationId}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{isUser && (
|
||||
<UploadedMessageFiles files={item.files}></UploadedMessageFiles>
|
||||
|
||||
@ -143,6 +143,9 @@ const TimeRangePicker = ({
|
||||
const [date, setDate] = useState<DateRange | undefined>(
|
||||
selectDateRange || { from: today, to: today },
|
||||
);
|
||||
useEffect(() => {
|
||||
setDate(selectDateRange);
|
||||
}, [selectDateRange]);
|
||||
const onChange = (e: DateRange | undefined) => {
|
||||
if (!e) return;
|
||||
setDate(e);
|
||||
|
||||
@ -20,6 +20,7 @@ export enum MessageEventType {
|
||||
export interface IAnswerEvent<T> {
|
||||
event: MessageEventType;
|
||||
message_id: string;
|
||||
session_id: string;
|
||||
created_at: number;
|
||||
task_id: string;
|
||||
data: T;
|
||||
|
||||
@ -180,6 +180,7 @@ export const useSendAgentMessage = (
|
||||
const { id: agentId } = useParams();
|
||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||
const inputs = useSelectBeginNodeDataInputs();
|
||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
||||
url || api.runCanvas,
|
||||
);
|
||||
@ -187,6 +188,12 @@ export const useSendAgentMessage = (
|
||||
return answerList[0]?.message_id;
|
||||
}, [answerList]);
|
||||
|
||||
useEffect(() => {
|
||||
if (answerList[0]?.session_id) {
|
||||
setSessionId(answerList[0]?.session_id);
|
||||
}
|
||||
}, [answerList]);
|
||||
|
||||
const { findReferenceByMessageId } = useFindMessageReference(answerList);
|
||||
const prologue = useGetBeginNodePrologue();
|
||||
const {
|
||||
@ -222,6 +229,8 @@ export const useSendAgentMessage = (
|
||||
params.inputs = transferInputsArrayToObject(query); // begin operator inputs
|
||||
|
||||
params.files = uploadResponseList;
|
||||
|
||||
params.session_id = sessionId;
|
||||
}
|
||||
const res = await send(params);
|
||||
|
||||
@ -239,6 +248,7 @@ export const useSendAgentMessage = (
|
||||
},
|
||||
[
|
||||
agentId,
|
||||
sessionId,
|
||||
send,
|
||||
inputs,
|
||||
uploadResponseList,
|
||||
|
||||
@ -43,19 +43,13 @@ export function useCacheChatLog() {
|
||||
setEventList([]);
|
||||
}, []);
|
||||
|
||||
const addEventList = useCallback(
|
||||
(events: IEventList, message_id: string) => {
|
||||
const nextList = [...eventList];
|
||||
events.forEach((x) => {
|
||||
if (nextList.every((y) => y !== x)) {
|
||||
nextList.push(x);
|
||||
}
|
||||
});
|
||||
setEventList(nextList);
|
||||
setMessageIdPool((prev) => ({ ...prev, [message_id]: nextList }));
|
||||
},
|
||||
[eventList],
|
||||
);
|
||||
const addEventList = useCallback((events: IEventList, message_id: string) => {
|
||||
setEventList((x) => {
|
||||
const list = [...x, ...events];
|
||||
setMessageIdPool((prev) => ({ ...prev, [message_id]: list }));
|
||||
return list;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const currentEventListWithoutMessage = useMemo(() => {
|
||||
const list = messageIdPool[currentMessageId]?.filter(
|
||||
|
||||
@ -18,16 +18,26 @@ import { JsonViewer } from './workFlowTimeline';
|
||||
|
||||
const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
if (!tools || tools.length === 0 || !Array.isArray(tools)) return null;
|
||||
const blackList = ['analyze_task', 'add_memory', 'gen_citations'];
|
||||
const blackList = ['add_memory', 'gen_citations'];
|
||||
const filteredTools = tools.filter(
|
||||
(tool) => !blackList.includes(tool.tool_name),
|
||||
);
|
||||
const capitalizeWords = (str: string, separator: string = '_'): string => {
|
||||
if (!str) return '';
|
||||
|
||||
return str
|
||||
.split(separator)
|
||||
.map((word) => {
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
})
|
||||
.join(' ');
|
||||
};
|
||||
return (
|
||||
<>
|
||||
{filteredTools?.map((tool, idx) => {
|
||||
return (
|
||||
<TimelineItem
|
||||
key={idx}
|
||||
key={'tool_' + idx}
|
||||
step={idx}
|
||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
|
||||
>
|
||||
@ -82,7 +92,10 @@ const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||
<AccordionItem value={idx.toString()}>
|
||||
<AccordionTrigger>
|
||||
<div className="flex gap-2 items-center">
|
||||
<span>{tool.tool_name}</span>
|
||||
<span>
|
||||
{tool.path + ' '}
|
||||
{capitalizeWords(tool.tool_name, '_')}
|
||||
</span>
|
||||
<span className="text-text-sub-title text-xs">
|
||||
{/* 0:00
|
||||
{x.data.elapsed_time?.toString().slice(0, 6)} */}
|
||||
|
||||
58
web/src/pages/agents/agent-log-detail-modal.tsx
Normal file
58
web/src/pages/agents/agent-log-detail-modal.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
import MessageItem from '@/components/next-message-item';
|
||||
import { Modal } from '@/components/ui/modal';
|
||||
import { useFetchAgent } from '@/hooks/use-agent-request';
|
||||
import { useFetchUserInfo } from '@/hooks/user-setting-hooks';
|
||||
import { IAgentLogMessage } from '@/interfaces/database/agent';
|
||||
import { IReferenceObject, Message } from '@/interfaces/database/chat';
|
||||
import { buildMessageUuidWithRole } from '@/utils/chat';
|
||||
import React from 'react';
|
||||
import { IMessage } from '../chat/interface';
|
||||
|
||||
interface CustomModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
message: IAgentLogMessage[];
|
||||
reference: IReferenceObject;
|
||||
}
|
||||
|
||||
export const AgentLogDetailModal: React.FC<CustomModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
message: derivedMessages,
|
||||
reference,
|
||||
}) => {
|
||||
const { data: userInfo } = useFetchUserInfo();
|
||||
const { data: canvasInfo } = useFetchAgent();
|
||||
return (
|
||||
<Modal
|
||||
open={isOpen}
|
||||
onCancel={onClose}
|
||||
showfooter={false}
|
||||
footer={null}
|
||||
title={derivedMessages?.length ? derivedMessages[0]?.content : ''}
|
||||
className="!w-[900px]"
|
||||
>
|
||||
<div className="flex items-start mb-4 flex-col gap-4 justify-start">
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
key={buildMessageUuidWithRole(
|
||||
message as Partial<Message | IMessage>,
|
||||
)}
|
||||
nickname={userInfo.nickname}
|
||||
avatar={userInfo.avatar}
|
||||
avatarDialog={canvasInfo.avatar}
|
||||
item={message as IMessage}
|
||||
reference={reference}
|
||||
index={i}
|
||||
showLikeButton={false}
|
||||
showLog={false}
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@ -13,7 +13,12 @@ import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
|
||||
import { Spin } from '@/components/ui/spin';
|
||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||
import { useFetchAgentLog } from '@/hooks/use-agent-request';
|
||||
import { IAgentLogResponse } from '@/interfaces/database/agent';
|
||||
import {
|
||||
IAgentLogMessage,
|
||||
IAgentLogResponse,
|
||||
} from '@/interfaces/database/agent';
|
||||
import { IReferenceObject } from '@/interfaces/database/chat';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { useParams } from 'umi';
|
||||
import { DateRange } from '../../components/originui/calendar/index';
|
||||
@ -26,16 +31,27 @@ import {
|
||||
TableRow,
|
||||
} from '../../components/ui/table';
|
||||
import { useFetchDataOnMount } from '../agent/hooks/use-fetch-data';
|
||||
import { AgentLogDetailModal } from './agent-log-detail-modal';
|
||||
const getStartOfToday = (): Date => {
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
return today;
|
||||
};
|
||||
|
||||
const getEndOfToday = (): Date => {
|
||||
const today = new Date();
|
||||
today.setHours(23, 59, 59, 999);
|
||||
return today;
|
||||
};
|
||||
const AgentLogPage: React.FC = () => {
|
||||
const { navigateToAgentList, navigateToAgent } = useNavigatePage();
|
||||
const { flowDetail: agentDetail } = useFetchDataOnMount();
|
||||
const { id: canvasId } = useParams();
|
||||
const today = new Date();
|
||||
const queryClient = useQueryClient();
|
||||
const init = {
|
||||
keywords: '',
|
||||
from_date: today,
|
||||
to_date: today,
|
||||
from_date: getStartOfToday(),
|
||||
to_date: getEndOfToday(),
|
||||
orderby: 'create_time',
|
||||
desc: false,
|
||||
page: 1,
|
||||
@ -152,6 +168,13 @@ const AgentLogPage: React.FC = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const handleClickSearch = () => {
|
||||
setPagination({ ...pagination, current: 1 });
|
||||
handleSearch();
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ['fetchAgentLog'],
|
||||
});
|
||||
};
|
||||
useEffect(() => {
|
||||
handleSearch();
|
||||
}, [pagination.current, pagination.pageSize, sortConfig]);
|
||||
@ -166,7 +189,17 @@ const AgentLogPage: React.FC = () => {
|
||||
|
||||
const handleReset = () => {
|
||||
setSearchParams(init);
|
||||
setKeywords(init.keywords);
|
||||
setCurrentDate({ from: init.from_date, to: init.to_date });
|
||||
};
|
||||
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
const [modalData, setModalData] = useState<IAgentLogResponse>();
|
||||
const showLogDetail = (item: IAgentLogResponse) => {
|
||||
setModalData(item);
|
||||
setOpenModal(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className=" text-white">
|
||||
<PageHeader>
|
||||
@ -209,15 +242,14 @@ const AgentLogPage: React.FC = () => {
|
||||
<span className="whitespace-nowrap">Latest Date</span>
|
||||
<TimeRangePicker
|
||||
onSelect={handleDateRangeChange}
|
||||
selectDateRange={{ from: currentDate.from, to: currentDate.to }}
|
||||
selectDateRange={currentDate}
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="bg-foreground text-text-title-invert px-4 py-1 rounded"
|
||||
onClick={() => {
|
||||
setPagination({ ...pagination, current: 1 });
|
||||
handleSearch();
|
||||
handleClickSearch();
|
||||
}}
|
||||
>
|
||||
Search
|
||||
@ -276,7 +308,12 @@ const AgentLogPage: React.FC = () => {
|
||||
)}
|
||||
{!loading &&
|
||||
data?.map((item) => (
|
||||
<TableRow key={item.id}>
|
||||
<TableRow
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
showLogDetail(item);
|
||||
}}
|
||||
>
|
||||
{columns.map((column) => (
|
||||
<TableCell key={column.dataIndex}>
|
||||
{column.render
|
||||
@ -312,6 +349,12 @@ const AgentLogPage: React.FC = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AgentLogDetailModal
|
||||
isOpen={openModal}
|
||||
message={modalData?.message as IAgentLogMessage[]}
|
||||
reference={modalData?.reference as unknown as IReferenceObject}
|
||||
onClose={() => setOpenModal(false)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -91,55 +91,63 @@ const ChatContainer = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="h-[100vh]">
|
||||
<section className={cn('flex flex-1 flex-col p-2.5 h-full')}>
|
||||
<div className={cn('flex flex-1 flex-col overflow-auto pr-2')}>
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
conversationId={conversationId}
|
||||
currentEventListWithoutMessageById={
|
||||
currentEventListWithoutMessageById
|
||||
}
|
||||
setCurrentMessageId={setCurrentMessageId}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
avatarDialog={avatarData.avatar}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
sendLoading={sendLoading}
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
<section className="h-[100vh] flex justify-center items-center">
|
||||
<div className=" w-[80vw]">
|
||||
<div className="flex flex-1 flex-col p-2.5 h-[90vh] border rounded-lg">
|
||||
<div
|
||||
className={cn('flex flex-1 flex-col overflow-auto m-auto w-5/6')}
|
||||
>
|
||||
<div>
|
||||
{derivedMessages?.map((message, i) => {
|
||||
return (
|
||||
<MessageItem
|
||||
visibleAvatar={visibleAvatar}
|
||||
conversationId={conversationId}
|
||||
currentEventListWithoutMessageById={
|
||||
currentEventListWithoutMessageById
|
||||
}
|
||||
setCurrentMessageId={setCurrentMessageId}
|
||||
key={buildMessageUuidWithRole(message)}
|
||||
avatarDialog={avatarData.avatar}
|
||||
item={message}
|
||||
nickname="You"
|
||||
reference={findReferenceByMessageId(message.id)}
|
||||
loading={
|
||||
message.role === MessageType.Assistant &&
|
||||
sendLoading &&
|
||||
derivedMessages?.length - 1 === i
|
||||
}
|
||||
index={i}
|
||||
clickDocumentButton={clickDocumentButton}
|
||||
showLikeButton={false}
|
||||
showLoudspeaker={false}
|
||||
showLog={false}
|
||||
sendLoading={sendLoading}
|
||||
></MessageItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div ref={ref} />
|
||||
</div>
|
||||
<div className="flex w-full justify-center mb-8">
|
||||
<div className="w-5/6">
|
||||
<NextMessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
onUpload={handleUploadFile}
|
||||
isUploading={loading}
|
||||
></NextMessageInput>
|
||||
</div>
|
||||
</div>
|
||||
<div ref={ref} />
|
||||
</div>
|
||||
<NextMessageInput
|
||||
isShared
|
||||
value={value}
|
||||
disabled={hasError}
|
||||
sendDisabled={sendDisabled}
|
||||
conversationId={conversationId}
|
||||
onInputChange={handleInputChange}
|
||||
onPressEnter={handlePressEnter}
|
||||
sendLoading={sendLoading}
|
||||
stopOutputMessage={stopOutputMessage}
|
||||
onUpload={handleUploadFile}
|
||||
isUploading={loading}
|
||||
></NextMessageInput>
|
||||
</section>
|
||||
</div>
|
||||
{visible && (
|
||||
<PdfDrawer
|
||||
visible={visible}
|
||||
|
||||
Reference in New Issue
Block a user