fix: 修复终止输出,修复思考模型<think>标签的兼容处理

This commit is contained in:
何嘉悦 2025-06-04 16:21:01 +08:00
parent b2574805fd
commit 63e4ce739b
5 changed files with 67 additions and 91 deletions

View File

@ -111,6 +111,7 @@ function handleClick(item: GetSessionListVO) {
.model-select-box { .model-select-box {
color: var(--el-color-primary, #409eff); color: var(--el-color-primary, #409eff);
border: 1px solid var(--el-color-primary, #409eff); border: 1px solid var(--el-color-primary, #409eff);
background: var(--el-color-primary-light-9, rgb(235.9, 245.3, 255));
border-radius: 10px; border-radius: 10px;
} }

View File

@ -1,79 +0,0 @@
<script lang="ts" setup>
import type { ConversationItem } from 'vue-element-plus-x/types/Conversations';
import type { ChatSessionVo } from '@/api/session/types';
// import { useUserStore } from '@/stores';
import { Conversations } from 'vue-element-plus-x';
import { useRoute, useRouter } from 'vue-router';
// import { get_session_list } from '@/api';
const route = useRoute();
const router = useRouter();
// const userStore = useUserStore();
const active = computed<string>(() => (route.params?.id as string) ?? '');
const items = ref<ConversationItem<ChatSessionVo>[]>([]);
function handleChange(item: ConversationItem<ChatSessionVo>) {
console.log(item);
// router.replace({
// name: 'chat',
// params: {
// id: item.id,
// },
// });
}
// async function getSessions() {
// try {
// const res = await get_session_list({
// userId: userStore.userInfo?.userId as number,
// });
// console.log(res);
// items.value
// = res.rows?.map(item => ({
// ...item,
// label: item.sessionTitle as string,
// })) ?? [];
// }
// catch (error) {
// console.error('getSessions:', error);
// }
// }
// getSessions();
const sessionId = computed<string>(() => route.params?.id as string);
function handleNewSession() {
if (sessionId.value) {
router.replace({ name: 'chatWithId' });
}
}
// watchEffect(() => {
// console.log('active', active.value, '>>>');
// });
</script>
<template>
<el-container class="h-screen overflow-hidden">
<el-aside>
<Conversations
:active="active"
:items="items"
row-key="id"
label-key="sessionTitle"
@change="handleChange"
>
<template #header>
<el-button @click="handleNewSession">
新增
</el-button>
</template>
<template #footer>
<div>用户信息</div>
</template>
</Conversations>
</el-aside>
<el-main>
<RouterView />
</el-main>
</el-container>
</template>

View File

@ -43,6 +43,8 @@ const bubbleListRef = ref<BubbleListInstance | null>(null);
const isLoading = ref(false); const isLoading = ref(false);
// //
let sendRequest: HookFetchRequest<any, any> | null = null; let sendRequest: HookFetchRequest<any, any> | null = null;
//
let isThinking = false;
watch( watch(
() => route.params?.id, () => route.params?.id,
@ -95,23 +97,47 @@ function handleDataChunk(chunk: AnyObject) {
// //
bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'thinking'; bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'thinking';
bubbleItems.value[bubbleItems.value.length - 1].loading = true; bubbleItems.value[bubbleItems.value.length - 1].loading = true;
bubbleItems.value[bubbleItems.value.length - 1].thinlCollapse = true;
if (bubbleItems.value.length) { if (bubbleItems.value.length) {
bubbleItems.value[bubbleItems.value.length - 1].reasoning_content += reasoningChunk; bubbleItems.value[bubbleItems.value.length - 1].reasoning_content += reasoningChunk;
} }
} }
// content <think></think>
// <think> </think>
const parsedChunk = chunk.choices?.[0].delta.content; const parsedChunk = chunk.choices?.[0].delta.content;
if (parsedChunk) { if (parsedChunk) {
const thinkStart = parsedChunk.includes('<think>');
const thinkEnd = parsedChunk.includes('</think>');
if (thinkStart) {
isThinking = true;
}
if (thinkEnd) {
isThinking = false;
}
if (isThinking) {
//
bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'thinking';
bubbleItems.value[bubbleItems.value.length - 1].loading = true;
bubbleItems.value[bubbleItems.value.length - 1].thinlCollapse = true;
if (bubbleItems.value.length) {
bubbleItems.value[bubbleItems.value.length - 1].reasoning_content += parsedChunk
.replace('<think>', '')
.replace('</think>', '');
}
}
else {
// //
bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'end'; bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'end';
bubbleItems.value[bubbleItems.value.length - 1].loading = false; bubbleItems.value[bubbleItems.value.length - 1].loading = false;
if (bubbleItems.value.length) { if (bubbleItems.value.length) {
bubbleItems.value[bubbleItems.value.length - 1].content += parsedChunk; bubbleItems.value[bubbleItems.value.length - 1].content += parsedChunk;
} }
} }
} }
}
catch (err) { catch (err) {
// 使
console.error('解析数据时出错:', err); console.error('解析数据时出错:', err);
} }
} }
@ -189,6 +215,7 @@ function addMessage(message: string, isUser: boolean) {
content: message || '', content: message || '',
reasoning_content: '', reasoning_content: '',
thinkingStatus: 'start', thinkingStatus: 'start',
thinlCollapse: false,
}; };
bubbleItems.value.push(obj); bubbleItems.value.push(obj);
} }
@ -226,6 +253,7 @@ watch(
<template #header="{ item }"> <template #header="{ item }">
<Thinking <Thinking
v-if="item.reasoning_content" v-if="item.reasoning_content"
v-model="item.thinlCollapse"
:content="item.reasoning_content" :content="item.reasoning_content"
:status="item.thinkingStatus" :status="item.thinkingStatus"
class="thinking-chain-warp" class="thinking-chain-warp"

View File

@ -25,6 +25,7 @@ export const useChatStore = defineStore('chat', () => {
const setChatMap = (id: string, data: ChatMessageVo[]) => { const setChatMap = (id: string, data: ChatMessageVo[]) => {
chatMap.value[id] = data?.map((item: ChatMessageVo) => { chatMap.value[id] = data?.map((item: ChatMessageVo) => {
const isUser = item.role === 'user'; const isUser = item.role === 'user';
const thinkContent = extractThkContent(item.content as string);
return { return {
...item, ...item,
key: item.id, key: item.id,
@ -37,6 +38,10 @@ export const useChatStore = defineStore('chat', () => {
: 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png', : 'https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png',
avatarSize: '32px', avatarSize: '32px',
typing: false, typing: false,
reasoning_content: thinkContent,
thinkingStatus: 'end',
content: extractThkContentAfter(item.content as string),
thinlCollapse: false,
}; };
}); });
}; };
@ -60,6 +65,27 @@ export const useChatStore = defineStore('chat', () => {
} }
}; };
// 对思考中的内容回显做处理
function extractThkContent(content: string) {
const regex = /<think>(.*?)<\/think>/s;
const matchResult = content.match(regex);
// 把这些内容从 content 中移除
content = content.replace(regex, '');
return matchResult?.[1] ?? '';
}
// 如果有 </think> 标签,则把 </think> 之后的 内容从 content 中返回
function extractThkContentAfter(content: string) {
if (!content.includes('</think>')) {
return content;
}
const regex = /<\/think>(.*)/s;
const matchResult = content.match(regex);
// 把这些内容从 content 中移除
content = content.replace(regex, '');
return matchResult?.[1] ?? '';
}
return { return {
chatMap, chatMap,
requestChatList, requestChatList,

View File

@ -28,23 +28,23 @@
height: fit-content; height: fit-content;
} }
.zoom-fade-enter-from { .rounded-tooltip-enter-from {
transform: scale(0.9); /* 进入前:缩小隐藏 */ transform: scale(0.9); /* 进入前:缩小隐藏 */
opacity: 0; opacity: 0;
} }
.zoom-fade-enter-active, .rounded-tooltip-enter-active,
.zoom-fade-leave-active { .rounded-tooltip-leave-active {
transition: transform 0.3s, opacity 0.3s; /* 缓入动画 */ transition: transform 0.3s, opacity 0.3s; /* 缓入动画 */
} }
.zoom-fade-enter-to { .rounded-tooltip-enter-to {
transform: scale(1); /* 进入后:正常大小 */ transform: scale(1); /* 进入后:正常大小 */
opacity: 1; opacity: 1;
} }
.zoom-fade-leave-from { .rounded-tooltip-leave-from {
transform: scale(1); /* 离开前:正常大小 */ transform: scale(1); /* 离开前:正常大小 */
opacity: 1; opacity: 1;
} }
.zoom-fade-leave-to { .rounded-tooltip-leave-to {
transform: scale(0.9); /* 离开后:缩小隐藏 */ transform: scale(0.9); /* 离开后:缩小隐藏 */
opacity: 0; opacity: 0;
} }