| |
| """Build and launch the AF3 + ChatGPT GUI stack (API + React UI).""" |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import shutil |
| import subprocess |
| import sys |
| import threading |
| import webbrowser |
| from pathlib import Path |
|
|
| PROJECT_ROOT = Path(__file__).resolve().parents[2] |
| if str(PROJECT_ROOT) not in sys.path: |
| sys.path.insert(0, str(PROJECT_ROOT)) |
|
|
| from utils.env_config import load_project_env |
|
|
|
|
| def _run(cmd: list[str], cwd: Path) -> None: |
| proc = subprocess.run(cmd, cwd=str(cwd), check=False) |
| if proc.returncode != 0: |
| raise RuntimeError(f"Command failed ({proc.returncode}): {' '.join(cmd)}") |
|
|
|
|
| def _build_frontend(skip_npm_install: bool, skip_build: bool) -> None: |
| react_dir = PROJECT_ROOT / "react-ui" |
| if not react_dir.exists(): |
| raise FileNotFoundError(f"React UI folder missing: {react_dir}") |
|
|
| npm = shutil.which("npm") |
| if not npm: |
| raise RuntimeError("`npm` was not found. Install Node.js (includes npm) first.") |
|
|
| if not skip_npm_install and not (react_dir / "node_modules").exists(): |
| _run([npm, "install"], cwd=react_dir) |
|
|
| if not skip_build: |
| _run([npm, "run", "build"], cwd=react_dir) |
|
|
|
|
| def build_parser() -> argparse.ArgumentParser: |
| p = argparse.ArgumentParser(description="Launch AF3 GUI (FastAPI + built React frontend)") |
| p.add_argument("--host", default="127.0.0.1") |
| p.add_argument("--port", type=int, default=8008) |
| p.add_argument("--reload", action="store_true", help="Enable uvicorn reload mode") |
| p.add_argument("--no-browser", action="store_true", help="Do not open browser automatically") |
| p.add_argument("--skip-npm-install", action="store_true", help="Skip npm install") |
| p.add_argument("--skip-build", action="store_true", help="Skip frontend build") |
| return p |
|
|
|
|
| def main() -> int: |
| args = build_parser().parse_args() |
| load_project_env() |
|
|
| _build_frontend(skip_npm_install=bool(args.skip_npm_install), skip_build=bool(args.skip_build)) |
|
|
| url = f"http://{args.host}:{args.port}" |
| if not args.no_browser: |
| threading.Timer(1.0, lambda: webbrowser.open(url)).start() |
|
|
| import uvicorn |
|
|
| uvicorn.run( |
| "services.pipeline_api:app", |
| host=args.host, |
| port=int(args.port), |
| reload=bool(args.reload), |
| ) |
| return 0 |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|