Download apps/core/exceptions/error_presentation.py from jun2024/fc: direct link, hf CLI and curl.
- Browser
- Download file 5.94 kB
-
https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/core/exceptions/error_presentation.py
- Command line
-
hf download hf://spaces/jun2024/fc/apps/core/exceptions/error_presentation.py
-
curl -L -o error_presentation.py https://huggingface.co/spaces/jun2024/fc/resolve/main/apps/core/exceptions/error_presentation.py
5.94 kB
| """Module for error presentation.""" | |
| from __future__ import annotations | |
| import logging | |
| from dataclasses import dataclass | |
| from typing import Any | |
| logger = logging.getLogger(__name__) | |
| class ErrorEnvelope: | |
| code: str | |
| message: str | |
| errors: dict[str, Any] | |
| retryable: bool = False | |
| channel: str = "http" | |
| def to_payload(self, *, include_legacy_error: bool = True) -> dict[str, Any]: | |
| payload: dict[str, Any] = { | |
| "code": self.code, | |
| "message": self.message, | |
| "errors": self.errors or {}, | |
| "retryable": bool(self.retryable), | |
| } | |
| if self.channel: | |
| payload["channel"] = self.channel | |
| if include_legacy_error: | |
| payload["error"] = self.message | |
| return payload | |
| class ExceptionPresenter: | |
| def present( | |
| self, | |
| exc: Exception, | |
| *, | |
| channel: str, | |
| debug: bool = False, | |
| ) -> tuple[ErrorEnvelope, int | None]: | |
| from apps.core.exceptions import ( | |
| AuthenticationError, | |
| BusinessException, | |
| ExternalServiceError, | |
| RecognitionTimeoutError, | |
| ServiceUnavailableError, | |
| ) | |
| if isinstance(exc, BusinessException): | |
| status = self._status_for_business_exception(exc) | |
| retryable = self._retryable_for_business_exception(exc) | |
| return ( | |
| ErrorEnvelope( | |
| code=exc.code, | |
| message=str(exc.message), | |
| errors=exc.errors or {}, | |
| retryable=retryable, | |
| channel=channel, | |
| ), | |
| status, | |
| ) | |
| try: | |
| from apps.core.llm.exceptions import ( | |
| LLMAPIError, | |
| LLMAuthenticationError, | |
| LLMBackendUnavailableError, | |
| LLMTimeoutError, | |
| ) | |
| if isinstance(exc, LLMAuthenticationError): | |
| mapped_auth = AuthenticationError( | |
| message=exc.message, code=exc.code, errors=getattr(exc, "errors", None) | |
| ) | |
| return self.present(mapped_auth, channel=channel, debug=debug) | |
| if isinstance(exc, LLMBackendUnavailableError): | |
| mapped_unavail = ServiceUnavailableError( | |
| message=exc.message, code=exc.code, errors=getattr(exc, "errors", None) | |
| ) | |
| return self.present(mapped_unavail, channel=channel, debug=debug) | |
| if isinstance(exc, LLMTimeoutError): | |
| mapped_timeout = RecognitionTimeoutError( | |
| message=exc.message, code=exc.code, errors=getattr(exc, "errors", None) | |
| ) | |
| return self.present(mapped_timeout, channel=channel, debug=debug) | |
| if isinstance(exc, LLMAPIError): | |
| retryable = bool( | |
| getattr(exc, "status_code", None) in (429, 500, 502, 503, 504) | |
| or getattr(exc, "code", "") == "LLM_ALL_BACKENDS_UNAVAILABLE" | |
| ) | |
| mapped_ext = ExternalServiceError( | |
| message=exc.message, code=exc.code, errors=getattr(exc, "errors", None) | |
| ) | |
| envelope, status = self.present(mapped_ext, channel=channel, debug=debug) | |
| return ( | |
| ErrorEnvelope( | |
| code=envelope.code, | |
| message=envelope.message, | |
| errors=envelope.errors, | |
| retryable=retryable, | |
| channel=envelope.channel, | |
| ), | |
| status, | |
| ) | |
| except (ImportError, AttributeError): | |
| pass | |
| try: | |
| import openai | |
| if isinstance(exc, openai.AuthenticationError): | |
| mapped = AuthenticationError( | |
| message="大模型鉴权失败,请检查配置", code="LLM_AUTH_ERROR", errors={"detail": str(exc)} | |
| ) | |
| return self.present(mapped, channel=channel, debug=debug) | |
| except (ImportError, AttributeError): | |
| pass | |
| message = str(exc) if debug else "系统错误,请稍后重试" | |
| return ( | |
| ErrorEnvelope(code="INTERNAL_ERROR", message=message, errors={}, retryable=False, channel=channel), | |
| 500 if channel == "http" else None, | |
| ) | |
| def _status_for_business_exception(self, exc: Exception) -> int | None: | |
| from apps.core.exceptions import ( | |
| AuthenticationError, | |
| ConflictError, | |
| ExternalServiceError, | |
| NotFoundError, | |
| PermissionDenied, | |
| RateLimitError, | |
| RecognitionTimeoutError, | |
| ServiceUnavailableError, | |
| ValidationException, | |
| ) | |
| status = getattr(exc, "status", None) | |
| if isinstance(status, int) and 100 <= status <= 599: | |
| return status | |
| _STATUS_MAP: list[tuple[type, int]] = [ | |
| (ValidationException, 400), | |
| (AuthenticationError, 401), | |
| (PermissionDenied, 403), | |
| (NotFoundError, 404), | |
| (ConflictError, 409), | |
| (RateLimitError, 429), | |
| (ServiceUnavailableError, 503), | |
| (RecognitionTimeoutError, 504), | |
| (ExternalServiceError, 502), | |
| ] | |
| for exc_type, code in _STATUS_MAP: | |
| if isinstance(exc, exc_type): | |
| return code | |
| return 400 | |
| def _retryable_for_business_exception(self, exc: Exception) -> bool: | |
| from apps.core.exceptions import ( | |
| ExternalServiceError, | |
| RateLimitError, | |
| RecognitionTimeoutError, | |
| ServiceUnavailableError, | |
| ) | |
| return isinstance(exc, (RateLimitError, ServiceUnavailableError, RecognitionTimeoutError, ExternalServiceError)) | |