diff --git a/web/src/hooks/useApiRequest.js b/web/src/hooks/useApiRequest.js index bbe49eac..55688726 100644 --- a/web/src/hooks/useApiRequest.js +++ b/web/src/hooks/useApiRequest.js @@ -348,41 +348,45 @@ export const useApiRequest = ( // 停止生成 const onStopGenerator = useCallback(() => { + // 如果仍有活动的 SSE 连接,首先关闭 if (sseSourceRef.current) { sseSourceRef.current.close(); sseSourceRef.current = null; - - setMessage(prevMessage => { - const lastMessage = prevMessage[prevMessage.length - 1]; - if (lastMessage.status === MESSAGE_STATUS.LOADING || - lastMessage.status === MESSAGE_STATUS.INCOMPLETE) { - - const processed = processIncompleteThinkTags( - lastMessage.content || '', - lastMessage.reasoningContent || '' - ); - - const autoCollapseState = applyAutoCollapseLogic(lastMessage, true); - - const updatedMessages = [ - ...prevMessage.slice(0, -1), - { - ...lastMessage, - status: MESSAGE_STATUS.COMPLETE, - reasoningContent: processed.reasoningContent || null, - content: processed.content, - ...autoCollapseState, - } - ]; - - // 停止生成时也保存,传入更新后的消息列表 - setTimeout(() => saveMessages(updatedMessages), 0); - - return updatedMessages; - } - return prevMessage; - }); } + + // 无论是否存在 SSE 连接,都尝试处理最后一条正在生成的消息 + setMessage(prevMessage => { + if (prevMessage.length === 0) return prevMessage; + const lastMessage = prevMessage[prevMessage.length - 1]; + + if (lastMessage.status === MESSAGE_STATUS.LOADING || + lastMessage.status === MESSAGE_STATUS.INCOMPLETE) { + + const processed = processIncompleteThinkTags( + lastMessage.content || '', + lastMessage.reasoningContent || '' + ); + + const autoCollapseState = applyAutoCollapseLogic(lastMessage, true); + + const updatedMessages = [ + ...prevMessage.slice(0, -1), + { + ...lastMessage, + status: MESSAGE_STATUS.COMPLETE, + reasoningContent: processed.reasoningContent || null, + content: processed.content, + ...autoCollapseState, + } + ]; + + // 停止生成时也保存,传入更新后的消息列表 + setTimeout(() => saveMessages(updatedMessages), 0); + + return updatedMessages; + } + return prevMessage; + }); }, [setMessage, applyAutoCollapseLogic, saveMessages]); // 发送请求 diff --git a/web/src/hooks/usePlaygroundState.js b/web/src/hooks/usePlaygroundState.js index 39e68902..9e76aa2e 100644 --- a/web/src/hooks/usePlaygroundState.js +++ b/web/src/hooks/usePlaygroundState.js @@ -1,6 +1,7 @@ import { useState, useCallback, useRef, useEffect } from 'react'; -import { DEFAULT_MESSAGES, DEFAULT_CONFIG, DEBUG_TABS } from '../utils/constants'; +import { DEFAULT_MESSAGES, DEFAULT_CONFIG, DEBUG_TABS, MESSAGE_STATUS } from '../utils/constants'; import { loadConfig, saveConfig, loadMessages, saveMessages } from '../components/playground/configStorage'; +import { processIncompleteThinkTags } from '../utils/messageUtils'; export const usePlaygroundState = () => { // 使用惰性初始化,确保只在组件首次挂载时加载配置和消息 @@ -138,6 +139,33 @@ export const usePlaygroundState = () => { }; }, []); + // 页面首次加载时,若最后一条消息仍处于 LOADING/INCOMPLETE 状态,自动修复 + useEffect(() => { + if (!Array.isArray(message) || message.length === 0) return; + + const lastMsg = message[message.length - 1]; + if (lastMsg.status === MESSAGE_STATUS.LOADING || lastMsg.status === MESSAGE_STATUS.INCOMPLETE) { + const processed = processIncompleteThinkTags( + lastMsg.content || '', + lastMsg.reasoningContent || '' + ); + + const fixedLastMsg = { + ...lastMsg, + status: MESSAGE_STATUS.COMPLETE, + content: processed.content, + reasoningContent: processed.reasoningContent || null, + isThinkingComplete: true, + }; + + const updatedMessages = [...message.slice(0, -1), fixedLastMsg]; + setMessage(updatedMessages); + + // 保存修复后的消息列表 + setTimeout(() => saveMessagesImmediately(updatedMessages), 0); + } + }, []); + return { // 配置状态 inputs,