__init__.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import os
  2. import re
  3. from litellm import completion
  4. from termcolor import colored
  5. from typing import List, Mapping
  6. from opendevin.agent import Agent
  7. from opendevin.state import State
  8. from opendevin.action import (
  9. Action,
  10. CmdRunAction,
  11. AgentEchoAction,
  12. AgentFinishAction,
  13. )
  14. from opendevin.observation import (
  15. CmdOutputObservation,
  16. AgentMessageObservation,
  17. )
  18. assert (
  19. "OPENAI_API_KEY" in os.environ
  20. ), "Please set the OPENAI_API_KEY environment variable."
  21. SYSTEM_MESSAGE = """You are a helpful assistant. You will be provided access (as root) to a bash shell to complete user-provided tasks.
  22. 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.
  23. 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>.
  24. For example:
  25. You can list the files in the current directory by executing the following command:
  26. <execute>ls</execute>
  27. You can also install packages using pip:
  28. <execute> pip install numpy </execute>
  29. You can also write a block of code to a file:
  30. <execute>
  31. echo "import math
  32. print(math.pi)" > math.py
  33. </execute>
  34. When you are done, execute "exit" to close the shell and end the conversation.
  35. """
  36. INVALID_INPUT_MESSAGE = (
  37. "I don't understand your input. \n"
  38. "If you want to execute command, please use <execute> YOUR_COMMAND_HERE </execute>.\n"
  39. "If you already completed the task, please exit the shell by generating: <execute> exit </execute>."
  40. )
  41. def parse_response(response) -> str:
  42. action = response.choices[0].message.content
  43. if "<execute>" in action and "</execute>" not in action:
  44. action += "</execute>"
  45. return action
  46. class CodeActAgent(Agent):
  47. def __init__(
  48. self,
  49. model_name: str
  50. ) -> None:
  51. """
  52. Initializes a new instance of the CodeActAgent class.
  53. Parameters:
  54. - instruction (str): The instruction for the agent to execute.
  55. - max_steps (int): The maximum number of steps to run the agent.
  56. """
  57. super().__init__(model_name)
  58. self.messages: List[Mapping[str, str]] = []
  59. self.instruction: str = ""
  60. def step(self, state: State) -> Action:
  61. if len(self.messages) == 0:
  62. assert self.instruction, "Expecting instruction to be set"
  63. self.messages = [
  64. {"role": "system", "content": SYSTEM_MESSAGE},
  65. {"role": "user", "content": self.instruction},
  66. ]
  67. print(colored("===USER:===\n" + self.instruction, "green"))
  68. updated_info = state.updated_info
  69. if updated_info:
  70. for prev_action, obs in updated_info:
  71. assert isinstance(prev_action, (CmdRunAction, AgentEchoAction)), "Expecting CmdRunAction or AgentEchoAction for Action"
  72. if isinstance(obs, AgentMessageObservation): # warning message from itself
  73. self.messages.append({"role": "user", "content": obs.content})
  74. print(colored("===USER:===\n" + obs.content, "green"))
  75. elif isinstance(obs, CmdOutputObservation):
  76. content = "OBSERVATION:\n" + obs.content
  77. content += f"\n[Command {obs.command_id} finished with exit code {obs.exit_code}]]"
  78. self.messages.append({"role": "user", "content": content})
  79. print(colored("===ENV OBSERVATION:===\n" + content, "blue"))
  80. else:
  81. raise NotImplementedError(f"Unknown observation type: {obs.__class__}")
  82. response = completion(
  83. messages=self.messages,
  84. model=self.model_name,
  85. stop=["</execute>"],
  86. temperature=0.0,
  87. seed=42,
  88. )
  89. action_str: str = parse_response(response)
  90. self.messages.append({"role": "assistant", "content": action_str})
  91. print(colored("===ASSISTANT:===\n" + action_str, "yellow"))
  92. command = re.search(r"<execute>(.*)</execute>", action_str, re.DOTALL)
  93. if command is not None:
  94. # a command was found
  95. command_group = command.group(1)
  96. if command_group.strip() == "exit":
  97. print(colored("Exit received. Exiting...", "red"))
  98. return AgentFinishAction()
  99. return CmdRunAction(command = command_group)
  100. # # execute the code
  101. # # TODO: does exit_code get loaded into Message?
  102. # exit_code, observation = self.env.execute(command_group)
  103. # self._history.append(Message(Role.ASSISTANT, observation))
  104. # print(colored("===ENV OBSERVATION:===\n" + observation, "blue"))
  105. else:
  106. # we could provide a error message for the model to continue similar to
  107. # https://github.com/xingyaoww/mint-bench/blob/main/mint/envs/general_env.py#L18-L23
  108. # observation = INVALID_INPUT_MESSAGE
  109. # self._history.append(Message(Role.ASSISTANT, observation))
  110. # print(colored("===ENV OBSERVATION:===\n" + observation, "blue"))
  111. return AgentEchoAction(content=INVALID_INPUT_MESSAGE) # warning message to itself
  112. def search_memory(self, query: str) -> List[str]:
  113. raise NotImplementedError("Implement this abstract method")
  114. Agent.register("CodeActAgent", CodeActAgent)