33import pytest
44
55from agent_framework import (
6+ ChatMessage ,
67 Executor ,
78 ExecutorCompletedEvent ,
89 ExecutorInvokedEvent ,
910 Message ,
1011 WorkflowBuilder ,
1112 WorkflowContext ,
13+ executor ,
1214 handler ,
1315)
1416
@@ -182,8 +184,8 @@ async def handle(self, text: str, ctx: WorkflowContext) -> None:
182184 assert collector_completed .data is None
183185
184186
185- async def test_executor_completed_event_none_when_no_messages_sent ():
186- """Test that ExecutorCompletedEvent.data is None when no messages are sent ."""
187+ async def test_executor_completed_event_includes_yielded_outputs ():
188+ """Test that ExecutorCompletedEvent.data includes yielded outputs ."""
187189 from typing_extensions import Never
188190
189191 from agent_framework import WorkflowOutputEvent
@@ -201,9 +203,10 @@ async def handle(self, text: str, ctx: WorkflowContext[Never, str]) -> None:
201203
202204 assert len (completed_events ) == 1
203205 assert completed_events [0 ].executor_id == "yielder"
204- assert completed_events [0 ].data is None
206+ # Yielded outputs are now included in ExecutorCompletedEvent.data
207+ assert completed_events [0 ].data == ["TEST" ]
205208
206- # Verify the output was still yielded correctly
209+ # Verify the output was also yielded as WorkflowOutputEvent
207210 output_events = [e for e in events if isinstance (e , WorkflowOutputEvent )]
208211 assert len (output_events ) == 1
209212 assert output_events [0 ].data == "TEST"
@@ -261,3 +264,35 @@ async def handle(self, response: Response, ctx: WorkflowContext) -> None:
261264 collector_invoked = next (e for e in invoked_events if e .executor_id == "collector" )
262265 assert isinstance (collector_invoked .data , Response )
263266 assert collector_invoked .data .results == ["HELLO" , "HELLO" , "HELLO" ]
267+
268+
269+ async def test_executor_invoked_event_data_not_mutated_by_handler ():
270+ """Test that ExecutorInvokedEvent.data captures original input, not mutated input."""
271+
272+ @executor (id = "Mutator" )
273+ async def mutator (messages : list [ChatMessage ], ctx : WorkflowContext [list [ChatMessage ]]) -> None :
274+ # The handler mutates the input list by appending new messages
275+ original_len = len (messages )
276+ messages .append (ChatMessage (role = "assistant" , text = "Added by executor" ))
277+ await ctx .send_message (messages )
278+ # Verify mutation happened
279+ assert len (messages ) == original_len + 1
280+
281+ workflow = WorkflowBuilder ().set_start_executor (mutator ).build ()
282+
283+ # Run with a single user message
284+ input_messages = [ChatMessage (role = "user" , text = "hello" )]
285+ events = await workflow .run (input_messages )
286+
287+ # Find the invoked event for the Mutator executor
288+ invoked_events = [e for e in events if isinstance (e , ExecutorInvokedEvent )]
289+ assert len (invoked_events ) == 1
290+ mutator_invoked = invoked_events [0 ]
291+
292+ # The event data should contain ONLY the original input (1 user message)
293+ assert mutator_invoked .executor_id == "Mutator"
294+ assert len (mutator_invoked .data ) == 1 , (
295+ f"Expected 1 message (original input), got { len (mutator_invoked .data )} : "
296+ f"{ [m .text for m in mutator_invoked .data ]} "
297+ )
298+ assert mutator_invoked .data [0 ].text == "hello"
0 commit comments