Files
gpt-pilot/core/cli/main.py
Senko Rasic 5b474ccc1f merge gpt-pilot 0.2 codebase
This is a complete rewrite of the GPT Pilot core, from the ground
up, making the agentic architecture front and center, and also
fixing some long-standing problems with the database architecture
that weren't feasible to solve without breaking compatibility.

As the database structure and config file syntax have changed,
we have automatic imports for projects and current configs,
see the README.md file for details.

This also relicenses the project to FSL-1.1-MIT license.
2024-05-22 21:42:25 +02:00

146 lines
4.3 KiB
Python

import sys
from argparse import Namespace
from asyncio import run
from core.agents.orchestrator import Orchestrator
from core.cli.helpers import delete_project, init, list_projects, list_projects_json, load_project, show_config
from core.db.session import SessionManager
from core.db.v0importer import LegacyDatabaseImporter
from core.llm.base import APIError
from core.log import get_logger
from core.state.state_manager import StateManager
from core.telemetry import telemetry
from core.ui.base import UIBase
log = get_logger(__name__)
async def run_project(sm: StateManager, ui: UIBase) -> bool:
"""
Work on the project.
Starts the orchestrator agent with the newly loaded/created project
and runs it until the orchestrator decides to exit.
:param sm: State manager.
:param ui: User interface.
:return: True if the orchestrator exited successfully, False otherwise.
"""
telemetry.start()
telemetry.set("app_id", str(sm.project.id))
telemetry.set("initial_prompt", sm.current_state.specification.description)
orca = Orchestrator(sm, ui)
success = False
try:
success = await orca.run()
except KeyboardInterrupt:
log.info("Interrupted by user")
telemetry.set("end_result", "interrupt")
await sm.rollback()
except APIError as err:
log.warning(f"LLM API error occurred: {err.message}")
await ui.send_message(f"LLM API error occurred: {err.message}")
await ui.send_message("Stopping Pythagora due to previous error.")
telemetry.set("end_result", "failure:api-error")
await sm.rollback()
except Exception as err:
telemetry.record_crash(err)
await sm.rollback()
log.error(f"Uncaught exception: {err}", exc_info=True)
await ui.send_message(f"Unrecoverable error occurred: {err}")
if success:
telemetry.set("end_result", "success:exit")
else:
# We assume unsuccessful exit (but not an exception) is a result
# of an API error that the user didn't retry.
telemetry.set("end_result", "failure:api-error")
await telemetry.send()
return success
async def start_new_project(sm: StateManager, ui: UIBase) -> bool:
"""
Start a new project.
:param sm: State manager.
:param ui: User interface.
:return: True if the project was created successfully, False otherwise.
"""
user_input = await ui.ask_question("What is the name of the project", allow_empty=False)
if user_input.cancelled:
return False
project_state = await sm.create_project(user_input.text)
return project_state is not None
async def async_main(
ui: UIBase,
db: SessionManager,
args: Namespace,
) -> bool:
"""
Main application coroutine.
:param ui: User interface.
:param db: Database session manager.
:param args: Command-line arguments.
:return: True if the application ran successfully, False otherwise.
"""
if args.list:
await list_projects(db)
return True
elif args.list_json:
await list_projects_json(db)
return True
if args.show_config:
show_config()
return True
elif args.import_v0:
importer = LegacyDatabaseImporter(db, args.import_v0)
await importer.import_database()
return True
telemetry.set("user_contact", args.email)
if args.extension_version:
telemetry.set("is_extension", True)
telemetry.set("extension_version", args.extension_version)
sm = StateManager(db, ui)
ui_started = await ui.start()
if not ui_started:
return False
if args.project or args.branch or args.step:
telemetry.set("is_continuation", True)
# FIXME: we should send the project stage and other runtime info to the UI
success = await load_project(sm, args.project, args.branch, args.step)
if not success:
return False
elif args.delete:
success = await delete_project(sm, args.delete)
return success
else:
success = await start_new_project(sm, ui)
if not success:
return False
return await run_project(sm, ui)
def run_pythagora():
ui, db, args = init()
if not ui or not db:
return -1
success = run(async_main(ui, db, args))
return 0 if success else -1
if __name__ == "__main__":
sys.exit(run_pythagora())