abort when clear conversations

This commit is contained in:
josc146 2023-05-21 13:48:11 +08:00
parent c3084a3290
commit db4e0a5734

View File

@ -1,22 +1,22 @@
import React, {FC, useEffect, useRef, useState} from 'react'; import React, { FC, useEffect, useRef, useState } from 'react';
import {useTranslation} from 'react-i18next'; import { useTranslation } from 'react-i18next';
import {RunButton} from '../components/RunButton'; import { RunButton } from '../components/RunButton';
import {Avatar, Divider, PresenceBadge, Text, Textarea} from '@fluentui/react-components'; import { Avatar, Divider, PresenceBadge, Text, Textarea } from '@fluentui/react-components';
import commonStore, {ModelStatus} from '../stores/commonStore'; import commonStore, { ModelStatus } from '../stores/commonStore';
import {observer} from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
import {PresenceBadgeStatus} from '@fluentui/react-badge'; import { PresenceBadgeStatus } from '@fluentui/react-badge';
import {ConfigSelector} from '../components/ConfigSelector'; import { ConfigSelector } from '../components/ConfigSelector';
import {v4 as uuid} from 'uuid'; import { v4 as uuid } from 'uuid';
import classnames from 'classnames'; import classnames from 'classnames';
import {fetchEventSource} from '@microsoft/fetch-event-source'; import { fetchEventSource } from '@microsoft/fetch-event-source';
import {ConversationPair, getConversationPairs, Record} from '../utils/get-conversation-pairs'; import { ConversationPair, getConversationPairs, Record } from '../utils/get-conversation-pairs';
import logo from '../../../build/appicon.png'; import logo from '../../../build/appicon.png';
import MarkdownRender from '../components/MarkdownRender'; import MarkdownRender from '../components/MarkdownRender';
import {ToolTipButton} from '../components/ToolTipButton'; import { ToolTipButton } from '../components/ToolTipButton';
import {ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular} from '@fluentui/react-icons'; import { ArrowCircleUp28Regular, Delete28Regular, RecordStop28Regular } from '@fluentui/react-icons';
import {CopyButton} from '../components/CopyButton'; import { CopyButton } from '../components/CopyButton';
import {ReadButton} from '../components/ReadButton'; import { ReadButton } from '../components/ReadButton';
import {toast} from 'react-toastify'; import { toast } from 'react-toastify';
export const userName = 'M E'; export const userName = 'M E';
export const botName = 'A I'; export const botName = 'A I';
@ -46,7 +46,7 @@ export type Conversations = {
} }
const ChatPanel: FC = observer(() => { const ChatPanel: FC = observer(() => {
const {t} = useTranslation(); const { t } = useTranslation();
const [message, setMessage] = useState(''); const [message, setMessage] = useState('');
const bodyRef = useRef<HTMLDivElement>(null); const bodyRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null); const inputRef = useRef<HTMLTextAreaElement>(null);
@ -95,7 +95,7 @@ const ChatPanel: FC = observer(() => {
if (e.type === 'click' || (e.keyCode === 13 && !e.shiftKey)) { if (e.type === 'click' || (e.keyCode === 13 && !e.shiftKey)) {
e.preventDefault(); e.preventDefault();
if (commonStore.modelStatus === ModelStatus.Offline) { if (commonStore.modelStatus === ModelStatus.Offline) {
toast(t('Please click the button in the top right corner to start the model'), {type: 'warning'}); toast(t('Please click the button in the top right corner to start the model'), { type: 'warning' });
return; return;
} }
if (!message) return; if (!message) return;
@ -127,13 +127,13 @@ const ChatPanel: FC = observer(() => {
const questionId = commonStore.conversationsOrder[index - 1]; const questionId = commonStore.conversationsOrder[index - 1];
const question = commonStore.conversations[questionId]; const question = commonStore.conversations[questionId];
if (question.done && question.type === MessageType.Normal && question.sender === userName) { if (question.done && question.type === MessageType.Normal && question.sender === userName) {
records.push({question: question.content, answer: conversation.content}); records.push({ question: question.content, answer: conversation.content });
} }
} }
} }
}); });
const messages = getConversationPairs(records, false); const messages = getConversationPairs(records, false);
(messages as ConversationPair[]).push({role: 'user', content: message}); (messages as ConversationPair[]).push({ role: 'user', content: message });
const answerId = uuid(); const answerId = uuid();
commonStore.conversations[answerId] = { commonStore.conversations[answerId] = {
@ -224,7 +224,7 @@ const ChatPanel: FC = observer(() => {
<Avatar <Avatar
color={conversation.color} color={conversation.color}
name={conversation.sender} name={conversation.sender}
image={conversation.avatarImg ? {src: conversation.avatarImg} : undefined} image={conversation.avatarImg ? { src: conversation.avatarImg } : undefined}
/> />
<div <div
className={classnames( className={classnames(
@ -236,15 +236,15 @@ const ChatPanel: FC = observer(() => {
<MarkdownRender>{conversation.content}</MarkdownRender> <MarkdownRender>{conversation.content}</MarkdownRender>
</div> </div>
<div className="flex flex-col gap-1 items-start"> <div className="flex flex-col gap-1 items-start">
<div className="grow"/> <div className="grow" />
{(conversation.type === MessageType.Error || !conversation.done) && {(conversation.type === MessageType.Error || !conversation.done) &&
<PresenceBadge size="extra-small" status={ <PresenceBadge size="extra-small" status={
conversation.type === MessageType.Error ? 'busy' : 'away' conversation.type === MessageType.Error ? 'busy' : 'away'
}/> } />
} }
<div className="flex invisible" id={'utils-' + uuid}> <div className="flex invisible" id={'utils-' + uuid}>
<ReadButton content={conversation.content}/> <ReadButton content={conversation.content} />
<CopyButton content={conversation.content}/> <CopyButton content={conversation.content} />
</div> </div>
</div> </div>
</div>; </div>;
@ -252,9 +252,11 @@ const ChatPanel: FC = observer(() => {
</div> </div>
<div className="flex items-end gap-2"> <div className="flex items-end gap-2">
<ToolTipButton desc={t('Clear')} <ToolTipButton desc={t('Clear')}
icon={<Delete28Regular/>} icon={<Delete28Regular />}
size="large" shape="circular" appearance="subtle" size="large" shape="circular" appearance="subtle"
onClick={(e) => { onClick={(e) => {
if (generating)
sseControllerRef.current?.abort();
commonStore.setConversations({}); commonStore.setConversations({});
commonStore.setConversationsOrder([]); commonStore.setConversationsOrder([]);
}} }}
@ -269,7 +271,7 @@ const ChatPanel: FC = observer(() => {
onKeyDown={handleKeyDownOrClick} onKeyDown={handleKeyDownOrClick}
/> />
<ToolTipButton desc={generating ? t('Stop') : t('Send')} <ToolTipButton desc={generating ? t('Stop') : t('Send')}
icon={generating ? <RecordStop28Regular/> : <ArrowCircleUp28Regular/>} icon={generating ? <RecordStop28Regular /> : <ArrowCircleUp28Regular />}
size="large" shape="circular" appearance="subtle" size="large" shape="circular" appearance="subtle"
onClick={(e) => { onClick={(e) => {
if (generating) { if (generating) {
@ -283,7 +285,7 @@ const ChatPanel: FC = observer(() => {
} else { } else {
handleKeyDownOrClick(e); handleKeyDownOrClick(e);
} }
}}/> }} />
</div> </div>
</div> </div>
); );
@ -304,26 +306,26 @@ const badgeStatus: { [modelStatus: number]: PresenceBadgeStatus } = {
}; };
export const Chat: FC = observer(() => { export const Chat: FC = observer(() => {
const {t} = useTranslation(); const { t } = useTranslation();
const port = commonStore.getCurrentModelConfig().apiParameters.apiPort; const port = commonStore.getCurrentModelConfig().apiParameters.apiPort;
return ( return (
<div className="flex flex-col gap-1 p-2 h-full overflow-hidden"> <div className="flex flex-col gap-1 p-2 h-full overflow-hidden">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<PresenceBadge status={badgeStatus[commonStore.modelStatus]}/> <PresenceBadge status={badgeStatus[commonStore.modelStatus]} />
<Text size={100}>{t('Model Status') + ': ' + t(statusText[commonStore.modelStatus])}</Text> <Text size={100}>{t('Model Status') + ': ' + t(statusText[commonStore.modelStatus])}</Text>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<ConfigSelector size="small"/> <ConfigSelector size="small" />
<RunButton iconMode/> <RunButton iconMode />
</div> </div>
</div> </div>
<Text size={100}> <Text size={100}>
{t('This tools API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'} {t('This tools API is compatible with OpenAI API. It can be used with any ChatGPT tool you like. Go to the settings of some ChatGPT tool, replace the \'https://api.openai.com\' part in the API address with \'') + `http://127.0.0.1:${port}` + '\'.'}
</Text> </Text>
<Divider style={{flexGrow: 0}}/> <Divider style={{ flexGrow: 0 }} />
<ChatPanel/> <ChatPanel />
</div> </div>
); );
}); });