Spaces:
Sleeping
Sleeping
File size: 4,734 Bytes
4d18cf9 | 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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | """
backend/main.py
βββββββββββββββββββββββββββββββββββββββββββββ
FastAPI application β Rift Breakdown Backend
Carga los 3 modelos ML al startup y expone endpoints
para predicciΓ³n de win probability minuto-a-minuto.
βββββββββββββββββββββββββββββββββββββββββββββ
"""
import logging
import os
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from backend.routers import predictions
from backend.services.model_service import model_service
# βββββββββββββββββββββββββββββββββββββββββββββ
# Logging
# βββββββββββββββββββββββββββββββββββββββββββββ
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(name)s | %(levelname)s | %(message)s",
)
logger = logging.getLogger(__name__)
# βββββββββββββββββββββββββββββββββββββββββββββ
# Lifespan (startup / shutdown)
# βββββββββββββββββββββββββββββββββββββββββββββ
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Load models once on startup, release on shutdown."""
logger.info("π Starting Rift Breakdown backendβ¦")
try:
model_service.load_models()
logger.info("β
All models loaded β server ready.")
except Exception as e:
logger.error("β Model loading failed: %s", e, exc_info=True)
# Server starts anyway but /predict will return 503
yield
logger.info("π Shutting down Rift Breakdown backend.")
# βββββββββββββββββββββββββββββββββββββββββββββ
# App
# βββββββββββββββββββββββββββββββββββββββββββββ
app = FastAPI(
title="Rift Breakdown β LoL Win Probability Predictor",
version="0.2.0",
description="Post-match analysis with 3 ML models (XGBoost, LSTM, LogReg)",
lifespan=lifespan,
# Docs bajo /api para no colisionar con React SPA
docs_url="/api/docs",
redoc_url="/api/redoc",
openapi_url="/api/openapi.json",
)
# βββββββββββββββββββββββββββββββββββββββββββββ
# CORS (Secured for Production)
# βββββββββββββββββββββββββββββββββββββββββββββ
allowed_origins_env = os.environ.get(
"ALLOWED_ORIGINS",
"http://localhost:5173,http://127.0.0.1:5173,http://localhost:3000,https://huggingface.co"
)
origins = [origin.strip() for origin in allowed_origins_env.split(",") if origin.strip()]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["GET", "POST", "OPTIONS"],
allow_headers=["Content-Type", "Authorization", "Accept"],
)
# βββββββββββββββββββββββββββββββββββββββββββββ
# Routers
# βββββββββββββββββββββββββββββββββββββββββββββ
app.include_router(predictions.router, prefix="/api/v1", tags=["predictions"])
# βββββββββββββββββββββββββββββββββββββββββββββ
# Health check
# βββββββββββββββββββββββββββββββββββββββββββββ
@app.get("/api/health", tags=["health"])
def health_check():
return {
"status": "ok",
"version": "0.2.0",
"models_loaded": model_service.is_loaded,
}
DIST = Path(__file__).parent.parent / "frontend" / "dist"
if DIST.exists():
# Archivos estΓ‘ticos (JS, CSS, imΓ‘genes)
app.mount("/assets", StaticFiles(directory=DIST / "assets"), name="assets")
@app.get("/{full_path:path}", include_in_schema=False)
def serve_spa(full_path: str):
"""Catch-all: cualquier ruta no-API sirve el index.html de React."""
return FileResponse(DIST / "index.html")
|