@@ -18,10 +18,12 @@ import {
18
18
LangGraphEventTypes ,
19
19
State ,
20
20
MessagesInProgressRecord ,
21
+ ThinkingInProgress ,
21
22
SchemaKeys ,
22
23
MessageInProgress ,
23
24
RunMetadata ,
24
25
PredictStateTool ,
26
+ LangGraphReasoning
25
27
} from "./types" ;
26
28
import {
27
29
AbstractAgent ,
@@ -44,6 +46,11 @@ import {
44
46
ToolCallArgsEvent ,
45
47
ToolCallEndEvent ,
46
48
ToolCallStartEvent ,
49
+ ThinkingTextMessageStartEvent ,
50
+ ThinkingTextMessageContentEvent ,
51
+ ThinkingTextMessageEndEvent ,
52
+ ThinkingStartEvent ,
53
+ ThinkingEndEvent ,
47
54
} from "@ag-ui/client" ;
48
55
import { RunsStreamPayload } from "@langchain/langgraph-sdk/dist/types" ;
49
56
import {
@@ -52,15 +59,22 @@ import {
52
59
filterObjectBySchemaKeys ,
53
60
getStreamPayloadInput ,
54
61
langchainMessagesToAgui ,
62
+ resolveMessageContent ,
63
+ resolveReasoningContent
55
64
} from "@/utils" ;
56
65
57
66
export type ProcessedEvents =
58
67
| TextMessageStartEvent
59
68
| TextMessageContentEvent
60
69
| TextMessageEndEvent
70
+ | ThinkingTextMessageStartEvent
71
+ | ThinkingTextMessageContentEvent
72
+ | ThinkingTextMessageEndEvent
61
73
| ToolCallStartEvent
62
74
| ToolCallArgsEvent
63
75
| ToolCallEndEvent
76
+ | ThinkingStartEvent
77
+ | ThinkingEndEvent
64
78
| StateSnapshotEvent
65
79
| StateDeltaEvent
66
80
| MessagesSnapshotEvent
@@ -98,6 +112,7 @@ export class LangGraphAgent extends AbstractAgent {
98
112
graphId : string ;
99
113
assistant ?: Assistant ;
100
114
messagesInProcess : MessagesInProgressRecord ;
115
+ thinkingProcess : null | ThinkingInProgress ;
101
116
activeRun ?: RunMetadata ;
102
117
// @ts -expect-error no need to initialize subscriber right now
103
118
subscriber : Subscriber < ProcessedEvents > ;
@@ -108,6 +123,7 @@ export class LangGraphAgent extends AbstractAgent {
108
123
this . agentName = config . agentName ;
109
124
this . graphId = config . graphId ;
110
125
this . assistantConfig = config . assistantConfig ;
126
+ this . thinkingProcess = null
111
127
this . client =
112
128
config ?. client ??
113
129
new LangGraphClient ( {
@@ -389,7 +405,7 @@ export class LangGraphAgent extends AbstractAgent {
389
405
let shouldEmitToolCalls = event . metadata [ "emit-tool-calls" ] ?? true ;
390
406
391
407
if ( event . data . chunk . response_metadata . finish_reason ) return ;
392
- const currentStream = this . getMessageInProgress ( this . activeRun ! . id ) ;
408
+ let currentStream = this . getMessageInProgress ( this . activeRun ! . id ) ;
393
409
const hasCurrentStream = Boolean ( currentStream ?. id ) ;
394
410
const toolCallData = event . data . chunk . tool_call_chunks ?. [ 0 ] ;
395
411
const toolCallUsedToPredictState = event . metadata [ "predict_state" ] ?. some (
@@ -401,11 +417,28 @@ export class LangGraphAgent extends AbstractAgent {
401
417
hasCurrentStream && currentStream ?. toolCallId && toolCallData . args ;
402
418
const isToolCallEndEvent = hasCurrentStream && currentStream ?. toolCallId && ! toolCallData ;
403
419
404
- const isMessageStartEvent = ! hasCurrentStream && ! toolCallData ;
405
- const isMessageContentEvent = hasCurrentStream && ! toolCallData ;
420
+ const reasoningData = resolveReasoningContent ( event . data ) ;
421
+ const messageContent = resolveMessageContent ( event . data . chunk . content ) ;
422
+ const isMessageContentEvent = Boolean ( ! toolCallData && messageContent ) ;
423
+
406
424
const isMessageEndEvent =
407
425
hasCurrentStream && ! currentStream ?. toolCallId && ! isMessageContentEvent ;
408
426
427
+ if ( reasoningData ) {
428
+ this . handleThinkingEvent ( reasoningData )
429
+ break ;
430
+ }
431
+
432
+ if ( ! reasoningData && this . thinkingProcess ) {
433
+ this . dispatchEvent ( {
434
+ type : EventType . THINKING_TEXT_MESSAGE_END ,
435
+ } )
436
+ this . dispatchEvent ( {
437
+ type : EventType . THINKING_END ,
438
+ } )
439
+ this . thinkingProcess = null ;
440
+ }
441
+
409
442
if ( toolCallUsedToPredictState ) {
410
443
this . dispatchEvent ( {
411
444
type : EventType . CUSTOM ,
@@ -417,7 +450,7 @@ export class LangGraphAgent extends AbstractAgent {
417
450
if ( isToolCallEndEvent ) {
418
451
const resolved = this . dispatchEvent ( {
419
452
type : EventType . TOOL_CALL_END ,
420
- toolCallId : currentStream . toolCallId ! ,
453
+ toolCallId : currentStream ? .toolCallId ! ,
421
454
rawEvent : event ,
422
455
} ) ;
423
456
if ( resolved ) {
@@ -460,36 +493,35 @@ export class LangGraphAgent extends AbstractAgent {
460
493
if ( isToolCallArgsEvent && shouldEmitToolCalls ) {
461
494
this . dispatchEvent ( {
462
495
type : EventType . TOOL_CALL_ARGS ,
463
- toolCallId : currentStream . toolCallId ! ,
496
+ toolCallId : currentStream ? .toolCallId ! ,
464
497
delta : toolCallData . args ,
465
498
rawEvent : event ,
466
499
} ) ;
467
500
break ;
468
501
}
469
502
470
- // Message started: emit TextMessageStart
471
- if ( isMessageStartEvent && shouldEmitMessages ) {
472
- const resolved = this . dispatchEvent ( {
473
- type : EventType . TEXT_MESSAGE_START ,
474
- role : "assistant" ,
475
- messageId : event . data . chunk . id ,
476
- rawEvent : event ,
477
- } ) ;
478
- if ( resolved ) {
503
+ // Message content: emit TextMessageContent
504
+ if ( isMessageContentEvent && shouldEmitMessages ) {
505
+ // No existing message yet, also init the message
506
+ if ( ! currentStream ) {
507
+ this . dispatchEvent ( {
508
+ type : EventType . TEXT_MESSAGE_START ,
509
+ role : "assistant" ,
510
+ messageId : event . data . chunk . id ,
511
+ rawEvent : event ,
512
+ } ) ;
479
513
this . setMessageInProgress ( this . activeRun ! . id , {
480
514
id : event . data . chunk . id ,
481
515
toolCallId : null ,
482
516
toolCallName : null ,
483
517
} ) ;
518
+ currentStream = this . getMessageInProgress ( this . activeRun ! . id ) ;
484
519
}
485
- break ;
486
- }
487
- // Message content: emit TextMessageContent
488
- if ( isMessageContentEvent && shouldEmitMessages ) {
520
+
489
521
this . dispatchEvent ( {
490
522
type : EventType . TEXT_MESSAGE_CONTENT ,
491
523
messageId : currentStream ! . id ,
492
- delta : event . data . chunk . content ,
524
+ delta : messageContent ! ,
493
525
rawEvent : event ,
494
526
} ) ;
495
527
break ;
@@ -583,6 +615,51 @@ export class LangGraphAgent extends AbstractAgent {
583
615
}
584
616
}
585
617
618
+ handleThinkingEvent ( reasoningData : LangGraphReasoning ) {
619
+ if ( ! reasoningData || ! reasoningData . type || ! reasoningData . text ) {
620
+ return ;
621
+ }
622
+
623
+ const thinkingStepIndex = reasoningData . index ;
624
+
625
+ if ( this . thinkingProcess ?. index && this . thinkingProcess . index !== thinkingStepIndex ) {
626
+ if ( this . thinkingProcess . type ) {
627
+ this . dispatchEvent ( {
628
+ type : EventType . THINKING_TEXT_MESSAGE_END ,
629
+ } )
630
+ }
631
+ this . dispatchEvent ( {
632
+ type : EventType . THINKING_END ,
633
+ } )
634
+ this . thinkingProcess = null ;
635
+ }
636
+
637
+ if ( ! this . thinkingProcess ) {
638
+ // No thinking step yet. Start a new one
639
+ this . dispatchEvent ( {
640
+ type : EventType . THINKING_START ,
641
+ } )
642
+ this . thinkingProcess = {
643
+ index : thinkingStepIndex ,
644
+ } ;
645
+ }
646
+
647
+
648
+ if ( this . thinkingProcess . type !== reasoningData . type ) {
649
+ this . dispatchEvent ( {
650
+ type : EventType . THINKING_TEXT_MESSAGE_START ,
651
+ } )
652
+ this . thinkingProcess . type = reasoningData . type
653
+ }
654
+
655
+ if ( this . thinkingProcess . type ) {
656
+ this . dispatchEvent ( {
657
+ type : EventType . THINKING_TEXT_MESSAGE_CONTENT ,
658
+ delta : reasoningData . text
659
+ } )
660
+ }
661
+ }
662
+
586
663
getStateSnapshot ( state : State ) {
587
664
const schemaKeys = this . activeRun ! . schemaKeys ! ;
588
665
// Do not emit state keys that are not part of the output schema
0 commit comments