import TransitionWrapper from '@/components/transition'; import HotKeys from '@/config/hotkeys'; import useContainerScroll from '@/hooks/use-container-scorll'; import { fetchChunkedData, readStreamData } from '@/utils/fetch-chunk-data'; import { EyeInvisibleOutlined, EyeOutlined } from '@ant-design/icons'; import { useIntl } from '@umijs/max'; import { Button, Input, Spin, Tooltip } from 'antd'; import _ from 'lodash'; import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { CHAT_API } from '../apis'; import { Roles } from '../config'; import '../style/ground-left.less'; import '../style/system-message-wrap.less'; import ChatFooter from './chat-footer'; import MessageItem from './message-item'; import ReferenceParams from './reference-params'; import ViewCodeModal from './view-code-modal'; interface MessageProps { parameters: any; ref?: any; } interface MessageItemProps { role: string; content: string; uid: number; } const MessageList: React.FC = forwardRef((props, ref) => { const { parameters } = props; const messageId = useRef(0); const [messageList, setMessageList] = useState([ { role: 'user', content: '', uid: messageId.current } ]); const intl = useIntl(); const [systemMessage, setSystemMessage] = useState(''); const [collapsed, setCollapsed] = useState(true); const [show, setShow] = useState(false); const [loading, setLoading] = useState(false); const [activeIndex, setActiveIndex] = useState(-1); const [tokenResult, setTokenResult] = useState(null); const [currentIsFocus, setCurrentIsFocus] = useState(false); const systemRef = useRef(null); const contentRef = useRef(''); const controllerRef = useRef(null); const scroller = useRef(null); const { updateScrollerPosition, handleContentWheel } = useContainerScroll( scroller, { toBottom: true } ); useEffect(() => { updateScrollerPosition(); }, [messageList]); useImperativeHandle(ref, () => { return { viewCode() { setShow(true); } }; }); const handleSystemMessageChange = (e: any) => { setSystemMessage(e.target.value); }; const setMessageId = () => { messageId.current = messageId.current + 1; }; const handleNewMessage = (role?: any) => { messageList.push({ role: _.last(messageList)?.role === Roles.User ? Roles.Assistant : Roles.User, content: '', uid: messageId.current + 1 }); setMessageId(); setMessageList([...messageList]); setActiveIndex(messageList.length - 1); }; const joinMessage = (chunk: any) => { if (!chunk) { return; } if (_.get(chunk, 'choices.0.finish_reason')) { setTokenResult({ ...chunk?.usage }); return; } contentRef.current = contentRef.current + _.get(chunk, 'choices.0.delta.content', ''); setMessageList([ ...messageList, { role: Roles.Assistant, content: contentRef.current, uid: messageId.current } ]); }; const handleStopConversation = () => { controllerRef.current?.abort?.(); setLoading(false); }; const submitMessage = async () => { if (!parameters.model) return; try { setLoading(true); setMessageId(); setTokenResult(null); controllerRef.current?.abort?.(); controllerRef.current = new AbortController(); const signal = controllerRef.current.signal; contentRef.current = ''; const chatParams = { messages: systemMessage ? [ { role: Roles.System, content: systemMessage }, ...messageList ] : [...messageList], ...parameters, stream: true }; const result = await fetchChunkedData({ data: chatParams, url: CHAT_API, signal }); if (!result) { return; } const { reader, decoder } = result; await readStreamData(reader, decoder, (chunk: any) => { joinMessage(chunk); }); setLoading(false); } catch (error) { console.log('error=====', error); setLoading(false); } }; const handleClear = () => { if (!messageList.length) { return; } setMessageId(); setMessageList([ { role: Roles.User, content: '', uid: messageId.current } ]); }; const handleView = () => { setShow(true); }; const handleSubmit = () => { submitMessage(); }; const handleCloseViewCode = () => { setShow(false); }; const handleDelete = (index: number) => { messageList.splice(index, 1); setMessageList([...messageList]); }; const handleUpdateMessage = (index: number, message: MessageItemProps) => { messageList[index] = message; setMessageList([...messageList]); }; const renderLabel = () => { return (
{intl.formatMessage({ id: 'playground.system' })}
); }; const handleFocus = () => { setCurrentIsFocus(true); }; const handleBlur = () => { setCurrentIsFocus(false); }; useHotkeys( HotKeys.SUBMIT, () => { handleSubmit(); }, { enabled: currentIsFocus && !loading, enableOnFormTags: currentIsFocus && !loading, preventDefault: true } ); return (
{messageList.map((item, index) => { return ( handleDelete(index)} updateMessage={(message: MessageItemProps) => handleUpdateMessage(index, message) } onSubmit={handleSubmit} message={item} /> ); })} {loading && (
)}
} >
); }); export default MessageList;