Agent Run Cancellation

Cancel a running agent execution from another thread.

This example demonstrates how to cancel a running agent execution by starting an agent run in a separate thread and cancelling it from another thread. It shows proper handling of cancelled responses and thread management.

Example

1"""
2Example demonstrating how to cancel a running agent execution.
3
4This example shows how to:
51. Start an agent run in a separate thread
62. Cancel the run from another thread
73. Handle the cancelled response
8"""
9
10import threading
11import time
12
13from kern.agent import Agent
14from kern.models.openai import OpenAIResponses
15from kern.run.agent import RunEvent
16from kern.run.base import RunStatus
17
18
19def long_running_task(agent: Agent, run_id_container: dict):
20 """
21 Simulate a long-running agent task that can be cancelled.
22
23 Args:
24 agent: The agent to run
25 run_id_container: Dictionary to store the run_id for cancellation
26
27 Returns:
28 Dictionary with run results and status
29 """
30 # Start the agent run - this simulates a long task
31 final_response = None
32 content_pieces = []
33
34 for chunk in agent.run(
35 "Write a very long story about a dragon who learns to code. "
36 "Make it at least 2000 words with detailed descriptions and dialogue. "
37 "Take your time and be very thorough.",
38 stream=True,
39 ):
40 if "run_id" not in run_id_container and chunk.run_id:
41 run_id_container["run_id"] = chunk.run_id
42
43 if chunk.event == RunEvent.run_content:
44 print(chunk.content, end="", flush=True)
45 content_pieces.append(chunk.content)
46 # When the run is cancelled, a `RunEvent.run_cancelled` event is emitted
47 elif chunk.event == RunEvent.run_cancelled:
48 print(f"\nRun was cancelled: {chunk.run_id}")
49 run_id_container["result"] = {
50 "status": "cancelled",
51 "run_id": chunk.run_id,
52 "cancelled": True,
53 "content": "".join(content_pieces)[:200] + "..."
54 if content_pieces
55 else "No content before cancellation",
56 }
57 return
58 elif hasattr(chunk, "status") and chunk.status == RunStatus.completed:
59 final_response = chunk
60
61 # If we get here, the run completed successfully
62 if final_response:
63 run_id_container["result"] = {
64 "status": final_response.status.value
65 if final_response.status
66 else "completed",
67 "run_id": final_response.run_id,
68 "cancelled": final_response.status == RunStatus.cancelled,
69 "content": ("".join(content_pieces)[:200] + "...")
70 if content_pieces
71 else "No content",
72 }
73 else:
74 run_id_container["result"] = {
75 "status": "unknown",
76 "run_id": run_id_container.get("run_id"),
77 "cancelled": False,
78 "content": ("".join(content_pieces)[:200] + "...")
79 if content_pieces
80 else "No content",
81 }
82
83
84def cancel_after_delay(agent: Agent, run_id_container: dict, delay_seconds: int = 3):
85 """
86 Cancel the agent run after a specified delay.
87
88 Args:
89 agent: The agent whose run should be cancelled
90 run_id_container: Dictionary containing the run_id to cancel
91 delay_seconds: How long to wait before cancelling
92 """
93 print(f"Will cancel run in {delay_seconds} seconds...")
94 time.sleep(delay_seconds)
95
96 run_id = run_id_container.get("run_id")
97 if run_id:
98 print(f"Cancelling run: {run_id}")
99 success = agent.cancel_run(run_id)
100 if success:
101 print(f"Run {run_id} marked for cancellation")
102 else:
103 print(
104 f"Failed to cancel run {run_id} (may not exist or already completed)"
105 )
106 else:
107 print("No run_id found to cancel")
108
109
110def main():
111 """Main function demonstrating agent run cancellation."""
112
113 # Initialize the agent with a model
114 agent = Agent(
115 name="StorytellerAgent",
116 model=OpenAIResponses(id="gpt-5.2"), # Use a model that can generate long responses
117 description="An agent that writes detailed stories",
118 )
119
120 print("Starting agent run cancellation example...")
121 print("=" * 50)
122
123 # Container to share run_id between threads
124 run_id_container = {}
125
126 # Start the agent run in a separate thread
127 agent_thread = threading.Thread(
128 target=lambda: long_running_task(agent, run_id_container), name="AgentRunThread"
129 )
130
131 # Start the cancellation thread
132 cancel_thread = threading.Thread(
133 target=cancel_after_delay,
134 args=(agent, run_id_container, 8), # Cancel after 8 seconds
135 name="CancelThread",
136 )
137
138 # Start both threads
139 print("Starting agent run thread...")
140 agent_thread.start()
141
142 print("Starting cancellation thread...")
143 cancel_thread.start()
144
145 # Wait for both threads to complete
146 print("Waiting for threads to complete...")
147 agent_thread.join()
148 cancel_thread.join()
149
150 # Print the results
151 print("\n" + "=" * 50)
152 print("RESULTS:")
153 print("=" * 50)
154
155 result = run_id_container.get("result")
156 if result:
157 print(f"Status: {result['status']}")
158 print(f"Run ID: {result['run_id']}")
159 print(f"Was Cancelled: {result['cancelled']}")
160
161 if result.get("error"):
162 print(f"Error: {result['error']}")
163 else:
164 print(f"Content Preview: {result['content']}")
165
166 if result["cancelled"]:
167 print("\nSUCCESS: Run was successfully cancelled!")
168 else:
169 print("\nWARNING: Run completed before cancellation")
170 else:
171 print("No result obtained - check if cancellation happened during streaming")
172
173 print("\nExample completed!")
174
175
176if __name__ == "__main__":
177 # Run the main example
178 main()

API Endpoint

Agent runs can be cancelled via the AgentOS API:

1POST /agents/{agent_id}/runs/{run_id}/cancel

Example:

1curl --location 'http://localhost:7777/agents/story-writer-agent/runs/123/cancel' \
2 --request POST

Reference: Cancel Agent Run API