Spaces:
Running
Running
| #!/usr/bin/env python3 | |
| """ | |
| FetalCLIP Server for Hugging Face Spaces | |
| This server: | |
| 1. Serves the React frontend as static files | |
| 2. Runs the FastAPI backend API | |
| 3. Runs on port 7860 (HF Spaces requirement) | |
| """ | |
| import sys | |
| from pathlib import Path | |
| # Add backend to path | |
| sys.path.insert(0, str(Path(__file__).parent / "backend")) | |
| import uvicorn | |
| from fastapi import FastAPI | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import FileResponse | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from contextlib import asynccontextmanager | |
| # Import the backend app components | |
| from app.routes import classification_router, gestational_age_router, feedback_router | |
| from app.services.model import model_service | |
| # Assets directory | |
| ASSETS_DIR = Path(__file__).parent / "assets" | |
| STATIC_DIR = Path(__file__).parent / "backend" / "static" | |
| async def lifespan(app: FastAPI): | |
| """Load model on startup.""" | |
| print("π Starting FetalCLIP on Hugging Face Spaces...") | |
| print(f"π Assets directory: {ASSETS_DIR}") | |
| model_service.load_model(ASSETS_DIR) | |
| yield | |
| print("π Shutting down FetalCLIP...") | |
| # Create the app | |
| app = FastAPI( | |
| title="FetalCLIP API", | |
| description="Foundation Model for Fetal Ultrasound Analysis - Hugging Face Spaces Edition", | |
| version="1.0.0", | |
| lifespan=lifespan, | |
| ) | |
| # CORS for HF Spaces | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # HF Spaces needs permissive CORS | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| # Include API routers | |
| app.include_router(classification_router) | |
| app.include_router(gestational_age_router) | |
| app.include_router(feedback_router) | |
| # Health check endpoint | |
| async def health_check(): | |
| return { | |
| "status": "healthy", | |
| "model_loaded": model_service.model is not None, | |
| "device": str(model_service.device), | |
| "available_views": len(model_service.list_plane), | |
| "platform": "huggingface-spaces" | |
| } | |
| # Serve React frontend | |
| if STATIC_DIR.exists(): | |
| # Mount static assets (JS, CSS, etc.) | |
| app.mount("/assets", StaticFiles(directory=STATIC_DIR / "assets"), name="static-assets") | |
| # Serve index.html for all non-API routes (SPA routing) | |
| async def serve_spa(full_path: str): | |
| # If it's an API route, skip | |
| if full_path.startswith("api/") or full_path == "health" or full_path == "docs": | |
| return None | |
| # Serve index.html for SPA routing | |
| index_path = STATIC_DIR / "index.html" | |
| if index_path.exists(): | |
| return FileResponse(index_path) | |
| return {"error": "Frontend not built"} | |
| if __name__ == "__main__": | |
| # HF Spaces requires port 7860 | |
| uvicorn.run( | |
| app, | |
| host="0.0.0.0", | |
| port=7860, | |
| reload=False | |
| ) | |