Sfoglia il codice sorgente

fix: remove duplicate MessageItem #1289 (#1566)

### What problem does this PR solve?

fix: remove duplicate MessageItem #1289

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
tags/v0.9.0
balibabu 1 anno fa
parent
commit
fe5dd5b70a
Nessun account collegato all'indirizzo email del committer

+ 20
- 0
web/src/components/message-item/index.less Vedi File

background-color: rgba(249, 250, 251, 1); background-color: rgba(249, 250, 251, 1);
word-break: break-all; word-break: break-all;
} }
.messageTextBase() {
padding: 6px 10px;
border-radius: 8px;
& > p {
margin: 0;
}
}
.messageText {
.chunkText();
.messageTextBase();
background-color: #e6f4ff;
word-break: break-all;
}
.messageUserText {
.chunkText();
.messageTextBase();
background-color: rgb(248, 247, 247);
word-break: break-all;
text-align: justify;
}
.messageEmpty { .messageEmpty {
width: 300px; width: 300px;
} }

+ 21
- 14
web/src/components/message-item/index.tsx Vedi File

import { useTranslate } from '@/hooks/commonHooks'; import { useTranslate } from '@/hooks/commonHooks';
import { useGetDocumentUrl } from '@/hooks/documentHooks'; import { useGetDocumentUrl } from '@/hooks/documentHooks';
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook'; import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
import { useSelectUserInfo } from '@/hooks/userSettingHook';
import { IReference, Message } from '@/interfaces/database/chat'; import { IReference, Message } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge'; import { IChunk } from '@/interfaces/database/knowledge';
import classNames from 'classnames'; import classNames from 'classnames';
import SvgIcon from '../svg-icon'; import SvgIcon from '../svg-icon';
import styles from './index.less'; import styles from './index.less';


interface IProps {
item: Message;
reference: IReference;
loading?: boolean;
nickname?: string;
avatar?: string;
clickDocumentButton?: (documentId: string, chunk: IChunk) => void;
}

