runtime.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. from abc import abstractmethod
  2. from typing import List
  3. from opendevin.core.config import config
  4. from opendevin.events.action import (
  5. ACTION_TYPE_TO_CLASS,
  6. Action,
  7. AgentRecallAction,
  8. BrowseURLAction,
  9. CmdKillAction,
  10. CmdRunAction,
  11. FileReadAction,
  12. FileWriteAction,
  13. IPythonRunCellAction,
  14. )
  15. from opendevin.events.observation import (
  16. CmdOutputObservation,
  17. ErrorObservation,
  18. NullObservation,
  19. Observation,
  20. )
  21. from opendevin.runtime import (
  22. DockerExecBox,
  23. DockerSSHBox,
  24. E2BBox,
  25. LocalBox,
  26. Sandbox,
  27. )
  28. from opendevin.runtime.browser.browser_env import BrowserEnv
  29. from opendevin.runtime.plugins import PluginRequirement
  30. def create_sandbox(sid: str = 'default', sandbox_type: str = 'exec') -> Sandbox:
  31. if sandbox_type == 'exec':
  32. return DockerExecBox(sid=sid, timeout=config.sandbox_timeout)
  33. elif sandbox_type == 'local':
  34. return LocalBox(timeout=config.sandbox_timeout)
  35. elif sandbox_type == 'ssh':
  36. return DockerSSHBox(sid=sid, timeout=config.sandbox_timeout)
  37. elif sandbox_type == 'e2b':
  38. return E2BBox(timeout=config.sandbox_timeout)
  39. else:
  40. raise ValueError(f'Invalid sandbox type: {sandbox_type}')
  41. class Runtime:
  42. """
  43. The runtime is how the agent interacts with the external environment.
  44. This includes a bash sandbox, a browser, and filesystem interactions.
  45. sid is the session id, which is used to identify the current user session.
  46. """
  47. sid: str
  48. sandbox: Sandbox
  49. def __init__(
  50. self,
  51. sid: str = 'default',
  52. ):
  53. self.sid = sid
  54. self.sandbox = create_sandbox(sid, config.sandbox_type)
  55. self.browser = BrowserEnv()
  56. def init_sandbox_plugins(self, plugins: List[PluginRequirement]) -> None:
  57. self.sandbox.init_plugins(plugins)
  58. async def run_action(self, action: Action) -> Observation:
  59. """
  60. Run an action and return the resulting observation.
  61. If the action is not runnable in any runtime, a NullObservation is returned.
  62. If the action is not supported by the current runtime, an ErrorObservation is returned.
  63. """
  64. if not action.runnable:
  65. return NullObservation('')
  66. action_id = action.action # type: ignore[attr-defined]
  67. if action_id not in ACTION_TYPE_TO_CLASS:
  68. return ErrorObservation(f'Action {action_id} does not exist.')
  69. if not hasattr(self, action_id):
  70. return ErrorObservation(
  71. f'Action {action_id} is not supported in the current runtime.'
  72. )
  73. observation = await getattr(self, action_id)(action)
  74. return observation
  75. def get_background_obs(self) -> List[CmdOutputObservation]:
  76. """
  77. Returns all observations that have accumulated in the runtime's background.
  78. Right now, this is just background commands, but could include e.g. asyncronous
  79. events happening in the browser.
  80. """
  81. obs = []
  82. for _id, cmd in self.sandbox.background_commands.items():
  83. output = cmd.read_logs()
  84. if output is not None and output != '':
  85. obs.append(
  86. CmdOutputObservation(
  87. content=output, command_id=_id, command=cmd.command
  88. )
  89. )
  90. return obs
  91. @abstractmethod
  92. async def run(self, action: CmdRunAction) -> Observation:
  93. pass
  94. @abstractmethod
  95. async def kill(self, action: CmdKillAction) -> Observation:
  96. pass
  97. @abstractmethod
  98. async def run_ipython(self, action: IPythonRunCellAction) -> Observation:
  99. pass
  100. @abstractmethod
  101. async def read(self, action: FileReadAction) -> Observation:
  102. pass
  103. @abstractmethod
  104. async def write(self, action: FileWriteAction) -> Observation:
  105. pass
  106. @abstractmethod
  107. async def browse(self, action: BrowseURLAction) -> Observation:
  108. pass
  109. @abstractmethod
  110. async def recall(self, action: AgentRecallAction) -> Observation:
  111. pass