Files
OpenHands/opendevin/runtime/server/runtime.py

152 lines
5.7 KiB
Python

from opendevin.core.config import config
from opendevin.events.action import (
AgentRecallAction,
BrowseInteractiveAction,
BrowseURLAction,
CmdKillAction,
CmdRunAction,
FileReadAction,
FileWriteAction,
IPythonRunCellAction,
)
from opendevin.events.observation import (
CmdOutputObservation,
ErrorObservation,
IPythonRunCellObservation,
NullObservation,
Observation,
)
from opendevin.events.stream import EventStream
from opendevin.runtime import Sandbox
from opendevin.runtime.runtime import Runtime
from opendevin.storage.local import LocalFileStore
from .browse import browse
from .files import read_file, write_file
class ServerRuntime(Runtime):
def __init__(
self,
event_stream: EventStream,
sid: str = 'default',
sandbox: Sandbox | None = None,
):
super().__init__(event_stream, sid, sandbox)
self.file_store = LocalFileStore(config.workspace_base)
async def run(self, action: CmdRunAction) -> Observation:
return self._run_command(action.command, background=action.background)
async def kill(self, action: CmdKillAction) -> Observation:
cmd = self.sandbox.kill_background(action.command_id)
return CmdOutputObservation(
content=f'Background command with id {action.command_id} has been killed.',
command_id=action.command_id,
command=cmd.command,
exit_code=0,
)
async def run_ipython(self, action: IPythonRunCellAction) -> Observation:
obs = self._run_command(
("cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n" f'{action.code}\n' 'EOL'),
background=False,
)
# run the code
obs = self._run_command(
('cat /tmp/opendevin_jupyter_temp.py | execute_cli'), background=False
)
output = obs.content
if 'pip install' in action.code and 'Successfully installed' in output:
print(output)
restart_kernel = 'import IPython\nIPython.Application.instance().kernel.do_shutdown(True)'
if (
'Note: you may need to restart the kernel to use updated packages.'
in output
):
obs = self._run_command(
(
"cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n"
f'{restart_kernel}\n'
'EOL'
),
background=False,
)
obs = self._run_command(
('cat /tmp/opendevin_jupyter_temp.py | execute_cli'),
background=False,
)
output = '[Package installed successfully]'
if "{'status': 'ok', 'restart': True}" != obs.content.strip():
print(obs.content)
output += '\n[But failed to restart the kernel to load the package]'
else:
output += '\n[Kernel restarted successfully to load the package]'
# re-init the kernel after restart
if action.kernel_init_code:
obs = self._run_command(
(
f"cat > /tmp/opendevin_jupyter_init.py <<'EOL'\n"
f'{action.kernel_init_code}\n'
'EOL'
),
background=False,
)
obs = self._run_command(
'cat /tmp/opendevin_jupyter_init.py | execute_cli',
background=False,
)
return IPythonRunCellObservation(content=output, code=action.code)
async def read(self, action: FileReadAction) -> Observation:
# TODO: use self.file_store
working_dir = self.sandbox.get_working_directory()
return await read_file(action.path, working_dir, action.start, action.end)
async def write(self, action: FileWriteAction) -> Observation:
# TODO: use self.file_store
working_dir = self.sandbox.get_working_directory()
return await write_file(
action.path, working_dir, action.content, action.start, action.end
)
async def browse(self, action: BrowseURLAction) -> Observation:
return await browse(action, self.browser)
async def browse_interactive(self, action: BrowseInteractiveAction) -> Observation:
return await browse(action, self.browser)
async def recall(self, action: AgentRecallAction) -> Observation:
return NullObservation('')
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)
if 'pip install' in command and 'Successfully installed' in output:
print(output)
output = 'Package installed successfully'
return CmdOutputObservation(
command_id=-1, content=str(output), command=command, exit_code=exit_code
)
except UnicodeDecodeError:
return ErrorObservation('Command output could not be decoded as utf-8')
def _run_background(self, command: str) -> Observation:
bg_cmd = self.sandbox.execute_in_background(command)
content = f'Background command started. To stop it, send a `kill` action with command_id {bg_cmd.pid}'
return CmdOutputObservation(
content=content,
command_id=bg_cmd.pid,
command=command,
exit_code=0,
)