Commit ·
66214f3
1
Parent(s): ed21180
Upload 9 files
Browse files- __init__.py +0 -0
- ai.py +63 -0
- chat_to_files.py +42 -0
- ci.yaml +32 -0
- db.py +43 -0
- main.py +65 -0
- pre-commit.yaml +14 -0
- release.yaml +52 -0
- steps.py +278 -0
__init__.py
ADDED
|
File without changes
|
ai.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import logging
|
| 4 |
+
|
| 5 |
+
import openai
|
| 6 |
+
|
| 7 |
+
logger = logging.getLogger(__name__)
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class AI:
|
| 11 |
+
def __init__(self, model="gpt-4", temperature=0.1):
|
| 12 |
+
self.temperature = temperature
|
| 13 |
+
|
| 14 |
+
try:
|
| 15 |
+
openai.Model.retrieve(model)
|
| 16 |
+
self.model = model
|
| 17 |
+
except openai.InvalidRequestError:
|
| 18 |
+
print(
|
| 19 |
+
f"Model {model} not available for provided API key. Reverting "
|
| 20 |
+
"to gpt-3.5-turbo. Sign up for the GPT-4 wait list here: "
|
| 21 |
+
"https://openai.com/waitlist/gpt-4-api"
|
| 22 |
+
)
|
| 23 |
+
self.model = "gpt-3.5-turbo"
|
| 24 |
+
|
| 25 |
+
def start(self, system, user):
|
| 26 |
+
messages = [
|
| 27 |
+
{"role": "system", "content": system},
|
| 28 |
+
{"role": "user", "content": user},
|
| 29 |
+
]
|
| 30 |
+
|
| 31 |
+
return self.next(messages)
|
| 32 |
+
|
| 33 |
+
def fsystem(self, msg):
|
| 34 |
+
return {"role": "system", "content": msg}
|
| 35 |
+
|
| 36 |
+
def fuser(self, msg):
|
| 37 |
+
return {"role": "user", "content": msg}
|
| 38 |
+
|
| 39 |
+
def fassistant(self, msg):
|
| 40 |
+
return {"role": "assistant", "content": msg}
|
| 41 |
+
|
| 42 |
+
def next(self, messages: list[dict[str, str]], prompt=None):
|
| 43 |
+
if prompt:
|
| 44 |
+
messages += [{"role": "user", "content": prompt}]
|
| 45 |
+
|
| 46 |
+
logger.debug(f"Creating a new chat completion: {messages}")
|
| 47 |
+
response = openai.ChatCompletion.create(
|
| 48 |
+
messages=messages,
|
| 49 |
+
stream=True,
|
| 50 |
+
model=self.model,
|
| 51 |
+
temperature=self.temperature,
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
chat = []
|
| 55 |
+
for chunk in response:
|
| 56 |
+
delta = chunk["choices"][0]["delta"]
|
| 57 |
+
msg = delta.get("content", "")
|
| 58 |
+
print(msg, end="")
|
| 59 |
+
chat.append(msg)
|
| 60 |
+
print()
|
| 61 |
+
messages += [{"role": "assistant", "content": "".join(chat)}]
|
| 62 |
+
logger.debug(f"Chat completion finished: {messages}")
|
| 63 |
+
return messages
|
chat_to_files.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def parse_chat(chat): # -> List[Tuple[str, str]]:
|
| 5 |
+
# Get all ``` blocks and preceding filenames
|
| 6 |
+
regex = r"(\S+)\n\s*```[^\n]*\n(.+?)```"
|
| 7 |
+
matches = re.finditer(regex, chat, re.DOTALL)
|
| 8 |
+
|
| 9 |
+
files = []
|
| 10 |
+
for match in matches:
|
| 11 |
+
# Strip the filename of any non-allowed characters and convert / to \
|
| 12 |
+
path = re.sub(r'[<>"|?*]', "", match.group(1))
|
| 13 |
+
|
| 14 |
+
# Remove leading and trailing brackets
|
| 15 |
+
path = re.sub(r"^\[(.*)\]$", r"\1", path)
|
| 16 |
+
|
| 17 |
+
# Remove leading and trailing backticks
|
| 18 |
+
path = re.sub(r"^`(.*)`$", r"\1", path)
|
| 19 |
+
|
| 20 |
+
# Remove trailing ]
|
| 21 |
+
path = re.sub(r"\]$", "", path)
|
| 22 |
+
|
| 23 |
+
# Get the code
|
| 24 |
+
code = match.group(2)
|
| 25 |
+
|
| 26 |
+
# Add the file to the list
|
| 27 |
+
files.append((path, code))
|
| 28 |
+
|
| 29 |
+
# Get all the text before the first ``` block
|
| 30 |
+
readme = chat.split("```")[0]
|
| 31 |
+
files.append(("README.md", readme))
|
| 32 |
+
|
| 33 |
+
# Return the files
|
| 34 |
+
return files
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def to_files(chat, workspace):
|
| 38 |
+
workspace["all_output.txt"] = chat
|
| 39 |
+
|
| 40 |
+
files = parse_chat(chat)
|
| 41 |
+
for file_name, file_content in files:
|
| 42 |
+
workspace[file_name] = file_content
|
ci.yaml
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Pytest Execution
|
| 2 |
+
on:
|
| 3 |
+
pull_request:
|
| 4 |
+
branches:
|
| 5 |
+
- main
|
| 6 |
+
push:
|
| 7 |
+
branches:
|
| 8 |
+
- main
|
| 9 |
+
|
| 10 |
+
jobs:
|
| 11 |
+
test:
|
| 12 |
+
runs-on: ubuntu-latest
|
| 13 |
+
strategy:
|
| 14 |
+
matrix:
|
| 15 |
+
python-version:
|
| 16 |
+
- "3.10"
|
| 17 |
+
steps:
|
| 18 |
+
- uses: actions/checkout@v3
|
| 19 |
+
|
| 20 |
+
- uses: actions/setup-python@v4
|
| 21 |
+
with:
|
| 22 |
+
python-version: ${{ matrix.python-version }}
|
| 23 |
+
cache: pip
|
| 24 |
+
|
| 25 |
+
- name: Install package
|
| 26 |
+
run: pip install -e .
|
| 27 |
+
|
| 28 |
+
- name: Install test runner
|
| 29 |
+
run: pip install pytest pytest-cov
|
| 30 |
+
|
| 31 |
+
- name: Run unit tests
|
| 32 |
+
run: pytest --cov=gpt_engineer
|
db.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# This class represents a simple database that stores its data as files in a directory.
|
| 6 |
+
class DB:
|
| 7 |
+
"""A simple key-value store, where keys are filenames and values are file contents."""
|
| 8 |
+
|
| 9 |
+
def __init__(self, path):
|
| 10 |
+
self.path = Path(path).absolute()
|
| 11 |
+
|
| 12 |
+
self.path.mkdir(parents=True, exist_ok=True)
|
| 13 |
+
|
| 14 |
+
def __contains__(self, key):
|
| 15 |
+
return (self.path / key).is_file()
|
| 16 |
+
|
| 17 |
+
def __getitem__(self, key):
|
| 18 |
+
full_path = self.path / key
|
| 19 |
+
|
| 20 |
+
if not full_path.is_file():
|
| 21 |
+
raise KeyError(key)
|
| 22 |
+
with full_path.open("r", encoding="utf-8") as f:
|
| 23 |
+
return f.read()
|
| 24 |
+
|
| 25 |
+
def __setitem__(self, key, val):
|
| 26 |
+
full_path = self.path / key
|
| 27 |
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
| 28 |
+
|
| 29 |
+
if isinstance(val, str):
|
| 30 |
+
full_path.write_text(val, encoding="utf-8")
|
| 31 |
+
else:
|
| 32 |
+
# If val is neither a string nor bytes, raise an error.
|
| 33 |
+
raise TypeError("val must be either a str or bytes")
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# dataclass for all dbs:
|
| 37 |
+
@dataclass
|
| 38 |
+
class DBs:
|
| 39 |
+
memory: DB
|
| 40 |
+
logs: DB
|
| 41 |
+
preprompts: DB
|
| 42 |
+
input: DB
|
| 43 |
+
workspace: DB
|
main.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import logging
|
| 3 |
+
import shutil
|
| 4 |
+
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
import typer
|
| 8 |
+
|
| 9 |
+
from gpt_engineer import steps
|
| 10 |
+
from gpt_engineer.ai import AI
|
| 11 |
+
from gpt_engineer.db import DB, DBs
|
| 12 |
+
from gpt_engineer.steps import STEPS
|
| 13 |
+
|
| 14 |
+
app = typer.Typer()
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@app.command()
|
| 18 |
+
def main(
|
| 19 |
+
project_path: str = typer.Argument("example", help="path"),
|
| 20 |
+
delete_existing: bool = typer.Argument(False, help="delete existing files"),
|
| 21 |
+
model: str = "gpt-4",
|
| 22 |
+
temperature: float = 0.1,
|
| 23 |
+
steps_config: steps.Config = typer.Option(
|
| 24 |
+
steps.Config.DEFAULT, "--steps", "-s", help="decide which steps to run"
|
| 25 |
+
),
|
| 26 |
+
verbose: bool = typer.Option(False, "--verbose", "-v"),
|
| 27 |
+
run_prefix: str = typer.Option(
|
| 28 |
+
"",
|
| 29 |
+
help=(
|
| 30 |
+
"run prefix, if you want to run multiple variants of the same project and "
|
| 31 |
+
"later compare them"
|
| 32 |
+
),
|
| 33 |
+
),
|
| 34 |
+
):
|
| 35 |
+
logging.basicConfig(level=logging.DEBUG if verbose else logging.INFO)
|
| 36 |
+
|
| 37 |
+
input_path = Path(project_path).absolute()
|
| 38 |
+
memory_path = input_path / f"{run_prefix}memory"
|
| 39 |
+
workspace_path = input_path / f"{run_prefix}workspace"
|
| 40 |
+
|
| 41 |
+
if delete_existing:
|
| 42 |
+
# Delete files and subdirectories in paths
|
| 43 |
+
shutil.rmtree(memory_path, ignore_errors=True)
|
| 44 |
+
shutil.rmtree(workspace_path, ignore_errors=True)
|
| 45 |
+
|
| 46 |
+
ai = AI(
|
| 47 |
+
model=model,
|
| 48 |
+
temperature=temperature,
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
dbs = DBs(
|
| 52 |
+
memory=DB(memory_path),
|
| 53 |
+
logs=DB(memory_path / "logs"),
|
| 54 |
+
input=DB(input_path),
|
| 55 |
+
workspace=DB(workspace_path),
|
| 56 |
+
preprompts=DB(Path(__file__).parent / "preprompts"),
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
for step in STEPS[steps_config]:
|
| 60 |
+
messages = step(ai, dbs)
|
| 61 |
+
dbs.logs[step.__name__] = json.dumps(messages)
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
if __name__ == "__main__":
|
| 65 |
+
app()
|
pre-commit.yaml
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: pre-commit
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
pull_request:
|
| 5 |
+
push:
|
| 6 |
+
branches: [main]
|
| 7 |
+
|
| 8 |
+
jobs:
|
| 9 |
+
pre-commit:
|
| 10 |
+
runs-on: ubuntu-latest
|
| 11 |
+
steps:
|
| 12 |
+
- uses: actions/checkout@v3
|
| 13 |
+
- uses: actions/setup-python@v4
|
| 14 |
+
- uses: pre-commit/action@v3.0.0
|
release.yaml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: Build and publish Python packages to PyPI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
release:
|
| 6 |
+
types:
|
| 7 |
+
- published
|
| 8 |
+
|
| 9 |
+
jobs:
|
| 10 |
+
build:
|
| 11 |
+
runs-on: ubuntu-latest
|
| 12 |
+
strategy:
|
| 13 |
+
matrix:
|
| 14 |
+
python-version:
|
| 15 |
+
- "3.10"
|
| 16 |
+
steps:
|
| 17 |
+
- uses: actions/checkout@v3
|
| 18 |
+
|
| 19 |
+
- uses: actions/setup-python@v4
|
| 20 |
+
with:
|
| 21 |
+
python-version: ${{ matrix.python-version }}
|
| 22 |
+
cache: pip
|
| 23 |
+
|
| 24 |
+
- name: Install build tool
|
| 25 |
+
run: pip install build
|
| 26 |
+
|
| 27 |
+
- name: Build package
|
| 28 |
+
run: python -m build
|
| 29 |
+
|
| 30 |
+
- name: Upload package as build artifact
|
| 31 |
+
uses: actions/upload-artifact@v3
|
| 32 |
+
with:
|
| 33 |
+
name: package
|
| 34 |
+
path: dist/
|
| 35 |
+
|
| 36 |
+
publish:
|
| 37 |
+
runs-on: ubuntu-latest
|
| 38 |
+
needs: build
|
| 39 |
+
environment:
|
| 40 |
+
name: pypi
|
| 41 |
+
url: https://pypi.org/p/gpt-engineer
|
| 42 |
+
permissions:
|
| 43 |
+
id-token: write
|
| 44 |
+
steps:
|
| 45 |
+
- name: Collect packages to release
|
| 46 |
+
uses: actions/download-artifact@v3
|
| 47 |
+
with:
|
| 48 |
+
name: package
|
| 49 |
+
path: dist/
|
| 50 |
+
|
| 51 |
+
- name: Publish packages to PyPI
|
| 52 |
+
uses: pypa/gh-action-pypi-publish@release/v1
|
steps.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import re
|
| 3 |
+
import subprocess
|
| 4 |
+
|
| 5 |
+
from enum import Enum
|
| 6 |
+
from typing import Callable, TypeVar
|
| 7 |
+
|
| 8 |
+
from gpt_engineer.ai import AI
|
| 9 |
+
from gpt_engineer.chat_to_files import to_files
|
| 10 |
+
from gpt_engineer.db import DBs
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def setup_sys_prompt(dbs):
|
| 14 |
+
return (
|
| 15 |
+
dbs.preprompts["generate"] + "\nUseful to know:\n" + dbs.preprompts["philosophy"]
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
Step = TypeVar("Step", bound=Callable[[AI, DBs], list[dict]])
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def simple_gen(ai: AI, dbs: DBs):
|
| 23 |
+
"""Run the AI on the main prompt and save the results"""
|
| 24 |
+
messages = ai.start(
|
| 25 |
+
setup_sys_prompt(dbs),
|
| 26 |
+
dbs.input["main_prompt"],
|
| 27 |
+
)
|
| 28 |
+
to_files(messages[-1]["content"], dbs.workspace)
|
| 29 |
+
return messages
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def clarify(ai: AI, dbs: DBs):
|
| 33 |
+
"""
|
| 34 |
+
Ask the user if they want to clarify anything and save the results to the workspace
|
| 35 |
+
"""
|
| 36 |
+
messages = [ai.fsystem(dbs.preprompts["qa"])]
|
| 37 |
+
user = dbs.input["main_prompt"]
|
| 38 |
+
while True:
|
| 39 |
+
messages = ai.next(messages, user)
|
| 40 |
+
|
| 41 |
+
if messages[-1]["content"].strip().lower().startswith("no"):
|
| 42 |
+
break
|
| 43 |
+
|
| 44 |
+
print()
|
| 45 |
+
user = input('(answer in text, or "c" to move on)\n')
|
| 46 |
+
print()
|
| 47 |
+
|
| 48 |
+
if not user or user == "c":
|
| 49 |
+
break
|
| 50 |
+
|
| 51 |
+
user += (
|
| 52 |
+
"\n\n"
|
| 53 |
+
"Is anything else unclear? If yes, only answer in the form:\n"
|
| 54 |
+
"{remaining unclear areas} remaining questions.\n"
|
| 55 |
+
"{Next question}\n"
|
| 56 |
+
'If everything is sufficiently clear, only answer "no".'
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
print()
|
| 60 |
+
return messages
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def gen_spec(ai: AI, dbs: DBs):
|
| 64 |
+
"""
|
| 65 |
+
Generate a spec from the main prompt + clarifications and save the results to
|
| 66 |
+
the workspace
|
| 67 |
+
"""
|
| 68 |
+
messages = [
|
| 69 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 70 |
+
ai.fsystem(f"Instructions: {dbs.input['main_prompt']}"),
|
| 71 |
+
]
|
| 72 |
+
|
| 73 |
+
messages = ai.next(messages, dbs.preprompts["spec"])
|
| 74 |
+
|
| 75 |
+
dbs.memory["specification"] = messages[-1]["content"]
|
| 76 |
+
|
| 77 |
+
return messages
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def respec(ai: AI, dbs: DBs):
|
| 81 |
+
messages = json.loads(dbs.logs[gen_spec.__name__])
|
| 82 |
+
messages += [ai.fsystem(dbs.preprompts["respec"])]
|
| 83 |
+
|
| 84 |
+
messages = ai.next(messages)
|
| 85 |
+
messages = ai.next(
|
| 86 |
+
messages,
|
| 87 |
+
(
|
| 88 |
+
"Based on the conversation so far, please reiterate the specification for "
|
| 89 |
+
"the program. "
|
| 90 |
+
"If there are things that can be improved, please incorporate the "
|
| 91 |
+
"improvements. "
|
| 92 |
+
"If you are satisfied with the specification, just write out the "
|
| 93 |
+
"specification word by word again."
|
| 94 |
+
),
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
dbs.memory["specification"] = messages[-1]["content"]
|
| 98 |
+
return messages
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def gen_unit_tests(ai: AI, dbs: DBs):
|
| 102 |
+
"""
|
| 103 |
+
Generate unit tests based on the specification, that should work.
|
| 104 |
+
"""
|
| 105 |
+
messages = [
|
| 106 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 107 |
+
ai.fuser(f"Instructions: {dbs.input['main_prompt']}"),
|
| 108 |
+
ai.fuser(f"Specification:\n\n{dbs.memory['specification']}"),
|
| 109 |
+
]
|
| 110 |
+
|
| 111 |
+
messages = ai.next(messages, dbs.preprompts["unit_tests"])
|
| 112 |
+
|
| 113 |
+
dbs.memory["unit_tests"] = messages[-1]["content"]
|
| 114 |
+
to_files(dbs.memory["unit_tests"], dbs.workspace)
|
| 115 |
+
|
| 116 |
+
return messages
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def gen_clarified_code(ai: AI, dbs: DBs):
|
| 120 |
+
# get the messages from previous step
|
| 121 |
+
|
| 122 |
+
messages = json.loads(dbs.logs[clarify.__name__])
|
| 123 |
+
|
| 124 |
+
messages = [
|
| 125 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 126 |
+
] + messages[1:]
|
| 127 |
+
messages = ai.next(messages, dbs.preprompts["use_qa"])
|
| 128 |
+
|
| 129 |
+
to_files(messages[-1]["content"], dbs.workspace)
|
| 130 |
+
return messages
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def gen_code(ai: AI, dbs: DBs):
|
| 134 |
+
# get the messages from previous step
|
| 135 |
+
|
| 136 |
+
messages = [
|
| 137 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 138 |
+
ai.fuser(f"Instructions: {dbs.input['main_prompt']}"),
|
| 139 |
+
ai.fuser(f"Specification:\n\n{dbs.memory['specification']}"),
|
| 140 |
+
ai.fuser(f"Unit tests:\n\n{dbs.memory['unit_tests']}"),
|
| 141 |
+
]
|
| 142 |
+
messages = ai.next(messages, dbs.preprompts["use_qa"])
|
| 143 |
+
to_files(messages[-1]["content"], dbs.workspace)
|
| 144 |
+
return messages
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def execute_entrypoint(ai, dbs):
|
| 148 |
+
command = dbs.workspace["run.sh"]
|
| 149 |
+
|
| 150 |
+
print("Do you want to execute this code?")
|
| 151 |
+
print()
|
| 152 |
+
print(command)
|
| 153 |
+
print()
|
| 154 |
+
print('If yes, press enter. Otherwise, type "no"')
|
| 155 |
+
print()
|
| 156 |
+
if input() not in ["", "y", "yes"]:
|
| 157 |
+
print("Ok, not executing the code.")
|
| 158 |
+
return []
|
| 159 |
+
print("Executing the code...")
|
| 160 |
+
print(
|
| 161 |
+
"\033[92m" # green color
|
| 162 |
+
+ "Note: If it does not work as expected, please consider running the code'"
|
| 163 |
+
+ " in another way than above."
|
| 164 |
+
+ "\033[0m"
|
| 165 |
+
)
|
| 166 |
+
print()
|
| 167 |
+
subprocess.run("bash run.sh", shell=True, cwd=dbs.workspace.path)
|
| 168 |
+
return []
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def gen_entrypoint(ai, dbs):
|
| 172 |
+
messages = ai.start(
|
| 173 |
+
system=(
|
| 174 |
+
"You will get information about a codebase that is currently on disk in "
|
| 175 |
+
"the current folder.\n"
|
| 176 |
+
"From this you will answer with code blocks that includes all the necessary "
|
| 177 |
+
"unix terminal commands to "
|
| 178 |
+
"a) install dependencies "
|
| 179 |
+
"b) run all necessary parts of the codebase (in parallell if necessary).\n"
|
| 180 |
+
"Do not install globally. Do not use sudo.\n"
|
| 181 |
+
"Do not explain the code, just give the commands.\n"
|
| 182 |
+
"Do not use placeholders, use example values (like . for a folder argument) "
|
| 183 |
+
"if necessary.\n"
|
| 184 |
+
),
|
| 185 |
+
user="Information about the codebase:\n\n" + dbs.workspace["all_output.txt"],
|
| 186 |
+
)
|
| 187 |
+
print()
|
| 188 |
+
|
| 189 |
+
regex = r"```\S*\n(.+?)```"
|
| 190 |
+
matches = re.finditer(regex, messages[-1]["content"], re.DOTALL)
|
| 191 |
+
dbs.workspace["run.sh"] = "\n".join(match.group(1) for match in matches)
|
| 192 |
+
return messages
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
def use_feedback(ai: AI, dbs: DBs):
|
| 196 |
+
messages = [
|
| 197 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 198 |
+
ai.fuser(f"Instructions: {dbs.input['main_prompt']}"),
|
| 199 |
+
ai.fassistant(dbs.workspace["all_output.txt"]),
|
| 200 |
+
ai.fsystem(dbs.preprompts["use_feedback"]),
|
| 201 |
+
]
|
| 202 |
+
messages = ai.next(messages, dbs.input["feedback"])
|
| 203 |
+
to_files(messages[-1]["content"], dbs.workspace)
|
| 204 |
+
return messages
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
def fix_code(ai: AI, dbs: DBs):
|
| 208 |
+
code_output = json.loads(dbs.logs[gen_code.__name__])[-1]["content"]
|
| 209 |
+
messages = [
|
| 210 |
+
ai.fsystem(setup_sys_prompt(dbs)),
|
| 211 |
+
ai.fuser(f"Instructions: {dbs.input['main_prompt']}"),
|
| 212 |
+
ai.fuser(code_output),
|
| 213 |
+
ai.fsystem(dbs.preprompts["fix_code"]),
|
| 214 |
+
]
|
| 215 |
+
messages = ai.next(messages, "Please fix any errors in the code above.")
|
| 216 |
+
to_files(messages[-1]["content"], dbs.workspace)
|
| 217 |
+
return messages
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
class Config(str, Enum):
|
| 221 |
+
DEFAULT = "default"
|
| 222 |
+
BENCHMARK = "benchmark"
|
| 223 |
+
SIMPLE = "simple"
|
| 224 |
+
TDD = "tdd"
|
| 225 |
+
TDD_PLUS = "tdd+"
|
| 226 |
+
CLARIFY = "clarify"
|
| 227 |
+
RESPEC = "respec"
|
| 228 |
+
EXECUTE_ONLY = "execute_only"
|
| 229 |
+
USE_FEEDBACK = "use_feedback"
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
# Different configs of what steps to run
|
| 233 |
+
STEPS = {
|
| 234 |
+
Config.DEFAULT: [
|
| 235 |
+
clarify,
|
| 236 |
+
gen_clarified_code,
|
| 237 |
+
gen_entrypoint,
|
| 238 |
+
execute_entrypoint,
|
| 239 |
+
],
|
| 240 |
+
Config.BENCHMARK: [simple_gen, gen_entrypoint],
|
| 241 |
+
Config.SIMPLE: [simple_gen, gen_entrypoint, execute_entrypoint],
|
| 242 |
+
Config.TDD: [
|
| 243 |
+
gen_spec,
|
| 244 |
+
gen_unit_tests,
|
| 245 |
+
gen_code,
|
| 246 |
+
gen_entrypoint,
|
| 247 |
+
execute_entrypoint,
|
| 248 |
+
],
|
| 249 |
+
Config.TDD_PLUS: [
|
| 250 |
+
gen_spec,
|
| 251 |
+
gen_unit_tests,
|
| 252 |
+
gen_code,
|
| 253 |
+
fix_code,
|
| 254 |
+
gen_entrypoint,
|
| 255 |
+
execute_entrypoint,
|
| 256 |
+
],
|
| 257 |
+
Config.CLARIFY: [
|
| 258 |
+
clarify,
|
| 259 |
+
gen_clarified_code,
|
| 260 |
+
gen_entrypoint,
|
| 261 |
+
execute_entrypoint,
|
| 262 |
+
],
|
| 263 |
+
Config.RESPEC: [
|
| 264 |
+
gen_spec,
|
| 265 |
+
respec,
|
| 266 |
+
gen_unit_tests,
|
| 267 |
+
gen_code,
|
| 268 |
+
fix_code,
|
| 269 |
+
gen_entrypoint,
|
| 270 |
+
execute_entrypoint,
|
| 271 |
+
],
|
| 272 |
+
Config.USE_FEEDBACK: [use_feedback, gen_entrypoint, execute_entrypoint],
|
| 273 |
+
Config.EXECUTE_ONLY: [gen_entrypoint, execute_entrypoint],
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
# Future steps that can be added:
|
| 277 |
+
# run_tests_and_fix_files
|
| 278 |
+
# execute_entrypoint_and_fix_files_if_needed
|