فهرست منبع

🐛 fix(chat): improve error handling and UI feedback for SSE communication

- Add comprehensive error handling for SSE events
- Implement proper error state UI with Semi Typography
- Prevent white screen issues on non-200 responses
- Add error logging for better debugging
- Enhance message state management for error conditions
- Improve user feedback with i18n error messages
- Ensure UI stability during error states
- Add try-catch blocks for JSON parsing and stream initialization
- Handle connection termination gracefully
- Preserve error states in message stream updates
Apple\Apple 9 ماه پیش
والد
کامیت
d96eb6fb1c
1فایلهای تغییر یافته به همراه52 افزوده شده و 16 حذف شده
  1. 52 16
      web/src/pages/Playground/Playground.js

+ 52 - 16
web/src/pages/Playground/Playground.js

@@ -203,6 +203,7 @@ const Playground = () => {
       method: 'POST',
       payload: JSON.stringify(payload),
     });
+
     source.addEventListener('message', (e) => {
       if (e.data === '[DONE]') {
         source.close();
@@ -210,32 +211,51 @@ const Playground = () => {
         return;
       }
 
-      let payload = JSON.parse(e.data);
-      const delta = payload.choices?.[0]?.delta;
-      if (delta) {
-        if (delta.reasoning_content) {
-          streamMessageUpdate(delta.reasoning_content, 'reasoning');
-        }
-        if (delta.content) {
-          streamMessageUpdate(delta.content, 'content');
+      try {
+        let payload = JSON.parse(e.data);
+        const delta = payload.choices?.[0]?.delta;
+        if (delta) {
+          if (delta.reasoning_content) {
+            streamMessageUpdate(delta.reasoning_content, 'reasoning');
+          }
+          if (delta.content) {
+            streamMessageUpdate(delta.content, 'content');
+          }
         }
+      } catch (error) {
+        console.error('Failed to parse SSE message:', error);
+        streamMessageUpdate(t('解析响应数据时发生错误'), 'content');
+        completeMessage('error');
       }
     });
 
     source.addEventListener('error', (e) => {
-      streamMessageUpdate(e.data, 'content');
+      console.error('SSE Error:', e);
+      const errorMessage = e.data || t('请求发生错误');
+      streamMessageUpdate(errorMessage, 'content');
       completeMessage('error');
+      source.close();
     });
 
     source.addEventListener('readystatechange', (e) => {
       if (e.readyState >= 2) {
-        if (source.status === undefined) {
+        if (source.status === undefined || source.status !== 200) {
           source.close();
-          completeMessage();
+          streamMessageUpdate(t('连接已断开'), 'content');
+          completeMessage('error');
+        } else if (source.status === 200) {
+          // 正常状态,不需要特殊处理
         }
       }
     });
-    source.stream();
+
+    try {
+      source.stream();
+    } catch (error) {
+      console.error('Failed to start SSE stream:', error);
+      streamMessageUpdate(t('建立连接时发生错误'), 'content');
+      completeMessage('error');
+    }
   };
 
   const onMessageSend = useCallback(
@@ -303,10 +323,13 @@ const Playground = () => {
     setMessage((prevMessage) => {
       const lastMessage = prevMessage[prevMessage.length - 1];
       let newMessage = { ...lastMessage };
-      if (
-        lastMessage.status === 'loading' ||
-        lastMessage.status === 'incomplete'
-      ) {
+
+      // 如果消息已经是错误状态,保持错误状态
+      if (lastMessage.status === 'error') {
+        return prevMessage;
+      }
+
+      if (lastMessage.status === 'loading' || lastMessage.status === 'incomplete') {
         if (type === 'reasoning') {
           newMessage = {
             ...newMessage,
@@ -379,6 +402,19 @@ const Playground = () => {
 
   const renderCustomChatContent = useCallback(
     ({ message, className }) => {
+      if (message.status === 'error') {
+        return (
+          <div className={className} style={{
+            display: 'flex',
+            alignItems: 'center',
+            padding: '12px',
+            color: 'var(--semi-color-danger)'
+          }}>
+            <Typography.Text type="danger">{message.content || t('请求发生错误')}</Typography.Text>
+          </div>
+        );
+      }
+
       const toggleReasoningExpansion = (messageId) => {
         setMessage(prevMessages =>
           prevMessages.map(msg =>