""" Custom Exception Classes for Flare Platform """ from typing import Optional, Dict, Any from datetime import datetime class FlareException(Exception): """Base exception for Flare""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): self.message = message self.details = details or {} self.timestamp = datetime.utcnow() super().__init__(self.message) def to_dict(self) -> Dict[str, Any]: """Convert exception to dictionary""" return { "error": self.__class__.__name__, "message": self.message, "details": self.details, "timestamp": self.timestamp.isoformat() } def to_http_detail(self) -> Dict[str, Any]: """Convert to HTTP response detail""" return { "detail": self.message, "error_type": self.__class__.__name__.lower().replace('error', ''), **self.details } class RaceConditionError(FlareException): """Raised when a race condition is detected during concurrent updates""" def __init__( self, message: str, current_user: Optional[str] = None, last_update_user: Optional[str] = None, last_update_date: Optional[str] = None, entity_type: Optional[str] = None, entity_id: Optional[Any] = None ): details = { "current_user": current_user, "last_update_user": last_update_user, "last_update_date": last_update_date, "entity_type": entity_type, "entity_id": entity_id, "action": "Please reload the data and try again" } super().__init__(message, details) self.current_user = current_user self.last_update_user = last_update_user self.last_update_date = last_update_date def to_http_detail(self) -> Dict[str, Any]: """Convert to HTTPException detail format with proper serialization""" return { "message": self.message, "last_update_user": self.last_update_user, "last_update_date": self.last_update_date.isoformat() if isinstance(self.last_update_date, datetime) else self.last_update_date, "type": "race_condition" } class ConfigurationError(FlareException): """Raised when there's a configuration issue""" def __init__(self, message: str, config_key: Optional[str] = None): details = {"config_key": config_key} if config_key else {} super().__init__(message, details) class ValidationError(FlareException): """Raised when validation fails""" def __init__(self, message: str, field: Optional[str] = None, value: Any = None): details = {} if field: details["field"] = field if value is not None: details["value"] = str(value) super().__init__(message, details) class AuthenticationError(FlareException): """Raised when authentication fails""" def __init__(self, message: str = "Authentication failed"): super().__init__(message) class AuthorizationError(FlareException): """Raised when authorization fails""" def __init__(self, message: str = "Insufficient permissions", required_permission: Optional[str] = None): details = {"required_permission": required_permission} if required_permission else {} super().__init__(message, details) class SessionError(FlareException): """Raised when there's a session-related error""" def __init__(self, message: str, session_id: Optional[str] = None): details = {"session_id": session_id} if session_id else {} super().__init__(message, details) class ProviderError(FlareException): """Raised when a provider (LLM, TTS, STT) fails""" def __init__(self, message: str, provider_type: str, provider_name: str, original_error: Optional[str] = None): details = { "provider_type": provider_type, "provider_name": provider_name, "original_error": original_error } super().__init__(message, details) class APICallError(FlareException): """Raised when an external API call fails""" def __init__( self, message: str, api_name: str, status_code: Optional[int] = None, response_body: Optional[str] = None ): details = { "api_name": api_name, "status_code": status_code, "response_body": response_body } super().__init__(message, details) class WebSocketError(FlareException): """Raised when WebSocket operations fail""" def __init__(self, message: str, session_id: Optional[str] = None, state: Optional[str] = None): details = { "session_id": session_id, "state": state } super().__init__(message, details) class ResourceNotFoundError(FlareException): """Raised when a requested resource is not found""" def __init__(self, resource_type: str, resource_id: Any): message = f"{resource_type} not found: {resource_id}" details = { "resource_type": resource_type, "resource_id": str(resource_id) } super().__init__(message, details) class DuplicateResourceError(FlareException): """Raised when attempting to create a duplicate resource""" def __init__(self, resource_type: str, identifier: str): message = f"{resource_type} already exists: {identifier}" details = { "resource_type": resource_type, "identifier": identifier } super().__init__(message, details) # Error response formatters def format_error_response(error: Exception, request_id: Optional[str] = None) -> Dict[str, Any]: """Format any exception into a standardized error response""" if isinstance(error, FlareException): response = error.to_dict() else: # Generic error response = { "error": error.__class__.__name__, "message": str(error), "details": {}, "timestamp": datetime.utcnow().isoformat() } if request_id: response["request_id"] = request_id return response def get_http_status_code(error: Exception) -> int: """Get appropriate HTTP status code for an exception""" status_map = { ValidationError: 422, AuthenticationError: 401, AuthorizationError: 403, ResourceNotFoundError: 404, DuplicateResourceError: 409, RaceConditionError: 409, ConfigurationError: 500, ProviderError: 503, APICallError: 502, WebSocketError: 500, SessionError: 400 } for error_class, status_code in status_map.items(): if isinstance(error, error_class): return status_code # Default to 500 for unknown errors return 500