User Story
As a user chatting with the AI, I want to see the AI's detailed reasoning ("thought process") as it generates an answer in real-time, so that I can follow how the answer is derived, build trust in the result, and better understand its sources.
Context & Motivation
Currently, while the AI "thinks", the user only sees a 3-dot typing indicator accompanied by high-level orchestrator states (e.g., "Searching knowledge base..."). With the introduction of LLM providers that emit a dedicated reasoning channel (e.g., DeepSeek-R1, Qwen, or Anthropic's extended thinking), we want to expose this raw text stream to the user to increase transparency.
Acceptance Criteria
Given the backend streams a new SSE event {"type": "reasoning", "content": "..."}, when the frontend receives it, then the content is accumulated in the active assistant message's state.
Given a message has accumulated reasoning text, when it is rendering, then a collapsible "Thinking…" panel is displayed above the final answer content.
Given the reasoning panel is displayed, when reasoning text streams in, then it replaces/shadows the default 3-dot typing indicator.
Given an active model does not emit reasoning events, when the assistant is thinking, then the chat behaves exactly as it does today (gracefully falling back to the 3-dot indicator and tool states).
Edge case: Given the reasoning phase takes a long time (e.g., >4s), when the reasoning stream is active, then the UI continues to show the reasoning text without triggering the fallback "brainrot" timeout indicator.
Edge case: Given malformed or empty reasoning deltas arrive from the backend, when parsing, then they are skipped gracefully without breaking the stream.
Technical Notes
API & Types (src/features/chatbot/types.ts & src/services/chatService.ts):
Extend the ChatEvent union to include type: "reasoning".
Update StreamHandlers to include onReasoning: (reasoning: string) => void.
In streamMessage(), add a switch case for "reasoning" that invokes the new handler.
Add an optional reasoning?: string property to the ChatMessage type.
State Management (src/context/ChatProvider.tsx):
Implement the onReasoning callback inside sendMessage to append the incoming string chunks to the active assistant message's reasoning state property.
UI Components (src/pages/ChatPage.tsx):
Build a new collapsible panel component (or inline it).
When message.reasoning is populated, render this panel instead of (or replacing) the static isThinking bouncing dots block (around lines 479-489).
Decoupled Development: The frontend team can implement and test this UI immediately. If the backend hasn't implemented the passthrough yet, the frontend will just safely ignore it and act as it currently does. Mock the SSE event locally if needed for styling the panel.
User Story
As a user chatting with the AI, I want to see the AI's detailed reasoning ("thought process") as it generates an answer in real-time, so that I can follow how the answer is derived, build trust in the result, and better understand its sources.
Context & Motivation
Currently, while the AI "thinks", the user only sees a 3-dot typing indicator accompanied by high-level orchestrator states (e.g., "Searching knowledge base..."). With the introduction of LLM providers that emit a dedicated reasoning channel (e.g., DeepSeek-R1, Qwen, or Anthropic's extended thinking), we want to expose this raw text stream to the user to increase transparency.
Acceptance Criteria
Given the backend streams a new SSE event {"type": "reasoning", "content": "..."}, when the frontend receives it, then the content is accumulated in the active assistant message's state.
Given a message has accumulated reasoning text, when it is rendering, then a collapsible "Thinking…" panel is displayed above the final answer content.
Given the reasoning panel is displayed, when reasoning text streams in, then it replaces/shadows the default 3-dot typing indicator.
Given an active model does not emit reasoning events, when the assistant is thinking, then the chat behaves exactly as it does today (gracefully falling back to the 3-dot indicator and tool states).
Edge case: Given the reasoning phase takes a long time (e.g., >4s), when the reasoning stream is active, then the UI continues to show the reasoning text without triggering the fallback "brainrot" timeout indicator.
Edge case: Given malformed or empty reasoning deltas arrive from the backend, when parsing, then they are skipped gracefully without breaking the stream.
Technical Notes
API & Types (src/features/chatbot/types.ts & src/services/chatService.ts):
Extend the ChatEvent union to include type: "reasoning".
Update StreamHandlers to include onReasoning: (reasoning: string) => void.
In streamMessage(), add a switch case for "reasoning" that invokes the new handler.
Add an optional reasoning?: string property to the ChatMessage type.
State Management (src/context/ChatProvider.tsx):
Implement the onReasoning callback inside sendMessage to append the incoming string chunks to the active assistant message's reasoning state property.
UI Components (src/pages/ChatPage.tsx):
Build a new collapsible panel component (or inline it).
When message.reasoning is populated, render this panel instead of (or replacing) the static isThinking bouncing dots block (around lines 479-489).
Decoupled Development: The frontend team can implement and test this UI immediately. If the backend hasn't implemented the passthrough yet, the frontend will just safely ignore it and act as it currently does. Mock the SSE event locally if needed for styling the panel.