From d52c674cf0bc5c801d989c380b056e888a3bd04c Mon Sep 17 00:00:00 2001 From: Zvonimir Sabljic Date: Thu, 7 Sep 2023 19:47:05 +0200 Subject: [PATCH 01/11] Fix --- pilot/utils/questionary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pilot/utils/questionary.py b/pilot/utils/questionary.py index b78c803..7cde642 100644 --- a/pilot/utils/questionary.py +++ b/pilot/utils/questionary.py @@ -43,4 +43,4 @@ def get_user_feedback(): config = { 'style': custom_style, } - return questionary.text("Thank you for trying GPT-Pilot. Please give us your feedback or just press ENTER to exit: ", **config).unsafe_ask() + return questionary.text("How did GPT Pilot do? Were you able to create any app that works? Please write any feedback you have or just press ENTER to exit: ", **config).unsafe_ask() From 720fa26bcfef3bbd2c8f0cbb29fb043d859c2a2c Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 03:56:17 +1000 Subject: [PATCH 02/11] user_id defaults to OS username --- pilot/utils/arguments.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index 8590373..79a66b9 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -1,3 +1,4 @@ +import getpass import sys import uuid @@ -36,7 +37,7 @@ def get_arguments(): arguments['app_id'] = str(uuid.uuid4()) if 'user_id' not in arguments: - arguments['user_id'] = str(uuid.uuid4()) + arguments['user_id'] = getpass.getuser() if 'email' not in arguments: # todo change email so its not uuid4 but make sure to fix storing of development steps where From bd67695828f471c6f2f27edef89c2a1eaa8d50aa Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 05:25:30 +1000 Subject: [PATCH 03/11] use {{ app_type }} from project.args in prompts --- .gitignore | 341 +++++++++--------- pilot/const/common.py | 2 +- pilot/helpers/Project.py | 2 +- pilot/helpers/agents/Developer.py | 9 +- pilot/helpers/agents/TechLead.py | 1 + .../development/env_setup/specs.prompt | 2 +- pilot/prompts/development/iteration.prompt | 2 +- pilot/prompts/development/plan.prompt | 2 +- .../prompts/development/task/breakdown.prompt | 2 +- pilot/prompts/prompts.py | 2 +- .../prompts/system_messages/architect.prompt | 2 +- 11 files changed, 188 insertions(+), 179 deletions(-) diff --git a/.gitignore b/.gitignore index d459a5f..104597d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,170 +1,171 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -.idea/ - - -# Logger -/pilot/logger/debug.log - -#sqlite -/pilot/gpt-pilot - -# workspace -workspace \ No newline at end of file +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +.idea/ + + +# Logger +/pilot/logger/debug.log + +#sqlite +/pilot/gpt-pilot + +# workspace +workspace +pilot-env/ diff --git a/pilot/const/common.py b/pilot/const/common.py index 6e39416..9ac0120 100644 --- a/pilot/const/common.py +++ b/pilot/const/common.py @@ -1,4 +1,4 @@ -APP_TYPES = ['Web App', 'Script', 'Mobile App (unavailable)', 'Chrome Extension (unavailable)'] +APP_TYPES = ['Web App', 'Script', 'Mobile App', 'Chrome Extension'] ROLES = { 'product_owner': ['project_description', 'user_stories', 'user_tasks'], 'architect': ['architecture'], diff --git a/pilot/helpers/Project.py b/pilot/helpers/Project.py index 90d3099..b20b871 100644 --- a/pilot/helpers/Project.py +++ b/pilot/helpers/Project.py @@ -25,7 +25,7 @@ class Project: Initialize a project. Args: - args (dict): Project arguments. + args (dict): Project arguments - app_id, (app_type, name), user_id, email, password, step name (str, optional): Project name. Default is None. description (str, optional): Project description. Default is None. user_stories (list, optional): List of user stories. Default is None. diff --git a/pilot/helpers/agents/Developer.py b/pilot/helpers/agents/Developer.py index a0074bc..03e4388 100644 --- a/pilot/helpers/agents/Developer.py +++ b/pilot/helpers/agents/Developer.py @@ -40,6 +40,7 @@ class Developer(Agent): convo_dev_task = AgentConvo(self) task_description = convo_dev_task.send_message('development/task/breakdown.prompt', { "name": self.project.args['name'], + "app_type": self.project.args['app_type'], "app_summary": self.project.project_description, "clarification": [], "user_stories": self.project.user_stories, @@ -134,6 +135,7 @@ class Developer(Agent): iteration_convo = AgentConvo(self) iteration_convo.send_message('development/iteration.prompt', { "name": self.project.args['name'], + "app_type": self.project.args['app_type'], "app_summary": self.project.project_description, "clarification": [], "user_stories": self.project.user_stories, @@ -175,7 +177,12 @@ class Developer(Agent): os_info = get_os_info() os_specific_techologies = self.convo_os_specific_tech.send_message('development/env_setup/specs.prompt', - { "name": self.project.args['name'], "os_info": os_info, "technologies": self.project.architecture }, FILTER_OS_TECHNOLOGIES) + { + "name": self.project.args['name'], + "app_type": self.project.args['app_type'], + "os_info": os_info, + "technologies": self.project.architecture + }, FILTER_OS_TECHNOLOGIES) for technology in os_specific_techologies: # TODO move the functions definisions to function_calls.py diff --git a/pilot/helpers/agents/TechLead.py b/pilot/helpers/agents/TechLead.py index 9ec5e89..08f55fb 100644 --- a/pilot/helpers/agents/TechLead.py +++ b/pilot/helpers/agents/TechLead.py @@ -36,6 +36,7 @@ class TechLead(Agent): self.development_plan = self.convo_development_plan.send_message('development/plan.prompt', { "name": self.project.args['name'], + "app_type": self.project.args['app_type'], "app_summary": self.project.project_description, "clarification": [], "user_stories": self.project.user_stories, diff --git a/pilot/prompts/development/env_setup/specs.prompt b/pilot/prompts/development/env_setup/specs.prompt index 1ea1b33..a6c978d 100644 --- a/pilot/prompts/development/env_setup/specs.prompt +++ b/pilot/prompts/development/env_setup/specs.prompt @@ -1,4 +1,4 @@ -You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to work on a new project. You are working on a web app called "{{ name }}" and your first job is to set up the environment on a computer. +You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to work on a new project. You are working on a {{ app_type }} called "{{ name }}" and your first job is to set up the environment on a computer. Here are the technologies that you need to use for this project: ``` diff --git a/pilot/prompts/development/iteration.prompt b/pilot/prompts/development/iteration.prompt index 4b984b1..29a973e 100644 --- a/pilot/prompts/development/iteration.prompt +++ b/pilot/prompts/development/iteration.prompt @@ -1,4 +1,4 @@ -You are working on a web app called "{{ name }}" and you need to write code for the entire application. +You are working on a {{ app_type }} called "{{ name }}" and you need to write code for the entire application. Here is a high level description of "{{ name }}": ``` diff --git a/pilot/prompts/development/plan.prompt b/pilot/prompts/development/plan.prompt index 40cfbd2..dd3a43f 100644 --- a/pilot/prompts/development/plan.prompt +++ b/pilot/prompts/development/plan.prompt @@ -1,4 +1,4 @@ -You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to work on a new project. You are working on a web app called "{{ name }}" and you need to create a detailed development plan so that developers can start developing the app. +You are working in a software development agency and a project manager and software architect approach you telling you that you're assigned to work on a new project. You are working on a {{ app_type }} called "{{ name }}" and you need to create a detailed development plan so that developers can start developing the app. Here is a high level description of "{{ name }}": ``` diff --git a/pilot/prompts/development/task/breakdown.prompt b/pilot/prompts/development/task/breakdown.prompt index 3be9d57..ee2eb0b 100644 --- a/pilot/prompts/development/task/breakdown.prompt +++ b/pilot/prompts/development/task/breakdown.prompt @@ -1,4 +1,4 @@ -You are working on a web app called "{{ name }}" and you need to write code for the entire application based on the tasks that the tech lead gives you. So that you understand better what you're working on, you're given other specs for "{{ name }}" as well. +You are working on a {{ app_type }} called "{{ name }}" and you need to write code for the entire application based on the tasks that the tech lead gives you. So that you understand better what you're working on, you're given other specs for "{{ name }}" as well. Here is a high level description of "{{ name }}": ``` diff --git a/pilot/prompts/prompts.py b/pilot/prompts/prompts.py index c667268..6780f09 100644 --- a/pilot/prompts/prompts.py +++ b/pilot/prompts/prompts.py @@ -12,7 +12,7 @@ from logger.logger import logger def ask_for_app_type(): - return 'app' + return 'Web App' answer = styled_select( "What type of app do you want to build?", choices=common.APP_TYPES diff --git a/pilot/prompts/system_messages/architect.prompt b/pilot/prompts/system_messages/architect.prompt index b32cdb9..3bf1d46 100644 --- a/pilot/prompts/system_messages/architect.prompt +++ b/pilot/prompts/system_messages/architect.prompt @@ -1,4 +1,4 @@ -You are an experienced software architect. Your expertise is in creating an architecture for an MVP (minimum viable products) for web apps that can be developed as fast as possible by using as many ready-made technologies as possible. The technologies that you prefer using when other technologies are not explicitly specified are: +You are an experienced software architect. Your expertise is in creating an architecture for an MVP (minimum viable products) for {{ app_type }}s that can be developed as fast as possible by using as many ready-made technologies as possible. The technologies that you prefer using when other technologies are not explicitly specified are: **Scripts**: you prefer using Node.js for writing scripts that are meant to be ran just with the CLI. **Backend**: you prefer using Node.js with Mongo database if not explicitely specified otherwise. When you're using Mongo, you always use Mongoose and when you're using Postgresql, you always use PeeWee as an ORM. From 258e89a8e0064546d065d22485a371ebc89eedbe Mon Sep 17 00:00:00 2001 From: Zvonimir Sabljic Date: Thu, 7 Sep 2023 22:07:46 +0200 Subject: [PATCH 04/11] Better logs --- pilot/main.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pilot/main.py b/pilot/main.py index a13abfc..751ea9a 100644 --- a/pilot/main.py +++ b/pilot/main.py @@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals import sys from dotenv import load_dotenv +from termcolor import colored load_dotenv() from helpers.Project import Project @@ -38,6 +39,9 @@ if __name__ == "__main__": except KeyboardInterrupt: exit_gpt_pilot() except Exception as e: + print(colored('---------- GPT PILOT EXITING WITH ERROR ----------', 'red')) + print(colored(e, 'red')) + print(colored('--------------------------------------------------', 'red')) exit_gpt_pilot() finally: sys.exit(0) From 0ec6da74ab82d0b91ed835fdd7a02fb7fc904a75 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 06:29:46 +1000 Subject: [PATCH 05/11] workspace path can be specified in CLI args --- .github/workflows/ci.yml | 43 ++++++++++++++++++++++++++++ pilot/helpers/agents/ProductOwner.py | 4 +-- pilot/utils/arguments.py | 3 ++ pilot/utils/files.py | 7 +++-- pilot/utils/test_files.py | 26 +++++++++++++++++ 5 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 pilot/utils/test_files.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4e7e0f3 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: Test & QA + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.8, 3.9, 3.10, 3.11] + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + + - name: Lint + run: | + pip install flake8 + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # stop the build if there are Python syntax errors or undefined names + #ruff --format=github --select=E9,F63,F7,F82 --target-version=py37 . + # default set of ruff rules with GitHub Annotations + #ruff --format=github --target-version=py37 . + + - name: Run tests + run: | + pip install pytest + pytest diff --git a/pilot/helpers/agents/ProductOwner.py b/pilot/helpers/agents/ProductOwner.py index d592fb3..bfeb466 100644 --- a/pilot/helpers/agents/ProductOwner.py +++ b/pilot/helpers/agents/ProductOwner.py @@ -24,7 +24,7 @@ class ProductOwner(Agent): step = get_progress_steps(self.project.args['app_id'], self.project.current_step) if step and not execute_step(self.project.args['step'], self.project.current_step): step_already_finished(self.project.args, step) - self.project.root_path = setup_workspace(self.project.args['name']) + self.project.root_path = setup_workspace(self.project.args) self.project.project_description = step['summary'] self.project.project_description_messages = step['messages'] return @@ -33,7 +33,7 @@ class ProductOwner(Agent): self.project.args['app_type'] = ask_for_app_type() self.project.args['name'] = clean_filename(ask_user(self.project, 'What is the project name?')) - self.project.root_path = setup_workspace(self.project.args['name']) + self.project.root_path = setup_workspace(self.project.args) self.project.app = save_app(self.project.args) diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index 8590373..939a76d 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -35,6 +35,9 @@ def get_arguments(): else: arguments['app_id'] = str(uuid.uuid4()) + if 'workspace' not in arguments: + arguments['workspace'] = None + if 'user_id' not in arguments: arguments['user_id'] = str(uuid.uuid4()) diff --git a/pilot/utils/files.py b/pilot/utils/files.py index bce78eb..1946c5f 100644 --- a/pilot/utils/files.py +++ b/pilot/utils/files.py @@ -11,10 +11,13 @@ def get_parent_folder(folder_name): return current_path.parent -def setup_workspace(project_name): +def setup_workspace(args): + if args['workspace'] is not None: + return args['workspace'] + root = get_parent_folder('pilot') create_directory(root, 'workspace') - project_path = create_directory(os.path.join(root, 'workspace'), project_name) + project_path = create_directory(os.path.join(root, 'workspace'), args['name']) create_directory(project_path, 'tests') return project_path diff --git a/pilot/utils/test_files.py b/pilot/utils/test_files.py new file mode 100644 index 0000000..74aa277 --- /dev/null +++ b/pilot/utils/test_files.py @@ -0,0 +1,26 @@ +import pytest +from .files import setup_workspace + + +def test_setup_workspace_with_existing_workspace(): + args = {'workspace': 'some_directory', 'name': 'sample'} + result = setup_workspace(args) + assert result == 'some_directory' + + +def mocked_create_directory(path, exist_ok=True): + return + + +def mocked_abspath(file): + return "/root_path/pilot/helpers" + + +def test_setup_workspace_without_existing_workspace(monkeypatch): + args = {'workspace': None, 'name': 'project_name'} + + monkeypatch.setattr('os.path.abspath', mocked_abspath) + monkeypatch.setattr('os.makedirs', mocked_create_directory) + + result = setup_workspace(args) + assert result.replace('\\', '/') == "/root_path/workspace/project_name" From 03a96e248704da1285b02c5b99e1e98da70d1551 Mon Sep 17 00:00:00 2001 From: RAMKRISHNA VERMA Date: Fri, 8 Sep 2023 03:13:58 +0530 Subject: [PATCH 06/11] Fix : ValueError: Unsupported DATABASE_TYPE: postgres --- pilot/database/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pilot/database/database.py b/pilot/database/database.py index 52d9711..fd8ddc3 100644 --- a/pilot/database/database.py +++ b/pilot/database/database.py @@ -377,7 +377,7 @@ def drop_tables(): UserInputs, File, ]: - if DATABASE_TYPE == "postgresql": + if DATABASE_TYPE == "postgres": sql = f'DROP TABLE IF EXISTS "{table._meta.table_name}" CASCADE' elif DATABASE_TYPE == "sqlite": sql = f'DROP TABLE IF EXISTS "{table._meta.table_name}"' From ee77f1ffac899fb7f3d7d06f4ccf8586a7cd2262 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 15:13:37 +1000 Subject: [PATCH 07/11] get_app_by_user_workspace(user_id, workspace) --- pilot/database/database.py | 25 ++++++++++++++++++-- pilot/database/models/user_apps.py | 18 +++++++++++++++ pilot/helpers/Project.py | 2 +- pilot/helpers/agents/Developer.py | 2 +- pilot/helpers/agents/ProductOwner.py | 10 ++++---- pilot/utils/arguments.py | 34 +++++++++++++++++++--------- pilot/utils/files.py | 7 +++++- 7 files changed, 78 insertions(+), 20 deletions(-) create mode 100644 pilot/database/models/user_apps.py diff --git a/pilot/database/database.py b/pilot/database/database.py index 52d9711..3487ca2 100644 --- a/pilot/database/database.py +++ b/pilot/database/database.py @@ -23,6 +23,7 @@ from database.models.environment_setup import EnvironmentSetup from database.models.development import Development from database.models.file_snapshot import FileSnapshot from database.models.command_runs import CommandRuns +from database.models.user_apps import UserApps from database.models.user_inputs import UserInputs from database.models.files import File @@ -84,6 +85,16 @@ def save_app(args): return app +def save_user_app(user_id, app_id, workspace): + try: + user_app = UserApps.get((UserApps.user == user_id) & (UserApps.app == app_id)) + user_app.workspace = workspace + user_app.save() + except DoesNotExist: + user_app = UserApps.create(user=user_id, app=app_id, workspace=workspace) + + return user_app + def save_progress(app_id, step, data): progress_table_map = { 'project_description': ProjectDescription, @@ -124,6 +135,14 @@ def get_app(app_id): raise ValueError(f"No app with id: {app_id}") +def get_app_by_user_workspace(user_id, workspace): + try: + user_app = UserApps.get((UserApps.user == user_id) & (UserApps.workspace == workspace)) + return user_app.app + except DoesNotExist: + return None + + def get_progress_steps(app_id, step=None): progress_table_map = { 'project_description': ProjectDescription, @@ -309,7 +328,7 @@ def get_all_connected_steps(step, previous_step_field_name): def delete_all_app_development_data(app): - models = [DevelopmentSteps, CommandRuns, UserInputs, File, FileSnapshot] + models = [DevelopmentSteps, CommandRuns, UserInputs, UserApps, File, FileSnapshot] for model in models: model.delete().where(model.app == app).execute() @@ -354,6 +373,7 @@ def create_tables(): Development, FileSnapshot, CommandRuns, + UserApps, UserInputs, File, ]) @@ -374,6 +394,7 @@ def drop_tables(): Development, FileSnapshot, CommandRuns, + UserApps, UserInputs, File, ]: @@ -423,7 +444,7 @@ def create_database(): def tables_exist(): tables = [User, App, ProjectDescription, UserStories, UserTasks, Architecture, DevelopmentPlanning, - DevelopmentSteps, EnvironmentSetup, Development, FileSnapshot, CommandRuns, UserInputs, File] + DevelopmentSteps, EnvironmentSetup, Development, FileSnapshot, CommandRuns, UserApps, UserInputs, File] if DATABASE_TYPE == "postgres": for table in tables: diff --git a/pilot/database/models/user_apps.py b/pilot/database/models/user_apps.py new file mode 100644 index 0000000..d70672f --- /dev/null +++ b/pilot/database/models/user_apps.py @@ -0,0 +1,18 @@ +from peewee import * + +from database.models.components.base_models import BaseModel +from database.models.app import App +from database.models.user import User + + +class UserApps(BaseModel): + id = AutoField() + app = ForeignKeyField(App, on_delete='CASCADE') + user = ForeignKeyField(User, on_delete='CASCADE') + workspace = CharField(null=True) + + class Meta: + db_table = 'user_apps' + indexes = ( + (('app', 'user'), True), + ) diff --git a/pilot/helpers/Project.py b/pilot/helpers/Project.py index 90d3099..900e8cd 100644 --- a/pilot/helpers/Project.py +++ b/pilot/helpers/Project.py @@ -97,7 +97,7 @@ class Project: # TODO END self.developer = Developer(self) - self.developer.set_up_environment(); + self.developer.set_up_environment() self.developer.start_coding() diff --git a/pilot/helpers/agents/Developer.py b/pilot/helpers/agents/Developer.py index a0074bc..3372124 100644 --- a/pilot/helpers/agents/Developer.py +++ b/pilot/helpers/agents/Developer.py @@ -248,7 +248,7 @@ class Developer(Agent): 'step_type': type, 'directory_tree': directory_tree, 'step_index': step_index - }, EXECUTE_COMMANDS); + }, EXECUTE_COMMANDS) if type == 'COMMAND': for cmd in step_details: run_command_until_success(cmd['command'], cmd['timeout'], convo) diff --git a/pilot/helpers/agents/ProductOwner.py b/pilot/helpers/agents/ProductOwner.py index bfeb466..4f4b932 100644 --- a/pilot/helpers/agents/ProductOwner.py +++ b/pilot/helpers/agents/ProductOwner.py @@ -31,7 +31,8 @@ class ProductOwner(Agent): # PROJECT DESCRIPTION self.project.args['app_type'] = ask_for_app_type() - self.project.args['name'] = clean_filename(ask_user(self.project, 'What is the project name?')) + if 'name' not in self.project.args: + self.project.args['name'] = clean_filename(ask_user(self.project, 'What is the project name?')) self.project.root_path = setup_workspace(self.project.args) @@ -45,7 +46,9 @@ class ProductOwner(Agent): print(colored('Project Summary:\n', 'green', attrs=['bold'])) high_level_summary = convo_project_description.send_message('utils/summary.prompt', - {'conversation': '\n'.join([f"{msg['role']}: {msg['content']}" for msg in high_level_messages])}) + {'conversation': '\n'.join( + [f"{msg['role']}: {msg['content']}" for msg in + high_level_messages])}) save_progress(self.project.args['app_id'], self.project.current_step, { "prompt": main_prompt, @@ -59,7 +62,6 @@ class ProductOwner(Agent): return # PROJECT DESCRIPTION END - def get_user_stories(self): self.project.current_step = 'user_stories' self.convo_user_stories = AgentConvo(self) @@ -111,7 +113,7 @@ class ProductOwner(Agent): logger.info(msg) self.project.user_tasks = self.convo_user_stories.continuous_conversation('user_stories/user_tasks.prompt', - { 'END_RESPONSE': END_RESPONSE }) + {'END_RESPONSE': END_RESPONSE}) logger.info(f"Final user tasks: {self.project.user_tasks}") diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index 939a76d..0505718 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -3,7 +3,7 @@ import uuid from termcolor import colored -from database.database import get_app +from database.database import get_app, get_app_by_user_workspace def get_arguments(): @@ -22,24 +22,40 @@ def get_arguments(): else: arguments[arg] = True + if 'user_id' not in arguments: + arguments['user_id'] = str(uuid.uuid4()) + + app = None + if 'workspace' in arguments: + app = get_app_by_user_workspace(arguments['user_id'], arguments['workspace']) + if app is not None: + arguments['app_id'] = app.id + else: + arguments['workspace'] = None + if 'app_id' in arguments: try: + if app is None: app = get_app(arguments['app_id']) - arguments['user_id'] = str(app.user.id) + + # arguments['user_id'] = str(app.user.id) arguments['app_type'] = app.app_type arguments['name'] = app.name # Add any other fields from the App model you wish to include + + print(colored('\n------------------ LOADING PROJECT ----------------------', 'green', attrs=['bold'])) + print(colored(f'{app.name} (app_id={arguments["app_id"]})', 'green', attrs=['bold'])) + print(colored('--------------------------------------------------------------\n', 'green', attrs=['bold'])) except ValueError as e: print(e) # Handle the error as needed, possibly exiting the script else: arguments['app_id'] = str(uuid.uuid4()) - if 'workspace' not in arguments: - arguments['workspace'] = None - - if 'user_id' not in arguments: - arguments['user_id'] = str(uuid.uuid4()) + print(colored('\n------------------ STARTING NEW PROJECT ----------------------', 'green', attrs=['bold'])) + print(f"If you wish to continue with this project in future run:") + print(colored(f'python {sys.argv[0]} app_id={arguments["app_id"]}', 'green', attrs=['bold'])) + print(colored('--------------------------------------------------------------\n', 'green', attrs=['bold'])) if 'email' not in arguments: # todo change email so its not uuid4 but make sure to fix storing of development steps where @@ -52,8 +68,4 @@ def get_arguments(): if 'step' not in arguments: arguments['step'] = None - print(colored('\n------------------ STARTING NEW PROJECT ----------------------', 'green', attrs=['bold'])) - print(f"If you wish to continue with this project in future run:") - print(colored(f'python main.py app_id={arguments["app_id"]}', 'green', attrs=['bold'])) - print(colored('--------------------------------------------------------------\n', 'green', attrs=['bold'])) return arguments diff --git a/pilot/utils/files.py b/pilot/utils/files.py index 1946c5f..b77b5ae 100644 --- a/pilot/utils/files.py +++ b/pilot/utils/files.py @@ -1,6 +1,6 @@ import os from pathlib import Path - +from database.database import save_user_app def get_parent_folder(folder_name): current_path = Path(os.path.abspath(__file__)) # get the path of the current script @@ -13,6 +13,11 @@ def get_parent_folder(folder_name): def setup_workspace(args): if args['workspace'] is not None: + try: + save_user_app(args['user_id'], args['app_id'], args['workspace']) + except Exception as e: + print(str(e)) + return args['workspace'] root = get_parent_folder('pilot') From 891d153a2b39046fa75cd4be1860a2c6d8233a60 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 15:14:49 +1000 Subject: [PATCH 08/11] BaseModel.id is a UUIDField, create UUID from username --- pilot/utils/arguments.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index 0505718..c702dda 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -1,3 +1,5 @@ +import getpass +import hashlib import sys import uuid @@ -23,7 +25,8 @@ def get_arguments(): arguments[arg] = True if 'user_id' not in arguments: - arguments['user_id'] = str(uuid.uuid4()) + # arguments['user_id'] = str(uuid.uuid4()) + arguments['user_id'] = username_to_uuid(getpass.getuser()) app = None if 'workspace' in arguments: @@ -36,7 +39,7 @@ def get_arguments(): if 'app_id' in arguments: try: if app is None: - app = get_app(arguments['app_id']) + app = get_app(arguments['app_id']) # arguments['user_id'] = str(app.user.id) arguments['app_type'] = app.app_type @@ -69,3 +72,10 @@ def get_arguments(): arguments['step'] = None return arguments + + +# TODO can we make BaseModel.id a CharField with default=uuid4? +def username_to_uuid(username): + sha1 = hashlib.sha1(username.encode()).hexdigest() + uuid_str = "{}-{}-{}-{}-{}".format(sha1[:8], sha1[8:12], sha1[12:16], sha1[16:20], sha1[20:32]) + return str(uuid.UUID(uuid_str)) From e33616450da4131751062c80cbbf2e526f219203 Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 15:14:49 +1000 Subject: [PATCH 09/11] BaseModel.id is a UUIDField, create UUID from username --- pilot/utils/arguments.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index 79a66b9..cb1aa89 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -1,4 +1,5 @@ import getpass +import hashlib import sys import uuid @@ -37,7 +38,7 @@ def get_arguments(): arguments['app_id'] = str(uuid.uuid4()) if 'user_id' not in arguments: - arguments['user_id'] = getpass.getuser() + arguments['user_id'] = username_to_uuid(getpass.getuser()) if 'email' not in arguments: # todo change email so its not uuid4 but make sure to fix storing of development steps where @@ -55,3 +56,10 @@ def get_arguments(): print(colored(f'python main.py app_id={arguments["app_id"]}', 'green', attrs=['bold'])) print(colored('--------------------------------------------------------------\n', 'green', attrs=['bold'])) return arguments + + +# TODO can we make BaseModel.id a CharField with default=uuid4? +def username_to_uuid(username): + sha1 = hashlib.sha1(username.encode()).hexdigest() + uuid_str = "{}-{}-{}-{}-{}".format(sha1[:8], sha1[8:12], sha1[12:16], sha1[16:20], sha1[20:32]) + return str(uuid.UUID(uuid_str)) From 95c6e26665b10b643f5b80808230cd586147681f Mon Sep 17 00:00:00 2001 From: Nicholas Albion Date: Fri, 8 Sep 2023 15:20:32 +1000 Subject: [PATCH 10/11] removed commented-out code --- pilot/utils/arguments.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pilot/utils/arguments.py b/pilot/utils/arguments.py index c702dda..9f82a07 100644 --- a/pilot/utils/arguments.py +++ b/pilot/utils/arguments.py @@ -25,7 +25,6 @@ def get_arguments(): arguments[arg] = True if 'user_id' not in arguments: - # arguments['user_id'] = str(uuid.uuid4()) arguments['user_id'] = username_to_uuid(getpass.getuser()) app = None From 0d522fcccbacf02410dd4921fcd0a05de60ff0ea Mon Sep 17 00:00:00 2001 From: zvone187 Date: Fri, 8 Sep 2023 09:13:25 +0200 Subject: [PATCH 11/11] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 27b34ba..2c43b00 100644 --- a/README.md +++ b/README.md @@ -156,7 +156,7 @@ Other than the research, GPT Pilot needs to be debugged to work in different sce # 🔗 Connect with us 🌟 As an open source tool, it would mean the world to us if you starred the GPT-pilot repo 🌟 -💬 Join [the Discord server](https://discord.gg/FWnRZdCb) to get in touch. +💬 Join [the Discord server](https://discord.gg/HaqXugmxr9) to get in touch.