runtime.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. from opendevin.core.config import config
  2. from opendevin.events.action import (
  3. AgentRecallAction,
  4. BrowseInteractiveAction,
  5. BrowseURLAction,
  6. CmdKillAction,
  7. CmdRunAction,
  8. FileReadAction,
  9. FileWriteAction,
  10. IPythonRunCellAction,
  11. )
  12. from opendevin.events.observation import (
  13. CmdOutputObservation,
  14. ErrorObservation,
  15. IPythonRunCellObservation,
  16. NullObservation,
  17. Observation,
  18. )
  19. from opendevin.events.stream import EventStream
  20. from opendevin.runtime import Sandbox
  21. from opendevin.runtime.runtime import Runtime
  22. from opendevin.storage.local import LocalFileStore
  23. from .browse import browse
  24. from .files import read_file, write_file
  25. class ServerRuntime(Runtime):
  26. def __init__(
  27. self,
  28. event_stream: EventStream,
  29. sid: str = 'default',
  30. sandbox: Sandbox | None = None,
  31. ):
  32. super().__init__(event_stream, sid, sandbox)
  33. self.file_store = LocalFileStore(config.workspace_base)
  34. async def run(self, action: CmdRunAction) -> Observation:
  35. return self._run_command(action.command, background=action.background)
  36. async def kill(self, action: CmdKillAction) -> Observation:
  37. cmd = self.sandbox.kill_background(action.command_id)
  38. return CmdOutputObservation(
  39. content=f'Background command with id {action.command_id} has been killed.',
  40. command_id=action.command_id,
  41. command=cmd.command,
  42. exit_code=0,
  43. )
  44. async def run_ipython(self, action: IPythonRunCellAction) -> Observation:
  45. obs = self._run_command(
  46. ("cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n" f'{action.code}\n' 'EOL'),
  47. background=False,
  48. )
  49. # run the code
  50. obs = self._run_command(
  51. ('cat /tmp/opendevin_jupyter_temp.py | execute_cli'), background=False
  52. )
  53. output = obs.content
  54. if 'pip install' in action.code and 'Successfully installed' in output:
  55. print(output)
  56. restart_kernel = 'import IPython\nIPython.Application.instance().kernel.do_shutdown(True)'
  57. if (
  58. 'Note: you may need to restart the kernel to use updated packages.'
  59. in output
  60. ):
  61. obs = self._run_command(
  62. (
  63. "cat > /tmp/opendevin_jupyter_temp.py <<'EOL'\n"
  64. f'{restart_kernel}\n'
  65. 'EOL'
  66. ),
  67. background=False,
  68. )
  69. obs = self._run_command(
  70. ('cat /tmp/opendevin_jupyter_temp.py | execute_cli'),
  71. background=False,
  72. )
  73. output = '[Package installed successfully]'
  74. if "{'status': 'ok', 'restart': True}" != obs.content.strip():
  75. print(obs.content)
  76. output += '\n[But failed to restart the kernel to load the package]'
  77. else:
  78. output += '\n[Kernel restarted successfully to load the package]'
  79. # re-init the kernel after restart
  80. if action.kernel_init_code:
  81. obs = self._run_command(
  82. (
  83. f"cat > /tmp/opendevin_jupyter_init.py <<'EOL'\n"
  84. f'{action.kernel_init_code}\n'
  85. 'EOL'
  86. ),
  87. background=False,
  88. )
  89. obs = self._run_command(
  90. 'cat /tmp/opendevin_jupyter_init.py | execute_cli',
  91. background=False,
  92. )
  93. return IPythonRunCellObservation(content=output, code=action.code)
  94. async def read(self, action: FileReadAction) -> Observation:
  95. # TODO: use self.file_store
  96. working_dir = self.sandbox.get_working_directory()
  97. return await read_file(action.path, working_dir, action.start, action.end)
  98. async def write(self, action: FileWriteAction) -> Observation:
  99. # TODO: use self.file_store
  100. working_dir = self.sandbox.get_working_directory()
  101. return await write_file(
  102. action.path, working_dir, action.content, action.start, action.end
  103. )
  104. async def browse(self, action: BrowseURLAction) -> Observation:
  105. return await browse(action, self.browser)
  106. async def browse_interactive(self, action: BrowseInteractiveAction) -> Observation:
  107. return await browse(action, self.browser)
  108. async def recall(self, action: AgentRecallAction) -> Observation:
  109. return NullObservation('')
  110. def _run_command(self, command: str, background=False) -> Observation:
  111. if background:
  112. return self._run_background(command)
  113. else:
  114. return self._run_immediately(command)
  115. def _run_immediately(self, command: str) -> Observation:
  116. try:
  117. exit_code, output = self.sandbox.execute(command)
  118. if 'pip install' in command and 'Successfully installed' in output:
  119. print(output)
  120. output = 'Package installed successfully'
  121. return CmdOutputObservation(
  122. command_id=-1, content=str(output), command=command, exit_code=exit_code
  123. )
  124. except UnicodeDecodeError:
  125. return ErrorObservation('Command output could not be decoded as utf-8')
  126. def _run_background(self, command: str) -> Observation:
  127. bg_cmd = self.sandbox.execute_in_background(command)
  128. content = f'Background command started. To stop it, send a `kill` action with command_id {bg_cmd.pid}'
  129. return CmdOutputObservation(
  130. content=content,
  131. command_id=bg_cmd.pid,
  132. command=command,
  133. exit_code=0,
  134. )