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>
|
></ReferenceDocumentList>
|
||||||
)}
|
)}
|
||||||
{isAssistant && currentEventListWithoutMessageById && (
|
{isAssistant && currentEventListWithoutMessageById && (
|
||||||
<WorkFlowTimeline
|
<div className="mt-4">
|
||||||
currentEventListWithoutMessage={currentEventListWithoutMessageById(
|
<WorkFlowTimeline
|
||||||
item.id,
|
currentEventListWithoutMessage={currentEventListWithoutMessageById(
|
||||||
)}
|
item.id,
|
||||||
currentMessageId={item.id}
|
)}
|
||||||
canvasId={conversationId}
|
currentMessageId={item.id}
|
||||||
/>
|
canvasId={conversationId}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{isUser && (
|
{isUser && (
|
||||||
<UploadedMessageFiles files={item.files}></UploadedMessageFiles>
|
<UploadedMessageFiles files={item.files}></UploadedMessageFiles>
|
||||||
|
|||||||
@ -143,6 +143,9 @@ const TimeRangePicker = ({
|
|||||||
const [date, setDate] = useState<DateRange | undefined>(
|
const [date, setDate] = useState<DateRange | undefined>(
|
||||||
selectDateRange || { from: today, to: today },
|
selectDateRange || { from: today, to: today },
|
||||||
);
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
setDate(selectDateRange);
|
||||||
|
}, [selectDateRange]);
|
||||||
const onChange = (e: DateRange | undefined) => {
|
const onChange = (e: DateRange | undefined) => {
|
||||||
if (!e) return;
|
if (!e) return;
|
||||||
setDate(e);
|
setDate(e);
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export enum MessageEventType {
|
|||||||
export interface IAnswerEvent<T> {
|
export interface IAnswerEvent<T> {
|
||||||
event: MessageEventType;
|
event: MessageEventType;
|
||||||
message_id: string;
|
message_id: string;
|
||||||
|
session_id: string;
|
||||||
created_at: number;
|
created_at: number;
|
||||||
task_id: string;
|
task_id: string;
|
||||||
data: T;
|
data: T;
|
||||||
|
|||||||
@ -180,6 +180,7 @@ export const useSendAgentMessage = (
|
|||||||
const { id: agentId } = useParams();
|
const { id: agentId } = useParams();
|
||||||
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
const { handleInputChange, value, setValue } = useHandleMessageInputChange();
|
||||||
const inputs = useSelectBeginNodeDataInputs();
|
const inputs = useSelectBeginNodeDataInputs();
|
||||||
|
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||||
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
const { send, answerList, done, stopOutputMessage } = useSendMessageBySSE(
|
||||||
url || api.runCanvas,
|
url || api.runCanvas,
|
||||||
);
|
);
|
||||||
@ -187,6 +188,12 @@ export const useSendAgentMessage = (
|
|||||||
return answerList[0]?.message_id;
|
return answerList[0]?.message_id;
|
||||||
}, [answerList]);
|
}, [answerList]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (answerList[0]?.session_id) {
|
||||||
|
setSessionId(answerList[0]?.session_id);
|
||||||
|
}
|
||||||
|
}, [answerList]);
|
||||||
|
|
||||||
const { findReferenceByMessageId } = useFindMessageReference(answerList);
|
const { findReferenceByMessageId } = useFindMessageReference(answerList);
|
||||||
const prologue = useGetBeginNodePrologue();
|
const prologue = useGetBeginNodePrologue();
|
||||||
const {
|
const {
|
||||||
@ -222,6 +229,8 @@ export const useSendAgentMessage = (
|
|||||||
params.inputs = transferInputsArrayToObject(query); // begin operator inputs
|
params.inputs = transferInputsArrayToObject(query); // begin operator inputs
|
||||||
|
|
||||||
params.files = uploadResponseList;
|
params.files = uploadResponseList;
|
||||||
|
|
||||||
|
params.session_id = sessionId;
|
||||||
}
|
}
|
||||||
const res = await send(params);
|
const res = await send(params);
|
||||||
|
|
||||||
@ -239,6 +248,7 @@ export const useSendAgentMessage = (
|
|||||||
},
|
},
|
||||||
[
|
[
|
||||||
agentId,
|
agentId,
|
||||||
|
sessionId,
|
||||||
send,
|
send,
|
||||||
inputs,
|
inputs,
|
||||||
uploadResponseList,
|
uploadResponseList,
|
||||||
|
|||||||
@ -43,19 +43,13 @@ export function useCacheChatLog() {
|
|||||||
setEventList([]);
|
setEventList([]);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addEventList = useCallback(
|
const addEventList = useCallback((events: IEventList, message_id: string) => {
|
||||||
(events: IEventList, message_id: string) => {
|
setEventList((x) => {
|
||||||
const nextList = [...eventList];
|
const list = [...x, ...events];
|
||||||
events.forEach((x) => {
|
setMessageIdPool((prev) => ({ ...prev, [message_id]: list }));
|
||||||
if (nextList.every((y) => y !== x)) {
|
return list;
|
||||||
nextList.push(x);
|
});
|
||||||
}
|
}, []);
|
||||||
});
|
|
||||||
setEventList(nextList);
|
|
||||||
setMessageIdPool((prev) => ({ ...prev, [message_id]: nextList }));
|
|
||||||
},
|
|
||||||
[eventList],
|
|
||||||
);
|
|
||||||
|
|
||||||
const currentEventListWithoutMessage = useMemo(() => {
|
const currentEventListWithoutMessage = useMemo(() => {
|
||||||
const list = messageIdPool[currentMessageId]?.filter(
|
const list = messageIdPool[currentMessageId]?.filter(
|
||||||
|
|||||||
@ -18,16 +18,26 @@ import { JsonViewer } from './workFlowTimeline';
|
|||||||
|
|
||||||
const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
const ToolTimelineItem = ({ tools }: { tools: Record<string, any>[] }) => {
|
||||||
if (!tools || tools.length === 0 || !Array.isArray(tools)) return null;
|
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(
|
const filteredTools = tools.filter(
|
||||||
(tool) => !blackList.includes(tool.tool_name),
|
(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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{filteredTools?.map((tool, idx) => {
|
{filteredTools?.map((tool, idx) => {
|
||||||
return (
|
return (
|
||||||
<TimelineItem
|
<TimelineItem
|
||||||
key={idx}
|
key={'tool_' + idx}
|
||||||
step={idx}
|
step={idx}
|
||||||
className="group-data-[orientation=vertical]/timeline:ms-10 group-data-[orientation=vertical]/timeline:not-last:pb-8"
|
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()}>
|
<AccordionItem value={idx.toString()}>
|
||||||
<AccordionTrigger>
|
<AccordionTrigger>
|
||||||
<div className="flex gap-2 items-center">
|
<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">
|
<span className="text-text-sub-title text-xs">
|
||||||
{/* 0:00
|
{/* 0:00
|
||||||
{x.data.elapsed_time?.toString().slice(0, 6)} */}
|
{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 { Spin } from '@/components/ui/spin';
|
||||||
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks';
|
||||||
import { useFetchAgentLog } from '@/hooks/use-agent-request';
|
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 React, { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
import { DateRange } from '../../components/originui/calendar/index';
|
import { DateRange } from '../../components/originui/calendar/index';
|
||||||
@ -26,16 +31,27 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from '../../components/ui/table';
|
} from '../../components/ui/table';
|
||||||
import { useFetchDataOnMount } from '../agent/hooks/use-fetch-data';
|
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 AgentLogPage: React.FC = () => {
|
||||||
const { navigateToAgentList, navigateToAgent } = useNavigatePage();
|
const { navigateToAgentList, navigateToAgent } = useNavigatePage();
|
||||||
const { flowDetail: agentDetail } = useFetchDataOnMount();
|
const { flowDetail: agentDetail } = useFetchDataOnMount();
|
||||||
const { id: canvasId } = useParams();
|
const { id: canvasId } = useParams();
|
||||||
const today = new Date();
|
const queryClient = useQueryClient();
|
||||||
const init = {
|
const init = {
|
||||||
keywords: '',
|
keywords: '',
|
||||||
from_date: today,
|
from_date: getStartOfToday(),
|
||||||
to_date: today,
|
to_date: getEndOfToday(),
|
||||||
orderby: 'create_time',
|
orderby: 'create_time',
|
||||||
desc: false,
|
desc: false,
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -152,6 +168,13 @@ const AgentLogPage: React.FC = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClickSearch = () => {
|
||||||
|
setPagination({ ...pagination, current: 1 });
|
||||||
|
handleSearch();
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ['fetchAgentLog'],
|
||||||
|
});
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
handleSearch();
|
handleSearch();
|
||||||
}, [pagination.current, pagination.pageSize, sortConfig]);
|
}, [pagination.current, pagination.pageSize, sortConfig]);
|
||||||
@ -166,7 +189,17 @@ const AgentLogPage: React.FC = () => {
|
|||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
setSearchParams(init);
|
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 (
|
return (
|
||||||
<div className=" text-white">
|
<div className=" text-white">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
@ -209,15 +242,14 @@ const AgentLogPage: React.FC = () => {
|
|||||||
<span className="whitespace-nowrap">Latest Date</span>
|
<span className="whitespace-nowrap">Latest Date</span>
|
||||||
<TimeRangePicker
|
<TimeRangePicker
|
||||||
onSelect={handleDateRangeChange}
|
onSelect={handleDateRangeChange}
|
||||||
selectDateRange={{ from: currentDate.from, to: currentDate.to }}
|
selectDateRange={currentDate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-foreground text-text-title-invert px-4 py-1 rounded"
|
className="bg-foreground text-text-title-invert px-4 py-1 rounded"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setPagination({ ...pagination, current: 1 });
|
handleClickSearch();
|
||||||
handleSearch();
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Search
|
Search
|
||||||
@ -276,7 +308,12 @@ const AgentLogPage: React.FC = () => {
|
|||||||
)}
|
)}
|
||||||
{!loading &&
|
{!loading &&
|
||||||
data?.map((item) => (
|
data?.map((item) => (
|
||||||
<TableRow key={item.id}>
|
<TableRow
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => {
|
||||||
|
showLogDetail(item);
|
||||||
|
}}
|
||||||
|
>
|
||||||
{columns.map((column) => (
|
{columns.map((column) => (
|
||||||
<TableCell key={column.dataIndex}>
|
<TableCell key={column.dataIndex}>
|
||||||
{column.render
|
{column.render
|
||||||
@ -312,6 +349,12 @@ const AgentLogPage: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<AgentLogDetailModal
|
||||||
|
isOpen={openModal}
|
||||||
|
message={modalData?.message as IAgentLogMessage[]}
|
||||||
|
reference={modalData?.reference as unknown as IReferenceObject}
|
||||||
|
onClose={() => setOpenModal(false)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -91,55 +91,63 @@ const ChatContainer = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="h-[100vh]">
|
<section className="h-[100vh] flex justify-center items-center">
|
||||||
<section className={cn('flex flex-1 flex-col p-2.5 h-full')}>
|
<div className=" w-[80vw]">
|
||||||
<div className={cn('flex flex-1 flex-col overflow-auto pr-2')}>
|
<div className="flex flex-1 flex-col p-2.5 h-[90vh] border rounded-lg">
|
||||||
<div>
|
<div
|
||||||
{derivedMessages?.map((message, i) => {
|
className={cn('flex flex-1 flex-col overflow-auto m-auto w-5/6')}
|
||||||
return (
|
>
|
||||||
<MessageItem
|
<div>
|
||||||
visibleAvatar={visibleAvatar}
|
{derivedMessages?.map((message, i) => {
|
||||||
conversationId={conversationId}
|
return (
|
||||||
currentEventListWithoutMessageById={
|
<MessageItem
|
||||||
currentEventListWithoutMessageById
|
visibleAvatar={visibleAvatar}
|
||||||
}
|
conversationId={conversationId}
|
||||||
setCurrentMessageId={setCurrentMessageId}
|
currentEventListWithoutMessageById={
|
||||||
key={buildMessageUuidWithRole(message)}
|
currentEventListWithoutMessageById
|
||||||
avatarDialog={avatarData.avatar}
|
}
|
||||||
item={message}
|
setCurrentMessageId={setCurrentMessageId}
|
||||||
nickname="You"
|
key={buildMessageUuidWithRole(message)}
|
||||||
reference={findReferenceByMessageId(message.id)}
|
avatarDialog={avatarData.avatar}
|
||||||
loading={
|
item={message}
|
||||||
message.role === MessageType.Assistant &&
|
nickname="You"
|
||||||
sendLoading &&
|
reference={findReferenceByMessageId(message.id)}
|
||||||
derivedMessages?.length - 1 === i
|
loading={
|
||||||
}
|
message.role === MessageType.Assistant &&
|
||||||
index={i}
|
sendLoading &&
|
||||||
clickDocumentButton={clickDocumentButton}
|
derivedMessages?.length - 1 === i
|
||||||
showLikeButton={false}
|
}
|
||||||
showLoudspeaker={false}
|
index={i}
|
||||||
showLog={false}
|
clickDocumentButton={clickDocumentButton}
|
||||||
sendLoading={sendLoading}
|
showLikeButton={false}
|
||||||
></MessageItem>
|
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>
|
||||||
<div ref={ref} />
|
|
||||||
</div>
|
</div>
|
||||||
<NextMessageInput
|
</div>
|
||||||
isShared
|
|
||||||
value={value}
|
|
||||||
disabled={hasError}
|
|
||||||
sendDisabled={sendDisabled}
|
|
||||||
conversationId={conversationId}
|
|
||||||
onInputChange={handleInputChange}
|
|
||||||
onPressEnter={handlePressEnter}
|
|
||||||
sendLoading={sendLoading}
|
|
||||||
stopOutputMessage={stopOutputMessage}
|
|
||||||
onUpload={handleUploadFile}
|
|
||||||
isUploading={loading}
|
|
||||||
></NextMessageInput>
|
|
||||||
</section>
|
|
||||||
{visible && (
|
{visible && (
|
||||||
<PdfDrawer
|
<PdfDrawer
|
||||||
visible={visible}
|
visible={visible}
|
||||||
|
|||||||
Reference in New Issue
Block a user