@@ -8,6 +8,7 @@ import type { Message } from 'ai'
88import { type Servers , type ToolItem } from '../lib/schemas'
99import { ToolCallMessage } from './ToolCallMessage'
1010import { Toolbox } from './Toolbox'
11+ import { ReasoningMessage } from './ReasoningMessage'
1112import { useModel } from '../contexts/ModelContext'
1213import { useUser } from '../contexts/UserContext'
1314import { Button } from './ui/button'
@@ -34,6 +35,15 @@ type StreamEvent =
3435 | 'arguments_done'
3536 }
3637 | { type : 'user' ; id : string ; content : string }
38+ | {
39+ type : 'reasoning'
40+ effort : string
41+ summary : string | null
42+ model ?: string
43+ serviceTier ?: string
44+ temperature ?: number
45+ topP ?: number
46+ }
3747
3848// Helper function to map tool types to status
3949const getToolStatus = (
@@ -112,6 +122,84 @@ export function Chat() {
112122 try {
113123 const toolState = JSON . parse ( line . slice ( 2 ) )
114124
125+ // Handle reasoning summary streaming
126+ if ( toolState . type === 'reasoning_summary_delta' ) {
127+ setStreamBuffer ( ( prev ) => {
128+ // Find the last reasoning message
129+ const last = prev [ prev . length - 1 ]
130+ if ( last && last . type === 'reasoning' && ! last . done ) {
131+ // Append delta to summary
132+ return [
133+ ...prev . slice ( 0 , - 1 ) ,
134+ {
135+ ...last ,
136+ summary : ( last . summary || '' ) + toolState . delta ,
137+ effort : toolState . effort || last . effort ,
138+ model : toolState . model || last . model ,
139+ serviceTier : toolState . serviceTier || last . serviceTier ,
140+ temperature : toolState . temperature ?? last . temperature ,
141+ topP : toolState . topP ?? last . topP ,
142+ } ,
143+ ]
144+ } else {
145+ // Start a new reasoning message
146+ return [
147+ ...prev ,
148+ {
149+ type : 'reasoning' ,
150+ summary : toolState . delta ,
151+ effort : toolState . effort || '' ,
152+ model : toolState . model ,
153+ serviceTier : toolState . serviceTier ,
154+ temperature : toolState . temperature ,
155+ topP : toolState . topP ,
156+ done : false ,
157+ } ,
158+ ]
159+ }
160+ } )
161+ return
162+ }
163+
164+ if ( toolState . type === 'reasoning_summary_done' ) {
165+ setStreamBuffer ( ( prev ) => {
166+ // Mark the last reasoning message as done
167+ const last = prev [ prev . length - 1 ]
168+ if ( last && last . type === 'reasoning' && ! last . done ) {
169+ return [
170+ ...prev . slice ( 0 , - 1 ) ,
171+ {
172+ ...last ,
173+ done : true ,
174+ effort : toolState . effort || last . effort ,
175+ model : toolState . model || last . model ,
176+ serviceTier : toolState . serviceTier || last . serviceTier ,
177+ temperature : toolState . temperature ?? last . temperature ,
178+ topP : toolState . topP ?? last . topP ,
179+ } ,
180+ ]
181+ }
182+ return prev
183+ } )
184+ return
185+ }
186+
187+ if ( toolState . type === 'reasoning' ) {
188+ setStreamBuffer ( ( prev ) => [
189+ ...prev ,
190+ {
191+ type : 'reasoning' ,
192+ effort : toolState . effort ,
193+ summary : toolState . summary ,
194+ model : toolState . model ,
195+ serviceTier : toolState . serviceTier ,
196+ temperature : toolState . temperature ,
197+ topP : toolState . topP ,
198+ } ,
199+ ] )
200+ return
201+ }
202+
115203 if ( 'delta' in toolState ) {
116204 try {
117205 toolState . delta =
@@ -323,6 +411,19 @@ export function Chat() {
323411 />
324412 )
325413 }
414+ } else if ( 'type' in event && event . type === 'reasoning' ) {
415+ return (
416+ < ReasoningMessage
417+ key = { `reasoning-${ idx } -${ event . effort } -${ event . summary || '' } ` }
418+ effort = { event . effort }
419+ summary = { event . summary }
420+ model = { event . model }
421+ serviceTier = { event . serviceTier }
422+ temperature = { event . temperature }
423+ topP = { event . topP }
424+ isLoading = { streaming && idx === renderEvents . length - 1 }
425+ />
426+ )
326427 } else if ( 'type' in event && event . type === 'assistant' ) {
327428 const assistantEvent = event as Extract <
328429 StreamEvent ,
0 commit comments