agent.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. from typing import Optional
  2. from agenthub.codeact_agent.codeact_agent import CodeActAgent
  3. from opendevin.const.guide_url import TROUBLESHOOTING_URL
  4. from opendevin.controller import AgentController
  5. from opendevin.controller.agent import Agent
  6. from opendevin.core.config import config
  7. from opendevin.core.logger import opendevin_logger as logger
  8. from opendevin.core.schema import ActionType, AgentState, ConfigType
  9. from opendevin.events.action import (
  10. ChangeAgentStateAction,
  11. NullAction,
  12. )
  13. from opendevin.events.event import Event
  14. from opendevin.events.observation import (
  15. NullObservation,
  16. )
  17. from opendevin.events.serialization.action import action_from_dict
  18. from opendevin.events.serialization.event import event_to_dict
  19. from opendevin.events.stream import EventSource, EventStream, EventStreamSubscriber
  20. from opendevin.llm.llm import LLM
  21. from opendevin.runtime import DockerSSHBox
  22. from opendevin.runtime.e2b.runtime import E2BRuntime
  23. from opendevin.runtime.runtime import Runtime
  24. from opendevin.runtime.server.runtime import ServerRuntime
  25. from opendevin.server.session import session_manager
  26. class AgentUnit:
  27. """Represents a session with an agent.
  28. Attributes:
  29. controller: The AgentController instance for controlling the agent.
  30. """
  31. sid: str
  32. event_stream: EventStream
  33. controller: Optional[AgentController] = None
  34. runtime: Optional[Runtime] = None
  35. def __init__(self, sid):
  36. """Initializes a new instance of the Session class."""
  37. self.sid = sid
  38. self.event_stream = EventStream(sid)
  39. self.event_stream.subscribe(EventStreamSubscriber.SERVER, self.on_event)
  40. if config.runtime == 'server':
  41. logger.info('Using server runtime')
  42. self.runtime = ServerRuntime(self.event_stream, sid)
  43. elif config.runtime == 'e2b':
  44. logger.info('Using E2B runtime')
  45. self.runtime = E2BRuntime(self.event_stream, sid)
  46. async def send_error(self, message):
  47. """Sends an error message to the client.
  48. Args:
  49. message: The error message to send.
  50. """
  51. await session_manager.send_error(self.sid, message)
  52. async def send_message(self, message):
  53. """Sends a message to the client.
  54. Args:
  55. message: The message to send.
  56. """
  57. await session_manager.send_message(self.sid, message)
  58. async def send(self, data):
  59. """Sends data to the client.
  60. Args:
  61. data: The data to send.
  62. """
  63. await session_manager.send(self.sid, data)
  64. async def dispatch(self, action: str | None, data: dict):
  65. """Dispatches actions to the agent from the client."""
  66. if action is None:
  67. await self.send_error('Invalid action')
  68. return
  69. if action == ActionType.INIT:
  70. await self.create_controller(data)
  71. await self.event_stream.add_event(
  72. ChangeAgentStateAction(AgentState.INIT), EventSource.USER
  73. )
  74. return
  75. action_dict = data.copy()
  76. action_dict['action'] = action
  77. action_obj = action_from_dict(action_dict)
  78. await self.event_stream.add_event(action_obj, EventSource.USER)
  79. async def create_controller(self, start_event: dict):
  80. """Creates an AgentController instance.
  81. Args:
  82. start_event: The start event data (optional).
  83. """
  84. args = {
  85. key: value
  86. for key, value in start_event.get('args', {}).items()
  87. if value != ''
  88. } # remove empty values, prevent FE from sending empty strings
  89. agent_cls = args.get(ConfigType.AGENT, config.agent.name)
  90. model = args.get(ConfigType.LLM_MODEL, config.llm.model)
  91. api_key = args.get(ConfigType.LLM_API_KEY, config.llm.api_key)
  92. api_base = config.llm.base_url
  93. max_iterations = args.get(ConfigType.MAX_ITERATIONS, config.max_iterations)
  94. max_chars = args.get(ConfigType.MAX_CHARS, config.llm.max_chars)
  95. logger.info(f'Creating agent {agent_cls} using LLM {model}')
  96. llm = LLM(model=model, api_key=api_key, base_url=api_base)
  97. agent = Agent.get_cls(agent_cls)(llm)
  98. if isinstance(agent, CodeActAgent):
  99. if not self.runtime or not isinstance(self.runtime.sandbox, DockerSSHBox):
  100. logger.warning(
  101. 'CodeActAgent requires DockerSSHBox as sandbox! Using other sandbox that are not stateful (LocalBox, DockerExecBox) will not work properly.'
  102. )
  103. # Initializing plugins into the runtime
  104. assert self.runtime is not None, 'Runtime is not initialized'
  105. self.runtime.init_sandbox_plugins(agent.sandbox_plugins)
  106. if self.controller is not None:
  107. await self.controller.close()
  108. try:
  109. self.controller = AgentController(
  110. sid=self.sid,
  111. event_stream=self.event_stream,
  112. agent=agent,
  113. max_iterations=int(max_iterations),
  114. max_chars=int(max_chars),
  115. )
  116. except Exception as e:
  117. logger.exception(f'Error creating controller: {e}')
  118. await self.send_error(
  119. f'Error creating controller. Please check Docker is running and visit `{TROUBLESHOOTING_URL}` for more debugging information..'
  120. )
  121. return
  122. async def on_event(self, event: Event):
  123. """Callback function for agent events.
  124. Args:
  125. event: The agent event (Observation or Action).
  126. """
  127. if isinstance(event, NullAction):
  128. return
  129. if isinstance(event, NullObservation):
  130. return
  131. if event.source == 'agent' and not isinstance(
  132. event, (NullAction, NullObservation)
  133. ):
  134. await self.send(event_to_dict(event))
  135. async def close(self):
  136. if self.controller is not None:
  137. await self.controller.close()
  138. if self.runtime is not None:
  139. self.runtime.close()