feat: 新增停止大模型回答功能(关联 Issue #104)

This commit is contained in:
zhaoshibao 2025-03-30 11:57:06 +08:00
parent 3b65dee0e8
commit 3e0b18d455

View File

@ -100,6 +100,10 @@
> >
请求错误请重试{{ message.message }} 请求错误请重试{{ message.message }}
</div> </div>
<div v-if="message.isStoppedByUser" class="retry-hint">
你停止生成了本次回答
<span class="retry-link" @click="retryStoppedMessage(message)">重新编辑问题</span>
</div>
<RefsComponent v-if="message.role=='received' && message.status=='finished'" :message="message" /> <RefsComponent v-if="message.role=='received' && message.status=='finished'" :message="message" />
</div> </div>
</div> </div>
@ -153,9 +157,19 @@
</a-dropdown> </a-dropdown>
</div> </div>
<div class="options__right"> <div class="options__right">
<a-button size="large" @click="sendMessage" :disabled="(!conv.inputText && !isStreaming)" type="link"> <a-tooltip :title="isStreaming ? '停止回答' : ''">
<template #icon> <ArrowUpOutlined v-if="!isStreaming" /> <LoadingOutlined v-else/> </template> <a-button
size="large"
@click="handleSendOrStop"
:disabled="(!conv.inputText && !isStreaming)"
type="link"
>
<template #icon>
<StopOutlined v-if="isStreaming" />
<ArrowUpOutlined v-else />
</template>
</a-button> </a-button>
</a-tooltip>
</div> </div>
</div> </div>
</div> </div>
@ -188,6 +202,7 @@ import {
RobotOutlined, RobotOutlined,
CaretRightOutlined, CaretRightOutlined,
DeploymentUnitOutlined, DeploymentUnitOutlined,
StopOutlined
} from '@ant-design/icons-vue' } from '@ant-design/icons-vue'
import { onClickOutside } from '@vueuse/core' import { onClickOutside } from '@vueuse/core'
import { Marked } from 'marked'; import { Marked } from 'marked';
@ -456,6 +471,9 @@ const loadDatabases = () => {
// fetch // fetch
const fetchChatResponse = (user_input, cur_res_id) => { const fetchChatResponse = (user_input, cur_res_id) => {
const controller = new AbortController();
const signal = controller.signal;
fetch('/api/chat/', { fetch('/api/chat/', {
method: 'POST', method: 'POST',
body: JSON.stringify({ body: JSON.stringify({
@ -466,7 +484,8 @@ const fetchChatResponse = (user_input, cur_res_id) => {
}), }),
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} },
signal // signal
}) })
.then((response) => { .then((response) => {
if (!response.body) throw new Error("ReadableStream not supported."); if (!response.body) throw new Error("ReadableStream not supported.");
@ -524,13 +543,24 @@ const fetchChatResponse = (user_input, cur_res_id) => {
readChunk(); readChunk();
}) })
.catch((error) => { .catch((error) => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error(error); console.error(error);
updateMessage({ updateMessage({
id: cur_res_id, id: cur_res_id,
status: "error", status: "error",
}); });
}
isStreaming.value = false; isStreaming.value = false;
}); });
// isStreaming false
watch(isStreaming, (newValue) => {
if (!newValue) {
controller.abort();
}
});
} }
@ -607,6 +637,36 @@ watch(
}, },
{ deep: true } { deep: true }
); );
//
const handleSendOrStop = () => {
if (isStreaming.value) {
//
isStreaming.value = false;
const lastMessage = conv.value.messages[conv.value.messages.length - 1];
if (lastMessage) {
lastMessage.isStoppedByUser = true;
lastMessage.status = 'stopped';
}
} else {
//
sendMessage();
}
}
//
const retryStoppedMessage = (message) => {
//
const messageIndex = conv.value.messages.findIndex(msg => msg.id === message.id);
if (messageIndex > 0) {
const userMessage = conv.value.messages[messageIndex - 1];
if (userMessage && userMessage.role === 'sent') {
conv.value.inputText = userMessage.text;
//
conv.value.messages = conv.value.messages.slice(0, messageIndex);
}
}
}
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
@ -1109,6 +1169,34 @@ watch(
margin-right: 8px; margin-right: 8px;
} }
} }
.retry-hint {
margin-top: 8px;
padding: 8px 16px;
color: #666;
font-size: 14px;
text-align: left;
}
.retry-link {
color: #1890ff;
cursor: pointer;
margin-left: 4px;
&:hover {
text-decoration: underline;
}
}
.ant-btn-icon-only {
&:has(.anticon-stop) {
background-color: #ff4d4f !important;
&:hover {
background-color: #ff7875 !important;
}
}
}
</style> </style>
<style lang="less"> <style lang="less">