#!/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" @asynccontextmanager 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 @app.get("/health") 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) @app.get("/{full_path:path}") 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 )