Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# Обработка ошибок
|
||||
|
||||
## Матрица ошибок по слоям
|
||||
|
||||
| Слой | Что делаем | Пример |
|
||||
|------|-----------|--------|
|
||||
| **API** | HTTPException с detail и status_code | `raise HTTPException(404, detail="Not found")` |
|
||||
| **Services** | Бизнес-исключения без HTTP-статусов | `raise IdeaNotFoundError(idea_id)` |
|
||||
| **Integrations** | try/except с fallback | `return AIResult(success=False, error=...)` |
|
||||
| **Data/DB** | Ошибки не всплывают выше | Ловим в сервисе |
|
||||
|
||||
---
|
||||
|
||||
## Иерархия исключений
|
||||
|
||||
```python
|
||||
# app/core/exceptions.py
|
||||
|
||||
class AppError(Exception):
|
||||
"""Базовое исключение приложения."""
|
||||
def __init__(self, message: str, details: dict | None = None):
|
||||
self.message = message
|
||||
self.details = details or {}
|
||||
|
||||
class NotFoundError(AppError):
|
||||
"""Ресурс не найден."""
|
||||
def __init__(self, resource: str, resource_id: str):
|
||||
super().__init__(f"{resource} not found: {resource_id}", {"resource": resource, "id": resource_id})
|
||||
|
||||
class ValidationError(AppError):
|
||||
"""Ошибка валидации."""
|
||||
def __init__(self, field: str, message: str):
|
||||
super().__init__(message, {"field": field})
|
||||
|
||||
class AuthError(AppError):
|
||||
"""Ошибка аутентификации."""
|
||||
def __init__(self, message: str = "Authentication failed"):
|
||||
super().__init__(message)
|
||||
|
||||
class ForbiddenError(AppError):
|
||||
"""Нет прав."""
|
||||
def __init__(self, message: str = "Access denied"):
|
||||
super().__init__(message)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fallback Pattern (для внешних вызовов)
|
||||
|
||||
```python
|
||||
# Паттерн для всех вызовов внешних API
|
||||
|
||||
async def call_with_fallback(provider: AIProvider, prompt: str) -> AIResult:
|
||||
max_retries = 2
|
||||
last_error = None
|
||||
|
||||
for attempt in range(max_retries + 1):
|
||||
try:
|
||||
result = await asyncio.wait_for(
|
||||
provider.analyze(prompt),
|
||||
timeout=10.0
|
||||
)
|
||||
if result.success:
|
||||
return result
|
||||
last_error = result
|
||||
except asyncio.TimeoutError:
|
||||
last_error = AIResult(success=False, error="Timeout")
|
||||
except Exception as e:
|
||||
last_error = AIResult(success=False, error=str(e))
|
||||
|
||||
if attempt < max_retries:
|
||||
await asyncio.sleep(2 if attempt == 0 else 5)
|
||||
|
||||
return last_error
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Логирование ошибок
|
||||
|
||||
| Уровень | Когда | Пример |
|
||||
|---------|-------|--------|
|
||||
| DEBUG | Входящие параметры | `Request params: id=123` |
|
||||
| INFO | Успешная операция | `User created: id=456` |
|
||||
| WARNING | Timeout, retry | `Yandex GPT timeout, retry 1/2` |
|
||||
| ERROR | Ошибка внешнего API | `GigaChat 500: Internal error` |
|
||||
| CRITICAL | Исчерпаны все retry | `All AI providers failed for idea 789` |
|
||||
|
||||
---
|
||||
|
||||
## Graceful Degradation
|
||||
|
||||
Когда внешний сервис недоступен:
|
||||
|
||||
1. **DB недоступна** → 503 Service Unavailable
|
||||
2. **Redis недоступен** → работаем без кэша (log WARNING)
|
||||
3. **AI провайдер недоступен** → возвращаем fallback результат
|
||||
4. **Celery недоступен** → выполняем задачу синхронно
|
||||
|
||||
---
|
||||
|
||||
## [ASK] Вопросы по обработке ошибок
|
||||
|
||||
- Нужны ли пользовательские исключения для всех бизнес-сценариев?
|
||||
- Нужен ли sentry или аналогичный мониторинг ошибок?
|
||||
- Как обрабатывать ошибки валидации на фронтенде?
|
||||
Reference in New Issue
Block a user