mirror of
https://github.com/Pythagora-io/gpt-pilot.git
synced 2026-01-09 21:27:53 -05:00
218 lines
6.4 KiB
Python
218 lines
6.4 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.config import LLMProvider, get_config
|
|
from core.db.session import SessionManager
|
|
from core.db.v0importer import LegacyDatabaseImporter
|
|
from core.llm.base import APIError, BaseLLMClient
|
|
from core.log import get_logger
|
|
from core.state.state_manager import StateManager
|
|
from core.telemetry import telemetry
|
|
from core.ui.base import UIBase, UIClosedError, UserInput, pythagora_source
|
|
|
|
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.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()
|
|
telemetry.set("end_result", "success:exit" if success else "failure:api-error")
|
|
except (KeyboardInterrupt, UIClosedError):
|
|
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"Stopping Pythagora due to an error while calling the LLM API: {err.message}",
|
|
source=pythagora_source,
|
|
)
|
|
telemetry.set("end_result", "failure:api-error")
|
|
await sm.rollback()
|
|
except Exception as err:
|
|
log.error(f"Uncaught exception: {err}", exc_info=True)
|
|
stack_trace = telemetry.record_crash(err)
|
|
await sm.rollback()
|
|
await ui.send_message(
|
|
f"Stopping Pythagora due to error:\n\n{stack_trace}",
|
|
source=pythagora_source,
|
|
)
|
|
|
|
return success
|
|
|
|
|
|
async def llm_api_check(ui: UIBase) -> bool:
|
|
"""
|
|
Check whether the configured LLMs are reachable.
|
|
|
|
:param ui: UI we'll use to report any issues
|
|
:return: True if all the LLMs are reachable.
|
|
"""
|
|
|
|
config = get_config()
|
|
|
|
async def handler(*args, **kwargs):
|
|
pass
|
|
|
|
success = True
|
|
checked_llms: set[LLMProvider] = set()
|
|
for llm_config in config.all_llms():
|
|
if llm_config.provider in checked_llms:
|
|
continue
|
|
|
|
client_class = BaseLLMClient.for_provider(llm_config.provider)
|
|
llm_client = client_class(llm_config, stream_handler=handler, error_handler=handler)
|
|
try:
|
|
resp = await llm_client.api_check()
|
|
if not resp:
|
|
success = False
|
|
log.warning(f"API check for {llm_config.provider.value} failed.")
|
|
else:
|
|
log.info(f"API check for {llm_config.provider.value} succeeded.")
|
|
except APIError as err:
|
|
await ui.send_message(
|
|
f"API check for {llm_config.provider.value} failed with: {err}",
|
|
source=pythagora_source,
|
|
)
|
|
log.warning(f"API check for {llm_config.provider.value} failed with: {err}")
|
|
success = False
|
|
|
|
if not success:
|
|
telemetry.set("end_result", "failure:api-error")
|
|
|
|
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.
|
|
"""
|
|
try:
|
|
user_input = await ui.ask_question(
|
|
"What is the project name?",
|
|
allow_empty=False,
|
|
source=pythagora_source,
|
|
)
|
|
except (KeyboardInterrupt, UIClosedError):
|
|
user_input = UserInput(cancelled=True)
|
|
|
|
if user_input.cancelled:
|
|
return False
|
|
|
|
project_state = await sm.create_project(user_input.text)
|
|
return project_state is not None
|
|
|
|
|
|
async def run_pythagora_session(sm: StateManager, ui: UIBase, args: Namespace):
|
|
"""
|
|
Run a Pythagora session.
|
|
|
|
:param sm: State manager.
|
|
:param ui: User interface.
|
|
:param args: Command-line arguments.
|
|
:return: True if the application ran successfully, False otherwise.
|
|
"""
|
|
|
|
if not await llm_api_check(ui):
|
|
await ui.send_message(
|
|
"Pythagora cannot start because the LLM API is not reachable.",
|
|
source=pythagora_source,
|
|
)
|
|
return False
|
|
|
|
if args.project or args.branch or args.step:
|
|
telemetry.set("is_continuation", True)
|
|
success = await load_project(sm, args.project, args.branch, args.step)
|
|
if not success:
|
|
return False
|
|
else:
|
|
success = await start_new_project(sm, ui)
|
|
if not success:
|
|
return False
|
|
|
|
return await run_project(sm, ui)
|
|
|
|
|
|
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
|
|
elif args.delete:
|
|
success = await delete_project(db, args.delete)
|
|
return success
|
|
|
|
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
|
|
|
|
telemetry.start()
|
|
success = await run_pythagora_session(sm, ui, args)
|
|
await telemetry.send()
|
|
await ui.stop()
|
|
|
|
return success
|
|
|
|
|
|
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())
|