1
+ import json
2
+
1
3
import pytest
2
4
3
5
from agents import Agent , Runner
4
6
5
7
from .fake_model import FakeModel
8
+ from .test_responses import get_function_tool , get_function_tool_call , get_text_message
6
9
7
10
8
11
@pytest .mark .asyncio
9
- async def test_joker_streamed_jokes_with_cancel ():
12
+ async def test_simple_streaming_with_cancel ():
10
13
model = FakeModel ()
11
14
agent = Agent (name = "Joker" , model = model )
12
15
@@ -16,7 +19,98 @@ async def test_joker_streamed_jokes_with_cancel():
16
19
17
20
async for _event in result .stream_events ():
18
21
num_events += 1
19
- if num_events == 1 :
22
+ if num_events == stop_after :
20
23
result .cancel ()
21
24
22
25
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