__init__.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import os
  2. import re
  3. from litellm import completion
  4. from termcolor import colored
  5. from typing import List, Dict
  6. from opendevin.agent import Agent, Message, Role
  7. from opendevin.lib.event import Event
  8. from opendevin.lib.command_manager import CommandManager
  9. from opendevin.sandbox.sandbox import DockerInteractive
  10. assert (
  11. "OPENAI_API_KEY" in os.environ
  12. ), "Please set the OPENAI_API_KEY environment variable."
  13. SYSTEM_MESSAGE = """You are a helpful assistant. You will be provided access (as root) to a bash shell to complete user-provided tasks.
  14. You will be able to execute commands in the bash shell, interact with the file system, install packages, and receive the output of your commands.
  15. DO NOT provide code in ```triple backticks```. Instead, you should execute bash command on behalf of the user by wrapping them with <execute> and </execute>.
  16. For example:
  17. You can list the files in the current directory by executing the following command:
  18. <execute>ls</execute>
  19. You can also install packages using pip:
  20. <execute> pip install numpy </execute>
  21. You can also write a block of code to a file:
  22. <execute>
  23. echo "import math
  24. print(math.pi)" > math.py
  25. </execute>
  26. When you are done, execute "exit" to close the shell and end the conversation.
  27. """
  28. INVALID_INPUT_MESSAGE = (
  29. "I don't understand your input. \n"
  30. "If you want to execute command, please use <execute> YOUR_COMMAND_HERE </execute>.\n"
  31. "If you already completed the task, please exit the shell by generating: <execute> exit </execute>."
  32. )
  33. def parse_response(response) -> str:
  34. action = response.choices[0].message.content
  35. if "<execute>" in action and "</execute>" not in action:
  36. action += "</execute>"
  37. return action
  38. class CodeActAgent(Agent):
  39. def __init__(
  40. self,
  41. instruction: str,
  42. workspace_dir: str,
  43. max_steps: int = 100
  44. ) -> None:
  45. """
  46. Initializes a new instance of the CodeActAgent class.
  47. Parameters:
  48. - instruction (str): The instruction for the agent to execute.
  49. - max_steps (int): The maximum number of steps to run the agent.
  50. """
  51. super().__init__(instruction, workspace_dir, max_steps)
  52. self._history = [Message(Role.SYSTEM, SYSTEM_MESSAGE)]
  53. self._history.append(Message(Role.USER, instruction))
  54. self.env = DockerInteractive(workspace_dir=workspace_dir)
  55. print(colored("===USER:===\n" + instruction, "green"))
  56. def _history_to_messages(self) -> List[Dict]:
  57. return [message.to_dict() for message in self._history]
  58. def run(self) -> None:
  59. """
  60. Starts the execution of the assigned instruction. This method should
  61. be implemented by subclasses to define the specific execution logic.
  62. """
  63. for _ in range(self.max_steps):
  64. response = completion(
  65. messages=self._history_to_messages(),
  66. model=self.model_name,
  67. stop=["</execute>"],
  68. temperature=0.0,
  69. seed=42,
  70. )
  71. action = parse_response(response)
  72. self._history.append(Message(Role.ASSISTANT, action))
  73. print(colored("===ASSISTANT:===\n" + action, "yellow"))
  74. command = re.search(r"<execute>(.*)</execute>", action, re.DOTALL)
  75. if command is not None:
  76. # a command was found
  77. command_group = command.group(1)
  78. if command_group.strip() == "exit":
  79. print(colored("Exit received. Exiting...", "red"))
  80. break
  81. # execute the code
  82. # TODO: does exit_code get loaded into Message?
  83. exit_code, observation = self.env.execute(command_group)
  84. self._history.append(Message(Role.ASSISTANT, observation))
  85. print(colored("===ENV OBSERVATION:===\n" + observation, "blue"))
  86. else:
  87. # we could provide a error message for the model to continue similar to
  88. # https://github.com/xingyaoww/mint-bench/blob/main/mint/envs/general_env.py#L18-L23
  89. observation = INVALID_INPUT_MESSAGE
  90. self._history.append(Message(Role.ASSISTANT, observation))
  91. print(colored("===ENV OBSERVATION:===\n" + observation, "blue"))
  92. self.env.close()
  93. def chat(self, message: str) -> None:
  94. """
  95. Optional method for interactive communication with the agent during its execution. Implementations
  96. can use this method to modify the agent's behavior or state based on chat inputs.
  97. Parameters:
  98. - message (str): The chat message or command.
  99. """
  100. raise NotImplementedError
  101. # TODO: implement these abstract methods
  102. def add_event(self, event: Event) -> None:
  103. raise NotImplementedError("Implement this abstract method")
  104. def step(self, cmd_mgr: CommandManager) -> Event:
  105. raise NotImplementedError("Implement this abstract method")
  106. def search_memory(self, query: str) -> List[str]:
  107. raise NotImplementedError("Implement this abstract method")
  108. Agent.register("CodeActAgent", CodeActAgent)