65 lines
1.7 KiB
Python
65 lines
1.7 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import logging
|
||
|
|
|
||
|
|
from yuxi.channel.protocols import ClassifiedError, ErrorSeverity
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
def classify_error(error: BaseException) -> ClassifiedError:
|
||
|
|
msg = str(error).upper()
|
||
|
|
|
||
|
|
if "M_LIMIT_EXCEEDED" in msg or "429" in str(error):
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.RATE_LIMITED,
|
||
|
|
retry_after_ms=_extract_retry_after(error),
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
if any(kw in msg for kw in ("M_UNKNOWN_TOKEN", "M_MISSING_TOKEN", "M_NOT_JSON")):
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.FORBIDDEN,
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
if "M_FORBIDDEN" in msg:
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.FORBIDDEN,
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
if "M_NOT_FOUND" in msg:
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.FATAL,
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
if isinstance(error, (ConnectionError, TimeoutError, OSError)):
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.NETWORK,
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
return ClassifiedError(
|
||
|
|
severity=ErrorSeverity.FATAL,
|
||
|
|
original_error=error,
|
||
|
|
error_message=str(error),
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _extract_retry_after(error) -> int:
|
||
|
|
try:
|
||
|
|
import re
|
||
|
|
|
||
|
|
m = re.search(r"retry_after_ms[:\s]+(\d+)", str(error), re.IGNORECASE)
|
||
|
|
if m:
|
||
|
|
return int(m.group(1))
|
||
|
|
except (ValueError, TypeError):
|
||
|
|
pass
|
||
|
|
return 5000
|