feat: stop chat

This commit is contained in:
archer 2023-04-25 21:06:04 +08:00
parent fb08f61eb5
commit 8767c576be
No known key found for this signature in database
GPG Key ID: 569A5660D2379E28
7 changed files with 59 additions and 53 deletions

View File

@ -1,4 +1,5 @@
### Fast GPT V2.8.1 ### Fast GPT V2.8.1
* 新增 - 暂停聊天。
* 优化 - 知识库升级,内容条数不上限! * 优化 - 知识库升级,内容条数不上限!
* 优化 - 导入去重效果,可防止导出后的 csv 重复导入。 * 优化 - 导入去重效果,可防止导出后的 csv 重复导入。
* 优化 - 聊天框,电脑端复制删除图标。 * 优化 - 聊天框,电脑端复制删除图标。

View File

@ -6,7 +6,7 @@ interface StreamFetchProps {
abortSignal: AbortController; abortSignal: AbortController;
} }
export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) => export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) =>
new Promise(async (resolve, reject) => { new Promise<string>(async (resolve, reject) => {
try { try {
const res = await fetch(url, { const res = await fetch(url, {
method: 'POST', method: 'POST',
@ -23,17 +23,14 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
let responseText = ''; let responseText = '';
const read = async () => { const read = async () => {
try {
const { done, value } = await reader?.read(); const { done, value } = await reader?.read();
if (done) { if (done) {
if (res.status === 200) { if (res.status === 200) {
resolve(responseText); resolve(responseText);
} else { } else {
try {
const parseError = JSON.parse(responseText); const parseError = JSON.parse(responseText);
reject(parseError?.message || '请求异常'); reject(parseError?.message || '请求异常');
} catch (err) {
reject('请求异常');
}
} }
return; return;
@ -42,7 +39,14 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
res.status === 200 && onMessage(text); res.status === 200 && onMessage(text);
responseText += text; responseText += text;
read(); read();
} catch (err: any) {
if (err?.message === 'The user aborted a request.') {
return resolve(responseText);
}
reject(typeof err === 'string' ? err : err?.message || '请求异常');
}
}; };
read(); read();
} catch (err: any) { } catch (err: any) {
console.log(err, '===='); console.log(err, '====');

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: none; display: block; shape-rendering: auto;" width="204px" height="204px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">
<circle cx="84" cy="50" r="10" fill="#e15b64">
<animate attributeName="r" repeatCount="indefinite" dur="0.5681818181818182s" calcMode="spline" keyTimes="0;1" values="15;0" keySplines="0 0.5 0.5 1" begin="0s"></animate>
<animate attributeName="fill" repeatCount="indefinite" dur="2.272727272727273s" calcMode="discrete" keyTimes="0;0.25;0.5;0.75;1" values="#e15b64;#abbd81;#f8b26a;#f47e60;#e15b64" begin="0s"></animate>
</circle><circle cx="16" cy="50" r="10" fill="#e15b64">
<animate attributeName="r" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;15;15;15" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="0s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="0s"></animate>
</circle><circle cx="50" cy="50" r="10" fill="#f47e60">
<animate attributeName="r" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;15;15;15" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.5681818181818182s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-0.5681818181818182s"></animate>
</circle><circle cx="84" cy="50" r="10" fill="#f8b26a">
<animate attributeName="r" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;15;15;15" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-1.1363636363636365s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-1.1363636363636365s"></animate>
</circle><circle cx="16" cy="50" r="10" fill="#abbd81">
<animate attributeName="r" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="0;0;15;15;15" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-1.7045454545454546s"></animate>
<animate attributeName="cx" repeatCount="indefinite" dur="2.272727272727273s" calcMode="spline" keyTimes="0;0.25;0.5;0.75;1" values="16;16;16;50;84" keySplines="0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1;0 0.5 0.5 1" begin="-1.7045454545454546s"></animate>
</circle>
<!-- [ldio] generated by https://loading.io/ --></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682424901088" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3662" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32"><path d="M885.333333 85.333333H138.666667a53.393333 53.393333 0 0 0-53.333334 53.333334v746.666666a53.393333 53.393333 0 0 0 53.333334 53.333334h746.666666a53.393333 53.393333 0 0 0 53.333334-53.333334V138.666667a53.393333 53.393333 0 0 0-53.333334-53.333334z m-160 602.666667a37.373333 37.373333 0 0 1-37.333333 37.333333H336a37.373333 37.373333 0 0 1-37.333333-37.333333V336a37.373333 37.373333 0 0 1 37.333333-37.333333h352a37.373333 37.373333 0 0 1 37.333333 37.333333z" p-id="3663"></path></svg>

After

Width:  |  Height:  |  Size: 823 B

View File

@ -13,12 +13,12 @@ const map = {
board: require('./icons/board.svg').default, board: require('./icons/board.svg').default,
develop: require('./icons/develop.svg').default, develop: require('./icons/develop.svg').default,
user: require('./icons/user.svg').default, user: require('./icons/user.svg').default,
chatting: require('./icons/chatting.svg').default,
promotion: require('./icons/promotion.svg').default, promotion: require('./icons/promotion.svg').default,
delete: require('./icons/delete.svg').default, delete: require('./icons/delete.svg').default,
withdraw: require('./icons/withdraw.svg').default, withdraw: require('./icons/withdraw.svg').default,
dbModel: require('./icons/dbModel.svg').default, dbModel: require('./icons/dbModel.svg').default,
history: require('./icons/history.svg').default history: require('./icons/history.svg').default,
stop: require('./icons/stop.svg').default
}; };
export type IconName = keyof typeof map; export type IconName = keyof typeof map;

View File

@ -0,0 +1,11 @@
.stopIcon {
animation: zoomStopIcon 0.4s infinite alternate;
}
@keyframes zoomStopIcon {
0% {
transform: scale(0.8);
}
100% {
transform: scale(1.2);
}
}

View File

@ -36,6 +36,8 @@ const SlideBar = dynamic(() => import('./components/SlideBar'));
const Empty = dynamic(() => import('./components/Empty')); const Empty = dynamic(() => import('./components/Empty'));
const Markdown = dynamic(() => import('@/components/Markdown')); const Markdown = dynamic(() => import('@/components/Markdown'));
import styles from './index.module.scss';
const textareaMinH = '22px'; const textareaMinH = '22px';
export type ChatSiteItemType = { export type ChatSiteItemType = {
@ -202,8 +204,9 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
obj: prompts.obj, obj: prompts.obj,
value: prompts.value value: prompts.value
}; };
// 流请求,获取数据 // 流请求,获取数据
const res = await streamFetch({ const responseText = await streamFetch({
url: urlMap[chatData.modelName], url: urlMap[chatData.modelName],
data: { data: {
prompt, prompt,
@ -226,22 +229,22 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
abortSignal abortSignal
}); });
let id = ''; let newChatId = '';
// 保存对话信息 // 保存对话信息
try { try {
id = await postSaveChat({ newChatId = await postSaveChat({
modelId, modelId,
chatId, chatId,
prompts: [ prompts: [
prompt, prompt,
{ {
obj: 'AI', obj: 'AI',
value: res as string value: responseText
} }
] ]
}); });
if (id) { if (newChatId) {
router.replace(`/chat?modelId=${modelId}&chatId=${id}`); router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`);
} }
} catch (err) { } catch (err) {
toast({ toast({
@ -252,10 +255,10 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
}); });
} }
// 设置完成状态 // 设置聊天内容为完成状态
setChatData((state) => ({ setChatData((state) => ({
...state, ...state,
chatId: id || state.chatId, // 如果有 Id说明是新创建的对话 chatId: newChatId || state.chatId, // 如果有 Id说明是新创建的对话
history: state.history.map((item, index) => { history: state.history.map((item, index) => {
if (index !== state.history.length - 1) return item; if (index !== state.history.length - 1) return item;
return { return {
@ -559,19 +562,23 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
<Flex <Flex
alignItems={'center'} alignItems={'center'}
justifyContent={'center'} justifyContent={'center'}
h={'30px'} h={'25px'}
w={'30px'} w={'25px'}
position={'absolute'} position={'absolute'}
right={['12px', '20px']} right={['12px', '20px']}
bottom={'15px'} bottom={'15px'}
onClick={sendPrompt}
> >
{isChatting ? ( {isChatting ? (
<Icon <Icon
style={{ transform: 'translateY(4px)' }} className={styles.stopIcon}
h={'30px'} width={['22px', '25px']}
w={'30px'} height={['22px', '25px']}
name={'chatting'} cursor={'pointer'}
name={'stop'}
color={useColorModeValue('gray.500', 'white')}
onClick={() => {
controller.current?.abort();
}}
/> />
) : ( ) : (
<Icon <Icon
@ -579,8 +586,9 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
width={['18px', '20px']} width={['18px', '20px']}
height={['18px', '20px']} height={['18px', '20px']}
cursor={'pointer'} cursor={'pointer'}
fill={useColorModeValue('#718096', 'white')} color={useColorModeValue('gray.500', 'white')}
></Icon> onClick={sendPrompt}
/>
)} )}
</Flex> </Flex>
</Box> </Box>