@@ -18,10 +18,12 @@ import {
1818 LangGraphEventTypes ,
1919 State ,
2020 MessagesInProgressRecord ,
21+ ThinkingInProgress ,
2122 SchemaKeys ,
2223 MessageInProgress ,
2324 RunMetadata ,
2425 PredictStateTool ,
26+ LangGraphReasoning
2527} from "./types" ;
2628import {
2729 AbstractAgent ,
@@ -44,6 +46,11 @@ import {
4446 ToolCallArgsEvent ,
4547 ToolCallEndEvent ,
4648 ToolCallStartEvent ,
49+ ThinkingTextMessageStartEvent ,
50+ ThinkingTextMessageContentEvent ,
51+ ThinkingTextMessageEndEvent ,
52+ ThinkingStartEvent ,
53+ ThinkingEndEvent ,
4754} from "@ag-ui/client" ;
4855import { RunsStreamPayload } from "@langchain/langgraph-sdk/dist/types" ;
4956import {
@@ -52,15 +59,22 @@ import {
5259 filterObjectBySchemaKeys ,
5360 getStreamPayloadInput ,
5461 langchainMessagesToAgui ,
62+ resolveMessageContent ,
63+ resolveReasoningContent
5564} from "@/utils" ;
5665
5766export type ProcessedEvents =
5867 | TextMessageStartEvent
5968 | TextMessageContentEvent
6069 | TextMessageEndEvent
70+ | ThinkingTextMessageStartEvent
71+ | ThinkingTextMessageContentEvent
72+ | ThinkingTextMessageEndEvent
6173 | ToolCallStartEvent
6274 | ToolCallArgsEvent
6375 | ToolCallEndEvent
76+ | ThinkingStartEvent
77+ | ThinkingEndEvent
6478 | StateSnapshotEvent
6579 | StateDeltaEvent
6680 | MessagesSnapshotEvent
@@ -98,6 +112,7 @@ export class LangGraphAgent extends AbstractAgent {
98112 graphId : string ;
99113 assistant ?: Assistant ;
100114 messagesInProcess : MessagesInProgressRecord ;
115+ thinkingProcess : null | ThinkingInProgress ;
101116 activeRun ?: RunMetadata ;
102117 // @ts -expect-error no need to initialize subscriber right now
103118 subscriber : Subscriber < ProcessedEvents > ;
@@ -108,6 +123,7 @@ export class LangGraphAgent extends AbstractAgent {
108123 this . agentName = config . agentName ;
109124 this . graphId = config . graphId ;
110125 this . assistantConfig = config . assistantConfig ;
126+ this . thinkingProcess = null
111127 this . client =
112128 config ?. client ??
113129 new LangGraphClient ( {
@@ -389,7 +405,7 @@ export class LangGraphAgent extends AbstractAgent {
389405 let shouldEmitToolCalls = event . metadata [ "emit-tool-calls" ] ?? true ;
390406
391407 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 ) ;
393409 const hasCurrentStream = Boolean ( currentStream ?. id ) ;
394410 const toolCallData = event . data . chunk . tool_call_chunks ?. [ 0 ] ;
395411 const toolCallUsedToPredictState = event . metadata [ "predict_state" ] ?. some (
@@ -401,11 +417,28 @@ export class LangGraphAgent extends AbstractAgent {
401417 hasCurrentStream && currentStream ?. toolCallId && toolCallData . args ;
402418 const isToolCallEndEvent = hasCurrentStream && currentStream ?. toolCallId && ! toolCallData ;
403419
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+
406424 const isMessageEndEvent =
407425 hasCurrentStream && ! currentStream ?. toolCallId && ! isMessageContentEvent ;
408426
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+
409442 if ( toolCallUsedToPredictState ) {
410443 this . dispatchEvent ( {
411444 type : EventType . CUSTOM ,
@@ -417,7 +450,7 @@ export class LangGraphAgent extends AbstractAgent {
417450 if ( isToolCallEndEvent ) {
418451 const resolved = this . dispatchEvent ( {
419452 type : EventType . TOOL_CALL_END ,
420- toolCallId : currentStream . toolCallId ! ,
453+ toolCallId : currentStream ? .toolCallId ! ,
421454 rawEvent : event ,
422455 } ) ;
423456 if ( resolved ) {
@@ -460,36 +493,35 @@ export class LangGraphAgent extends AbstractAgent {
460493 if ( isToolCallArgsEvent && shouldEmitToolCalls ) {
461494 this . dispatchEvent ( {
462495 type : EventType . TOOL_CALL_ARGS ,
463- toolCallId : currentStream . toolCallId ! ,
496+ toolCallId : currentStream ? .toolCallId ! ,
464497 delta : toolCallData . args ,
465498 rawEvent : event ,
466499 } ) ;
467500 break ;
468501 }
469502
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+ } ) ;
479513 this . setMessageInProgress ( this . activeRun ! . id , {
480514 id : event . data . chunk . id ,
481515 toolCallId : null ,
482516 toolCallName : null ,
483517 } ) ;
518+ currentStream = this . getMessageInProgress ( this . activeRun ! . id ) ;
484519 }
485- break ;
486- }
487- // Message content: emit TextMessageContent
488- if ( isMessageContentEvent && shouldEmitMessages ) {
520+
489521 this . dispatchEvent ( {
490522 type : EventType . TEXT_MESSAGE_CONTENT ,
491523 messageId : currentStream ! . id ,
492- delta : event . data . chunk . content ,
524+ delta : messageContent ! ,
493525 rawEvent : event ,
494526 } ) ;
495527 break ;
@@ -583,6 +615,51 @@ export class LangGraphAgent extends AbstractAgent {
583615 }
584616 }
585617
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+
586663 getStateSnapshot ( state : State ) {
587664 const schemaKeys = this . activeRun ! . schemaKeys ! ;
588665 // Do not emit state keys that are not part of the output schema
0 commit comments