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