Pārlūkot izejas kodu

Feat: Render the uploaded agent message file #3221 (#9081)

### What problem does this PR solve?

Feat: Render the uploaded agent message file #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
balibabu pirms 3 mēnešiem
vecāks
revīzija
7e7619bdc0
Revīzijas autora e-pasta adrese nav piesaistīta nevienam kontam

+ 10
- 78
web/src/components/next-message-item/index.tsx Parādīt failu

@@ -12,28 +12,20 @@ import {
useState,
} from 'react';

import {
useFetchDocumentInfosByIds,
useFetchDocumentThumbnailsByIds,
} from '@/hooks/document-hooks';
import { IRegenerateMessage, IRemoveMessageById } from '@/hooks/logic-hooks';
import { INodeEvent } from '@/hooks/use-send-message';
import { cn } from '@/lib/utils';
import { WorkFlowTimeline } from '@/pages/agent/log-sheet/workFlowTimeline';
import { IMessage } from '@/pages/chat/interface';
import { getExtension, isImage } from '@/utils/document-util';
import { Avatar, Button, Flex, List, Space, Typography } from 'antd';
import { isEmpty } from 'lodash';
import FileIcon from '../file-icon';
import IndentedTreeModal from '../indented-tree/modal';
import NewDocumentLink from '../new-document-link';
import MarkdownContent from '../next-markdown-content';
import { RAGFlowAvatar } from '../ragflow-avatar';
import { useTheme } from '../theme-provider';
import { AssistantGroupButton, UserGroupButton } from './group-button';
import styles from './index.less';
import { ReferenceDocumentList } from './reference-document-list';

const { Text } = Typography;
import { UploadedMessageFiles } from './uploaded-message-files';

