Team Ai
Apppublic

Yash030/claude-code-proxy

sourceHugging Faceupdated 5mo agoView on Hugging Face
2likes
error_mapping.py118 linesDownload Raw Back to providers
1"""Provider-specific exception mapping."""2 3import httpx4import openai5 6from core.anthropic import get_user_facing_error_message7from providers.exceptions import (8    APIError,9    AuthenticationError,10    InvalidRequestError,11    OverloadedError,12    RateLimitError,13)14from providers.rate_limit import GlobalRateLimiter15 16 17def user_visible_message_for_mapped_provider_error(18    mapped: Exception,19    *,20    provider_name: str,21    read_timeout_s: float | None,22) -> str:23    """Return the user-visible string after :func:`map_error` (405 + mapped types)."""24    if getattr(mapped, "status_code", None) == 405:25        return (26            f"Upstream provider {provider_name} rejected the request method "27            "or endpoint (HTTP 405)."28        )29    return get_user_facing_error_message(mapped, read_timeout_s=read_timeout_s)30 31 32def map_error(33    e: Exception, *, rate_limiter: GlobalRateLimiter | None = None34) -> Exception:35    """Map OpenAI or HTTPX exception to specific ProviderError.36 37    Streaming transports should pass their scoped limiter (``self._global_rate_limiter``)38    so reactive 429 handling applies to the correct provider. Tests may omit39    ``rate_limiter`` to use the process-wide singleton.40    """41    from loguru import logger42 43    message = get_user_facing_error_message(e)44    logger.info(45        "map_error: original_exc_type={} message={}", type(e).__name__, message[:100]46    )47    limiter = rate_limiter or GlobalRateLimiter.get_instance()48 49    if isinstance(e, openai.AuthenticationError):50        logger.info("map_error: mapped to AuthenticationError")51        return AuthenticationError(message, raw_error=str(e))52    if isinstance(e, openai.RateLimitError):53        limiter.set_blocked(60)54        logger.info("map_error: mapped to RateLimitError")55        return RateLimitError(message, raw_error=str(e))56    if isinstance(e, openai.BadRequestError):57        # Check if it's actually a rate limit in disguise58        raw = str(e).lower()59        if "too_many_requests" in raw or "rate_limit" in raw or "quota" in raw:60            limiter.set_blocked(60)61            logger.info("map_error: BadRequestError actually rate limit")62            return RateLimitError(message, raw_error=str(e))63        logger.info("map_error: mapped to InvalidRequestError")64        return InvalidRequestError(message, raw_error=str(e))65    if isinstance(e, openai.InternalServerError):66        raw_message = str(e)67        if "overloaded" in raw_message.lower() or "capacity" in raw_message.lower():68            logger.info("map_error: mapped to OverloadedError")69            return OverloadedError(message, raw_error=raw_message)70        logger.info("map_error: mapped to APIError (InternalServerError)")71        return APIError(message, status_code=500, raw_error=str(e))72    if isinstance(e, openai.APIError):73        logger.info("map_error: mapped to APIError (openai.APIError)")74        return APIError(75            message, status_code=getattr(e, "status_code", 500), raw_error=str(e)76        )77 78    if isinstance(e, httpx.HTTPStatusError):79        status = e.response.status_code80        logger.info("map_error: httpx.HTTPStatusError status={}", status)81        if status in (401, 403):82            logger.info("map_error: mapped to AuthenticationError (httpx)")83            return AuthenticationError(message, raw_error=str(e))84        if status == 429:85            limiter.set_blocked(60)86            logger.info("map_error: mapped to RateLimitError (httpx)")87            return RateLimitError(message, raw_error=str(e))88        if status == 413:89            # "Request too large" - often actually a rate limit or quota issue90            limiter.set_blocked(60)91            logger.info("map_error: mapped to RateLimitError (413)")92            return RateLimitError(message, raw_error=str(e))93        if status == 400:94            logger.info("map_error: mapped to InvalidRequestError (httpx)")95            return InvalidRequestError(message, raw_error=str(e))96        # Check response body for rate limit indicators97        try:98            body = e.response.json()99            if body and isinstance(body, dict):100                err_type = body.get("type", "")101                if "too_many_requests" in err_type or "rate_limit" in err_type.lower():102                    limiter.set_blocked(60)103                    logger.info("map_error: detected rate limit from response body")104                    return RateLimitError(message, raw_error=str(e))105        except Exception:106            pass107        if status >= 500:108            if status in (502, 503, 504):109                logger.info("map_error: mapped to OverloadedError (httpx)")110                return OverloadedError(message, raw_error=str(e))111            logger.info("map_error: mapped to APIError (httpx 5xx)")112            return APIError(message, status_code=status, raw_error=str(e))113        logger.info("map_error: mapped to APIError (httpx)")114        return APIError(message, status_code=status, raw_error=str(e))115 116    logger.info("map_error: falling through, returning original exception")117    return e118