Skip to content

Commit

Permalink
Fix history loading when state was corrupt/non-existent (#5946)
Browse files Browse the repository at this point in the history
Co-authored-by: openhands <[email protected]>
  • Loading branch information
enyst and openhands-agent authored Dec 31, 2024
1 parent d29cc61 commit 40d8245
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 5 deletions.
11 changes: 10 additions & 1 deletion openhands/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -730,12 +730,20 @@ def set_initial_state(
# - the previous session, in which case it has history
# - from a parent agent, in which case it has no history
# - None / a new state

# If state is None, we create a brand new state and still load the event stream so we can restore the history
if state is None:
self.state = State(
inputs={},
max_iterations=max_iterations,
confirmation_mode=confirmation_mode,
)
self.state.start_id = 0

self.log(
'debug',
f'AgentController {self.id} - created new state. start_id: {self.state.start_id}',
)
else:
self.state = state

Expand All @@ -747,7 +755,8 @@ def set_initial_state(
f'AgentController {self.id} initializing history from event {self.state.start_id}',
)

self._init_history()
# Always load from the event stream to avoid losing history
self._init_history()

def _init_history(self) -> None:
"""Initializes the agent's history from the event stream.
Expand Down
23 changes: 19 additions & 4 deletions openhands/server/session/agent_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,11 +269,26 @@ def _create_controller(
headless_mode=False,
status_callback=self._status_callback,
)

# Note: We now attempt to restore the state from session here,
# but if it fails, we fall back to None and still initialize the controller
# with a fresh state. That way, the controller will always load events from the event stream
# even if the state file was corrupt.

restored_state = None
try:
agent_state = State.restore_from_session(self.sid, self.file_store)
controller.set_initial_state(agent_state, max_iterations, confirmation_mode)
logger.debug(f'Restored agent state from session, sid: {self.sid}')
restored_state = State.restore_from_session(self.sid, self.file_store)
except Exception as e:
logger.debug(f'State could not be restored: {e}')
if self.event_stream.get_latest_event_id() > 0:
# if we have events, we should have a state
logger.warning(f'State could not be restored: {e}')

# Set the initial state through the controller.
controller.set_initial_state(restored_state, max_iterations, confirmation_mode)
if restored_state:
logger.debug(f'Restored agent state from session, sid: {self.sid}')
else:
logger.debug('New session state created.')

logger.debug('Agent controller initialized.')
return controller
2 changes: 2 additions & 0 deletions tests/unit/test_truncation.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ def mock_event_stream():
stream = MagicMock()
# Mock get_events to return an empty list by default
stream.get_events.return_value = []
# Mock get_latest_event_id to return a valid integer
stream.get_latest_event_id.return_value = 0
return stream


Expand Down

0 comments on commit 40d8245

Please sign in to comment.