rubric-ai / main.py
itslikethisnow's picture
three stages complete
a43aaf6
"""
Main entry point for Rubric AI.
Runs both FastAPI backend and NiceGUI frontend on a single Uvicorn process.
Designed for deployment on Hugging Face Spaces (port 7860).
"""
import re
from starlette.middleware.base import BaseHTTPMiddleware
from backend.app import create_app
from backend.core.config import settings
from frontend.ui import init_frontend
from nicegui import ui
# Create FastAPI application
app = create_app()
# Import API routes (registers @app decorators)
import frontend.api_routes # noqa: F401
# Middleware to allow iframe embedding on HF Spaces
class AllowIframeMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
response = await call_next(request)
# Remove X-Frame-Options to allow HF Spaces iframe embedding
if "x-frame-options" in response.headers:
del response.headers["x-frame-options"]
# Remove or modify Content-Security-Policy frame-ancestors directive
if "content-security-policy" in response.headers:
csp = response.headers["content-security-policy"]
# Remove frame-ancestors directive entirely to allow any iframe embedding
csp = re.sub(r"frame-ancestors\s+[^;]+;?\s*", "", csp)
if csp.strip():
response.headers["content-security-policy"] = csp
else:
del response.headers["content-security-policy"]
return response
app.add_middleware(AllowIframeMiddleware)
# Initialize NiceGUI frontend routes BEFORE mounting
init_frontend()
# Mount NiceGUI onto FastAPI (modifies app in place)
# This must happen at module level so Uvicorn can import 'main:app'
ui.run_with(
app,
title="Rubric AI",
favicon="favicon.png",
storage_secret=settings.SUPABASE_ANON_KEY or "rubric-ai-secret",
mount_path="/",
reconnect_timeout=10, # Handle HF Spaces proxy timeouts
)
if __name__ == "__main__":
import uvicorn
uvicorn.run(
"main:app",
host=settings.HOST,
port=settings.PORT,
reload=True,
)