manager.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import asyncio
  2. import os
  3. from typing import Optional
  4. from opendevin import config
  5. from opendevin.action import (
  6. Action,
  7. NullAction,
  8. )
  9. from opendevin.agent import Agent
  10. from opendevin.controller import AgentController
  11. from opendevin.llm.llm import LLM
  12. from opendevin.logger import opendevin_logger as logger
  13. from opendevin.observation import NullObservation, Observation, UserMessageObservation
  14. from opendevin.schema import ActionType, ConfigType
  15. from opendevin.server.session import session_manager
  16. class AgentManager:
  17. """Represents a session with an agent.
  18. Attributes:
  19. controller: The AgentController instance for controlling the agent.
  20. agent: The Agent instance representing the agent.
  21. agent_task: The task representing the agent's execution.
  22. """
  23. sid: str
  24. def __init__(self, sid):
  25. """Initializes a new instance of the Session class."""
  26. self.sid = sid
  27. self.controller: Optional[AgentController] = None
  28. self.agent: Optional[Agent] = None
  29. self.agent_task = None
  30. async def send_error(self, message):
  31. """Sends an error message to the client.
  32. Args:
  33. message: The error message to send.
  34. """
  35. await session_manager.send_error(self.sid, message)
  36. async def send_message(self, message):
  37. """Sends a message to the client.
  38. Args:
  39. message: The message to send.
  40. """
  41. await session_manager.send_message(self.sid, message)
  42. async def send(self, data):
  43. """Sends data to the client.
  44. Args:
  45. data: The data to send.
  46. """
  47. await session_manager.send(self.sid, data)
  48. async def dispatch(self, action: str | None, data: dict):
  49. """Dispatches actions to the agent from the client."""
  50. if action is None:
  51. await self.send_error('Invalid action')
  52. return
  53. if action == ActionType.INIT:
  54. await self.create_controller(data)
  55. elif action == ActionType.START:
  56. await self.start_task(data)
  57. else:
  58. if self.controller is None:
  59. await self.send_error('No agent started. Please wait a second...')
  60. elif action == ActionType.CHAT:
  61. self.controller.add_history(
  62. NullAction(), UserMessageObservation(data['message'])
  63. )
  64. else:
  65. await self.send_error("I didn't recognize this action:" + action)
  66. def get_arg_or_default(self, _args: dict, key: ConfigType) -> str:
  67. """Gets an argument from the args dictionary or the default value.
  68. Args:
  69. _args: The args dictionary.
  70. key: The key to get.
  71. Returns:
  72. The value of the key or the default value.
  73. """
  74. return _args.get(key, config.get(key))
  75. async def create_controller(self, start_event: dict):
  76. """Creates an AgentController instance.
  77. Args:
  78. start_event: The start event data (optional).
  79. """
  80. args = {
  81. key: value
  82. for key, value in start_event.get('args', {}).items()
  83. if value != ''
  84. } # remove empty values, prevent FE from sending empty strings
  85. directory = self.get_arg_or_default(args, ConfigType.WORKSPACE_DIR)
  86. agent_cls = self.get_arg_or_default(args, ConfigType.AGENT)
  87. model = self.get_arg_or_default(args, ConfigType.LLM_MODEL)
  88. api_key = config.get(ConfigType.LLM_API_KEY)
  89. api_base = config.get(ConfigType.LLM_BASE_URL)
  90. container_image = config.get(ConfigType.SANDBOX_CONTAINER_IMAGE)
  91. max_iterations = self.get_arg_or_default(
  92. args, ConfigType.MAX_ITERATIONS)
  93. if not os.path.exists(directory):
  94. logger.info(
  95. 'Workspace directory %s does not exist. Creating it...', directory
  96. )
  97. os.makedirs(directory)
  98. directory = os.path.relpath(directory, os.getcwd())
  99. llm = LLM(model=model, api_key=api_key, base_url=api_base)
  100. AgentCls = Agent.get_cls(agent_cls)
  101. self.agent = AgentCls(llm)
  102. try:
  103. self.controller = AgentController(
  104. id=self.sid,
  105. agent=self.agent,
  106. workdir=directory,
  107. max_iterations=int(max_iterations),
  108. container_image=container_image,
  109. callbacks=[self.on_agent_event],
  110. )
  111. except Exception:
  112. logger.exception('Error creating controller.')
  113. await self.send_error(
  114. 'Error creating controller. Please check Docker is running using `docker ps`.'
  115. )
  116. return
  117. await self.send({'action': ActionType.INIT, 'message': 'Control loop started.'})
  118. async def start_task(self, start_event):
  119. """Starts a task for the agent.
  120. Args:
  121. start_event: The start event data.
  122. """
  123. if 'task' not in start_event['args']:
  124. await self.send_error('No task specified')
  125. return
  126. await self.send_message('Starting new task...')
  127. task = start_event['args']['task']
  128. if self.controller is None:
  129. await self.send_error('No agent started. Please wait a second...')
  130. return
  131. try:
  132. self.agent_task = await asyncio.create_task(
  133. self.controller.start_loop(task), name='agent loop'
  134. )
  135. except Exception:
  136. await self.send_error('Error during task loop.')
  137. def on_agent_event(self, event: Observation | Action):
  138. """Callback function for agent events.
  139. Args:
  140. event: The agent event (Observation or Action).
  141. """
  142. if isinstance(event, NullAction):
  143. return
  144. if isinstance(event, NullObservation):
  145. return
  146. event_dict = event.to_dict()
  147. asyncio.create_task(self.send(event_dict),
  148. name='send event in callback')
  149. def disconnect(self):
  150. self.websocket = None
  151. if self.agent_task:
  152. self.agent_task.cancel()
  153. if self.controller is not None:
  154. self.controller.command_manager.shell.close()