import { ReactComponent as AssistantIcon } from '@/assets/svg/assistant.svg'; import { MessageType } from '@/constants/chat'; import { useOneNamespaceEffectsLoading } from '@/hooks/storeHooks'; import { useSelectUserInfo } from '@/hooks/userSettingHook'; import { IReference, Message } from '@/interfaces/database/chat'; import { Avatar, Button, Flex, Input, List, Popover, Space } from 'antd'; import classNames from 'classnames'; import { ChangeEventHandler, useCallback, useMemo, useState } from 'react'; import reactStringReplace from 'react-string-replace'; import { useFetchConversationOnMount, useGetFileIcon, useSendMessage, } from '../hooks'; import Image from '@/components/image'; import NewDocumentLink from '@/components/new-document-link'; import { useSelectFileThumbnails } from '@/hooks/knowledgeHook'; import { InfoCircleOutlined } from '@ant-design/icons'; import Markdown from 'react-markdown'; import { visitParents } from 'unist-util-visit-parents'; import styles from './index.less'; const reg = /(#{2}\d+\${2})/g; const getChunkIndex = (match: string) => Number(match.slice(2, 3)); const rehypeWrapReference = () => { return function wrapTextTransform(tree: any) { visitParents(tree, 'text', (node, ancestors) => { if (ancestors.at(-1).tagName !== 'custom-typography') { node.type = 'element'; node.tagName = 'custom-typography'; node.properties = {}; node.children = [{ type: 'text', value: node.value }]; } }); }; }; const MessageItem = ({ item, reference, }: { item: Message; reference: IReference; }) => { const userInfo = useSelectUserInfo(); const fileThumbnails = useSelectFileThumbnails(); const isAssistant = item.role === MessageType.Assistant; const getPopoverContent = useCallback( (chunkIndex: number) => { const chunks = reference?.chunks ?? []; const chunkItem = chunks[chunkIndex]; const document = reference?.doc_aggs.find( (x) => x?.doc_id === chunkItem?.doc_id, ); const documentId = document?.doc_id; return ( } >
{chunkItem?.content_with_weight}
{documentId && ( {document?.doc_name} )}
); }, [reference, fileThumbnails], ); const renderReference = useCallback( (text: string) => { return reactStringReplace(text, reg, (match, i) => { const chunkIndex = getChunkIndex(match); return ( ); }); }, [getPopoverContent], ); const referenceDocumentList = useMemo(() => { return reference?.doc_aggs ?? []; }, [reference?.doc_aggs]); return (
{item.role === MessageType.User ? ( userInfo.avatar ?? ( ) ) : ( )} {isAssistant ? 'Resume Assistant' : 'You'}
renderReference(children), } as any } > {item.content}
{isAssistant && referenceDocumentList.length > 0 && ( ( {/* */} {item.doc_name} )} /> )}
); }; const ChatContainer = () => { const [value, setValue] = useState(''); const { ref, currentConversation: conversation, addNewestConversation, } = useFetchConversationOnMount(); const { sendMessage } = useSendMessage(); const loading = useOneNamespaceEffectsLoading('chatModel', [ 'completeConversation', ]); useGetFileIcon(); const handlePressEnter = () => { if (!loading) { setValue(''); addNewestConversation(value); sendMessage(value); } }; const handleInputChange: ChangeEventHandler = (e) => { setValue(e.target.value); }; return (
{conversation?.message?.map((message) => { const assistantMessages = conversation?.message ?.filter((x) => x.role === MessageType.Assistant) .slice(1); const referenceIndex = assistantMessages.findIndex( (x) => x.id === message.id, ); const reference = conversation.reference[referenceIndex]; return ( ); })}
Send } onPressEnter={handlePressEnter} onChange={handleInputChange} /> ); }; export default ChatContainer;