Spaces:
Paused
Paused
| from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import HTMLResponse, JSONResponse | |
| from fastapi.openapi.utils import get_openapi | |
| from app.core.storage import ensure_storage_dirs | |
| from app.core.config import Settings | |
| # Page renderers | |
| from app.pages.landing import render_landing | |
| from app.pages.phone import render_phone_page | |
| from app.pages.gbridge import render_gbridge_page | |
| from app.pages.windsurf import render_windsurf_page | |
| from app.pages.receipts import render_receipts_page | |
| from app.pages.chat import render_chat_page | |
| from app.pages.m5 import render_m5_page | |
| from app.pages.m5_landing import render_m5_landing | |
| from app.pages.docs import render_docs | |
| from app.pages.api_docs import render_api_docs | |
| from app.pages.public_chat import render_public_chat | |
| # API routes | |
| from app.api.routes_sessions import router as sessions_router | |
| from app.api.routes_jobs import router as jobs_router | |
| from app.api.routes_receipts import router as receipts_router | |
| from app.api.routes_gbridge import router as gbridge_router | |
| from app.api.routes_windsurf import router as windsurf_router | |
| from app.api.routes_health import router as health_router | |
| from app.api.routes_chat import router as chat_router | |
| from app.api.routes_mesh import router as mesh_router | |
| from app.api.routes_accounts import router as accounts_router | |
| from app.api.routes_openai import router as openai_router | |
| from app.api.routes_auth import router as auth_router | |
| from app.api.routes_chats_public import router as chats_public_router | |
| from app.api.routes_pipeline import router as pipeline_router | |
| # Phone relay | |
| from app.phone.relay import ( | |
| connect_worker_websocket, connect_client_websocket, | |
| disconnect_worker, disconnect_client, | |
| handle_worker_message, handle_client_message, | |
| handle_mac_node_message, | |
| ) | |
| from app.mac_relay import connect_mac_node, disconnect_mac_node | |
| from app.phone.sessions import verify_worker_join_token | |
| # gbridge router | |
| from app.gbridge.router import route_capability_call | |
| app = FastAPI(title="M5 API Gateway", version="1.0.0", description="Local compute gateway with OpenAI-compatible API") | |
| def custom_openapi(): | |
| if app.openapi_schema: | |
| return app.openapi_schema | |
| openapi_schema = get_openapi( | |
| title="M5 API Gateway", | |
| version="1.0.0", | |
| description="Local compute gateway with OpenAI-compatible API", | |
| routes=app.routes, | |
| ) | |
| openapi_schema["info"]["x-logo"] = {"url": "https://m5.hf.space/logo.png"} | |
| app.openapi_schema = openapi_schema | |
| return app.openapi_schema | |
| app.openapi = custom_openapi | |
| # Mount static assets | |
| app.mount("/static", StaticFiles(directory="app/static", html=True), name="static") | |
| # Include API routes | |
| app.include_router(sessions_router) | |
| app.include_router(jobs_router) | |
| app.include_router(receipts_router) | |
| app.include_router(gbridge_router) | |
| app.include_router(windsurf_router) | |
| app.include_router(health_router) | |
| app.include_router(chat_router) | |
| app.include_router(mesh_router) | |
| app.include_router(accounts_router) | |
| app.include_router(openai_router) | |
| app.include_router(auth_router) | |
| app.include_router(chats_public_router) | |
| app.include_router(pipeline_router) | |
| async def startup_event(): | |
| ensure_storage_dirs() | |
| # Attempt to start Ollama service (only works if Ollama is installed locally) | |
| try: | |
| from app.compute.ollama_manager import get_ollama_manager | |
| ollama = get_ollama_manager() | |
| await ollama.start() | |
| await ollama.ensure_default_models() | |
| except Exception as e: | |
| import logging | |
| logging.getLogger(__name__).warning(f"Ollama manager not available in this environment: {e}") | |
| async def shutdown_event(): | |
| pass | |
| # Page routes | |
| async def root(): | |
| return render_landing() | |
| async def phone_page(request: Request): | |
| return render_phone_page(request) | |
| async def gbridge_page(request: Request): | |
| return render_gbridge_page(request) | |
| async def windsurf_page(request: Request): | |
| return render_windsurf_page(request) | |
| async def chat_page(request: Request): | |
| return render_chat_page(request) | |
| async def receipts_page(request: Request): | |
| return render_receipts_page(request) | |
| async def m5_page(request: Request): | |
| return render_m5_page(request) | |
| async def m5_landing_page(request: Request): | |
| return render_m5_landing() | |
| async def docs_page(request: Request): | |
| return render_docs() | |
| async def api_docs_page(request: Request): | |
| return render_api_docs() | |
| async def public_chat_page(public_id: str, request: Request): | |
| return render_public_chat() | |
| async def dashboard_page(): | |
| with open("app/static/dashboard.html", "r") as f: | |
| return f.read() | |
| async def worker_page(request: Request, session_id: str, token: str): | |
| if not verify_worker_join_token(session_id, token): | |
| return HTMLResponse(content="<h1>Invalid token</h1>", status_code=403) | |
| # Serve the iPhone worker PWA | |
| with open("app/static/worker/worker.html", "r") as f: | |
| return HTMLResponse(content=f.read(), status_code=200) | |
| # gbridge capability endpoint — the core primitive | |
| async def capability_endpoint(capability_name: str, request: Request): | |
| body = await request.json() | |
| session_id = body.get("session_id") | |
| payload = body.get("payload", {}) | |
| result = route_capability_call(capability_name, payload, session_id) | |
| return result | |
| # WebSocket routes | |
| async def worker_websocket(ws: WebSocket, session_id: str, worker_id: str): | |
| await ws.accept() | |
| await connect_worker_websocket(session_id, worker_id, ws) | |
| try: | |
| while True: | |
| data = await ws.receive_json() | |
| await handle_worker_message(session_id, worker_id, data) | |
| except WebSocketDisconnect: | |
| await disconnect_worker(session_id, worker_id) | |
| async def client_websocket(ws: WebSocket, session_id: str): | |
| await ws.accept() | |
| await connect_client_websocket(session_id, ws) | |
| try: | |
| while True: | |
| data = await ws.receive_json() | |
| await handle_client_message(session_id, data) | |
| except WebSocketDisconnect: | |
| await disconnect_client(session_id) | |
| async def mac_websocket(ws: WebSocket, session_id: str, node_id: str): | |
| await ws.accept() | |
| await connect_mac_node(node_id, ws) | |
| try: | |
| while True: | |
| data = await ws.receive_json() | |
| await handle_mac_node_message(node_id, data) | |
| except WebSocketDisconnect: | |
| await disconnect_mac_node(node_id) | |