You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by li...@apache.org on 2026/05/30 07:34:00 UTC

(answer) branch dev updated: feat: add reasoning content to AI conversation records and update related components (#1530)

This is an automated email from the ASF dual-hosted git repository.

LinkinStars pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/answer.git


The following commit(s) were added to refs/heads/dev by this push:
     new 7c210a48 feat: add reasoning content to AI conversation records and update related components (#1530)
7c210a48 is described below

commit 7c210a4855270ef0b91d8e5471479b04f24c65ae
Author: Luke Gao <dh...@hotmail.com>
AuthorDate: Sat May 30 15:33:54 2026 +0800

    feat: add reasoning content to AI conversation records and update related components (#1530)
    
    Fix #1524
    
    Root cause
    
    DeepSeek's reasoning models stream reasoning_content alongside content.
    Answer ignored it, so follow-up requests failed with 400: The
    reasoning_content in the thinking mode must be passed back to the API,
    and the thinking text was never shown or saved.
    
    Fix
    
    - Capture reasoning_content from the stream and pass it back to theAPI
    on subsequent rounds.
     - Persist it with the conversation (new DB column via migrationv2.0.2).
    - Render it in the chat UI as a collapsible "Thinking…/Thoughts"panel
    above the answer.
    
    Compatibility
    
    Nullable column, omitempty field, UI hides the panel when empty — old
    conversations and non-reasoning models behave exactly as before.
    
    Demo
    
    
    
    https://github.com/user-attachments/assets/49b1a2a1-9133-4ac2-bbeb-860215a50285
---
 docs/docs.go                                       |  3 +
 docs/swagger.json                                  |  3 +
 docs/swagger.yaml                                  |  2 +
 i18n/en_US.yaml                                    |  2 +
 internal/controller/ai_controller.go               | 74 +++++++++++++++-------
 internal/entity/ai_conversation_record.go          |  1 +
 internal/migrations/migrations.go                  |  1 +
 internal/migrations/v33.go                         | 39 ++++++++++++
 internal/schema/ai_conversation_schema.go          |  1 +
 .../ai_conversation/ai_conversation_service.go     |  9 +++
 ui/src/common/interface.ts                         |  1 +
 ui/src/components/BubbleAi/index.tsx               | 38 +++++++++++
 .../AiAssistant/components/DetailModal/index.tsx   |  1 +
 ui/src/pages/AiAssistant/index.tsx                 | 15 +++--
 ui/src/pages/Search/components/AiCard/index.tsx    | 15 +++--
 15 files changed, 175 insertions(+), 30 deletions(-)

diff --git a/docs/docs.go b/docs/docs.go
index 57a23d43..03b06701 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -8781,6 +8781,9 @@ const docTemplate = `{
                 "helpful": {
                     "type": "integer"
                 },
+                "reasoning_content": {
+                    "type": "string"
+                },
                 "role": {
                     "type": "string"
                 },
diff --git a/docs/swagger.json b/docs/swagger.json
index dac2b38f..3bfb2e3c 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -8754,6 +8754,9 @@
                 "helpful": {
                     "type": "integer"
                 },
+                "reasoning_content": {
+                    "type": "string"
+                },
                 "role": {
                     "type": "string"
                 },
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 7a7adb68..c08f1e8d 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -228,6 +228,8 @@ definitions:
         type: integer
       helpful:
         type: integer
+      reasoning_content:
+        type: string
       role:
         type: string
       unhelpful:
diff --git a/i18n/en_US.yaml b/i18n/en_US.yaml
index 9a0d198b..61f49650 100644
--- a/i18n/en_US.yaml
+++ b/i18n/en_US.yaml
@@ -867,6 +867,8 @@ ui:
     copy: Copy
     ask_a_follow_up: Ask a follow-up
     ask_placeholder: Ask a question
+    thinking: Thinking…
+    thoughts: Thoughts
   notifications:
     title: Notifications
     inbox: Inbox
diff --git a/internal/controller/ai_controller.go b/internal/controller/ai_controller.go
index 125cdab2..e7495253 100644
--- a/internal/controller/ai_controller.go
+++ b/internal/controller/ai_controller.go
@@ -143,8 +143,9 @@ type StreamChoice struct {
 }
 
 type Delta struct {
-	Role    string `json:"role,omitempty"`
-	Content string `json:"content,omitempty"`
+	Role             string `json:"role,omitempty"`
+	Content          string `json:"content,omitempty"`
+	ReasoningContent string `json:"reasoning_content,omitempty"`
 }
 
 type Usage struct {
@@ -443,14 +444,15 @@ func (c *AIController) handleAIConversation(ctx *gin.Context, w http.ResponseWri
 			Stream:   true,
 		}
 
-		toolCalls, newMessages, finished, aiResponse := c.processAIStream(ctx, w, id, conversationCtx.Model, client, aiReq, messages)
+		toolCalls, newMessages, finished, aiResponse, reasoningContent := c.processAIStream(ctx, w, id, conversationCtx.Model, client, aiReq, messages)
 		messages = newMessages
 
 		log.Debugf("Round %d: toolCalls=%v", round+1, toolCalls)
-		if aiResponse != "" {
+		if aiResponse != "" || reasoningContent != "" {
 			conversationCtx.Messages = append(conversationCtx.Messages, &ai_conversation.ConversationMessage{
-				Role:    "assistant",
-				Content: aiResponse,
+				Role:             "assistant",
+				Content:          aiResponse,
+				ReasoningContent: reasoningContent,
 			})
 		}
 
@@ -459,7 +461,7 @@ func (c *AIController) handleAIConversation(ctx *gin.Context, w http.ResponseWri
 		}
 
 		if len(toolCalls) > 0 {
-			messages = c.executeToolCalls(ctx, w, id, conversationCtx.Model, toolCalls, messages)
+			messages = c.executeToolCalls(ctx, w, id, conversationCtx.Model, toolCalls, messages, aiResponse, reasoningContent)
 		} else {
 			return
 		}
@@ -471,12 +473,12 @@ func (c *AIController) handleAIConversation(ctx *gin.Context, w http.ResponseWri
 // processAIStream
 func (c *AIController) processAIStream(
 	_ *gin.Context, w http.ResponseWriter, id, model string, client *openai.Client, aiReq openai.ChatCompletionRequest, messages []openai.ChatCompletionMessage) (
-	[]openai.ToolCall, []openai.ChatCompletionMessage, bool, string) {
+	[]openai.ToolCall, []openai.ChatCompletionMessage, bool, string, string) {
 	stream, err := client.CreateChatCompletionStream(context.Background(), aiReq)
 	if err != nil {
 		log.Errorf("Failed to create stream: %v", err)
 		c.sendErrorResponse(w, id, model, "Failed to create AI stream")
-		return nil, messages, true, ""
+		return nil, messages, true, "", ""
 	}
 	defer func() {
 		_ = stream.Close()
@@ -484,6 +486,7 @@ func (c *AIController) processAIStream(
 
 	var currentToolCalls []openai.ToolCall
 	var accumulatedContent strings.Builder
+	var accumulatedReasoning strings.Builder
 	var accumulatedMessage openai.ChatCompletionMessage
 	toolCallsMap := make(map[int]*openai.ToolCall)
 
@@ -528,6 +531,27 @@ func (c *AIController) processAIStream(
 			}
 		}
 
+		if choice.Delta.ReasoningContent != "" {
+			accumulatedReasoning.WriteString(choice.Delta.ReasoningContent)
+
+			reasoningResponse := StreamResponse{
+				ChatCompletionID: id,
+				Object:           "chat.completion.chunk",
+				Created:          time.Now().Unix(),
+				Model:            model,
+				Choices: []StreamChoice{
+					{
+						Index: 0,
+						Delta: Delta{
+							ReasoningContent: choice.Delta.ReasoningContent,
+						},
+						FinishReason: nil,
+					},
+				},
+			}
+			sendStreamData(w, reasoningResponse)
+		}
+
 		if choice.Delta.Content != "" {
 			accumulatedContent.WriteString(choice.Delta.Content)
 
@@ -554,26 +578,30 @@ func (c *AIController) processAIStream(
 				for _, toolCall := range toolCallsMap {
 					currentToolCalls = append(currentToolCalls, *toolCall)
 				}
-				return currentToolCalls, messages, false, accumulatedContent.String()
+				return currentToolCalls, messages, false, accumulatedContent.String(), accumulatedReasoning.String()
 			} else {
 				aiResponseContent := accumulatedContent.String()
-				if aiResponseContent != "" {
+				aiReasoningContent := accumulatedReasoning.String()
+				if aiResponseContent != "" || aiReasoningContent != "" {
 					accumulatedMessage = openai.ChatCompletionMessage{
-						Role:    openai.ChatMessageRoleAssistant,
-						Content: aiResponseContent,
+						Role:             openai.ChatMessageRoleAssistant,
+						Content:          aiResponseContent,
+						ReasoningContent: aiReasoningContent,
 					}
 					messages = append(messages, accumulatedMessage)
 				}
-				return nil, messages, true, aiResponseContent
+				return nil, messages, true, aiResponseContent, aiReasoningContent
 			}
 		}
 	}
 
 	aiResponseContent := accumulatedContent.String()
-	if aiResponseContent != "" {
+	aiReasoningContent := accumulatedReasoning.String()
+	if aiResponseContent != "" || aiReasoningContent != "" {
 		accumulatedMessage = openai.ChatCompletionMessage{
-			Role:    openai.ChatMessageRoleAssistant,
-			Content: aiResponseContent,
+			Role:             openai.ChatMessageRoleAssistant,
+			Content:          aiResponseContent,
+			ReasoningContent: aiReasoningContent,
 		}
 		messages = append(messages, accumulatedMessage)
 	}
@@ -582,14 +610,14 @@ func (c *AIController) processAIStream(
 		for _, toolCall := range toolCallsMap {
 			currentToolCalls = append(currentToolCalls, *toolCall)
 		}
-		return currentToolCalls, messages, false, aiResponseContent
+		return currentToolCalls, messages, false, aiResponseContent, aiReasoningContent
 	}
 
-	return currentToolCalls, messages, len(currentToolCalls) == 0, aiResponseContent
+	return currentToolCalls, messages, len(currentToolCalls) == 0, aiResponseContent, aiReasoningContent
 }
 
 // executeToolCalls
-func (c *AIController) executeToolCalls(ctx *gin.Context, _ http.ResponseWriter, _, _ string, toolCalls []openai.ToolCall, messages []openai.ChatCompletionMessage) []openai.ChatCompletionMessage {
+func (c *AIController) executeToolCalls(ctx *gin.Context, _ http.ResponseWriter, _, _ string, toolCalls []openai.ToolCall, messages []openai.ChatCompletionMessage, assistantContent, reasoningContent string) []openai.ChatCompletionMessage {
 	validToolCalls := make([]openai.ToolCall, 0)
 	for _, toolCall := range toolCalls {
 		if toolCall.ID == "" || toolCall.Function.Name == "" {
@@ -611,8 +639,10 @@ func (c *AIController) executeToolCalls(ctx *gin.Context, _ http.ResponseWriter,
 	}
 
 	assistantMsg := openai.ChatCompletionMessage{
-		Role:      openai.ChatMessageRoleAssistant,
-		ToolCalls: validToolCalls,
+		Role:             openai.ChatMessageRoleAssistant,
+		Content:          assistantContent,
+		ReasoningContent: reasoningContent,
+		ToolCalls:        validToolCalls,
 	}
 	messages = append(messages, assistantMsg)
 
diff --git a/internal/entity/ai_conversation_record.go b/internal/entity/ai_conversation_record.go
index 14dea347..da8f11b1 100644
--- a/internal/entity/ai_conversation_record.go
+++ b/internal/entity/ai_conversation_record.go
@@ -30,6 +30,7 @@ type AIConversationRecord struct {
 	ChatCompletionID string    `xorm:"not null VARCHAR(255) chat_completion_id"`
 	Role             string    `xorm:"not null default '' VARCHAR(128) role"`
 	Content          string    `xorm:"not null MEDIUMTEXT content"`
+	ReasoningContent string    `xorm:"MEDIUMTEXT reasoning_content"`
 	Helpful          int       `xorm:"not null default 0 INT(11) helpful"`
 	Unhelpful        int       `xorm:"not null default 0 INT(11) unhelpful"`
 }
diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go
index 682a7b20..7fca2d50 100644
--- a/internal/migrations/migrations.go
+++ b/internal/migrations/migrations.go
@@ -108,6 +108,7 @@ var migrations = []Migration{
 	NewMigration("v1.8.0", "change admin menu", updateAdminMenuSettings, true),
 	NewMigration("v1.8.1", "ai feat", aiFeat, true),
 	NewMigration("v2.0.1", "change avatar type to text", updateAvatarType, false),
+	NewMigration("v2.0.2", "add reasoning content to ai conversation record", addAIConversationReasoningContent, false),
 }
 
 func GetMigrations() []Migration {
diff --git a/internal/migrations/v33.go b/internal/migrations/v33.go
new file mode 100644
index 00000000..810c2101
--- /dev/null
+++ b/internal/migrations/v33.go
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package migrations
+
+import (
+	"context"
+	"fmt"
+
+	"github.com/apache/answer/internal/entity"
+	"xorm.io/xorm"
+)
+
+// addAIConversationReasoningContent adds a reasoning_content column to the
+// ai_conversation_record table so that the chain-of-thought returned by
+// reasoning/thinking-capable models (e.g. DeepSeek) is persisted along with
+// the regular content and can be re-displayed when reloading a conversation.
+func addAIConversationReasoningContent(ctx context.Context, x *xorm.Engine) error {
+	if err := x.Context(ctx).Sync(new(entity.AIConversationRecord)); err != nil {
+		return fmt.Errorf("sync ai_conversation_record table failed: %w", err)
+	}
+	return nil
+}
diff --git a/internal/schema/ai_conversation_schema.go b/internal/schema/ai_conversation_schema.go
index fd34278a..60ec5d74 100644
--- a/internal/schema/ai_conversation_schema.go
+++ b/internal/schema/ai_conversation_schema.go
@@ -48,6 +48,7 @@ type AIConversationRecord struct {
 	ChatCompletionID string `json:"chat_completion_id"`
 	Role             string `json:"role"`
 	Content          string `json:"content"`
+	ReasoningContent string `json:"reasoning_content,omitempty"`
 	Helpful          int    `json:"helpful"`
 	Unhelpful        int    `json:"unhelpful"`
 	CreatedAt        int64  `json:"created_at"`
diff --git a/internal/service/ai_conversation/ai_conversation_service.go b/internal/service/ai_conversation/ai_conversation_service.go
index d095ac0e..b7ddc6b1 100644
--- a/internal/service/ai_conversation/ai_conversation_service.go
+++ b/internal/service/ai_conversation/ai_conversation_service.go
@@ -51,6 +51,7 @@ type ConversationMessage struct {
 	ChatCompletionID string `json:"chat_completion_id"`
 	Role             string `json:"role"`
 	Content          string `json:"content"`
+	ReasoningContent string `json:"reasoning_content,omitempty"`
 }
 
 // aiConversationService
@@ -97,6 +98,7 @@ func (s *aiConversationService) SaveConversationRecords(ctx context.Context, con
 	}
 
 	content := strings.Builder{}
+	reasoning := strings.Builder{}
 
 	for _, record := range records {
 		if len(record.ChatCompletionID) > 0 {
@@ -120,12 +122,17 @@ func (s *aiConversationService) SaveConversationRecords(ctx context.Context, con
 
 		content.WriteString(record.Content)
 		content.WriteString("\n")
+		if record.ReasoningContent != "" {
+			reasoning.WriteString(record.ReasoningContent)
+			reasoning.WriteString("\n")
+		}
 	}
 	aiRecord := &entity.AIConversationRecord{
 		ConversationID:   conversationID,
 		ChatCompletionID: chatcmplID,
 		Role:             "assistant",
 		Content:          content.String(),
+		ReasoningContent: reasoning.String(),
 		Helpful:          0,
 		Unhelpful:        0,
 	}
@@ -190,6 +197,7 @@ func (s *aiConversationService) GetConversationDetail(ctx context.Context, req *
 			ChatCompletionID: record.ChatCompletionID,
 			Role:             record.Role,
 			Content:          record.Content,
+			ReasoningContent: record.ReasoningContent,
 			Helpful:          record.Helpful,
 			Unhelpful:        record.Unhelpful,
 			CreatedAt:        record.CreatedAt.Unix(),
@@ -319,6 +327,7 @@ func (s *aiConversationService) GetConversationDetailForAdmin(ctx context.Contex
 			ChatCompletionID: record.ChatCompletionID,
 			Role:             record.Role,
 			Content:          record.Content,
+			ReasoningContent: record.ReasoningContent,
 			Helpful:          record.Helpful,
 			Unhelpful:        record.Unhelpful,
 			CreatedAt:        record.CreatedAt.Unix(),
diff --git a/ui/src/common/interface.ts b/ui/src/common/interface.ts
index 308726e8..dbcec6c0 100644
--- a/ui/src/common/interface.ts
+++ b/ui/src/common/interface.ts
@@ -860,6 +860,7 @@ export interface AdminConversationListItem {
 export interface ConversationDetailItem {
   chat_completion_id: string;
   content: string;
+  reasoning_content?: string;
   role: string;
   helpful: number;
   unhelpful: number;
diff --git a/ui/src/components/BubbleAi/index.tsx b/ui/src/components/BubbleAi/index.tsx
index 1e79ca7c..4cbd2417 100644
--- a/ui/src/components/BubbleAi/index.tsx
+++ b/ui/src/components/BubbleAi/index.tsx
@@ -32,6 +32,7 @@ interface IProps {
   isLast: boolean;
   isCompleted: boolean;
   content: string;
+  reasoningContent?: string;
   minHeight?: number;
   actionData: {
     helpful: number;
@@ -55,6 +56,7 @@ const BubbleAi: FC<IProps> = ({
   isLast,
   isCompleted,
   content,
+  reasoningContent = '',
   chatId = '',
   actionData,
   minHeight = 0,
@@ -65,6 +67,7 @@ const BubbleAi: FC<IProps> = ({
   const [isHelpful, setIsHelpful] = useState(false);
   const [isUnhelpful, setIsUnhelpful] = useState(false);
   const [canShowAction, setCanShowAction] = useState(false);
+  const [isThinkingOpen, setIsThinkingOpen] = useState(true);
   const [safeHtml, setSafeHtml] = useState('');
   const typewriterRef = useRef<{
     timer: NodeJS.Timeout | null;
@@ -255,6 +258,14 @@ const BubbleAi: FC<IProps> = ({
     setIsUnhelpful(actionData.unhelpful > 0);
   }, [actionData]);
 
+  // Auto-collapse the "Thinking" panel once the actual answer starts streaming
+  // (only while the message is being generated; users can still toggle manually).
+  useEffect(() => {
+    if (content && !isCompleted) {
+      setIsThinkingOpen(false);
+    }
+  }, [content, isCompleted]);
+
   useEffect(() => {
     if (fmtContainer.current && isCompleted && safeHtml) {
       htmlRender(fmtContainer.current, {
@@ -275,6 +286,33 @@ const BubbleAi: FC<IProps> = ({
       ref={containerRef}
       style={{ minHeight: `${minHeight}px`, overflowAnchor: 'none' }}>
       <div id={chatId}>
+        {reasoningContent ? (
+          <div
+            className="bubble-ai-thinking mb-2 border-start border-2 ps-2 small text-secondary"
+            style={{ borderColor: 'var(--bs-border-color)' }}>
+            <Button
+              variant="link"
+              className="p-0 link-secondary small text-decoration-none d-inline-flex align-items-center"
+              onClick={() => setIsThinkingOpen((v) => !v)}>
+              <Icon name={isThinkingOpen ? 'chevron-down' : 'chevron-right'} />
+              <span className="ms-1">
+                {isCompleted ? t('thoughts') : t('thinking')}
+              </span>
+            </Button>
+            {isThinkingOpen && (
+              <div
+                className="mt-1 text-secondary"
+                style={{
+                  whiteSpace: 'pre-wrap',
+                  fontStyle: 'italic',
+                  opacity: 0.85,
+                }}>
+                {reasoningContent}
+              </div>
+            )}
+          </div>
+        ) : null}
+
         <div
           className="fmt text-break text-wrap"
           ref={fmtContainer}
diff --git a/ui/src/pages/Admin/AiAssistant/components/DetailModal/index.tsx b/ui/src/pages/Admin/AiAssistant/components/DetailModal/index.tsx
index ec4bd7d7..02bbc682 100644
--- a/ui/src/pages/Admin/AiAssistant/components/DetailModal/index.tsx
+++ b/ui/src/pages/Admin/AiAssistant/components/DetailModal/index.tsx
@@ -64,6 +64,7 @@ const Index: FC<IProps> = ({ visible, id, onClose }) => {
                   isLast={false}
                   isCompleted
                   content={item.content}
+                  reasoningContent={item.reasoning_content || ''}
                   actionData={{
                     helpful: item.helpful,
                     unhelpful: item.unhelpful,
diff --git a/ui/src/pages/AiAssistant/index.tsx b/ui/src/pages/AiAssistant/index.tsx
index 83ffe8f1..e355e804 100644
--- a/ui/src/pages/AiAssistant/index.tsx
+++ b/ui/src/pages/AiAssistant/index.tsx
@@ -154,7 +154,10 @@ const Index = () => {
     await requestAi('/answer/api/v1/chat/completions', {
       body: JSON.stringify(params),
       onMessage: (res) => {
-        if (!res.choices[0].delta?.content) {
+        const delta = res.choices[0]?.delta;
+        const deltaContent = delta?.content || '';
+        const deltaReasoning = delta?.reasoning_content || '';
+        if (!deltaContent && !deltaReasoning) {
           return;
         }
         setIsLoading(false);
@@ -165,13 +168,16 @@ const Index = () => {
           if (lastConversion?.chat_completion_id === res?.chat_completion_id) {
             updatedRecords[updatedRecords.length - 1] = {
               ...lastConversion,
-              content: lastConversion.content + res.choices[0].delta.content,
+              content: (lastConversion.content || '') + deltaContent,
+              reasoning_content:
+                (lastConversion.reasoning_content || '') + deltaReasoning,
             };
           } else {
             updatedRecords.push({
               chat_completion_id: res.chat_completion_id,
-              role: res.choices[0].delta.role || 'assistant',
-              content: res.choices[0].delta.content,
+              role: delta?.role || 'assistant',
+              content: deltaContent,
+              reasoning_content: deltaReasoning,
               helpful: 0,
               unhelpful: 0,
               created_at: Date.now(),
@@ -330,6 +336,7 @@ const Index = () => {
                         isLast={isLastMessage}
                         isCompleted={!isGenerate || !isLastMessage}
                         content={item.content}
+                        reasoningContent={item.reasoning_content || ''}
                         actionData={{
                           helpful: item.helpful,
                           unhelpful: item.unhelpful,
diff --git a/ui/src/pages/Search/components/AiCard/index.tsx b/ui/src/pages/Search/components/AiCard/index.tsx
index 99e21adc..c6cc699c 100644
--- a/ui/src/pages/Search/components/AiCard/index.tsx
+++ b/ui/src/pages/Search/components/AiCard/index.tsx
@@ -78,7 +78,10 @@ const Index = () => {
     await requestAi('/answer/api/v1/chat/completions', {
       body: JSON.stringify(params),
       onMessage: (res) => {
-        if (!res.choices[0].delta?.content) {
+        const delta = res.choices[0]?.delta;
+        const deltaContent = delta?.content || '';
+        const deltaReasoning = delta?.reasoning_content || '';
+        if (!deltaContent && !deltaReasoning) {
           return;
         }
         setIsLoading(false);
@@ -90,13 +93,16 @@ const Index = () => {
           if (lastConversion?.chat_completion_id === res?.chat_completion_id) {
             updatedRecords[updatedRecords.length - 1] = {
               ...lastConversion,
-              content: lastConversion.content + res.choices[0].delta.content,
+              content: (lastConversion.content || '') + deltaContent,
+              reasoning_content:
+                (lastConversion.reasoning_content || '') + deltaReasoning,
             };
           } else {
             updatedRecords.push({
               chat_completion_id: res.chat_completion_id,
-              role: res.choices[0].delta.role || 'assistant',
-              content: res.choices[0].delta.content,
+              role: delta?.role || 'assistant',
+              content: deltaContent,
+              reasoning_content: deltaReasoning,
               helpful: 0,
               unhelpful: 0,
               created_at: Date.now(),
@@ -154,6 +160,7 @@ const Index = () => {
                   isLast={isLastMessage}
                   isCompleted={!isGenerate || !isLastMessage}
                   content={item.content}
+                  reasoningContent={item.reasoning_content || ''}
                   actionData={{
                     helpful: item.helpful,
                     unhelpful: item.unhelpful,