File size: 4,353 Bytes
6b556be
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import os
import asyncio
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from starlette.middleware.base import BaseHTTPMiddleware

# Import our modules
from app.config import config
from app.ml_utils import model_manager
from app.database import db_manager
from app.cloudinary_utils import initialize_cloudinary

# Import route modules
from app.routes.prediction import router as prediction_router
from app.routes.history import router as history_router
from app.routes.system import router as system_router


class ConnectionErrorMiddleware(BaseHTTPMiddleware):
    """Handle connection errors gracefully for Hugging Face deployment"""
    async def dispatch(self, request: Request, call_next):
        try:
            response = await call_next(request)
            return response
        except ConnectionResetError as e:
            print(f"Connection reset error for {request.url}: {e}")
            return JSONResponse(
                status_code=499,
                content={"detail": "Connection was closed by client"}
            )
        except asyncio.CancelledError:
            print(f"Request cancelled for {request.url}")
            return JSONResponse(
                status_code=499,
                content={"detail": "Request was cancelled"}
            )
        except Exception as e:
            print(f"Unexpected error for {request.url}: {e}")
            return JSONResponse(
                status_code=500,
                content={"detail": "Internal server error"}
            )


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Application lifespan manager for startup and shutdown"""
    print("===== Application Startup =====")
    
    try:
        # Configure asyncio for Windows if needed
        if os.name == 'nt':
            asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
        
        # Validate essential configuration
        config.validate_required_config()
        print("✓ Configuration validation passed")
        
        # Load ML model (essential)
        if model_manager.load_model():
            print("✓ Model loaded successfully")
        else:
            print("✗ Model loading failed")
            raise RuntimeError("Model loading failed - cannot start application")
        
        # Initialize optional services
        await db_manager.initialize()
        if db_manager.is_available():
            print("✓ Database initialized")
        else:
            print("ℹ Database not available - predictions will not be persisted")
        
        initialize_cloudinary()
        print("ℹ Cloudinary initialization completed")
        
        print(f"✓ Server ready on {config.HOST}:{config.PORT}")
        print("===== Startup Complete =====")
        
    except Exception as e:
        print(f"✗ Startup error: {e}")
        raise
    
    yield  # Application runs here
    
    print("===== Application Shutdown =====")


# Initialize FastAPI app with lifespan manager
app = FastAPI(
    title="Brain Stroke Segmentation API",
    description="AI-powered brain stroke detection and segmentation",
    version="1.0.0",
    debug=config.DEBUG,
    lifespan=lifespan
)

# Add middlewares
app.add_middleware(ConnectionErrorMiddleware)

# CORS middleware - more permissive for Hugging Face
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"] if "*" in config.CORS_ORIGINS else config.CORS_ORIGINS,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Include routers
app.include_router(prediction_router, tags=["Prediction"])
app.include_router(history_router, tags=["History"])
app.include_router(system_router, tags=["System"])


if __name__ == "__main__":
    import uvicorn
    
    try:
        print(f"Starting server on {config.HOST}:{config.PORT}")
        uvicorn.run(
            "app.server:app",
            host=config.HOST,
            port=config.PORT,
            reload=config.DEBUG,
            access_log=False,
            server_header=False,
            date_header=False,
        )
    except KeyboardInterrupt:
        print("Server stopped by user")
    except Exception as e:
        print(f"Server startup failed: {e}")
        raise