"""Custom exceptions and error handlers for the chat agent application.""" from typing import Any, Dict, Optional from fastapi import HTTPException, Request, status from fastapi.responses import JSONResponse from typing import Union from pydantic import BaseModel from datetime import datetime import json from starlette.status import ( HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND, HTTP_429_TOO_MANY_REQUESTS, HTTP_500_INTERNAL_SERVER_ERROR, ) # 创建一个完整的响应模型 class FullHxfResponseModel(BaseModel): """完整的响应模型,包含状态码、数据、错误信息等""" code: int status: int data: Dict[str, Any] error: Optional[Dict[str, Any]] message: Optional[str] class HxfResponse(JSONResponse): def __init__(self, response: Union[BaseModel, Dict[str, Any]]): if isinstance(response, BaseModel): data_dict = response.model_dump(mode='json') else: data_dict = response content = { "code": 0, "status": status.HTTP_200_OK, "data": data_dict, "error": None, "message": None } super().__init__( content=content, status_code=status.HTTP_200_OK, media_type="application/json" ) class HxfErrorResponse(JSONResponse): """Custom JSON response class with standard format.""" def __init__(self, message: Union[str, Exception], status_code: int = status.HTTP_401_UNAUTHORIZED): """Return a JSON error response.""" if isinstance(message, Exception): content = { "code": -1, "status": message.status_code, "data": None, "error": message.details, "message": message.message } else: content = { "code": -1, "status": status_code, "data": None, "error": None, "message": message } super().__init__(content=content, status_code=status_code) class ChatAgentException(Exception): """Base exception for chat agent application.""" def __init__( self, message: str, status_code: int = HTTP_500_INTERNAL_SERVER_ERROR, details: Optional[Dict[str, Any]] = None ): self.message = message self.status_code = status_code self.details = details or {} super().__init__(self.message) class ValidationError(ChatAgentException): """Validation error exception.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, HTTP_422_UNPROCESSABLE_ENTITY, details) class AuthenticationError(ChatAgentException): """Authentication error exception.""" def __init__(self, message: str = "Authentication failed"): super().__init__(message, HTTP_401_UNAUTHORIZED) class AuthorizationError(ChatAgentException): """Authorization error exception.""" def __init__(self, message: str = "Access denied"): super().__init__(message, HTTP_403_FORBIDDEN) class NotFoundError(ChatAgentException): """Resource not found exception.""" def __init__(self, message: str = "Resource not found"): super().__init__(message, HTTP_404_NOT_FOUND) class ConversationNotFoundError(NotFoundError): """Conversation not found exception.""" def __init__(self, conversation_id: str): super().__init__(f"Conversation with ID {conversation_id} not found") class UserNotFoundError(NotFoundError): """User not found exception.""" def __init__(self, user_id: str): super().__init__(f"User with ID {user_id} not found") class ChatServiceError(ChatAgentException): """Chat service error exception.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, HTTP_500_INTERNAL_SERVER_ERROR, details) class OpenAIError(ChatServiceError): """OpenAI API error exception.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(f"OpenAI API error: {message}", details) class RateLimitError(ChatAgentException): """Rate limit exceeded error.""" pass class DatabaseError(ChatAgentException): """Database operation error exception.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(f"Database error: {message}", HTTP_500_INTERNAL_SERVER_ERROR, details) # Error handlers async def chat_agent_exception_handler(request: Request, exc: ChatAgentException) -> JSONResponse: """Handle ChatAgentException and its subclasses.""" from loguru import logger logger.error( f"ChatAgentException: {exc.message}", extra={ "status_code": exc.status_code, "details": exc.details, "path": request.url.path, "method": request.method } ) return HxfErrorResponse(exc) async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse: """Handle HTTPException.""" from loguru import logger logger.warning( f"HTTPException: {exc.detail}", extra={ "status_code": exc.status_code, "path": request.url.path, "method": request.method } ) return HxfErrorResponse(exc) async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse: """Handle general exceptions.""" from loguru import logger logger.error( f"Unhandled exception: {str(exc)}", extra={ "exception_type": exc.__class__.__name__, "path": request.url.path, "method": request.method }, exc_info=True ) return HxfErrorResponse(exc)