From 6e3b554317de7bc5d96ef81b4097287e05c0c4d0 Mon Sep 17 00:00:00 2001 From: RaGe Date: Sun, 7 Apr 2024 15:57:31 -0400 Subject: [PATCH] Create a CommandExecutor abstract class (#874) * Create abstract CommandExecutor class * Use CommandExecutor for Sandbox --- opendevin/controller/command_executor.py | 24 ++++++++++++++++++++++++ opendevin/controller/command_manager.py | 9 ++++----- opendevin/sandbox/sandbox.py | 9 +++++---- 3 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 opendevin/controller/command_executor.py diff --git a/opendevin/controller/command_executor.py b/opendevin/controller/command_executor.py new file mode 100644 index 000000000000..26f4510a5ebc --- /dev/null +++ b/opendevin/controller/command_executor.py @@ -0,0 +1,24 @@ +from typing import Tuple +from abc import ABC, abstractmethod + + +class CommandExecutor(ABC): + @abstractmethod + def execute(self, cmd: str) -> Tuple[int, str]: + pass + + @abstractmethod + def execute_in_background(self, cmd: str): + pass + + @abstractmethod + def kill_background(self, id: int): + pass + + @abstractmethod + def read_logs(self, id: int) -> str: + pass + + @abstractmethod + def close(self): + pass diff --git a/opendevin/controller/command_manager.py b/opendevin/controller/command_manager.py index b442bd90a19d..2eed6d970c17 100644 --- a/opendevin/controller/command_manager.py +++ b/opendevin/controller/command_manager.py @@ -1,5 +1,4 @@ from typing import List - from opendevin.observation import CmdOutputObservation from opendevin.sandbox.sandbox import DockerInteractive @@ -13,7 +12,7 @@ def __init__( ): self.directory = dir self.shell = DockerInteractive( - id=(id or "default"), workspace_dir=dir, container_image=container_image + id=(id or 'default'), workspace_dir=dir, container_image=container_image ) def run_command(self, command: str, background=False) -> CmdOutputObservation: @@ -31,7 +30,7 @@ def _run_immediately(self, command: str) -> CmdOutputObservation: def _run_background(self, command: str) -> CmdOutputObservation: bg_cmd = self.shell.execute_in_background(command) return CmdOutputObservation( - content=f"Background command started. To stop it, send a `kill` action with id {bg_cmd.id}", + content=f'Background command started. To stop it, send a `kill` action with id {bg_cmd.id}', command_id=bg_cmd.id, command=command, exit_code=0, @@ -40,7 +39,7 @@ def _run_background(self, command: str) -> CmdOutputObservation: def kill_command(self, id: int) -> CmdOutputObservation: cmd = self.shell.kill_background(id) return CmdOutputObservation( - content=f"Background command with id {id} has been killed.", + content=f'Background command with id {id} has been killed.', command_id=id, command=cmd.command, exit_code=0, @@ -50,7 +49,7 @@ def get_background_obs(self) -> List[CmdOutputObservation]: obs = [] for _id, cmd in self.shell.background_commands.items(): output = cmd.read_logs() - if output is not None and output != "": + if output is not None and output != '': obs.append( CmdOutputObservation( content=output, command_id=_id, command=cmd.command diff --git a/opendevin/sandbox/sandbox.py b/opendevin/sandbox/sandbox.py index 5e1a6a10935c..072733638f32 100644 --- a/opendevin/sandbox/sandbox.py +++ b/opendevin/sandbox/sandbox.py @@ -12,6 +12,7 @@ from opendevin import config from opendevin.logging import opendevin_logger as logger +from opendevin.controller.command_executor import CommandExecutor InputType = namedtuple('InputType', ['content']) OutputType = namedtuple('OutputType', ['content']) @@ -84,7 +85,7 @@ def read_logs(self) -> str: return (logs + last_remains).decode('utf-8', errors='replace') -class DockerInteractive: +class DockerInteractive(CommandExecutor): closed = False cur_background_id = 0 background_commands: Dict[int, BackgroundCommand] = {} @@ -127,7 +128,7 @@ def __init__( else: self.container_image = container_image - self.container_name = f"sandbox-{self.instance_id}" + self.container_name = f'sandbox-{self.instance_id}' if not self.is_container_running(): self.restart_docker_container() @@ -175,7 +176,7 @@ def run_command(container, command): pid = self.get_pid(cmd) if pid is not None: self.container.exec_run( - f"kill -9 {pid}", workdir='/workspace') + f'kill -9 {pid}', workdir='/workspace') return -1, f'Command: "{cmd}" timed out' return exit_code, logs.decode('utf-8') @@ -207,7 +208,7 @@ def kill_background(self, id: int) -> BackgroundCommand: bg_cmd = self.background_commands[id] if bg_cmd.pid is not None: self.container.exec_run( - f"kill -9 {bg_cmd.pid}", workdir='/workspace') + f'kill -9 {bg_cmd.pid}', workdir='/workspace') bg_cmd.result.output.close() self.background_commands.pop(id) return bg_cmd