4747)
4848from openhands .events .serialization .event import truncate_content
4949from openhands .llm .llm import LLM
50- from openhands .utils .shutdown_listener import should_continue
5150
5251# note: RESUME is only available on web GUI
5352TRAFFIC_CONTROL_REMINDER = (
@@ -64,7 +63,6 @@ class AgentController:
6463 confirmation_mode : bool
6564 agent_to_llm_config : dict [str , LLMConfig ]
6665 agent_configs : dict [str , AgentConfig ]
67- agent_task : asyncio .Future | None = None
6866 parent : 'AgentController | None' = None
6967 delegate : 'AgentController | None' = None
7068 _pending_action : Action | None = None
@@ -109,7 +107,6 @@ def __init__(
109107 headless_mode: Whether the agent is run in headless mode.
110108 status_callback: Optional callback function to handle status updates.
111109 """
112- self ._step_lock = asyncio .Lock ()
113110 self .id = sid
114111 self .agent = agent
115112 self .headless_mode = headless_mode
@@ -199,32 +196,44 @@ async def _react_to_exception(
199196 err_id = 'STATUS$ERROR_LLM_AUTHENTICATION'
200197 self .status_callback ('error' , err_id , type (e ).__name__ + ': ' + str (e ))
201198
202- async def start_step_loop (self ):
203- """The main loop for the agent's step-by-step execution."""
204- self .log ('info' , 'Starting step loop...' )
205- while True :
206- if not self ._is_awaiting_observation () and not should_continue ():
207- break
208- if self ._closed :
209- break
210- try :
211- await self ._step ()
212- except asyncio .CancelledError :
213- self .log ('debug' , 'AgentController task was cancelled' )
214- break
215- except Exception as e :
216- traceback .print_exc ()
217- self .log ('error' , f'Error while running the agent: { e } ' )
218- await self ._react_to_exception (e )
199+ def step (self ):
200+ asyncio .create_task (self ._step_with_exception_handling ())
219201
220- await asyncio .sleep (0.1 )
202+ async def _step_with_exception_handling (self ):
203+ try :
204+ await self ._step ()
205+ except Exception as e :
206+ traceback .print_exc ()
207+ self .log ('error' , f'Error while running the agent: { e } ' )
208+ reported = RuntimeError (
209+ 'There was an unexpected error while running the agent.'
210+ )
211+ if isinstance (e , litellm .LLMError ):
212+ reported = e
213+ await self ._react_to_exception (reported )
221214
222- async def on_event (self , event : Event ) -> None :
215+ def should_step (self , event : Event ) -> bool :
216+ if isinstance (event , Action ):
217+ if isinstance (event , MessageAction ) and event .source == EventSource .USER :
218+ return True
219+ return False
220+ if isinstance (event , Observation ):
221+ if isinstance (event , NullObservation ) or isinstance (
222+ event , AgentStateChangedObservation
223+ ):
224+ return False
225+ return True
226+ return False
227+
228+ def on_event (self , event : Event ) -> None :
223229 """Callback from the event stream. Notifies the controller of incoming events.
224230
225231 Args:
226232 event (Event): The incoming event to process.
227233 """
234+ asyncio .get_event_loop ().run_until_complete (self ._on_event (event ))
235+
236+ async def _on_event (self , event : Event ) -> None :
228237 if hasattr (event , 'hidden' ) and event .hidden :
229238 return
230239
@@ -237,6 +246,9 @@ async def on_event(self, event: Event) -> None:
237246 elif isinstance (event , Observation ):
238247 await self ._handle_observation (event )
239248
249+ if self .should_step (event ):
250+ self .step ()
251+
240252 async def _handle_action (self , action : Action ) -> None :
241253 """Handles actions from the event stream.
242254
@@ -487,19 +499,16 @@ async def start_delegate(self, action: AgentDelegateAction) -> None:
487499 async def _step (self ) -> None :
488500 """Executes a single step of the parent or delegate agent. Detects stuck agents and limits on the number of iterations and the task budget."""
489501 if self .get_agent_state () != AgentState .RUNNING :
490- await asyncio .sleep (1 )
491502 return
492503
493504 if self ._pending_action :
494- await asyncio .sleep (1 )
495505 return
496506
497507 if self .delegate is not None :
498508 assert self .delegate != self
499- if self .delegate .get_agent_state () == AgentState .PAUSED :
500- # no need to check too often
501- await asyncio .sleep (1 )
502- else :
509+ # TODO this conditional will always be false, because the parent controllers are unsubscribed
510+ # remove if it's still useless when delegation is reworked
511+ if self .delegate .get_agent_state () != AgentState .PAUSED :
503512 await self ._delegate_step ()
504513 return
505514
@@ -509,7 +518,6 @@ async def _step(self) -> None:
509518 extra = {'msg_type' : 'STEP' },
510519 )
511520
512- # check if agent hit the resources limit
513521 stop_step = False
514522 if self .state .iteration >= self .state .max_iterations :
515523 stop_step = await self ._handle_traffic_control (
@@ -522,6 +530,7 @@ async def _step(self) -> None:
522530 'budget' , current_cost , self .max_budget_per_task
523531 )
524532 if stop_step :
533+ logger .warning ('Stopping agent due to traffic control' )
525534 return
526535
527536 if self ._is_stuck ():
@@ -967,7 +976,7 @@ def __repr__(self):
967976 return (
968977 f'AgentController(id={ self .id } , agent={ self .agent !r} , '
969978 f'event_stream={ self .event_stream !r} , '
970- f'state={ self .state !r} , agent_task= { self . agent_task !r } , '
979+ f'state={ self .state !r} , '
971980 f'delegate={ self .delegate !r} , _pending_action={ self ._pending_action !r} )'
972981 )
973982
0 commit comments