from fastapi import FastAPI, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from src.models.schemas import ErrorDetail, ErrorEnvelope from src.services.auth import AuthServiceError CLIENT_SAFE_MESSAGES = { "agent_unavailable": "Agent service is unavailable.", "internal_error": "An unexpected server error occurred.", "validation_error": "Request validation failed.", } def sanitize_validation_errors(errors: list[dict]) -> list[dict]: """Remove raw request input from FastAPI validation errors.""" sanitized_errors = [] for error in errors: sanitized_errors.append( { key: value for key, value in error.items() if key not in {"input", "ctx", "url"} } ) return sanitized_errors def error_envelope( code: str, message: str | None = None, details: dict | None = None, ) -> ErrorEnvelope: """Build the shared sanitized API error envelope.""" return ErrorEnvelope( error=ErrorDetail( code=code, message=message or CLIENT_SAFE_MESSAGES.get(code, "Request failed."), details=details or {}, ) ) def error_response( status_code: int, code: str, message: str | None = None, details: dict | None = None, ) -> JSONResponse: envelope = error_envelope(code=code, message=message, details=details) return JSONResponse(status_code=status_code, content=envelope.model_dump()) def install_exception_handlers(app: FastAPI) -> None: """Install client-safe API exception handlers.""" @app.exception_handler(RequestValidationError) async def validation_exception_handler( request: Request, exc: RequestValidationError, ) -> JSONResponse: del request return error_response( status_code=422, code="validation_error", message=CLIENT_SAFE_MESSAGES["validation_error"], details={"errors": sanitize_validation_errors(exc.errors())}, ) @app.exception_handler(AuthServiceError) async def auth_service_exception_handler( request: Request, exc: AuthServiceError, ) -> JSONResponse: del request return error_response( status_code=exc.status_code, code=exc.code, message=exc.message, )