feat: stop chat

This commit is contained in:
archer
2023-04-25 21:06:04 +08:00
parent fb08f61eb5
commit 8767c576be
7 changed files with 59 additions and 53 deletions

View File

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

View File

@@ -6,7 +6,7 @@ interface StreamFetchProps {
abortSignal: AbortController;
}
export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) =>
new Promise(async (resolve, reject) => {
new Promise<string>(async (resolve, reject) => {
try {
const res = await fetch(url, {
method: 'POST',
@@ -23,26 +23,30 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
let responseText = '';
const read = async () => {
const { done, value } = await reader?.read();
if (done) {
if (res.status === 200) {
resolve(responseText);
} else {
try {
try {
const { done, value } = await reader?.read();
if (done) {
if (res.status === 200) {
resolve(responseText);
} else {
const parseError = JSON.parse(responseText);
reject(parseError?.message || '请求异常');
} catch (err) {
reject('请求异常');
}
}
return;
return;
}
const text = decoder.decode(value).replace(/<br\/>/g, '\n');
res.status === 200 && onMessage(text);
responseText += text;
read();
} catch (err: any) {
if (err?.message === 'The user aborted a request.') {
return resolve(responseText);
}
reject(typeof err === 'string' ? err : err?.message || '请求异常');
}
const text = decoder.decode(value).replace(/<br\/>/g, '\n');
res.status === 200 && onMessage(text);
responseText += text;
read();
};
read();
} catch (err: any) {
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,
develop: require('./icons/develop.svg').default,
user: require('./icons/user.svg').default,
chatting: require('./icons/chatting.svg').default,
promotion: require('./icons/promotion.svg').default,
delete: require('./icons/delete.svg').default,
withdraw: require('./icons/withdraw.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;

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