Compare commits

...

1 Commits

Author SHA1 Message Date
openhands 0b5200e632 Add reason field to AgentStateChangedObservation for ERROR state 2025-03-29 18:53:12 +00:00
4 changed files with 17 additions and 7 deletions
+12 -4
View File
@@ -228,7 +228,8 @@ class AgentController:
e: Exception, e: Exception,
): ):
"""React to an exception by setting the agent state to error and sending a status message.""" """React to an exception by setting the agent state to error and sending a status message."""
await self.set_agent_state_to(AgentState.ERROR) error_message = type(e).__name__ + ': ' + str(e)
if self.status_callback is not None: if self.status_callback is not None:
err_id = '' err_id = ''
if isinstance(e, AuthenticationError): if isinstance(e, AuthenticationError):
@@ -249,7 +250,9 @@ class AgentController:
elif isinstance(e, RateLimitError): elif isinstance(e, RateLimitError):
await self.set_agent_state_to(AgentState.RATE_LIMITED) await self.set_agent_state_to(AgentState.RATE_LIMITED)
return return
self.status_callback('error', err_id, type(e).__name__ + ': ' + str(e)) self.status_callback('error', err_id, error_message)
await self.set_agent_state_to(AgentState.ERROR, reason=error_message)
def step(self): def step(self):
asyncio.create_task(self._step_with_exception_handling()) asyncio.create_task(self._step_with_exception_handling())
@@ -524,11 +527,12 @@ class AgentController:
self._pending_action = None self._pending_action = None
self.agent.reset() self.agent.reset()
async def set_agent_state_to(self, new_state: AgentState) -> None: async def set_agent_state_to(self, new_state: AgentState, reason: str = "") -> None:
"""Updates the agent's state and handles side effects. Can emit events to the event stream. """Updates the agent's state and handles side effects. Can emit events to the event stream.
Args: Args:
new_state (AgentState): The new state to set for the agent. new_state (AgentState): The new state to set for the agent.
reason (str, optional): The reason for the state change, particularly useful for ERROR state.
""" """
self.log( self.log(
'info', 'info',
@@ -538,6 +542,10 @@ class AgentController:
if new_state == self.state.agent_state: if new_state == self.state.agent_state:
return return
# Store error reason if provided
if new_state == AgentState.ERROR and reason:
self.state.last_error = reason
if new_state in (AgentState.STOPPED, AgentState.ERROR): if new_state in (AgentState.STOPPED, AgentState.ERROR):
# sync existing metrics BEFORE resetting the agent # sync existing metrics BEFORE resetting the agent
await self.update_state_after_step() await self.update_state_after_step()
@@ -583,7 +591,7 @@ class AgentController:
self.state.agent_state = new_state self.state.agent_state = new_state
self.event_stream.add_event( self.event_stream.add_event(
AgentStateChangedObservation('', self.state.agent_state), AgentStateChangedObservation('', self.state.agent_state, reason=reason if new_state == AgentState.ERROR else ""),
EventSource.ENVIRONMENT, EventSource.ENVIRONMENT,
) )
+1 -2
View File
@@ -23,8 +23,7 @@ async def run_agent_until_done(
if msg_type == 'error': if msg_type == 'error':
logger.error(msg) logger.error(msg)
if controller: if controller:
controller.state.last_error = msg asyncio.create_task(controller.set_agent_state_to(AgentState.ERROR, reason=msg))
asyncio.create_task(controller.set_agent_state_to(AgentState.ERROR))
else: else:
logger.info(msg) logger.info(msg)
+3
View File
@@ -10,10 +10,13 @@ class AgentStateChangedObservation(Observation):
"""This data class represents the result from delegating to another agent""" """This data class represents the result from delegating to another agent"""
agent_state: str agent_state: str
reason: str = ""
observation: str = ObservationType.AGENT_STATE_CHANGED observation: str = ObservationType.AGENT_STATE_CHANGED
@property @property
def message(self) -> str: def message(self) -> str:
if self.agent_state == "error" and self.reason:
return f"Error: {self.reason}"
return '' return ''
+1 -1
View File
@@ -267,7 +267,7 @@ class Session:
agent_session = self.agent_session agent_session = self.agent_session
controller = self.agent_session.controller controller = self.agent_session.controller
if controller is not None and not agent_session.is_closed(): if controller is not None and not agent_session.is_closed():
await controller.set_agent_state_to(AgentState.ERROR) await controller.set_agent_state_to(AgentState.ERROR, reason=message)
self.logger.info( self.logger.info(
'Agent status error', 'Agent status error',
extra={'signal': 'agent_status_error'}, extra={'signal': 'agent_status_error'},