File size: 2,377 Bytes
8bdd018
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#!/usr/bin/env python
"""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())