1+ import { AbstractAgent , RunAgentResult } from "../agent" ;
2+ import { BaseEvent , EventType , Message , RunAgentInput , TextMessageStartEvent , TextMessageContentEvent , TextMessageEndEvent , RunStartedEvent , RunFinishedEvent } from "@ag-ui/core" ;
3+ import { Observable , of } from "rxjs" ;
4+
5+ describe ( "AbstractAgent multiple runs" , ( ) => {
6+ class TestAgent extends AbstractAgent {
7+ private events : BaseEvent [ ] = [ ] ;
8+
9+ setEvents ( events : BaseEvent [ ] ) {
10+ this . events = events ;
11+ }
12+
13+ protected run ( input : RunAgentInput ) : Observable < BaseEvent > {
14+ return of ( ...this . events ) ;
15+ }
16+ }
17+
18+ it ( "should accumulate messages across multiple sequential runs" , async ( ) => {
19+ const agent = new TestAgent ( {
20+ threadId : "test-thread" ,
21+ initialMessages : [ ] ,
22+ } ) ;
23+
24+ // First run events
25+ const firstRunEvents : BaseEvent [ ] = [
26+ {
27+ type : EventType . RUN_STARTED ,
28+ threadId : "test-thread" ,
29+ runId : "run-1" ,
30+ } as RunStartedEvent ,
31+ {
32+ type : EventType . TEXT_MESSAGE_START ,
33+ messageId : "msg-1" ,
34+ role : "assistant" ,
35+ } as TextMessageStartEvent ,
36+ {
37+ type : EventType . TEXT_MESSAGE_CONTENT ,
38+ messageId : "msg-1" ,
39+ delta : "Hello from run 1" ,
40+ } as TextMessageContentEvent ,
41+ {
42+ type : EventType . TEXT_MESSAGE_END ,
43+ messageId : "msg-1" ,
44+ } as TextMessageEndEvent ,
45+ {
46+ type : EventType . RUN_FINISHED ,
47+ } as RunFinishedEvent ,
48+ ] ;
49+
50+ // Execute first run
51+ agent . setEvents ( firstRunEvents ) ;
52+ const result1 = await agent . runAgent ( { runId : "run-1" } ) ;
53+
54+ // Verify first run results
55+ expect ( result1 . newMessages . length ) . toBe ( 1 ) ;
56+ expect ( result1 . newMessages [ 0 ] . content ) . toBe ( "Hello from run 1" ) ;
57+ expect ( agent . messages . length ) . toBe ( 1 ) ;
58+ expect ( agent . messages [ 0 ] . content ) . toBe ( "Hello from run 1" ) ;
59+
60+ // Second run events
61+ const secondRunEvents : BaseEvent [ ] = [
62+ {
63+ type : EventType . RUN_STARTED ,
64+ threadId : "test-thread" ,
65+ runId : "run-2" ,
66+ } as RunStartedEvent ,
67+ {
68+ type : EventType . TEXT_MESSAGE_START ,
69+ messageId : "msg-2" ,
70+ role : "assistant" ,
71+ } as TextMessageStartEvent ,
72+ {
73+ type : EventType . TEXT_MESSAGE_CONTENT ,
74+ messageId : "msg-2" ,
75+ delta : "Hello from run 2" ,
76+ } as TextMessageContentEvent ,
77+ {
78+ type : EventType . TEXT_MESSAGE_END ,
79+ messageId : "msg-2" ,
80+ } as TextMessageEndEvent ,
81+ {
82+ type : EventType . RUN_FINISHED ,
83+ } as RunFinishedEvent ,
84+ ] ;
85+
86+ // Execute second run
87+ agent . setEvents ( secondRunEvents ) ;
88+ const result2 = await agent . runAgent ( { runId : "run-2" } ) ;
89+
90+ // Verify second run results
91+ expect ( result2 . newMessages . length ) . toBe ( 1 ) ;
92+ expect ( result2 . newMessages [ 0 ] . content ) . toBe ( "Hello from run 2" ) ;
93+
94+ // Verify messages are accumulated
95+ expect ( agent . messages . length ) . toBe ( 2 ) ;
96+ expect ( agent . messages [ 0 ] . content ) . toBe ( "Hello from run 1" ) ;
97+ expect ( agent . messages [ 1 ] . content ) . toBe ( "Hello from run 2" ) ;
98+ } ) ;
99+
100+ it ( "should handle three sequential runs with message accumulation" , async ( ) => {
101+ const agent = new TestAgent ( {
102+ threadId : "test-thread" ,
103+ initialMessages : [ ] ,
104+ } ) ;
105+
106+ const messages = [ "First message" , "Second message" , "Third message" ] ;
107+
108+ for ( let i = 0 ; i < 3 ; i ++ ) {
109+ const runEvents : BaseEvent [ ] = [
110+ {
111+ type : EventType . RUN_STARTED ,
112+ threadId : "test-thread" ,
113+ runId : `run-${ i + 1 } ` ,
114+ } as RunStartedEvent ,
115+ {
116+ type : EventType . TEXT_MESSAGE_START ,
117+ messageId : `msg-${ i + 1 } ` ,
118+ role : "assistant" ,
119+ } as TextMessageStartEvent ,
120+ {
121+ type : EventType . TEXT_MESSAGE_CONTENT ,
122+ messageId : `msg-${ i + 1 } ` ,
123+ delta : messages [ i ] ,
124+ } as TextMessageContentEvent ,
125+ {
126+ type : EventType . TEXT_MESSAGE_END ,
127+ messageId : `msg-${ i + 1 } ` ,
128+ } as TextMessageEndEvent ,
129+ {
130+ type : EventType . RUN_FINISHED ,
131+ } as RunFinishedEvent ,
132+ ] ;
133+
134+ agent . setEvents ( runEvents ) ;
135+ const result = await agent . runAgent ( { runId : `run-${ i + 1 } ` } ) ;
136+
137+ // Verify new messages for this run
138+ expect ( result . newMessages . length ) . toBe ( 1 ) ;
139+ expect ( result . newMessages [ 0 ] . content ) . toBe ( messages [ i ] ) ;
140+
141+ // Verify total accumulated messages
142+ expect ( agent . messages . length ) . toBe ( i + 1 ) ;
143+ for ( let j = 0 ; j <= i ; j ++ ) {
144+ expect ( agent . messages [ j ] . content ) . toBe ( messages [ j ] ) ;
145+ }
146+ }
147+
148+ // Final verification
149+ expect ( agent . messages . length ) . toBe ( 3 ) ;
150+ expect ( agent . messages . map ( m => m . content ) ) . toEqual ( messages ) ;
151+ } ) ;
152+
153+ it ( "should handle multiple runs in a single event stream" , async ( ) => {
154+ const agent = new TestAgent ( {
155+ threadId : "test-thread" ,
156+ initialMessages : [ ] ,
157+ } ) ;
158+
159+ // Create a single event stream with two runs
160+ const allEvents : BaseEvent [ ] = [
161+ // First run
162+ {
163+ type : EventType . RUN_STARTED ,
164+ threadId : "test-thread" ,
165+ runId : "run-1" ,
166+ } as RunStartedEvent ,
167+ {
168+ type : EventType . TEXT_MESSAGE_START ,
169+ messageId : "msg-1" ,
170+ role : "assistant" ,
171+ } as TextMessageStartEvent ,
172+ {
173+ type : EventType . TEXT_MESSAGE_CONTENT ,
174+ messageId : "msg-1" ,
175+ delta : "Message from run 1" ,
176+ } as TextMessageContentEvent ,
177+ {
178+ type : EventType . TEXT_MESSAGE_END ,
179+ messageId : "msg-1" ,
180+ } as TextMessageEndEvent ,
181+ {
182+ type : EventType . RUN_FINISHED ,
183+ } as RunFinishedEvent ,
184+ // Second run
185+ {
186+ type : EventType . RUN_STARTED ,
187+ threadId : "test-thread" ,
188+ runId : "run-2" ,
189+ } as RunStartedEvent ,
190+ {
191+ type : EventType . TEXT_MESSAGE_START ,
192+ messageId : "msg-2" ,
193+ role : "assistant" ,
194+ } as TextMessageStartEvent ,
195+ {
196+ type : EventType . TEXT_MESSAGE_CONTENT ,
197+ messageId : "msg-2" ,
198+ delta : "Message from run 2" ,
199+ } as TextMessageContentEvent ,
200+ {
201+ type : EventType . TEXT_MESSAGE_END ,
202+ messageId : "msg-2" ,
203+ } as TextMessageEndEvent ,
204+ {
205+ type : EventType . RUN_FINISHED ,
206+ } as RunFinishedEvent ,
207+ ] ;
208+
209+ // Execute with the combined event stream
210+ agent . setEvents ( allEvents ) ;
211+ const result = await agent . runAgent ( { runId : "combined-run" } ) ;
212+
213+ // Verify results
214+ expect ( result . newMessages . length ) . toBe ( 2 ) ;
215+ expect ( result . newMessages [ 0 ] . content ) . toBe ( "Message from run 1" ) ;
216+ expect ( result . newMessages [ 1 ] . content ) . toBe ( "Message from run 2" ) ;
217+
218+ // Verify all messages are accumulated
219+ expect ( agent . messages . length ) . toBe ( 2 ) ;
220+ expect ( agent . messages [ 0 ] . content ) . toBe ( "Message from run 1" ) ;
221+ expect ( agent . messages [ 1 ] . content ) . toBe ( "Message from run 2" ) ;
222+ } ) ;
223+
224+ it ( "should start with initial messages and accumulate new ones" , async ( ) => {
225+ const initialMessages : Message [ ] = [
226+ {
227+ id : "initial-1" ,
228+ role : "user" ,
229+ content : "Initial message" ,
230+ } ,
231+ ] ;
232+
233+ const agent = new TestAgent ( {
234+ threadId : "test-thread" ,
235+ initialMessages,
236+ } ) ;
237+
238+ // Run events
239+ const runEvents : BaseEvent [ ] = [
240+ {
241+ type : EventType . RUN_STARTED ,
242+ threadId : "test-thread" ,
243+ runId : "run-1" ,
244+ } as RunStartedEvent ,
245+ {
246+ type : EventType . TEXT_MESSAGE_START ,
247+ messageId : "msg-1" ,
248+ role : "assistant" ,
249+ } as TextMessageStartEvent ,
250+ {
251+ type : EventType . TEXT_MESSAGE_CONTENT ,
252+ messageId : "msg-1" ,
253+ delta : "Response message" ,
254+ } as TextMessageContentEvent ,
255+ {
256+ type : EventType . TEXT_MESSAGE_END ,
257+ messageId : "msg-1" ,
258+ } as TextMessageEndEvent ,
259+ {
260+ type : EventType . RUN_FINISHED ,
261+ } as RunFinishedEvent ,
262+ ] ;
263+
264+ agent . setEvents ( runEvents ) ;
265+ const result = await agent . runAgent ( { runId : "run-1" } ) ;
266+
267+ // Verify new messages don't include initial messages
268+ expect ( result . newMessages . length ) . toBe ( 1 ) ;
269+ expect ( result . newMessages [ 0 ] . content ) . toBe ( "Response message" ) ;
270+
271+ // Verify total messages include both initial and new
272+ expect ( agent . messages . length ) . toBe ( 2 ) ;
273+ expect ( agent . messages [ 0 ] . content ) . toBe ( "Initial message" ) ;
274+ expect ( agent . messages [ 1 ] . content ) . toBe ( "Response message" ) ;
275+ } ) ;
276+ } ) ;
0 commit comments