""" Exception handling for AI Service. [AC-AISVC-03, AC-AISVC-04, AC-AISVC-05] Structured error responses. """ from fastapi import HTTPException, Request, status from fastapi.responses import JSONResponse from app.models import ErrorCode, ErrorResponse class AIServiceException(Exception): def __init__( self, code: ErrorCode, message: str, status_code: int = status.HTTP_500_INTERNAL_SERVER_ERROR, details: list[dict] | None = None, ): self.code = code self.message = message self.status_code = status_code self.details = details super().__init__(message) class MissingTenantIdException(AIServiceException): def __init__(self, message: str = "Missing required header: X-Tenant-Id"): super().__init__( code=ErrorCode.MISSING_TENANT_ID, message=message, status_code=status.HTTP_400_BAD_REQUEST, ) class InvalidRequestException(AIServiceException): def __init__(self, message: str, details: list[dict] | None = None): super().__init__( code=ErrorCode.INVALID_REQUEST, message=message, status_code=status.HTTP_400_BAD_REQUEST, details=details, ) class ServiceUnavailableException(AIServiceException): def __init__(self, message: str = "Service temporarily unavailable"): super().__init__( code=ErrorCode.SERVICE_UNAVAILABLE, message=message, status_code=status.HTTP_503_SERVICE_UNAVAILABLE, ) class TimeoutException(AIServiceException): def __init__(self, message: str = "Request timeout"): super().__init__( code=ErrorCode.TIMEOUT, message=message, status_code=status.HTTP_503_SERVICE_UNAVAILABLE, ) async def ai_service_exception_handler(request: Request, exc: AIServiceException) -> JSONResponse: return JSONResponse( status_code=exc.status_code, content=ErrorResponse( code=exc.code.value, message=exc.message, details=exc.details, ).model_dump(exclude_none=True), ) async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse: if exc.status_code == status.HTTP_400_BAD_REQUEST: code = ErrorCode.INVALID_REQUEST elif exc.status_code == status.HTTP_503_SERVICE_UNAVAILABLE: code = ErrorCode.SERVICE_UNAVAILABLE else: code = ErrorCode.INTERNAL_ERROR return JSONResponse( status_code=exc.status_code, content=ErrorResponse( code=code.value, message=exc.detail or "An error occurred", ).model_dump(exclude_none=True), ) async def generic_exception_handler(request: Request, exc: Exception) -> JSONResponse: return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=ErrorResponse( code=ErrorCode.INTERNAL_ERROR.value, message="An unexpected error occurred", ).model_dump(exclude_none=True), )