From 1d37867f399a397b6a23d0abea52b03a55d92319 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Mon, 2 Jun 2025 06:21:05 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(playground):=20ensure=20prop?= =?UTF-8?q?er=20streaming=20updates=20&=20safeguard=20message=20handling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary This commit addresses two critical issues affecting the real-time chat experience in the Playground: 1. Optimized re-rendering of reasoning content • Added `reasoningContent` to the comparison function of `OptimizedMessageContent` (`web/src/components/playground/OptimizedComponents.js`). • Ensures the component re-renders while reasoning text streams, resolving the bug where only the first characters (“好,”) were shown until the stream finished. 2. Defensive checks for SSE message updates • Added early-return guards in `streamMessageUpdate` (`web/src/hooks/useApiRequest.js`). • Skips updates when `lastMessage` is undefined or the last message isn’t from the assistant, preventing `TypeError: Cannot read properties of undefined (reading 'status')` during rapid SSE responses. Impact • Real-time reasoning content now appears progressively, enhancing user feedback. • Eliminates runtime crashes caused by undefined message references, improving overall stability. --- web/src/components/playground/OptimizedComponents.js | 1 + web/src/components/playground/ThinkingContent.js | 10 +++++++++- web/src/hooks/useApiRequest.js | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/web/src/components/playground/OptimizedComponents.js b/web/src/components/playground/OptimizedComponents.js index baf39273..92b57bec 100644 --- a/web/src/components/playground/OptimizedComponents.js +++ b/web/src/components/playground/OptimizedComponents.js @@ -12,6 +12,7 @@ export const OptimizedMessageContent = React.memo(MessageContent, (prevProps, ne prevProps.message.content === nextProps.message.content && prevProps.message.status === nextProps.message.status && prevProps.message.role === nextProps.message.role && + prevProps.message.reasoningContent === nextProps.message.reasoningContent && prevProps.message.isReasoningExpanded === nextProps.message.isReasoningExpanded && prevProps.isEditing === nextProps.isEditing && prevProps.editValue === nextProps.editValue && diff --git a/web/src/components/playground/ThinkingContent.js b/web/src/components/playground/ThinkingContent.js index 9636b073..6cd91dd8 100644 --- a/web/src/components/playground/ThinkingContent.js +++ b/web/src/components/playground/ThinkingContent.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useRef } from 'react'; import { Typography } from '@douyinfe/semi-ui'; import MarkdownRenderer from '../common/markdown/MarkdownRenderer'; import { ChevronRight, ChevronUp, Brain, Loader2 } from 'lucide-react'; @@ -12,12 +12,19 @@ const ThinkingContent = ({ onToggleReasoningExpansion }) => { const { t } = useTranslation(); + const scrollRef = useRef(null); if (!finalExtractedThinkingContent) return null; const isThinkingStatus = message.status === 'loading' || message.status === 'incomplete'; const headerText = (isThinkingStatus && !message.isThinkingComplete) ? t('思考中...') : t('思考过程'); + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTop = scrollRef.current.scrollHeight; + } + }, [finalExtractedThinkingContent, message.isReasoningExpanded]); + return (
{ setMessage(prevMessage => { const lastMessage = prevMessage[prevMessage.length - 1]; + if (!lastMessage) return prevMessage; + if (lastMessage.role !== 'assistant') return prevMessage; if (lastMessage.status === MESSAGE_STATUS.ERROR) { return prevMessage; }