action_manager.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. from typing import List
  2. from opendevin import config
  3. from opendevin.action import (
  4. Action,
  5. )
  6. from opendevin.observation import (
  7. AgentErrorObservation,
  8. CmdOutputObservation,
  9. NullObservation,
  10. Observation,
  11. )
  12. from opendevin.sandbox import DockerExecBox, DockerSSHBox, E2BBox, LocalBox, Sandbox
  13. from opendevin.sandbox.plugins import PluginRequirement
  14. from opendevin.schema import ConfigType
  15. class ActionManager:
  16. id: str
  17. sandbox: Sandbox
  18. def __init__(
  19. self,
  20. sid: str = 'default',
  21. ):
  22. sandbox_type = config.get(ConfigType.SANDBOX_TYPE).lower()
  23. if sandbox_type == 'exec':
  24. self.sandbox = DockerExecBox(
  25. sid=(sid or 'default'), timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
  26. )
  27. elif sandbox_type == 'local':
  28. self.sandbox = LocalBox(timeout=config.get(ConfigType.SANDBOX_TIMEOUT))
  29. elif sandbox_type == 'ssh':
  30. self.sandbox = DockerSSHBox(
  31. sid=(sid or 'default'), timeout=config.get(ConfigType.SANDBOX_TIMEOUT)
  32. )
  33. elif sandbox_type == 'e2b':
  34. self.sandbox = E2BBox(timeout=config.get(ConfigType.SANDBOX_TIMEOUT))
  35. else:
  36. raise ValueError(f'Invalid sandbox type: {sandbox_type}')
  37. def init_sandbox_plugins(self, plugins: List[PluginRequirement]):
  38. self.sandbox.init_plugins(plugins)
  39. async def run_action(self, action: Action, agent_controller) -> Observation:
  40. observation: Observation = NullObservation('')
  41. if not action.executable:
  42. return observation
  43. observation = await action.run(agent_controller)
  44. return observation
  45. def run_command(self, command: str, background=False) -> Observation:
  46. if background:
  47. return self._run_background(command)
  48. else:
  49. return self._run_immediately(command)
  50. def _run_immediately(self, command: str) -> Observation:
  51. try:
  52. exit_code, output = self.sandbox.execute(command)
  53. return CmdOutputObservation(
  54. command_id=-1, content=output, command=command, exit_code=exit_code
  55. )
  56. except UnicodeDecodeError:
  57. return AgentErrorObservation('Command output could not be decoded as utf-8')
  58. def _run_background(self, command: str) -> CmdOutputObservation:
  59. bg_cmd = self.sandbox.execute_in_background(command)
  60. content = f'Background command started. To stop it, send a `kill` action with id {bg_cmd.pid}'
  61. return CmdOutputObservation(
  62. content=content,
  63. command_id=bg_cmd.pid,
  64. command=command,
  65. exit_code=0,
  66. )
  67. def kill_command(self, id: int) -> CmdOutputObservation:
  68. cmd = self.sandbox.kill_background(id)
  69. return CmdOutputObservation(
  70. content=f'Background command with id {id} has been killed.',
  71. command_id=id,
  72. command=cmd.command,
  73. exit_code=0,
  74. )
  75. def get_background_obs(self) -> List[CmdOutputObservation]:
  76. obs = []
  77. for _id, cmd in self.sandbox.background_commands.items():
  78. output = cmd.read_logs()
  79. if output is not None and output != '':
  80. obs.append(
  81. CmdOutputObservation(
  82. content=output, command_id=_id, command=cmd.command
  83. )
  84. )
  85. return obs