""" Exceções customizadas para para.AI API v3.0 Define hierarquia de exceções para tratamento de erros """ from typing import Optional, Dict, Any class ParaAIException(Exception): """ Exceção base para todas as exceções customizadas do para.AI. Todas as outras exceções devem herdar desta classe. """ def __init__( self, message: str, details: Optional[Dict[str, Any]] = None, status_code: int = 500 ): """ Args: message: Mensagem de erro principal details: Dicionário com detalhes adicionais do erro status_code: Código HTTP sugerido para este erro """ self.message = message self.details = details or {} self.status_code = status_code super().__init__(self.message) def to_dict(self) -> Dict[str, Any]: """Converte exceção para dicionário (útil para JSON responses).""" return { "error": self.__class__.__name__, "message": self.message, "details": self.details, "status_code": self.status_code } def __str__(self) -> str: """String representation.""" if self.details: return f"{self.message} | Details: {self.details}" return self.message # ============================================================================ # EXCEÇÕES DE DATABASE # ============================================================================ class DatabaseException(ParaAIException): """Exceção genérica de banco de dados.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, details, status_code=500) class DatabaseConnectionError(DatabaseException): """Erro de conexão com banco de dados.""" def __init__(self, message: str = "Falha ao conectar com banco de dados", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) class RecordNotFoundError(DatabaseException): """Registro não encontrado no banco.""" def __init__(self, message: str = "Registro não encontrado", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 404 class DuplicateRecordError(DatabaseException): """Tentativa de inserir registro duplicado.""" def __init__(self, message: str = "Registro duplicado", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 409 # ============================================================================ # EXCEÇÕES DE LLM # ============================================================================ class LLMException(ParaAIException): """Exceção genérica de LLM.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, details, status_code=500) class LLMProviderNotAvailable(LLMException): """Provedor LLM não disponível.""" def __init__(self, provider: str, details: Optional[Dict[str, Any]] = None): message = f"Provedor LLM '{provider}' não disponível ou não configurado" super().__init__(message, details) self.status_code = 503 class LLMAPIError(LLMException): """Erro na chamada da API do LLM.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(f"Erro na API do LLM: {message}", details) class LLMTimeoutError(LLMException): """Timeout na chamada do LLM.""" def __init__(self, timeout: int, details: Optional[Dict[str, Any]] = None): message = f"Timeout de {timeout}s excedido na chamada do LLM" super().__init__(message, details) self.status_code = 504 class LLMResponseParseError(LLMException): """Erro ao fazer parse da resposta do LLM.""" def __init__(self, message: str = "Falha ao processar resposta do LLM", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) # ============================================================================ # EXCEÇÕES DE PROCESSAMENTO # ============================================================================ class ProcessingException(ParaAIException): """Exceção genérica de processamento.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, details, status_code=500) class ProcessorNotFoundError(ProcessingException): """Processador não encontrado.""" def __init__(self, processor_name: str, details: Optional[Dict[str, Any]] = None): message = f"Processador '{processor_name}' não encontrado" super().__init__(message, details) self.status_code = 404 class ProcessingTimeoutError(ProcessingException): """Timeout no processamento.""" def __init__(self, timeout: int, details: Optional[Dict[str, Any]] = None): message = f"Timeout de {timeout}s excedido no processamento" super().__init__(message, details) self.status_code = 504 class ValidationError(ProcessingException): """Erro de validação de dados.""" def __init__(self, message: str = "Dados inválidos", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 422 # ============================================================================ # EXCEÇÕES DE ARQUIVO # ============================================================================ class FileException(ParaAIException): """Exceção genérica de arquivo.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(message, details, status_code=500) class FileNotFoundError(FileException): """Arquivo não encontrado.""" def __init__(self, filepath: str, details: Optional[Dict[str, Any]] = None): message = f"Arquivo não encontrado: {filepath}" super().__init__(message, details) self.status_code = 404 class FileUploadError(FileException): """Erro no upload de arquivo.""" def __init__(self, message: str = "Erro ao fazer upload do arquivo", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 400 class FileFormatError(FileException): """Formato de arquivo inválido.""" def __init__(self, message: str = "Formato de arquivo inválido", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 415 class FileSizeExceededError(FileException): """Tamanho de arquivo excedido.""" def __init__(self, size: int, max_size: int, details: Optional[Dict[str, Any]] = None): message = f"Arquivo muito grande: {size} bytes (máximo: {max_size} bytes)" super().__init__(message, details) self.status_code = 413 # ============================================================================ # EXCEÇÕES DE CONFIGURAÇÃO # ============================================================================ class ConfigurationError(ParaAIException): """Erro de configuração.""" def __init__(self, message: str, details: Optional[Dict[str, Any]] = None): super().__init__(f"Erro de configuração: {message}", details) self.status_code = 500 class MissingConfigurationError(ConfigurationError): """Configuração obrigatória ausente.""" def __init__(self, config_name: str, details: Optional[Dict[str, Any]] = None): message = f"Configuração obrigatória ausente: {config_name}" super().__init__(message, details) # ============================================================================ # EXCEÇÕES DE AUTENTICAÇÃO/AUTORIZAÇÃO # ============================================================================ class AuthenticationError(ParaAIException): """Erro de autenticação.""" def __init__(self, message: str = "Falha na autenticação", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 401 class AuthorizationError(ParaAIException): """Erro de autorização.""" def __init__(self, message: str = "Acesso negado", details: Optional[Dict[str, Any]] = None): super().__init__(message, details) self.status_code = 403 class InvalidAPIKeyError(AuthenticationError): """API Key inválida.""" def __init__(self, details: Optional[Dict[str, Any]] = None): super().__init__("API Key inválida ou expirada", details) # ============================================================================ # FUNÇÃO AUXILIAR # ============================================================================ def handle_exception(exc: Exception) -> Dict[str, Any]: """ Converte qualquer exceção para dicionário padronizado. Args: exc: Exceção a ser convertida Returns: Dicionário com informações do erro """ if isinstance(exc, ParaAIException): return exc.to_dict() # Exceção genérica return { "error": exc.__class__.__name__, "message": str(exc), "details": {}, "status_code": 500 }