@@ -275,36 +275,38 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
275275 reject ( new Error ( "WebSocket connection timeout" ) ) ;
276276 } , 800 ) ;
277277
278+ const ensureAssistantCreated = ( initialThoughts ?: string [ ] , initialSources ?: SourceChunk [ ] ) => {
279+ if ( ! assistantCreated ) {
280+ assistantCreated = true ;
281+ setIsTyping ( false ) ;
282+ const assistantMsg : ChatMsg = {
283+ id : assistantId ,
284+ role : "assistant" ,
285+ content : "" ,
286+ sources : initialSources || [ ] ,
287+ isStreaming : true ,
288+ thoughts : initialThoughts || [ ] ,
289+ } ;
290+ setMessages ( ( prev ) => [ ...prev , assistantMsg ] ) ;
291+ }
292+ } ;
293+
278294 ws . onmessage = ( ev ) => {
279295 clearTimeout ( connectTimeout ) ;
280296 try {
281297 const event = JSON . parse ( ev . data ) ;
282298 if ( event . type === "token" ) {
283- if ( ! assistantCreated ) {
284- assistantCreated = true ;
285- setIsTyping ( false ) ;
286-
287- const assistantMsg : ChatMsg = {
288- id : assistantId ,
289- role : "assistant" ,
290- content : event . data as string ,
291- sources : [ ] ,
292- isStreaming : true ,
293- } ;
294-
295- setMessages ( ( prev ) => [ ...prev , assistantMsg ] ) ;
296- } else {
297- setMessages ( ( prev ) =>
298- prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : m . content + ( event . data as string ) } : m ) )
299- ) ;
300- }
299+ ensureAssistantCreated ( ) ;
300+ setMessages ( ( prev ) =>
301+ prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : m . content + ( event . data as string ) } : m ) )
302+ ) ;
301303 } else if ( event . type === "sources" ) {
304+ ensureAssistantCreated ( undefined , event . data as SourceChunk [ ] ) ;
302305 setMessages ( ( prev ) => prev . map ( ( m ) => ( m . id === assistantId ? { ...m , sources : event . data as SourceChunk [ ] } : m ) ) ) ;
303306 } else if ( event . type === "thought" ) {
304- // Append thoughts as a temporary assistant note (optional UI handling)
305- // For simplicity, add to assistant message content in brackets
307+ ensureAssistantCreated ( [ event . data as string ] ) ;
306308 setMessages ( ( prev ) =>
307- prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : m . content + `\n[thought] ${ event . data } ` } : m ) )
309+ prev . map ( ( m ) => ( m . id === assistantId ? { ...m , thoughts : [ ... ( m . thoughts || [ ] ) , event . data as string ] } : m ) )
308310 ) ;
309311 } else if ( event . type === "error" ) {
310312 setIsTyping ( false ) ;
@@ -354,28 +356,37 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
354356 session_id : activeSessionId ,
355357 } , abortController . signal ) ;
356358
359+ let sseAssistantCreated = false ;
360+ const ensureSseAssistantCreated = ( initialThoughts ?: string [ ] , initialSources ?: SourceChunk [ ] ) => {
361+ if ( ! sseAssistantCreated ) {
362+ sseAssistantCreated = true ;
363+ setIsTyping ( false ) ;
364+ const assistantMsg : ChatMsg = {
365+ id : assistantId ,
366+ role : "assistant" ,
367+ content : "" ,
368+ sources : initialSources || [ ] ,
369+ isStreaming : true ,
370+ thoughts : initialThoughts || [ ] ,
371+ } ;
372+ setMessages ( ( prev ) => [ ...prev , assistantMsg ] ) ;
373+ }
374+ } ;
375+
357376 for await ( const event of stream ) {
358377 if ( event . type === "token" ) {
359- if ( ! assistantCreated ) {
360- assistantCreated = true ;
361- setIsTyping ( false ) ;
362-
363- const assistantMsg : ChatMsg = {
364- id : assistantId ,
365- role : "assistant" ,
366- content : event . data as string ,
367- sources : [ ] ,
368- isStreaming : true ,
369- } ;
370-
371- setMessages ( ( prev ) => [ ...prev , assistantMsg ] ) ;
372- } else {
373- setMessages ( ( prev ) =>
374- prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : m . content + ( event . data as string ) } : m ) )
375- ) ;
376- }
378+ ensureSseAssistantCreated ( ) ;
379+ setMessages ( ( prev ) =>
380+ prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : m . content + ( event . data as string ) } : m ) )
381+ ) ;
377382 } else if ( event . type === "sources" ) {
383+ ensureSseAssistantCreated ( undefined , event . data as SourceChunk [ ] ) ;
378384 setMessages ( ( prev ) => prev . map ( ( m ) => ( m . id === assistantId ? { ...m , sources : event . data as SourceChunk [ ] } : m ) ) ) ;
385+ } else if ( event . type === "thought" ) {
386+ ensureSseAssistantCreated ( [ event . data as string ] ) ;
387+ setMessages ( ( prev ) =>
388+ prev . map ( ( m ) => ( m . id === assistantId ? { ...m , thoughts : [ ...( m . thoughts || [ ] ) , event . data as string ] } : m ) )
389+ ) ;
379390 } else if ( event . type === "error" ) {
380391 setIsTyping ( false ) ;
381392 setMessages ( ( prev ) => prev . map ( ( m ) => ( m . id === assistantId ? { ...m , content : `Error: ${ event . data } ` , isStreaming : false } : m ) ) ) ;
0 commit comments