const MessageItem = ({ const MessageItem = ({
item, item,
reference, reference,
loading = false, loading = false,
avatar = '',
nickname = '',
clickDocumentButton, clickDocumentButton,
}: {
item: Message;
reference: IReference;
loading?: boolean;
clickDocumentButton: (documentId: string, chunk: IChunk) => void;
}) => {
const userInfo = useSelectUserInfo();
}: IProps) => {
const isAssistant = item.role === MessageType.Assistant;
const { t } = useTranslate('chat');
const fileThumbnails = useSelectFileThumbnails(); const fileThumbnails = useSelectFileThumbnails();
const getDocumentUrl = useGetDocumentUrl(); const getDocumentUrl = useGetDocumentUrl();
const { t } = useTranslate('chat');

const isAssistant = item.role === MessageType.Assistant;


const referenceDocumentList = useMemo(() => { const referenceDocumentList = useMemo(() => {
return reference?.doc_aggs ?? []; return reference?.doc_aggs ?? [];
<Avatar <Avatar
size={40} size={40}
src={ src={
userInfo.avatar ??
avatar ??
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png' 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
} }
/> />
<AssistantIcon></AssistantIcon> <AssistantIcon></AssistantIcon>
)} )}
<Flex vertical gap={8} flex={1}> <Flex vertical gap={8} flex={1}>
<b>{isAssistant ? '' : userInfo.nickname}</b>
<div className={styles.messageText}>
<b>{isAssistant ? '' : nickname}</b>
<div
className={
isAssistant ? styles.messageText : styles.messageUserText
}
>
<MarkdownContent <MarkdownContent
content={content} content={content}
reference={reference} reference={reference}

+ 1
- 1
web/src/hooks/flow-hooks.ts Vedi File

dsl?: DSL; dsl?: DSL;
avatar?: string; avatar?: string;
}) => { }) => {
const { data } = await flowService.setCanvas(params);
const { data = {} } = await flowService.setCanvas(params);
if (data.retcode === 0) { if (data.retcode === 0) {
message.success( message.success(
i18n.t(`message.${params?.id ? 'modified' : 'created'}`), i18n.t(`message.${params?.id ? 'modified' : 'created'}`),

+ 4
- 1
web/src/hooks/logicHooks.ts Vedi File

const d = val?.data; const d = val?.data;
if (typeof d !== 'boolean') { if (typeof d !== 'boolean') {
console.info('data:', d); console.info('data:', d);
setAnswer(d);
setAnswer({
...d,
conversationId: body?.conversation_id,
});
} }
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);

+ 1
- 0
web/src/interfaces/database/chat.ts Vedi File

export interface IAnswer { export interface IAnswer {
answer: string; answer: string;
reference: IReference; reference: IReference;
conversationId?: string;
} }


export interface Docagg { export interface Docagg {

+ 0
- 79
web/src/pages/chat/chat-container/index.less Vedi File

padding-right: 24px; padding-right: 24px;
} }
} }

.messageItem {
padding: 24px 0;
.messageItemSection {
display: inline-block;
}
.messageItemSectionLeft {
width: 70%;
}
.messageItemSectionRight {
width: 40%;
}
.messageItemContent {
display: inline-flex;
gap: 20px;
}
.messageItemContentReverse {
flex-direction: row-reverse;
}
.messageTextBase() {
padding: 6px 10px;
border-radius: 8px;
& > p {
margin: 0;
}
}
.messageText {
.chunkText();
.messageTextBase();
background-color: #e6f4ff;
word-break: break-all;
}
.messageUserText {
.chunkText();
.messageTextBase();
background-color: rgb(248, 247, 247);
word-break: break-all;
text-align: justify;
}
.messageEmpty {
width: 300px;
}
// .referenceIcon {
// padding: 0 6px;
// }
.thumbnailImg {
max-width: 20px;
}
}

.messageItemLeft {
text-align: left;
}

.messageItemRight {
text-align: right;
}

// .referencePopoverWrapper {
// max-width: 50vw;
// }

// .referenceChunkImage {
// width: 10vw;
// object-fit: contain;
// }

// .referenceImagePreview {
// max-width: 45vw;
// max-height: 45vh;
// }
// .chunkContentText {
// .chunkText;
// max-height: 45vh;
// overflow-y: auto;
// }
// .documentLink {
// padding: 0;
// }

+ 10
- 125
web/src/pages/chat/chat-container/index.tsx Vedi File

import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import NewDocumentLink from '@/components/new-document-link';
import MessageItem from '@/components/message-item';
import DocumentPreviewer from '@/components/pdf-previewer'; import DocumentPreviewer from '@/components/pdf-previewer';
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
import { useSelectUserInfo } from '@/hooks/userSettingHook';
import { IReference, Message } from '@/interfaces/database/chat';
import { IChunk } from '@/interfaces/database/knowledge';
import { Avatar, Button, Drawer, Flex, Input, List, Spin } from 'antd';
import classNames from 'classnames';
import { useMemo } from 'react';
import { useTranslate } from '@/hooks/commonHooks';
import { Button, Drawer, Flex, Input, Spin } from 'antd';
import { import {
useClickDrawer, useClickDrawer,
useFetchConversationOnMount, useFetchConversationOnMount,
useGetFileIcon, useGetFileIcon,
useGetSendButtonDisabled, useGetSendButtonDisabled,
useSelectConversationLoading, useSendButtonDisabled,
useSelectConversationLoading,
useSendButtonDisabled,
useSendMessage, useSendMessage,
} from '../hooks'; } from '../hooks';
import MarkdownContent from '../markdown-content';

import SvgIcon from '@/components/svg-icon';
import { useTranslate } from '@/hooks/commonHooks';
import { useGetDocumentUrl } from '@/hooks/documentHooks';
import { getExtension, isPdf } from '@/utils/documentUtils';
import { buildMessageItemReference } from '../utils'; import { buildMessageItemReference } from '../utils';
import styles from './index.less';

const MessageItem = ({
item,
reference,
loading = false,
clickDocumentButton,
}: {
item: Message;
reference: IReference;
loading?: boolean;
clickDocumentButton: (documentId: string, chunk: IChunk) => void;
}) => {
const userInfo = useSelectUserInfo();
const fileThumbnails = useSelectFileThumbnails();
const getDocumentUrl = useGetDocumentUrl();
const { t } = useTranslate('chat');

const isAssistant = item.role === MessageType.Assistant;

const referenceDocumentList = useMemo(() => {
return reference?.doc_aggs ?? [];
}, [reference?.doc_aggs]);


const content = useMemo(() => {
let text = item.content;
if (text === '') {
text = t('searching');
}
return loading ? text?.concat('~~2$$') : text;
}, [item.content, loading, t]);

return (
<div
className={classNames(styles.messageItem, {
[styles.messageItemLeft]: item.role === MessageType.Assistant,
[styles.messageItemRight]: item.role === MessageType.User,
})}
>
<section
className={classNames(styles.messageItemSection, {
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
[styles.messageItemSectionRight]: item.role === MessageType.User,
})}
>
<div
className={classNames(styles.messageItemContent, {
[styles.messageItemContentReverse]: item.role === MessageType.User,
})}
>
{item.role === MessageType.User ? (
<Avatar
size={40}
src={
userInfo.avatar ??
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
}
/>
) : (
<AssistantIcon></AssistantIcon>
)}
<Flex vertical gap={8} flex={1}>
<b>{isAssistant ? '' : userInfo.nickname}</b>
<div className={isAssistant ? styles.messageText : styles.messageUserText}>
<MarkdownContent
content={content}
reference={reference}
clickDocumentButton={clickDocumentButton}
></MarkdownContent>
</div>
{isAssistant && referenceDocumentList.length > 0 && (
<List
bordered
dataSource={referenceDocumentList}
renderItem={(item) => {
const fileThumbnail = fileThumbnails[item.doc_id];
const fileExtension = getExtension(item.doc_name);
return (
<List.Item>
<Flex gap={'small'} align="center">
{fileThumbnail ? (
<img
src={fileThumbnail}
className={styles.thumbnailImg}
></img>
) : (
<SvgIcon
name={`file-icon/${fileExtension}`}
width={24}
></SvgIcon>
)}

<NewDocumentLink
link={getDocumentUrl(item.doc_id)}
preventDefault={!isPdf(item.doc_name)}
>
{item.doc_name}
</NewDocumentLink>
</Flex>
</List.Item>
);
}}
/>
)}
</Flex>
</div>
</section>
</div>
);
};
import { useSelectUserInfo } from '@/hooks/userSettingHook';
import styles from './index.less';


const ChatContainer = () => { const ChatContainer = () => {
const { const {
useGetFileIcon(); useGetFileIcon();
const loading = useSelectConversationLoading(); const loading = useSelectConversationLoading();
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const userInfo = useSelectUserInfo();


return ( return (
<> <>
} }
key={message.id} key={message.id}
item={message} item={message}
nickname={userInfo.nickname}
avatar={userInfo.avatar}
reference={buildMessageItemReference(conversation, message)} reference={buildMessageItemReference(conversation, message)}
clickDocumentButton={clickDocumentButton} clickDocumentButton={clickDocumentButton}
></MessageItem> ></MessageItem>

+ 4
- 15
web/src/pages/chat/hooks.ts Vedi File



return pre; return pre;
}); });
}, [conversationList, dialogId, prologue]);
}, [conversationList, dialogId, prologue, t]);


