Skip to content

Commit

Permalink
Add checks to stop infinite loops (All-Hands-AI#1293)
Browse files Browse the repository at this point in the history
* Add checks to stop infinite loops

* Send an AgentErrorObservation for the user to see an oops loop

* (NullAction, Obs) problem should be (NullAction, error Obs)

* Merge the two with AgentErrorObs.

* Update opendevin/controller/agent_controller.py
  • Loading branch information
enyst authored Apr 23, 2024
1 parent f6dcbea commit d1551c3
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 4 deletions.
2 changes: 1 addition & 1 deletion opendevin/action/fileop.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class FileWriteAction(ExecutableAction):

def _insert_lines(self, to_insert: list[str], original: list[str]):
"""
Insert the new conent to the original content based on self.start and self.end
Insert the new content to the original content based on self.start and self.end
"""
new_lines = [''] if self.start == 0 else original[:self.start]
new_lines += [i + '\n' for i in to_insert]
Expand Down
37 changes: 34 additions & 3 deletions opendevin/controller/agent_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
from opendevin.plan import Plan
from opendevin.state import State

from ..action.tasks import TaskStateChangedAction
from ..schema import TaskState
from .action_manager import ActionManager
from opendevin.action.tasks import TaskStateChangedAction
from opendevin.schema import TaskState
from opendevin.controller.action_manager import ActionManager

MAX_ITERATIONS = config.get('MAX_ITERATIONS')
MAX_CHARS = config.get('MAX_CHARS')
Expand Down Expand Up @@ -112,6 +112,13 @@ async def _run(self):
await self.notify_task_state_changed()
break

if self._is_stuck():
logger.info('Loop detected, stopping task')
observation = AgentErrorObservation('I got stuck into a loop, the task has stopped.')
await self._run_callbacks(observation)
await self.set_task_state_to(TaskState.STOPPED)
break

async def start(self, task: str):
"""Starts the agent controller with a task.
If task already run before, it will continue from the last step.
Expand Down Expand Up @@ -221,3 +228,27 @@ async def _run_callbacks(self, event):

def get_state(self):
return self.state

def _is_stuck(self):
if self.state is None or self.state.history is None or len(self.state.history) < 3:
return False

# if the last three (Action, Observation) tuples are too repetitive
# the agent got stuck in a loop
if all(
[self.state.history[-i][0] == self.state.history[-3][0] for i in range(1, 3)]
):
# it repeats same action, give it a chance, but not if:
if (all
(isinstance(self.state.history[-i][1], NullObservation) for i in range(1, 4))):
# same (Action, NullObservation): like 'think' the same thought over and over
logger.debug('Action, NullObservation loop detected')
return True
elif (all
(isinstance(self.state.history[-i][1], AgentErrorObservation) for i in range(1, 4))):
# (NullAction, AgentErrorObservation): errors coming from an exception
# (Action, AgentErrorObservation): the same action getting an error, even if not necessarily the same error
logger.debug('Action, AgentErrorObservation loop detected')
return True

return False

0 comments on commit d1551c3

Please sign in to comment.