浏览代码

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

+ 9
- 7
web/src/components/next-message-item/index.tsx 查看文件

@@ -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>

+ 3
- 0
web/src/components/originui/time-range-picker.tsx 查看文件

@@ -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);

+ 1
- 0
web/src/hooks/use-send-message.ts 查看文件

@@ -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;

+ 10
- 0
web/src/pages/agent/chat/use-send-agent-message.ts 查看文件

@@ -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,

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

@@ -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(

+ 16
- 3
web/src/pages/agent/log-sheet/toolTimelineItem.tsx 查看文件

@@ -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
- 0
web/src/pages/agents/agent-log-detail-modal.tsx 查看文件

@@ -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>
);
};

+ 51
- 8
web/src/pages/agents/agent-log-page.tsx 查看文件

@@ -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>
);
};

+ 55
- 47
web/src/pages/next-chats/share/index.tsx 查看文件

@@ -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}

正在加载...
取消
保存