From 02bc3cde53eb772627ac567ec7a826fcf96fe95a Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 31 May 2025 01:12:45 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20improve=20thinking=20state?= =?UTF-8?q?=20management=20for=20better=20UX=20in=20reasoning=20display?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the "thinking" indicator and loading icon would only disappear after the entire message generation was complete, which created a poor user experience where users had to wait for the full response to see that the reasoning phase had finished. Changes made: - Add `isThinkingComplete` field to independently track reasoning state - Update streaming logic to mark thinking complete when content starts flowing - Detect closed `` tags to mark reasoning completion - Modify MessageContent component to use independent thinking state - Update "思考中..." text and loading icon display conditions - Ensure thinking state is properly set in all completion scenarios (non-stream, errors, manual stop) Now the thinking section immediately shows as complete when reasoning ends, rather than waiting for the entire message to finish, providing much better real-time feedback to users. Files modified: - web/src/hooks/useApiRequest.js - web/src/components/playground/MessageContent.js - web/src/utils/messageUtils.js --- web/src/components/playground/MessageContent.js | 6 +++--- web/src/hooks/useApiRequest.js | 13 +++++++++++++ web/src/utils/messageUtils.js | 1 + 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/web/src/components/playground/MessageContent.js b/web/src/components/playground/MessageContent.js index 1b9634d1..52aa1577 100644 --- a/web/src/components/playground/MessageContent.js +++ b/web/src/components/playground/MessageContent.js @@ -128,7 +128,7 @@ const MessageContent = ({ currentDisplayableFinalContent = baseContentForDisplay.replace(/<\/?think>/g, '').trim(); } - const headerText = isThinkingStatus ? t('思考中...') : t('思考过程'); + const headerText = (isThinkingStatus && !message.isThinkingComplete) ? t('思考中...') : t('思考过程'); const finalExtractedThinkingContent = currentExtractedThinkingContent; const finalDisplayableFinalContent = currentDisplayableFinalContent; @@ -192,7 +192,7 @@ const MessageContent = ({
- {isThinkingStatus && ( + {isThinkingStatus && !message.isThinkingComplete && (
@@ -200,7 +200,7 @@ const MessageContent = ({
)} - {!isThinkingStatus && ( + {(!isThinkingStatus || message.isThinkingComplete) && (
{message.isReasoningExpanded ? : diff --git a/web/src/hooks/useApiRequest.js b/web/src/hooks/useApiRequest.js index 65e7e6e1..78e0f952 100644 --- a/web/src/hooks/useApiRequest.js +++ b/web/src/hooks/useApiRequest.js @@ -42,25 +42,34 @@ export const useApiRequest = ( ...newMessage, reasoningContent: (lastMessage.reasoningContent || '') + textChunk, status: MESSAGE_STATUS.INCOMPLETE, + isThinkingComplete: false, }; } else if (type === 'content') { const shouldCollapseReasoning = !lastMessage.content && lastMessage.reasoningContent; const newContent = (lastMessage.content || '') + textChunk; let shouldCollapseFromThinkTag = false; + let thinkingCompleteFromTags = lastMessage.isThinkingComplete; + if (lastMessage.isReasoningExpanded && newContent.includes('')) { const thinkMatches = newContent.match(//g); const thinkCloseMatches = newContent.match(/<\/think>/g); if (thinkMatches && thinkCloseMatches && thinkCloseMatches.length >= thinkMatches.length) { shouldCollapseFromThinkTag = true; + thinkingCompleteFromTags = true; // think标签闭合也标记思考完成 } } + // 如果开始接收content内容,且之前有reasoning内容,或者think标签已闭合,则标记思考完成 + const isThinkingComplete = (lastMessage.reasoningContent && !lastMessage.isThinkingComplete) || + thinkingCompleteFromTags; + newMessage = { ...newMessage, content: newContent, status: MESSAGE_STATUS.INCOMPLETE, + isThinkingComplete: isThinkingComplete, isReasoningExpanded: (shouldCollapseReasoning || shouldCollapseFromThinkTag) ? false : lastMessage.isReasoningExpanded, }; @@ -86,6 +95,7 @@ export const useApiRequest = ( { ...lastMessage, status: status, + isThinkingComplete: true, isReasoningExpanded: false } ]; @@ -158,6 +168,7 @@ export const useApiRequest = ( content: processed.content, reasoningContent: processed.reasoningContent, status: MESSAGE_STATUS.COMPLETE, + isThinkingComplete: true, isReasoningExpanded: false }; } @@ -182,6 +193,7 @@ export const useApiRequest = ( ...lastMessage, content: t('请求发生错误: ') + error.message, status: MESSAGE_STATUS.ERROR, + isThinkingComplete: true, isReasoningExpanded: false }; } @@ -333,6 +345,7 @@ export const useApiRequest = ( status: MESSAGE_STATUS.COMPLETE, reasoningContent: processed.reasoningContent || null, content: processed.content, + isThinkingComplete: true, isReasoningExpanded: false } ]; diff --git a/web/src/utils/messageUtils.js b/web/src/utils/messageUtils.js index 019116d8..52267e07 100644 --- a/web/src/utils/messageUtils.js +++ b/web/src/utils/messageUtils.js @@ -106,6 +106,7 @@ export const createLoadingAssistantMessage = () => createMessage( { reasoningContent: '', isReasoningExpanded: true, + isThinkingComplete: false, status: 'loading' } );