102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
"""Typované výjimky + centrální exception handlery.
|
|
|
|
Platí pravidlo: žádná tichá selhání — každá chyba se loguje (bez secrets).
|
|
"""
|
|
from fastapi import FastAPI, Request
|
|
from fastapi.responses import JSONResponse
|
|
|
|
from .logging_config import get_logger
|
|
|
|
log = get_logger("audio-transcription.errors")
|
|
|
|
|
|
class TranscriptionError(Exception):
|
|
"""Základní chyba služby s HTTP status kódem."""
|
|
|
|
status_code = 500
|
|
|
|
def __init__(self, message: str, status_code: int | None = None, detail=None):
|
|
super().__init__(message)
|
|
self.message = message
|
|
if status_code is not None:
|
|
self.status_code = status_code
|
|
self.detail = detail
|
|
|
|
|
|
class CredentialsError(TranscriptionError):
|
|
"""Chybějící / neplatné přihlašovací údaje (X- hlavičky)."""
|
|
|
|
status_code = 401
|
|
|
|
|
|
class BadRequestError(TranscriptionError):
|
|
"""Neplatný vstup (chybí audio, špatné parametry)."""
|
|
|
|
status_code = 400
|
|
|
|
|
|
class RateLimitError(TranscriptionError):
|
|
"""Upstream rate limit (HTTP 429)."""
|
|
|
|
status_code = 429
|
|
|
|
|
|
class UpstreamError(TranscriptionError):
|
|
"""Chyba při volání upstream API (Deepgram / OpenAI)."""
|
|
|
|
status_code = 502
|
|
|
|
|
|
def raise_for_upstream(upstream: str, status: int, body: str) -> None:
|
|
"""Zmapuje HTTP status z upstreamu (Deepgram/OpenAI) na správnou chybu služby.
|
|
|
|
- 400/415/422 → 400 (špatný / nepodporovaný audio soubor = chyba klienta)
|
|
- 401/403 → 401 (upstream odmítl API klíč volajícího)
|
|
- 429 → 429 (rate limit)
|
|
- jinak → 502 (výpadek / neočekávaná chyba upstreamu)
|
|
"""
|
|
snippet = (body or "")[:500]
|
|
if status in (400, 415, 422):
|
|
raise BadRequestError(
|
|
f"{upstream} odmítl audio (HTTP {status}) — pravděpodobně nepodporovaný "
|
|
f"formát nebo poškozený soubor.",
|
|
detail=snippet,
|
|
)
|
|
if status in (401, 403):
|
|
raise CredentialsError(
|
|
f"{upstream} odmítl API klíč (HTTP {status}).",
|
|
detail=snippet,
|
|
)
|
|
if status == 429:
|
|
raise RateLimitError(f"{upstream} rate limit (HTTP 429).", detail=snippet)
|
|
raise UpstreamError(f"{upstream} vrátil chybu {status}.", detail=snippet)
|
|
|
|
|
|
def register_exception_handlers(app: FastAPI) -> None:
|
|
@app.exception_handler(TranscriptionError)
|
|
async def _handle_transcription_error(request: Request, exc: TranscriptionError):
|
|
log.warning(
|
|
"%s on %s: %s",
|
|
exc.__class__.__name__,
|
|
request.url.path,
|
|
exc.message,
|
|
)
|
|
body = {"error": exc.__class__.__name__, "message": exc.message}
|
|
if exc.detail is not None:
|
|
body["detail"] = exc.detail
|
|
return JSONResponse(status_code=exc.status_code, content=body)
|
|
|
|
@app.exception_handler(Exception)
|
|
async def _handle_unexpected(request: Request, exc: Exception):
|
|
# Nelogujeme celý stack s možnými secrets ve vstupu; logujeme typ + zprávu.
|
|
log.error(
|
|
"Unhandled %s on %s: %s",
|
|
exc.__class__.__name__,
|
|
request.url.path,
|
|
exc,
|
|
)
|
|
return JSONResponse(
|
|
status_code=500,
|
|
content={"error": "InternalError", "message": "Neočekávaná chyba serveru."},
|
|
)
|