interface IProps
extends Partial<IRemoveMessageById>,
@@ -79,9 +71,6 @@ function MessageItem({
const { theme } = useTheme();
const isAssistant = item.role === MessageType.Assistant;
const isUser = item.role === MessageType.User;
const { data: documentList, setDocumentIds } = useFetchDocumentInfosByIds();
const { data: documentThumbnails, setDocumentIds: setIds } =
useFetchDocumentThumbnailsByIds();
const { visible, hideModal, showModal } = useSetModalState();
const [clickedDocumentId, setClickedDocumentId] = useState('');

@@ -91,29 +80,10 @@ function MessageItem({
return Object.values(docs);
}, [reference?.doc_aggs]);

const handleUserDocumentClick = useCallback(
(id: string) => () => {
setClickedDocumentId(id);
showModal();
},
[showModal],
);

const handleRegenerateMessage = useCallback(() => {
regenerateMessage?.(item);
}, [regenerateMessage, item]);

useEffect(() => {
const ids = item?.doc_ids ?? [];
if (ids.length) {
setDocumentIds(ids);
const documentIds = ids.filter((x) => !(x in documentThumbnails));
if (documentIds.length) {
setIds(documentIds);
}
}
}, [item.doc_ids, setDocumentIds, setIds, documentThumbnails]);

useEffect(() => {
if (typeof setCurrentMessageId === 'function') {
setCurrentMessageId(item.id);
@@ -139,15 +109,15 @@ function MessageItem({
>
{visibleAvatar &&
(item.role === MessageType.User ? (
<Avatar size={40} src={avatar ?? '/logo.svg'} />
<RAGFlowAvatar avatar={avatar ?? '/logo.svg'} />
) : avatarDialog ? (
<Avatar size={40} src={avatarDialog} />
<RAGFlowAvatar avatar={avatarDialog} />
) : (
<AssistantIcon />
))}

<Flex vertical gap={8} flex={1}>
<Space>
<section className="flex-col gap-2 flex-1">
<div className="space-x-1">
{isAssistant ? (
<AssistantGroupButton
messageId={item.id}
@@ -171,7 +141,7 @@ function MessageItem({
)}

{/* <b>{isAssistant ? '' : nickname}</b> */}
</Space>
</div>
<div
className={cn({
[theme === 'dark'
@@ -208,48 +178,10 @@ function MessageItem({
canvasId={conversationId}
/>
)}
{isUser && documentList.length > 0 && (
<List
bordered
dataSource={documentList}
renderItem={(item) => {
// TODO:
// const fileThumbnail =
// documentThumbnails[item.id] || documentThumbnails[item.id];
const fileExtension = getExtension(item.name);
return (
<List.Item>
<Flex gap={'small'} align="center">
<FileIcon id={item.id} name={item.name}></FileIcon>

{isImage(fileExtension) ? (
<NewDocumentLink
documentId={item.id}
documentName={item.name}
prefix="document"
>
{item.name}
</NewDocumentLink>
) : (
<Button
type={'text'}
onClick={handleUserDocumentClick(item.id)}
>
<Text
style={{ maxWidth: '40vw' }}
ellipsis={{ tooltip: item.name }}
>
{item.name}
</Text>
</Button>
)}
</Flex>
</List.Item>
);
}}
/>
{isUser && (
<UploadedMessageFiles files={item.files}></UploadedMessageFiles>
)}
</Flex>
</section>
</div>
</section>
{visible && (

+ 36
- 0
web/src/components/next-message-item/uploaded-message-files.tsx Parādīt failu

@@ -0,0 +1,36 @@
import { getExtension } from '@/utils/document-util';
import { formatBytes } from '@/utils/file-util';
import { memo } from 'react';
import SvgIcon from '../svg-icon';

interface IProps {
files?: File[];
}
export function InnerUploadedMessageFiles({ files = [] }: IProps) {
return (
<section className="flex gap-2 pt-2">
{files?.map((file, idx) => (
<div key={idx} className="flex gap-1 border rounded-md p-1.5">
{file.type.startsWith('image/') ? (
<img
src={URL.createObjectURL(file)}
alt={file.name}
className="size-10 object-cover"
/>
) : (
<SvgIcon
name={`file-icon/${getExtension(file.name)}`}
width={24}
></SvgIcon>
)}
<div className="text-xs max-w-20">
<div className="truncate">{file.name}</div>
<p className="text-text-sub-title pt-1">{formatBytes(file.size)}</p>
</div>
</div>
))}
</section>
);
}

export const UploadedMessageFiles = memo(InnerUploadedMessageFiles);

+ 1
- 0
web/src/interfaces/database/chat.ts Parādīt failu

@@ -74,6 +74,7 @@ export interface Message {
id?: string;
audio_binary?: string;
data?: any;
files?: File[];
}

export interface IReferenceChunk {

+ 1
- 1
web/src/pages/agent/chat/box.tsx Parādīt failu

@@ -76,7 +76,7 @@ const AgentChatBox = () => {
useCallback(
async (files, options) => {
const ret = await uploadCanvasFile({ files, options });
appendUploadResponseList(ret.data);
appendUploadResponseList(ret.data, files);
},
[appendUploadResponseList, uploadCanvasFile],
);

+ 14
- 8
web/src/pages/agent/chat/use-send-agent-message.ts Parādīt failu

@@ -152,17 +152,21 @@ export function useSetUploadResponseData() {
const [uploadResponseList, setUploadResponseList] = useState<
UploadResponseDataType[]
>([]);
const [fileList, setFileList] = useState<File[]>([]);

const append = useCallback((data: UploadResponseDataType) => {
const append = useCallback((data: UploadResponseDataType, files: File[]) => {
setUploadResponseList((prev) => [...prev, data]);
setFileList((pre) => [...pre, ...files]);
}, []);

const clear = useCallback(() => {
setUploadResponseList([]);
setFileList([]);
}, []);

return {
uploadResponseList,
fileList,
setUploadResponseList,
appendUploadResponseList: append,
clearUploadResponseList: clear,
@@ -198,6 +202,7 @@ export const useSendAgentMessage = (
appendUploadResponseList,
clearUploadResponseList,
uploadResponseList,
fileList,
} = useSetUploadResponseData();

const sendMessage = useCallback(
@@ -259,18 +264,19 @@ export const useSendAgentMessage = (
const handlePressEnter = useCallback(() => {
if (trim(value) === '') return;
const id = uuid();
const msgBody = {
id,
content: value.trim(),
role: MessageType.User,
};
if (done) {
setValue('');
sendMessage({
message: { id, content: value.trim(), role: MessageType.User },
message: msgBody,
});
}
addNewestOneQuestion({
content: value,
id,
role: MessageType.User,
});
}, [value, done, addNewestOneQuestion, setValue, sendMessage]);
addNewestOneQuestion({ ...msgBody, files: fileList });
}, [value, done, addNewestOneQuestion, fileList, setValue, sendMessage]);

useEffect(() => {
const { content, id } = findMessageFromList(answerList);

+ 1
- 3
web/src/pages/next-chats/share/index.tsx Parādīt failu

@@ -36,8 +36,6 @@ const ChatContainer = () => {
addEventList,
setCurrentMessageId,
currentEventListWithoutMessageById,
clearEventList,
currentMessageId,
} = useCacheChatLog();
const {
handlePressEnter,
@@ -76,7 +74,7 @@ const ChatContainer = () => {
useCallback(
async (files, options) => {
const ret = await uploadCanvasFile({ files, options });
appendUploadResponseList(ret.data);
appendUploadResponseList(ret.data, files);
},
[appendUploadResponseList, uploadCanvasFile],
);

Notiek ielāde…
Atcelt
Saglabāt