Spaces:
Runtime error
Runtime error
File size: 5,385 Bytes
6cf4784 | 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 | """
CodeCraftLab — FastAPI entrypoint.
Startup order:
1. Logging configured (structlog)
2. Settings validated (Pydantic v2)
3. Database initialised
4. Routers mounted
5. Background job scheduler started
"""
from __future__ import annotations
import contextlib
from collections.abc import AsyncGenerator
import structlog
import uvicorn
from fastapi import Depends, FastAPI, HTTPException, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import JSONResponse
from core.logging import configure_logging
from core.settings import Settings, get_settings
from routers import auth, datasets, inference, training
# ---------------------------------------------------------------------------
# Logging — configure before anything else imports the logger
# ---------------------------------------------------------------------------
configure_logging()
log = structlog.get_logger(__name__)
# ---------------------------------------------------------------------------
# Lifespan
# ---------------------------------------------------------------------------
@contextlib.asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
settings: Settings = get_settings()
log.info(
"codecraftlab.startup",
env=settings.env,
max_concurrent_jobs=settings.max_concurrent_jobs,
hub_enabled=bool(settings.hf_token),
)
yield
log.info("codecraftlab.shutdown")
# ---------------------------------------------------------------------------
# App factory
# ---------------------------------------------------------------------------
def create_app(settings: Settings | None = None) -> FastAPI:
cfg = settings or get_settings()
app = FastAPI(
title="CodeCraftLab",
description=(
"Production fine-tuning platform for code generation models. "
"Manage datasets, launch training jobs, evaluate models, and serve inference."
),
version="1.0.0",
docs_url="/docs" if cfg.env != "production" else None,
redoc_url="/redoc" if cfg.env != "production" else None,
lifespan=lifespan,
)
# ------------------------------------------------------------------
# Middleware
# ------------------------------------------------------------------
app.add_middleware(
CORSMiddleware,
allow_origins=cfg.cors_origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
if cfg.env == "production":
app.add_middleware(TrustedHostMiddleware, allowed_hosts=cfg.allowed_hosts)
# ------------------------------------------------------------------
# Request logging middleware
# ------------------------------------------------------------------
@app.middleware("http")
async def log_requests(request: Request, call_next): # type: ignore[no-untyped-def]
bound_log = log.bind(
method=request.method,
path=request.url.path,
client=request.client.host if request.client else "unknown",
)
bound_log.info("request.received")
response = await call_next(request)
bound_log.info("request.completed", status_code=response.status_code)
return response
# ------------------------------------------------------------------
# Global exception handler
# ------------------------------------------------------------------
@app.exception_handler(Exception)
async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse:
log.error(
"unhandled_exception",
path=request.url.path,
error=str(exc),
exc_info=True,
)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={"detail": "Internal server error"},
)
# ------------------------------------------------------------------
# Routers
# ------------------------------------------------------------------
app.include_router(auth.router, prefix="/auth", tags=["Authentication"])
app.include_router(datasets.router, prefix="/datasets", tags=["Datasets"])
app.include_router(training.router, prefix="/training", tags=["Training"])
app.include_router(inference.router, prefix="/inference", tags=["Inference"])
# ------------------------------------------------------------------
# Health endpoints
# ------------------------------------------------------------------
@app.get("/health", tags=["System"])
async def health() -> dict[str, str]:
return {"status": "ok"}
@app.get("/health/ready", tags=["System"])
async def readiness(settings: Settings = Depends(get_settings)) -> dict[str, object]:
return {
"status": "ready",
"env": settings.env,
"hub_connected": bool(settings.hf_token),
}
return app
app = create_app()
if __name__ == "__main__":
settings = get_settings()
uvicorn.run(
"app:app",
host="0.0.0.0",
port=8000,
reload=settings.env == "development",
log_config=None, # structlog handles logging
workers=1 if settings.env == "development" else 4,
)
|