1+ import json
2+
13import pytest
24
35from agents import Agent , Runner
46
57from .fake_model import FakeModel
8+ from .test_responses import get_function_tool , get_function_tool_call , get_text_message
69
710
811@pytest .mark .asyncio
9- async def test_joker_streamed_jokes_with_cancel ():
12+ async def test_simple_streaming_with_cancel ():
1013 model = FakeModel ()
1114 agent = Agent (name = "Joker" , model = model )
1215
@@ -16,7 +19,98 @@ async def test_joker_streamed_jokes_with_cancel():
1619
1720 async for _event in result .stream_events ():
1821 num_events += 1
19- if num_events == 1 :
22+ if num_events == stop_after :
2023 result .cancel ()
2124
2225 assert num_events == 1 , f"Expected { stop_after } visible events, but got { num_events } "
26+
27+
28+ @pytest .mark .asyncio
29+ async def test_multiple_events_streaming_with_cancel ():
30+ model = FakeModel ()
31+ agent = Agent (
32+ name = "Joker" ,
33+ model = model ,
34+ tools = [get_function_tool ("foo" , "tool_result" )],
35+ )
36+
37+ model .add_multiple_turn_outputs (
38+ [
39+ # First turn: a message and tool call
40+ [
41+ get_text_message ("a_message" ),
42+ get_function_tool_call ("foo" , json .dumps ({"a" : "b" })),
43+ ],
44+ # Second turn: text message
45+ [get_text_message ("done" )],
46+ ]
47+ )
48+
49+ result = Runner .run_streamed (agent , input = "Please tell me 5 jokes." )
50+ num_events = 0
51+ stop_after = 2
52+
53+ async for _ in result .stream_events ():
54+ num_events += 1
55+ if num_events == stop_after :
56+ result .cancel ()
57+
58+ assert num_events == stop_after , f"Expected { stop_after } visible events, but got { num_events } "
59+
60+
61+ @pytest .mark .asyncio
62+ async def test_cancel_prevents_further_events ():
63+ model = FakeModel ()
64+ agent = Agent (name = "Joker" , model = model )
65+ result = Runner .run_streamed (agent , input = "Please tell me 5 jokes." )
66+ events = []
67+ async for event in result .stream_events ():
68+ events .append (event )
69+ result .cancel ()
70+ break # Cancel after first event
71+ # Try to get more events after cancel
72+ more_events = [e async for e in result .stream_events ()]
73+ assert len (events ) == 1
74+ assert more_events == [], "No events should be yielded after cancel()"
75+
76+
77+ @pytest .mark .asyncio
78+ async def test_cancel_is_idempotent ():
79+ model = FakeModel ()
80+ agent = Agent (name = "Joker" , model = model )
81+ result = Runner .run_streamed (agent , input = "Please tell me 5 jokes." )
82+ events = []
83+ async for event in result .stream_events ():
84+ events .append (event )
85+ result .cancel ()
86+ result .cancel () # Call cancel again
87+ break
88+ # Should not raise or misbehave
89+ assert len (events ) == 1
90+
91+
92+ @pytest .mark .asyncio
93+ async def test_cancel_before_streaming ():
94+ model = FakeModel ()
95+ agent = Agent (name = "Joker" , model = model )
96+ result = Runner .run_streamed (agent , input = "Please tell me 5 jokes." )
97+ result .cancel () # Cancel before streaming
98+ events = [e async for e in result .stream_events ()]
99+ assert events == [], "No events should be yielded if cancel() is called before streaming."
100+
101+
102+ @pytest .mark .asyncio
103+ async def test_cancel_cleans_up_resources ():
104+ model = FakeModel ()
105+ agent = Agent (name = "Joker" , model = model )
106+ result = Runner .run_streamed (agent , input = "Please tell me 5 jokes." )
107+ # Start streaming, then cancel
108+ async for _ in result .stream_events ():
109+ result .cancel ()
110+ break
111+ # After cancel, queues should be empty and is_complete True
112+ assert result .is_complete , "Result should be marked complete after cancel."
113+ assert result ._event_queue .empty (), "Event queue should be empty after cancel."
114+ assert result ._input_guardrail_queue .empty (), (
115+ "Input guardrail queue should be empty after cancel."
116+ )
0 commit comments