1+ #!/usr/bin/env python3
2+ """
3+ Example: Bot In-Flight Run Control
4+
5+ Demonstrates how to use SessionRunControl to provide better UX for bots
6+ during long-running agent operations. Shows busy feedback, pending message
7+ handling, and /stop command support.
8+
9+ This example shows the "before" and "after" behavior described in issue #1914.
10+ """
11+
12+ import asyncio
13+ import time
14+ from typing import Optional
15+
16+ # Mock agent for demonstration
17+ class MockAgent :
18+ """Simple mock agent that simulates long-running tasks."""
19+
20+ def __init__ (self , name = "assistant" ):
21+ self .name = name
22+ self .chat_history = []
23+ self ._interrupt_controller = None
24+
25+ def chat (self , prompt : str ) -> str :
26+ """Simulate a long-running chat response."""
27+ print (f"[Agent] Starting to process: '{ prompt [:50 ]} ...'" )
28+
29+ # Simulate long processing with interrupt checking
30+ for i in range (30 ): # 3 second task (30 x 0.1s)
31+ time .sleep (0.1 )
32+
33+ # Check for interruption
34+ if (self ._interrupt_controller and
35+ hasattr (self ._interrupt_controller , 'is_set' ) and
36+ self ._interrupt_controller .is_set ()):
37+ reason = getattr (self ._interrupt_controller , 'reason' , 'unknown' )
38+ print (f"[Agent] ⚠️ Interrupted: { reason } " )
39+ raise InterruptedError (f"Task cancelled: { reason } " )
40+
41+ response = f"Completed analysis of: { prompt } "
42+ print (f"[Agent] ✅ Finished: { response } " )
43+ return response
44+
45+
46+ async def demo_without_run_control ():
47+ """Demonstrate the old behavior (silent blocking)."""
48+ print ("\n " + "=" * 60 )
49+ print ("🔴 BEFORE: Without Run Control (Silent Blocking)" )
50+ print ("=" * 60 )
51+
52+ from praisonai .bots ._session import BotSessionManager
53+
54+ # Standard session manager (no run control)
55+ session_mgr = BotSessionManager ()
56+ agent = MockAgent ("research-agent" )
57+ user_id = "user123"
58+
59+ print ("\n 1. User sends long task:" )
60+ print (" User: 'research quantum computing trends'" )
61+
62+ # Start first task (this will run for 3 seconds)
63+ task1 = asyncio .create_task (
64+ session_mgr .chat (agent , user_id , "research quantum computing trends" )
65+ )
66+
67+ # Simulate user sending follow-up messages during execution
68+ await asyncio .sleep (0.5 )
69+ print ("\n 2. User sends follow-up (this will block silently):" )
70+ print (" User: 'actually focus on business impact'" )
71+
72+ task2 = asyncio .create_task (
73+ session_mgr .chat (agent , user_id , "actually focus on business impact" )
74+ )
75+
76+ await asyncio .sleep (0.5 )
77+ print ("\n 3. User tries to send /stop (this will also block):" )
78+ print (" User: '/stop'" )
79+
80+ task3 = asyncio .create_task (
81+ session_mgr .chat (agent , user_id , "/stop" )
82+ )
83+
84+ print ("\n ⏳ Waiting for all messages to complete..." )
85+ results = await asyncio .gather (task1 , task2 , task3 )
86+
87+ print ("\n 📊 Results (all processed sequentially with no feedback):" )
88+ for i , result in enumerate (results , 1 ):
89+ print (f" { i } . { result } " )
90+
91+ print ("\n ❌ Problems with this approach:" )
92+ print (" - No immediate feedback on follow-up messages" )
93+ print (" - No way to cancel long-running tasks" )
94+ print (" - User doesn't know if bot is working or broken" )
95+
96+
97+ async def demo_with_run_control ():
98+ """Demonstrate the new behavior with run control."""
99+ print ("\n " + "=" * 60 )
100+ print ("🟢 AFTER: With Run Control (Better UX)" )
101+ print ("=" * 60 )
102+
103+ from praisonai .bots ._run_control import SessionRunControl
104+ from praisonai .bots ._session import BotSessionManager
105+ from praisonai .bots ._commands import handle_stop_command
106+
107+ # Session manager with run control
108+ run_control = SessionRunControl (
109+ busy_mode = "queue" ,
110+ busy_ack_template = "⏳ {action} — will process after current task finishes"
111+ )
112+ session_mgr = BotSessionManager (run_control = run_control )
113+ agent = MockAgent ("research-agent" )
114+ user_id = "user123"
115+
116+ print ("\n 1. User sends long task:" )
117+ print (" User: 'research quantum computing trends'" )
118+
119+ # Start first task
120+ task1 = asyncio .create_task (
121+ session_mgr .chat_with_run_control (agent , user_id , "research quantum computing trends" )
122+ )
123+
124+ # Immediate follow-up messages get acknowledgments
125+ await asyncio .sleep (0.5 )
126+ print ("\n 2. User sends follow-up (gets immediate feedback):" )
127+ print (" User: 'actually focus on business impact'" )
128+
129+ result2 = await session_mgr .chat_with_run_control (
130+ agent , user_id , "actually focus on business impact"
131+ )
132+ print (f" Bot: { result2 ['response' ]} " )
133+ print (f" Metadata: { result2 ['metadata' ]} " )
134+
135+ await asyncio .sleep (0.5 )
136+ print ("\n 3. User decides to stop current task:" )
137+ print (" User: '/stop'" )
138+
139+ stop_response = await handle_stop_command (user_id , run_control )
140+ print (f" Bot: { stop_response } " )
141+
142+ # Wait for first task to complete (it should be cancelled)
143+ try :
144+ result1 = await task1
145+ print (f"\n 📊 Task 1 result: { result1 ['response' ]} " )
146+ print (f" Metadata: { result1 ['metadata' ]} " )
147+ except asyncio .CancelledError :
148+ print ("\n 📊 Task 1 was cancelled as expected" )
149+
150+ print ("\n 4. User sends fresh request after stopping:" )
151+ print (" User: 'what's the weather like?'" )
152+
153+ result4 = await session_mgr .chat_with_run_control (
154+ agent , user_id , "what's the weather like?"
155+ )
156+ print (f" Bot: { result4 ['response' ]} " )
157+
158+ print ("\n ✅ Benefits of this approach:" )
159+ print (" - Immediate feedback on all messages" )
160+ print (" - Can cancel long-running tasks with /stop" )
161+ print (" - Pending messages are queued and processed in order" )
162+ print (" - User always knows what's happening" )
163+
164+
165+ async def demo_interrupt_mode ():
166+ """Demonstrate interrupt mode."""
167+ print ("\n " + "=" * 60 )
168+ print ("⚡ BONUS: Interrupt Mode (Cancel and Restart)" )
169+ print ("=" * 60 )
170+
171+ from praisonai .bots ._run_control import SessionRunControl
172+ from praisonai .bots ._session import BotSessionManager
173+
174+ # Use interrupt mode instead of queue mode
175+ run_control = SessionRunControl (busy_mode = "interrupt" )
176+ session_mgr = BotSessionManager (run_control = run_control )
177+ agent = MockAgent ("research-agent" )
178+ user_id = "user123"
179+
180+ print ("\n 1. User starts long task:" )
181+ print (" User: 'research renewable energy for 30 pages'" )
182+
183+ task1 = asyncio .create_task (
184+ session_mgr .chat_with_run_control (agent , user_id , "research renewable energy for 30 pages" )
185+ )
186+
187+ await asyncio .sleep (0.5 )
188+ print ("\n 2. User changes mind (interrupts and restarts):" )
189+ print (" User: 'actually just summarize solar panel efficiency'" )
190+
191+ result2 = await session_mgr .chat_with_run_control (
192+ agent , user_id , "actually just summarize solar panel efficiency"
193+ )
194+ print (f" Bot: { result2 ['response' ]} " )
195+
196+ # Wait for original task (should be interrupted)
197+ try :
198+ result1 = await task1
199+ print (f"\n 📊 Original task result: { result1 ['response' ]} " )
200+ if result1 ['metadata' ].get ('interrupted' ):
201+ print (" ✅ Original task was properly interrupted" )
202+ except :
203+ print ("\n 📊 Original task was cancelled" )
204+
205+ print ("\n ✅ Interrupt mode is great for:" )
206+ print (" - When users frequently change direction" )
207+ print (" - Interactive sessions where latest intent matters most" )
208+ print (" - Real-time collaboration scenarios" )
209+
210+
211+ async def main ():
212+ """Run all demonstrations."""
213+ print ("🚀 Bot In-Flight Run Control Demo" )
214+ print ("Solving the silent lock problem described in issue #1914" )
215+
216+ # Show the problem
217+ await demo_without_run_control ()
218+
219+ # Show the solution
220+ await demo_with_run_control ()
221+
222+ # Show advanced feature
223+ await demo_interrupt_mode ()
224+
225+ print ("\n " + "=" * 60 )
226+ print ("🎯 Summary" )
227+ print ("=" * 60 )
228+ print ("SessionRunControl solves the silent blocking problem by providing:" )
229+ print ("1. Immediate feedback for mid-run messages" )
230+ print ("2. /stop command support via InterruptController" )
231+ print ("3. Configurable policies (queue/interrupt/steer)" )
232+ print ("4. Run generation tracking to prevent race conditions" )
233+ print ("5. Pending message merging and ordering" )
234+ print ("\n This makes bot interactions feel responsive and predictable!" )
235+
236+
237+ if __name__ == "__main__" :
238+ asyncio .run (main ())
0 commit comments