caarleexx commited on
Commit
7e8cfe3
·
verified ·
1 Parent(s): 775808d

Upload 2 files

Browse files
Files changed (1) hide show
  1. api/exceptions.py +277 -0
api/exceptions.py ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Exceções customizadas para para.AI API v3.0
3
+ Define hierarquia de exceções para tratamento de erros
4
+ """
5
+ from typing import Optional, Dict, Any
6
+
7
+
8
+ class ParaAIException(Exception):
9
+ """
10
+ Exceção base para todas as exceções customizadas do para.AI.
11
+
12
+ Todas as outras exceções devem herdar desta classe.
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ message: str,
18
+ details: Optional[Dict[str, Any]] = None,
19
+ status_code: int = 500
20
+ ):
21
+ """
22
+ Args:
23
+ message: Mensagem de erro principal
24
+ details: Dicionário com detalhes adicionais do erro
25
+ status_code: Código HTTP sugerido para este erro
26
+ """
27
+ self.message = message
28
+ self.details = details or {}
29
+ self.status_code = status_code
30
+ super().__init__(self.message)
31
+
32
+ def to_dict(self) -> Dict[str, Any]:
33
+ """Converte exceção para dicionário (útil para JSON responses)."""
34
+ return {
35
+ "error": self.__class__.__name__,
36
+ "message": self.message,
37
+ "details": self.details,
38
+ "status_code": self.status_code
39
+ }
40
+
41
+ def __str__(self) -> str:
42
+ """String representation."""
43
+ if self.details:
44
+ return f"{self.message} | Details: {self.details}"
45
+ return self.message
46
+
47
+
48
+ # ============================================================================
49
+ # EXCEÇÕES DE DATABASE
50
+ # ============================================================================
51
+
52
+ class DatabaseException(ParaAIException):
53
+ """Exceção genérica de banco de dados."""
54
+
55
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
56
+ super().__init__(message, details, status_code=500)
57
+
58
+
59
+ class DatabaseConnectionError(DatabaseException):
60
+ """Erro de conexão com banco de dados."""
61
+
62
+ def __init__(self, message: str = "Falha ao conectar com banco de dados", details: Optional[Dict[str, Any]] = None):
63
+ super().__init__(message, details)
64
+
65
+
66
+ class RecordNotFoundError(DatabaseException):
67
+ """Registro não encontrado no banco."""
68
+
69
+ def __init__(self, message: str = "Registro não encontrado", details: Optional[Dict[str, Any]] = None):
70
+ super().__init__(message, details)
71
+ self.status_code = 404
72
+
73
+
74
+ class DuplicateRecordError(DatabaseException):
75
+ """Tentativa de inserir registro duplicado."""
76
+
77
+ def __init__(self, message: str = "Registro duplicado", details: Optional[Dict[str, Any]] = None):
78
+ super().__init__(message, details)
79
+ self.status_code = 409
80
+
81
+
82
+ # ============================================================================
83
+ # EXCEÇÕES DE LLM
84
+ # ============================================================================
85
+
86
+ class LLMException(ParaAIException):
87
+ """Exceção genérica de LLM."""
88
+
89
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
90
+ super().__init__(message, details, status_code=500)
91
+
92
+
93
+ class LLMProviderNotAvailable(LLMException):
94
+ """Provedor LLM não disponível."""
95
+
96
+ def __init__(self, provider: str, details: Optional[Dict[str, Any]] = None):
97
+ message = f"Provedor LLM '{provider}' não disponível ou não configurado"
98
+ super().__init__(message, details)
99
+ self.status_code = 503
100
+
101
+
102
+ class LLMAPIError(LLMException):
103
+ """Erro na chamada da API do LLM."""
104
+
105
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
106
+ super().__init__(f"Erro na API do LLM: {message}", details)
107
+
108
+
109
+ class LLMTimeoutError(LLMException):
110
+ """Timeout na chamada do LLM."""
111
+
112
+ def __init__(self, timeout: int, details: Optional[Dict[str, Any]] = None):
113
+ message = f"Timeout de {timeout}s excedido na chamada do LLM"
114
+ super().__init__(message, details)
115
+ self.status_code = 504
116
+
117
+
118
+ class LLMResponseParseError(LLMException):
119
+ """Erro ao fazer parse da resposta do LLM."""
120
+
121
+ def __init__(self, message: str = "Falha ao processar resposta do LLM", details: Optional[Dict[str, Any]] = None):
122
+ super().__init__(message, details)
123
+
124
+
125
+ # ============================================================================
126
+ # EXCEÇÕES DE PROCESSAMENTO
127
+ # ============================================================================
128
+
129
+ class ProcessingException(ParaAIException):
130
+ """Exceção genérica de processamento."""
131
+
132
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
133
+ super().__init__(message, details, status_code=500)
134
+
135
+
136
+ class ProcessorNotFoundError(ProcessingException):
137
+ """Processador não encontrado."""
138
+
139
+ def __init__(self, processor_name: str, details: Optional[Dict[str, Any]] = None):
140
+ message = f"Processador '{processor_name}' não encontrado"
141
+ super().__init__(message, details)
142
+ self.status_code = 404
143
+
144
+
145
+ class ProcessingTimeoutError(ProcessingException):
146
+ """Timeout no processamento."""
147
+
148
+ def __init__(self, timeout: int, details: Optional[Dict[str, Any]] = None):
149
+ message = f"Timeout de {timeout}s excedido no processamento"
150
+ super().__init__(message, details)
151
+ self.status_code = 504
152
+
153
+
154
+ class ValidationError(ProcessingException):
155
+ """Erro de validação de dados."""
156
+
157
+ def __init__(self, message: str = "Dados inválidos", details: Optional[Dict[str, Any]] = None):
158
+ super().__init__(message, details)
159
+ self.status_code = 422
160
+
161
+
162
+ # ============================================================================
163
+ # EXCEÇÕES DE ARQUIVO
164
+ # ============================================================================
165
+
166
+ class FileException(ParaAIException):
167
+ """Exceção genérica de arquivo."""
168
+
169
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
170
+ super().__init__(message, details, status_code=500)
171
+
172
+
173
+ class FileNotFoundError(FileException):
174
+ """Arquivo não encontrado."""
175
+
176
+ def __init__(self, filepath: str, details: Optional[Dict[str, Any]] = None):
177
+ message = f"Arquivo não encontrado: {filepath}"
178
+ super().__init__(message, details)
179
+ self.status_code = 404
180
+
181
+
182
+ class FileUploadError(FileException):
183
+ """Erro no upload de arquivo."""
184
+
185
+ def __init__(self, message: str = "Erro ao fazer upload do arquivo", details: Optional[Dict[str, Any]] = None):
186
+ super().__init__(message, details)
187
+ self.status_code = 400
188
+
189
+
190
+ class FileFormatError(FileException):
191
+ """Formato de arquivo inválido."""
192
+
193
+ def __init__(self, message: str = "Formato de arquivo inválido", details: Optional[Dict[str, Any]] = None):
194
+ super().__init__(message, details)
195
+ self.status_code = 415
196
+
197
+
198
+ class FileSizeExceededError(FileException):
199
+ """Tamanho de arquivo excedido."""
200
+
201
+ def __init__(self, size: int, max_size: int, details: Optional[Dict[str, Any]] = None):
202
+ message = f"Arquivo muito grande: {size} bytes (máximo: {max_size} bytes)"
203
+ super().__init__(message, details)
204
+ self.status_code = 413
205
+
206
+
207
+ # ============================================================================
208
+ # EXCEÇÕES DE CONFIGURAÇÃO
209
+ # ============================================================================
210
+
211
+ class ConfigurationError(ParaAIException):
212
+ """Erro de configuração."""
213
+
214
+ def __init__(self, message: str, details: Optional[Dict[str, Any]] = None):
215
+ super().__init__(f"Erro de configuração: {message}", details)
216
+ self.status_code = 500
217
+
218
+
219
+ class MissingConfigurationError(ConfigurationError):
220
+ """Configuração obrigatória ausente."""
221
+
222
+ def __init__(self, config_name: str, details: Optional[Dict[str, Any]] = None):
223
+ message = f"Configuração obrigatória ausente: {config_name}"
224
+ super().__init__(message, details)
225
+
226
+
227
+ # ============================================================================
228
+ # EXCEÇÕES DE AUTENTICAÇÃO/AUTORIZAÇÃO
229
+ # ============================================================================
230
+
231
+ class AuthenticationError(ParaAIException):
232
+ """Erro de autenticação."""
233
+
234
+ def __init__(self, message: str = "Falha na autenticação", details: Optional[Dict[str, Any]] = None):
235
+ super().__init__(message, details)
236
+ self.status_code = 401
237
+
238
+
239
+ class AuthorizationError(ParaAIException):
240
+ """Erro de autorização."""
241
+
242
+ def __init__(self, message: str = "Acesso negado", details: Optional[Dict[str, Any]] = None):
243
+ super().__init__(message, details)
244
+ self.status_code = 403
245
+
246
+
247
+ class InvalidAPIKeyError(AuthenticationError):
248
+ """API Key inválida."""
249
+
250
+ def __init__(self, details: Optional[Dict[str, Any]] = None):
251
+ super().__init__("API Key inválida ou expirada", details)
252
+
253
+
254
+ # ============================================================================
255
+ # FUNÇÃO AUXILIAR
256
+ # ============================================================================
257
+
258
+ def handle_exception(exc: Exception) -> Dict[str, Any]:
259
+ """
260
+ Converte qualquer exceção para dicionário padronizado.
261
+
262
+ Args:
263
+ exc: Exceção a ser convertida
264
+
265
+ Returns:
266
+ Dicionário com informações do erro
267
+ """
268
+ if isinstance(exc, ParaAIException):
269
+ return exc.to_dict()
270
+
271
+ # Exceção genérica
272
+ return {
273
+ "error": exc.__class__.__name__,
274
+ "message": str(exc),
275
+ "details": {},
276
+ "status_code": 500
277
+ }