OpenHands/opendevin/controller/action_manager.py
Christian Balcom 24b71927c3
fix(backend) changes to improve Command-R+ behavior, plus file i/o error improvements, attempt 2 (#1417)
* Some improvements to prompts, some better exception handling for various file IO errors, added timeout and max return token configurations for the LLM api.

* More monologue prompt improvements

* Dynamically set username provided in prompt.

* Remove absolute paths from llm prompts, fetch working directory from sandbox when resolving paths in fileio operations, add customizable timeout for bash commands, mention said timeout in llm prompt.

* Switched ssh_box to disabling tty echo and removed the logic attempting to delete it from the response afterwards, fixed get_working_directory for ssh_box.

* Update prompts in integration tests to match monologue agent changes.

* Minor tweaks to make merge easier.

* Another minor prompt tweak, better invalid json handling.

* Fix lint error

* More catch-up to fix lint errors introduced by merge.

* Force WORKSPACE_MOUNT_PATH_IN_SANDBOX to match WORKSPACE_MOUNT_PATH in local sandbox mode, combine exception handlers in prompts.py.

---------

Co-authored-by: Jim Su <jimsu@protonmail.com>
Co-authored-by: Engel Nyst <enyst@users.noreply.github.com>
2024-04-28 21:58:53 -04:00

102 lines
3.5 KiB
Python

from typing import List
from opendevin import config
from opendevin.observation import CmdOutputObservation, AgentErrorObservation
from opendevin.sandbox import DockerExecBox, DockerSSHBox, Sandbox, LocalBox, E2BBox
from opendevin.schema import ConfigType
from opendevin.action import (
Action,
)
from opendevin.observation import (
Observation,
NullObservation,
)
from opendevin.sandbox.plugins import PluginRequirement
class ActionManager:
id: str
sandbox: Sandbox
def __init__(
self,
sid: str,
):
sandbox_type = config.get(ConfigType.SANDBOX_TYPE).lower()
if sandbox_type == 'exec':
self.sandbox = DockerExecBox(
sid=(sid or 'default'),
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
elif sandbox_type == 'local':
self.sandbox = LocalBox(
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
elif sandbox_type == 'ssh':
self.sandbox = DockerSSHBox(
sid=(sid or 'default'),
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
elif sandbox_type == 'e2b':
self.sandbox = E2BBox(
timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
)
else:
raise ValueError(f'Invalid sandbox type: {sandbox_type}')
def init_sandbox_plugins(self, plugins: List[PluginRequirement]):
self.sandbox.init_plugins(plugins)
async def run_action(self, action: Action, agent_controller) -> Observation:
observation: Observation = NullObservation('')
if not action.executable:
return observation
observation = await action.run(agent_controller)
return observation
def run_command(self, command: str, background=False) -> Observation:
if background:
return self._run_background(command)
else:
return self._run_immediately(command)
def _run_immediately(self, command: str) -> Observation:
try:
exit_code, output = self.sandbox.execute(command)
return CmdOutputObservation(
command_id=-1, content=output, command=command, exit_code=exit_code
)
except UnicodeDecodeError:
return AgentErrorObservation('Command output could not be decoded as utf-8')
def _run_background(self, command: str) -> CmdOutputObservation:
bg_cmd = self.sandbox.execute_in_background(command)
content = f'Background command started. To stop it, send a `kill` action with id {bg_cmd.pid}'
return CmdOutputObservation(
content=content,
command_id=bg_cmd.pid,
command=command,
exit_code=0,
)
def kill_command(self, id: int) -> CmdOutputObservation:
cmd = self.sandbox.kill_background(id)
return CmdOutputObservation(
content=f'Background command with id {id} has been killed.',
command_id=id,
command=cmd.command,
exit_code=0,
)
def get_background_obs(self) -> List[CmdOutputObservation]:
obs = []
for _id, cmd in self.sandbox.background_commands.items():
output = cmd.read_logs()
if output is not None and output != '':
obs.append(
CmdOutputObservation(
content=output, command_id=_id, command=cmd.command
)
)
return obs