| 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, |
| ) |
|
|