Skip to content

Commit

Permalink
Merge pull request emrgnt-cmplxty#25 from maks-ivanov/babyagi-archite…
Browse files Browse the repository at this point in the history
…cture

Babyagi architecture
  • Loading branch information
maks-ivanov authored Apr 11, 2023
2 parents 3910acd + c973231 commit f60ae33
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 88 deletions.
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
YOUR_TABLE_NAME = os.getenv("TABLE_NAME", "")
OBJECTIVE = os.getenv("OBJECTIVE", "")
DO_RETRY = bool(os.getenv("DO_RETRY", 1))
PLANNER_AGENT_OUTPUT_STRING = "Planner Agent Output: "
86 changes: 56 additions & 30 deletions custom_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
from github.Issue import Issue
from github.PullRequest import PullRequest
from github.Repository import Repository
from langchain.agents import Tool
from langchain.agents import Tool, tool
import requests

from utils import PassThroughBuffer
from utils import PassThroughBuffer, remove_html_tags


class GitToolBuilder:
Expand Down Expand Up @@ -59,50 +60,75 @@ def create_new_branch(self, branch_name: str) -> str:
Creates and checks out a new branch in the specified repository. The only input is the branch name. For exmpale: "my-branch". Before creating a new branch, make sure to pick a name that is not taken."
"""
# Create branch
self.pygit_repo.git.branch(branch_name)
# Checkout branch
self.pygit_repo.git.checkout(branch_name)
try:
self.pygit_repo.git.branch(branch_name)
# Checkout branch
self.pygit_repo.git.checkout(branch_name)
return f"Created and checked out branch {branch_name} in {self.github_repo.name} repository."

return f"Created and checked out branch {branch_name} in {self.github_repo.name} repository."
except Exception as e:
return f"Error: {e}"

def checkout_branch(self, branch_name: str) -> str:
"""
Creates and checks out a new branch in the specified repository. The only input is the branch name. For exmpale: "my-branch"
"""
# Checkout branch
self.pygit_repo.git.checkout(branch_name)
self.pygit_repo.git.pull()
return f"Checked out an existing branch {branch_name} in {self.github_repo.name} repository."
try:
self.pygit_repo.git.checkout(branch_name)
self.pygit_repo.git.pull()
return f"Checked out an existing branch {branch_name} in {self.github_repo.name} repository."
except Exception as e:
return f"Error: {e}"

def commit_to_git(self, file_names: str) -> str:
"""
Takes a string of comma-separated file names and commits them to git. For example "file1.py,file2.py"
"""
file_names = file_names.split(",")
for file_name in file_names:
self.pygit_repo.git.add(file_name)
try:
file_names = file_names.split(",")
for file_name in file_names:
self.pygit_repo.git.add(file_name)

self.pygit_repo.git.commit(m="Committing changes")
self.pygit_repo.git.push(
"--set-upstream", "origin", self.pygit_repo.git.branch("--show-current")
)
return f"Committed {file_names} to {self.github_repo.name} repository."
self.pygit_repo.git.commit(m="Committing changes")
self.pygit_repo.git.push(
"--set-upstream", "origin", self.pygit_repo.git.branch("--show-current")
)
return f"Committed {file_names} to {self.github_repo.name} repository."
except Exception as e:
return f"Error: {e}"

def create_pull_request(self, body) -> str:
"""
Creates a pull request in the specified repository.
"""
# get current branch name
assert type(self.work_item) == Issue
current_branch = self.pygit_repo.git.branch("--show-current")
title = "Fix for issue #" + str(self.work_item.number)
pull: github.PullRequest.PullRequest = self.github_repo.create_pull(
head=current_branch,
base=self.github_repo.default_branch,
issue=self.work_item,
)
if self.logger:
pull.create_issue_comment(self.logger.saved_output)
return (
f"Created pull request for {title} in {self.github_repo.name} repository."
)
try:
assert type(self.work_item) == Issue
current_branch = self.pygit_repo.git.branch("--show-current")
title = "Fix for issue #" + str(self.work_item.number)
pull: github.PullRequest.PullRequest = self.github_repo.create_pull(
head=current_branch,
base=self.github_repo.default_branch_name,
issue=self.work_item,
)
if self.logger:
pull.create_issue_comment(self.logger.saved_output)
return f"Created pull request for {title} in {self.github_repo.name} repository."
except Exception as e:
return f"Error: {e}"


@tool
def requests_get_clean(url: str) -> str:
"""
Sends a get request to a specified URL and returns clean text in the response.
"""
response = requests.get(url)
try:
if response.status_code == 200:
return remove_html_tags(response.text)
else:
raise Exception(f"Error: {response.status_code} {response.text}")
except Exception as e:
return f"Error: {e}"
128 changes: 71 additions & 57 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
import traceback

from git import Repo
from github.Issue import Issue
from github.PullRequest import PullRequest
from langchain.agents import initialize_agent, load_tools, AgentType
from langchain.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory

from config import *
from custom_tools import GitToolBuilder
from custom_tools import GitToolBuilder, requests_get_clean
from prompts import make_planning_task, make_execution_task
from utils import login_github, list_repositories, choose_work_item, PassThroughBuffer

# Log into GitHub
Expand All @@ -29,74 +29,88 @@
# create a repo object which represents the repository we are inside of
pygit_repo = Repo(os.getcwd())

default_branch_name = "babyagi-architecture"
# reset to default branch if necessary
if pygit_repo.active_branch.name != "main":
pygit_repo.git.checkout("main")
if pygit_repo.active_branch.name != default_branch_name:
pygit_repo.git.checkout(default_branch_name)

# checkout default branch and pull
pygit_repo.git.checkout("main")
pygit_repo.git.pull()

work_item = choose_work_item(github_repo)


llm = ChatOpenAI(temperature=0.01, model="gpt-4")
llm = ChatOpenAI(temperature=0.01, model="gpt-3.5-turbo")
# llm1 = OpenAI(temperature=0)
pass_through_buffer = PassThroughBuffer(sys.stdout)
assert pass_through_buffer.saved_output == ""
sys.stdout = pass_through_buffer
tools = load_tools(["python_repl", "terminal", "serpapi", "requests_get"], llm=llm)
tools += GitToolBuilder(github_repo, pygit_repo, work_item).build_tools()

exec_agent = initialize_agent(
tools, llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True
base_tools = load_tools(["python_repl", "terminal", "human"], llm=llm)
base_tools += [requests_get_clean]
exec_tools = (
base_tools + GitToolBuilder(github_repo, pygit_repo, work_item).build_tools()
)

memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

task = (
f"You are a GPT-4-powered software engineer agent."
f" You are built with langchain, a framework for building language-based agents. "
f" You can read about it here: https://python.langchain.com/en/latest/modules/agents.html"
f" Your task is to contribute clean, high-quality code to the given codebase."
f" You are working in {os.getcwd()} on {github_repo.name} repository."
f" Feel free to look around this repo. You can browse the internet for documentation and examples."
f" You may need to create, modify, or delete one or more files in this repository."
plan_agent = initialize_agent(
base_tools,
llm,
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
verbose=True,
memory=memory,
)

if type(work_item) == Issue:
task += f" You must create a new branch, implement a solution and submit a pull request to address the following issue: \n\n Title: {work_item.title}.\n\n Body: {work_item.body}."
if type(work_item) == PullRequest:
task += (
f" You must checkout the branch, understand the following pull request feedback make a commit with changes to address it:"
f" \n\n Title: {work_item.title}.\n\n Body: {work_item.body}. \n\n Files: {[f.filename for f in work_item.get_files()]}:"
)


comments = work_item.get_comments()

if list(comments):
task += f" Comments:"
for comment in comments:
task += f" {comment.body}"


task += (
f"\n\nUseful tips: Don't use nano, vim or other text editors, but rather modify files directly either via python or terminal. "
f" Before creating a new branch, make sure to pick a name that is not taken."
exec_agent = initialize_agent(
exec_tools, llm, agent=AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION, verbose=True
)

try:
print("Task:", task)
x = exec_agent.run(task)
except ValueError as e:
if DO_RETRY:
tb = traceback.format_exc()
task += f" This is your second attempt. During the previous attempt, you crashed with the following sequence: <run>{pass_through_buffer.saved_output}</run> Let's try again, avoiding previous mistakes."
pass_through_buffer.saved_output = ""
print("Failed to complete task with following error:", e, tb)
print("New task:", task)
print("Retrying...")
x = exec_agent.run(task)
finally:
sys.stdout = pass_through_buffer.original_buffer
pygit_repo.git.checkout("main")
# check if instrutions are already attached to the issue
instructions = [
c.body
for c in work_item.get_comments()
if c.body.startswith(PLANNER_AGENT_OUTPUT_STRING)
]
if instructions:
instructions = instructions.pop()
instructions.replace(PLANNER_AGENT_OUTPUT_STRING, "")
print("Found instructions:", instructions)

# ask user if they want to run planner agent, default is yes if no instructions

do_plan = input("Do you want to run the planning agent? (y/n)")

if do_plan == "y":
plan_task = make_planning_task(work_item, exec_tools, github_repo.name)
print("Planning task:", plan_task)
approved = False
while not approved:
instructions = plan_agent.run(plan_task)
print("Created new Instructions:", instructions)
feedback = input(
"Do you approve? If approved, type 'y'. If not approved, type why so the agent can try again: "
)
approved = feedback == "y"
plan_task = feedback

# save instructions to issue
work_item.create_comment(PLANNER_AGENT_OUTPUT_STRING + instructions)


# ask user if they want to run exec agent
do_exec = input("Do you want to run the execution agent? (y/n)")
if do_exec == "y":
exec_task = make_execution_task(work_item, instructions, github_repo.name)
print("Execution task:", exec_task)
try:
exec_agent.run(exec_task)
except ValueError as e:
if DO_RETRY:
tb = traceback.format_exc()
exec_task += f" This is your second attempt. During the previous attempt, you crashed with the following sequence: <run>{pass_through_buffer.saved_output}</run> Let's try again, avoiding previous mistakes."
pass_through_buffer.saved_output = ""
print("Failed to complete execution task")
print("New task:", exec_task)
print("Retrying...")
exec_agent.run(exec_task)
finally:
sys.stdout = pass_through_buffer.original_buffer
pygit_repo.git.checkout(default_branch_name)
74 changes: 74 additions & 0 deletions prompts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import os
from typing import List, Union

from github.Issue import Issue
from github.PullRequest import PullRequest
from langchain.tools import BaseTool

from config import PLANNER_AGENT_OUTPUT_STRING


def make_planning_task(
work_item: Union[Issue, PullRequest],
exec_tools: List[BaseTool],
github_repo_name: str,
):
pr_or_issue_str = (
" submit a pull request" if isinstance(work_item, Issue) else " make a commit "
)
return (
f"You are a GPT-4 software engineering lead agent."
f" You plan out software engineering work for developer agents."
f" You are built with langchain, a framework for building language-based agents."
f" You can read about it here: https://python.langchain.com/en/latest/modules/agents.html"
f" Assume you have the code locally on your machine." # todo
f" You are working in {os.getcwd()} on {github_repo_name} repository."
f" Your task is to thoroughly understand the following work item and "
f" create simple and thorough step-by-step instructions for a developer to implement the solution."
f" You should not make the changes yourself, but rather output instructions for a developer to make the changes."
f" \n\nTitle: {work_item.title}"
f" \n\nBody: {work_item.body}"
f" \n\nComments: {[c.body for c in work_item.get_comments() if not c.body.startswith(PLANNER_AGENT_OUTPUT_STRING)]}"
f" \n\n The developer will use your instructions to make changes to the repository and"
f" {pr_or_issue_str} with working, clean, and documented code."
f" Your developer is also a GPT-4-powered agent, so keep that in mind when creating instructions."
f" You should acquire an excellent understanding of the current state of the repository and the code within it."
f" You should also look up documentation on the internet whenever necessary."
f" Your instructions should be clear and concise, and should not contain any typos or grammatical errors."
f" You should tell the developer which files to create/modify/delete, and what to write in them."
f" You should also tell the developer which external libraries to use, if any."
f" For external libraries, you should provide a link to the documentation."
f" Make sure not to regress any existing functionality."
f" The developer agent will have access to the following tools: {[(tool.name, tool.description) for tool in exec_tools]}, so keep that in mind when creating instructions."
f" Begin."
)


def make_execution_task(
work_item: Union[Issue, PullRequest],
solution_instructions: str,
github_repo_name: str,
):
pr_or_issue_str = (
" create a pull request with your changes."
if isinstance(work_item, Issue)
else f" make a commit with your changes to the appropriate branch."
)
return (
f"You are a GPT-4-powered coding agent."
f" You are built with langchain, a framework for building language-based agents. "
f" You can read about it here: https://python.langchain.com/en/latest/modules/agents.html"
f" Your task is to contribute clean, high-quality code to the given codebase."
f" You are working in {os.getcwd()} on {github_repo_name} repository."
f" You are working on the following work item: "
f"\n\nTitle: {work_item.title};"
f"\n\nBody: {work_item.body};"
f"\n\nComments: {[c.body for c in work_item.get_comments() if not c.body.startswith(PLANNER_AGENT_OUTPUT_STRING)]};"
f"\n\n A planning agent has created the following step-by-step instructions for you: <instructions>{solution_instructions}</instructions>"
f" Execute the instructions thoroughly and"
f" {pr_or_issue_str}"
f" Some of the instructions may be high level, so it's up to you to understand what exactly needs to be done."
f" Make sure not to regress any existing functionality."
f"\n\nUseful tips: Do NOT use nano, vim or other text editors, but rather modify files directly either via python or terminal. "
f" Important: when following git-create-branch instructions, make sure to use a branch name that's not taken. "
)
7 changes: 6 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
GitPython==3.1.31
langchain==0.0.134
langchain~=0.0.137
openai==0.25.0
pinecone==0.1.0
PyGithub==1.58.1
python-dotenv==1.0.0

regex~=2022.10.31
typing~=3.10.0.0
beautifulsoup4~=4.12.0
requests~=2.28.1
10 changes: 10 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# New functions
from typing import Union
import github
from bs4 import BeautifulSoup
from github import Github
import regex as re


def login_github(token):
Expand Down Expand Up @@ -56,3 +58,11 @@ def write(self, message):

def __getattr__(self, attr):
return getattr(self.original_buffer, attr)


def remove_html_tags(text) -> str:
clean = re.compile("<.*?>")
soup = BeautifulSoup(text, "html.parser")
raw_text = soup.get_text(strip=True, separator="\n")
clean_text = re.sub(clean, "", raw_text)
return clean_text

0 comments on commit f60ae33

Please sign in to comment.