🐛 fix(playground): ensure proper streaming updates & safeguard message handling

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.
This commit is contained in:
Apple\Apple
2025-06-02 06:21:05 +08:00
parent 70b673d12c
commit 1d37867f39
3 changed files with 12 additions and 1 deletions

View File

@@ -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 &&

View File

@@ -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 (
<div className="rounded-xl sm:rounded-2xl mb-2 sm:mb-4 overflow-hidden shadow-sm backdrop-blur-sm">
<div
@@ -73,6 +80,7 @@ const ThinkingContent = ({
{message.isReasoningExpanded && (
<div className="p-3 sm:p-5 pt-2 sm:pt-4">
<div
ref={scrollRef}
className="bg-white/70 backdrop-blur-sm rounded-lg sm:rounded-xl p-2 shadow-inner overflow-x-auto overflow-y-auto thinking-content-scroll"
style={{
maxHeight: '200px',

View File

@@ -38,6 +38,8 @@ export const useApiRequest = (
const streamMessageUpdate = useCallback((textChunk, type) => {
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;
}