mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-08 12:53:50 -05:00
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.
146 lines
4.3 KiB
Python
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())
|