PARA.AI / api /utils /exceptions.py
Carlex22's picture
Revert "ParaAIV3.1"
1f24745
"""
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
}