- Implemented SettingsPage for configuring default provider, temperature, and model paths. - Added SuitePage for managing a suite of questions with features to add, edit, duplicate, and delete questions. - Introduced TypeScript configuration files for app and node environments. - Set up Vite configuration for development server with API proxying to backend.
115 lines
3.2 KiB
Python
115 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""LM-Gambit desktop entrypoint.
|
|
|
|
Starts the FastAPI backend, serves the compiled React interface from the same
|
|
origin and opens a browser at it:
|
|
|
|
python app.py
|
|
|
|
For frontend development run the Vite dev server alongside it instead:
|
|
|
|
python app.py --no-browser # terminal 1
|
|
cd web && npm run dev # terminal 2 -> http://localhost:5173
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import socket
|
|
import sys
|
|
import threading
|
|
import webbrowser
|
|
from pathlib import Path
|
|
|
|
ROOT_DIR = Path(__file__).resolve().parent
|
|
if str(ROOT_DIR) not in sys.path:
|
|
sys.path.insert(0, str(ROOT_DIR))
|
|
|
|
DEFAULT_PORT = 8765
|
|
WEB_DIST = ROOT_DIR / "web" / "dist"
|
|
|
|
|
|
def port_is_free(host: str, port: int) -> bool:
|
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as probe:
|
|
probe.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
try:
|
|
probe.bind((host, port))
|
|
except OSError:
|
|
return False
|
|
return True
|
|
|
|
|
|
def find_port(host: str, preferred: int) -> int:
|
|
for candidate in range(preferred, preferred + 20):
|
|
if port_is_free(host, candidate):
|
|
return candidate
|
|
raise SystemExit(
|
|
f"No free port found between {preferred} and {preferred + 19}. "
|
|
"Pass --port to choose one explicitly."
|
|
)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(
|
|
prog="app.py",
|
|
description="Run the LM-Gambit web interface.",
|
|
)
|
|
parser.add_argument("--host", default="127.0.0.1", help="Interface to bind (default: 127.0.0.1)")
|
|
parser.add_argument(
|
|
"--port",
|
|
type=int,
|
|
default=DEFAULT_PORT,
|
|
help=f"Port to serve on (default: {DEFAULT_PORT}; the next free one is used if taken)",
|
|
)
|
|
parser.add_argument("--no-browser", action="store_true", help="Do not open a browser window")
|
|
parser.add_argument("--reload", action="store_true", help="Auto-reload on Python changes (dev)")
|
|
args = parser.parse_args()
|
|
|
|
try:
|
|
import uvicorn
|
|
except ImportError:
|
|
print(
|
|
"FastAPI/uvicorn are not installed.\n"
|
|
" python -m pip install -r requirements.txt",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
port = args.port if port_is_free(args.host, args.port) else find_port(args.host, args.port)
|
|
url = f"http://{'localhost' if args.host in {'127.0.0.1', '0.0.0.0'} else args.host}:{port}"
|
|
|
|
if not (WEB_DIST / "index.html").is_file():
|
|
print(
|
|
"The web interface has not been built yet.\n"
|
|
" cd web && npm install && npm run build\n"
|
|
f"Starting the API anyway — docs at {url}/api/docs\n",
|
|
file=sys.stderr,
|
|
)
|
|
elif not args.no_browser:
|
|
threading.Timer(1.0, lambda: webbrowser.open(url)).start()
|
|
|
|
print(f"\n LM-Gambit -> {url}\n Press Ctrl+C to stop.\n")
|
|
|
|
uvicorn.run(
|
|
"server.main:app" if args.reload else _build_app(),
|
|
host=args.host,
|
|
port=port,
|
|
reload=args.reload,
|
|
log_level="info",
|
|
access_log=False,
|
|
)
|
|
return 0
|
|
|
|
|
|
def _build_app():
|
|
from server.main import create_app
|
|
|
|
return create_app()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
raise SystemExit(main())
|
|
except KeyboardInterrupt:
|
|
print("\nStopped.")
|