FetalCLIP / server.py
Numan Saeed
Add Not Sure feedback, save preprocessed scans, add Help tab
f84688d
#!/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
)