File size: 4,372 Bytes
df4a21a
 
 
 
 
 
9fd7a87
 
df4a21a
 
 
9fd7a87
 
 
 
 
 
df4a21a
 
 
 
 
 
 
 
 
 
 
 
 
9fd7a87
 
 
 
df4a21a
 
 
 
 
 
 
 
 
 
 
9fd7a87
 
 
df4a21a
 
 
 
 
 
 
9fd7a87
df4a21a
 
9fd7a87
 
 
 
df4a21a
 
 
 
9fd7a87
 
 
 
 
df4a21a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"""
FastAPI application entry point.

DeepFake Detector API - Milestone 1: Hugging Face hosted dummy models.
"""

import time
from datetime import datetime, timezone
from contextlib import asynccontextmanager
from typing import AsyncGenerator

MAIN_IMPORT_T0 = time.perf_counter()
print(
    f"{datetime.now(timezone.utc).isoformat()} | INFO     | app.main | phase3 module_import_start",
    flush=True,
)

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from app.api import routes_health, routes_models, routes_predict
from app.core.config import settings
from app.core.errors import DeepFakeDetectorError
from app.core.logging import setup_logging, get_logger
from app.services.model_registry import get_model_registry

# Set up logging
setup_logging()
logger = get_logger(__name__)
logger.info(
    "phase3 module_import_complete duration_seconds=%.3f",
    time.perf_counter() - MAIN_IMPORT_T0,
)


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """
    Application lifespan manager.
    
    Handles startup and shutdown events:
    - Startup: Load models from Hugging Face
    - Shutdown: Cleanup resources
    """
    startup_t0 = time.perf_counter()
    logger.info("phase3 startup_lifespan_begin")

    # Startup
    logger.info("Starting DeepFake Detector API...")
    logger.info(f"Configuration: HF_FUSION_REPO_ID={settings.HF_FUSION_REPO_ID}")
    logger.info(f"Configuration: HF_CACHE_DIR={settings.HF_CACHE_DIR}")
    
    # Load models from Hugging Face
    try:
        model_load_t0 = time.perf_counter()
        registry = get_model_registry()
        await registry.load_from_fusion_repo(settings.HF_FUSION_REPO_ID)
        logger.info(
            "phase3 startup_model_load_duration_seconds=%.3f",
            time.perf_counter() - model_load_t0,
        )
        logger.info("Models loaded successfully!")
    except Exception as e:
        logger.error(f"Failed to load models on startup: {e}")
        logger.warning("API will start but /ready will report not_ready until models are loaded")

    logger.info(
        "phase3 startup_lifespan_total_duration_seconds=%.3f",
        time.perf_counter() - startup_t0,
    )
    
    yield  # Application runs here
    
    # Shutdown
    logger.info("Shutting down DeepFake Detector API...")


# Create FastAPI application
app = FastAPI(
    title=settings.PROJECT_NAME,
    version=settings.VERSION,
    description="""
    DeepFake Detector API - Analyze images to detect AI-generated content.
    
    ## Features
    
    - **Fusion prediction**: Combines multiple model predictions using majority vote
    - **Individual model prediction**: Run specific submodels directly
    - **Timing information**: Detailed performance metrics for each request
    
    ## Milestone 1
    
    This is the initial milestone using dummy random models hosted on Hugging Face
    for testing the API infrastructure.
    """,
    lifespan=lifespan,
    debug=settings.ENABLE_DEBUG
)

# Add CORS middleware
app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origins_list,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

logger.info(f"CORS enabled for origins: {settings.cors_origins_list}")


# Global exception handler for custom errors
@app.exception_handler(DeepFakeDetectorError)
async def deepfake_error_handler(request: Request, exc: DeepFakeDetectorError):
    """Handle custom DeepFakeDetector exceptions."""
    return JSONResponse(
        status_code=500,
        content={
            "error": type(exc).__name__,
            "message": exc.message,
            "details": exc.details
        }
    )


# Include routers
app.include_router(routes_health.router)
app.include_router(routes_models.router)
app.include_router(routes_predict.router)


# Root endpoint
@app.get("/", tags=["root"])
async def root():
    """Root endpoint with API information."""
    return {
        "name": settings.PROJECT_NAME,
        "version": settings.VERSION,
        "docs": "/docs",
        "health": "/health",
        "ready": "/ready"
    }


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(
        "app.main:app",
        host=settings.HOST,
        port=settings.PORT,
        reload=settings.ENABLE_DEBUG
    )