useEffect(() => { useEffect(() => {
addTemporaryConversation(); addTemporaryConversation();
role: MessageType.Assistant, role: MessageType.Assistant,
content: answer, content: answer,
id: uuid(), id: uuid(),
reference: [],
reference: {},
} as IMessage, } as IMessage,
], ],
}; };
}, []); }, []);


const removeLatestMessage = useCallback(() => { const removeLatestMessage = useCallback(() => {
console.info('removeLatestMessage');
setCurrentConversation((pre) => { setCurrentConversation((pre) => {
const nextMessages = pre.message?.slice(0, -2) ?? []; const nextMessages = pre.message?.slice(0, -2) ?? [];
return { return {
const ref = useRef<HTMLDivElement>(null); const ref = useRef<HTMLDivElement>(null);


const scrollToBottom = useCallback(() => { const scrollToBottom = useCallback(() => {
console.info('useScrollToBottom');
if (currentConversation.id) { if (currentConversation.id) {
ref.current?.scrollIntoView({ behavior: 'instant' }); ref.current?.scrollIntoView({ behavior: 'instant' });
} }
[ [
conversation?.message, conversation?.message,
conversationId, conversationId,
// fetchConversation,
handleClickConversation, handleClickConversation,
removeLatestMessage, removeLatestMessage,
setValue, setValue,
); );


useEffect(() => { useEffect(() => {
if (answer.answer) {
if (answer.answer && answer?.conversationId === conversationId) {
addNewestAnswer(answer); addNewestAnswer(answer);
console.info('true?');
console.info('send msg:', answer.answer);
} }
}, [answer, addNewestAnswer]);
}, [answer, addNewestAnswer, conversationId]);


const handlePressEnter = useCallback(() => { const handlePressEnter = useCallback(() => {
if (trim(value) === '') return; if (trim(value) === '') return;
}; };


export const useGetFileIcon = () => { export const useGetFileIcon = () => {
// const req = require.context('@/assets/svg/file-icon');
// const ret = req.keys().map(req);
// console.info(ret);
// useEffect(() => {}, []);

const getFileIcon = (filename: string) => { const getFileIcon = (filename: string) => {
const ext: string = getFileExtension(filename); const ext: string = getFileExtension(filename);
const iconPath = fileIconMap[ext as keyof typeof fileIconMap]; const iconPath = fileIconMap[ext as keyof typeof fileIconMap];
// const x = require(`@/assets/svg/file-icon/${iconPath}`);
return `@/assets/svg/file-icon/${iconPath}`; return `@/assets/svg/file-icon/${iconPath}`;
}; };



+ 2
- 2
web/src/pages/chat/markdown-content/index.tsx Vedi File

}: { }: {
content: string; content: string;
reference: IReference; reference: IReference;
clickDocumentButton: (documentId: string, chunk: IChunk) => void;
clickDocumentButton?: (documentId: string, chunk: IChunk) => void;
}) => { }) => {
const fileThumbnails = useSelectFileThumbnails(); const fileThumbnails = useSelectFileThumbnails();


if (!isPdf) { if (!isPdf) {
return; return;
} }
clickDocumentButton(documentId, chunk);
clickDocumentButton?.(documentId, chunk);
}, },
[clickDocumentButton], [clickDocumentButton],
); );

+ 0
- 54
web/src/pages/chat/share/index.less Vedi File

padding-right: 6px; padding-right: 6px;
} }
} }

.messageItem {
padding: 24px 0;
.messageItemSection {
display: inline-block;
}
.messageItemSectionLeft {
width: 70%;
}
.messageItemSectionRight {
width: 40%;
}
.messageItemContent {
display: inline-flex;
gap: 20px;
}
.messageItemContentReverse {
flex-direction: row-reverse;
}
.messageTextBase() {
padding: 6px 10px;
border-radius: 8px;
& > p {
margin: 0;
}
}
.messageText {
.chunkText();
.messageTextBase();
background-color: #e6f4ff;
word-break: break-all;
}
.messageUserText {
.chunkText();
.messageTextBase();
background-color: rgb(248, 247, 247);
word-break: break-all;
text-align: justify;
}
.messageEmpty {
width: 300px;
}
.thumbnailImg {
max-width: 20px;
}
}

.messageItemLeft {
text-align: left;
}

.messageItemRight {
text-align: right;
}

+ 6
- 117
web/src/pages/chat/share/large.tsx Vedi File

import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg';
import MessageItem from '@/components/message-item';
import { MessageType } from '@/constants/chat'; import { MessageType } from '@/constants/chat';
import { useTranslate } from '@/hooks/commonHooks'; import { useTranslate } from '@/hooks/commonHooks';
import { IReference, Message } from '@/interfaces/database/chat';
import { Avatar, Button, Flex, Input, List, Spin } from 'antd';
import classNames from 'classnames';

import NewDocumentLink from '@/components/new-document-link';
import SvgIcon from '@/components/svg-icon';
import { useGetDocumentUrl } from '@/hooks/documentHooks';
import { useSelectFileThumbnails } from '@/hooks/knowledgeHook';
import { getExtension, isPdf } from '@/utils/documentUtils';
import { forwardRef, useMemo } from 'react';
import MarkdownContent from '../markdown-content';
import { useSendButtonDisabled } from '@/pages/chat/hooks';
import { Button, Flex, Input, Spin } from 'antd';
import { forwardRef } from 'react';
import { import {
useCreateSharedConversationOnMount, useCreateSharedConversationOnMount,
useSelectCurrentSharedConversation, useSelectCurrentSharedConversation,
useSendSharedMessage, useSendSharedMessage,
} from '../shared-hooks'; } from '../shared-hooks';
import { buildMessageItemReference } from '../utils'; import { buildMessageItemReference } from '../utils';
import styles from './index.less';
import {useSendButtonDisabled} from "@/pages/chat/hooks";

const MessageItem = ({
item,
reference,
loading = false,
}: {
item: Message;
reference: IReference;
loading?: boolean;
}) => {
const isAssistant = item.role === MessageType.Assistant;
const { t } = useTranslate('chat');
const fileThumbnails = useSelectFileThumbnails();
const getDocumentUrl = useGetDocumentUrl();

const referenceDocumentList = useMemo(() => {
return reference?.doc_aggs ?? [];
}, [reference?.doc_aggs]);

const content = useMemo(() => {
let text = item.content;
if (text === '') {
text = t('searching');
}
return loading ? text?.concat('~~2$$') : text;
}, [item.content, loading, t]);


return (
<div
className={classNames(styles.messageItem, {
[styles.messageItemLeft]: item.role === MessageType.Assistant,
[styles.messageItemRight]: item.role === MessageType.User,
})}
>
<section
className={classNames(styles.messageItemSection, {
[styles.messageItemSectionLeft]: item.role === MessageType.Assistant,
[styles.messageItemSectionRight]: item.role === MessageType.User,
})}
>
<div
className={classNames(styles.messageItemContent, {
[styles.messageItemContentReverse]: item.role === MessageType.User,
})}
>
{item.role === MessageType.User ? (
<Avatar
size={40}
src={
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'
}
/>
) : (
<AssistantIcon></AssistantIcon>
)}
<Flex vertical gap={8} flex={1}>
<b>{isAssistant ? '' : 'You'}</b>
<div className={isAssistant ? styles.messageText : styles.messageUserText}>
<MarkdownContent
reference={reference}
clickDocumentButton={() => {}}
content={content}
></MarkdownContent>
</div>
{isAssistant && referenceDocumentList.length > 0 && (
<List
bordered
dataSource={referenceDocumentList}
renderItem={(item) => {
const fileThumbnail = fileThumbnails[item.doc_id];
const fileExtension = getExtension(item.doc_name);
return (
<List.Item>
<Flex gap={'small'} align="center">
{fileThumbnail ? (
<img
src={fileThumbnail}
className={styles.thumbnailImg}
></img>
) : (
<SvgIcon
name={`file-icon/${fileExtension}`}
width={24}
></SvgIcon>
)}

<NewDocumentLink
link={getDocumentUrl(item.doc_id)}
preventDefault={!isPdf(item.doc_name)}
>
{item.doc_name}
</NewDocumentLink>
</Flex>
</List.Item>
);
}}
/>
)}
</Flex>
</div>
</section>
</div>
);
};
import styles from './index.less';


const ChatContainer = () => { const ChatContainer = () => {
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
<MessageItem <MessageItem
key={message.id} key={message.id}
item={message} item={message}
nickname="You"
reference={buildMessageItemReference(conversation, message)} reference={buildMessageItemReference(conversation, message)}
loading={ loading={
message.role === MessageType.Assistant && message.role === MessageType.Assistant &&

+ 4
- 0
web/src/pages/flow/chat/box.tsx Vedi File



import { useSelectCurrentMessages, useSendMessage } from './hooks'; import { useSelectCurrentMessages, useSendMessage } from './hooks';


import { useSelectUserInfo } from '@/hooks/userSettingHook';
import styles from './index.less'; import styles from './index.less';


const FlowChatBox = () => { const FlowChatBox = () => {
useClickDrawer(); useClickDrawer();
useGetFileIcon(); useGetFileIcon();
const { t } = useTranslate('chat'); const { t } = useTranslate('chat');
const userInfo = useSelectUserInfo();


return ( return (
<> <>
currentMessages.length - 1 === i currentMessages.length - 1 === i
} }
key={message.id} key={message.id}
nickname={userInfo.nickname}
avatar={userInfo.avatar}
item={message} item={message}
reference={buildMessageItemReference( reference={buildMessageItemReference(
{ message: currentMessages, reference }, { message: currentMessages, reference },

Loading…
Annulla
Salva