Spaces:
Sleeping
Sleeping
| """Error mapping for OpenAI-compatible providers (NIM, OpenRouter, LM Studio).""" | |
| import httpx | |
| import openai | |
| from providers.exceptions import ( | |
| APIError, | |
| AuthenticationError, | |
| InvalidRequestError, | |
| OverloadedError, | |
| ProviderError, | |
| RateLimitError, | |
| ) | |
| from providers.rate_limit import GlobalRateLimiter | |
| def get_user_facing_error_message( | |
| e: Exception, | |
| *, | |
| read_timeout_s: float | None = None, | |
| ) -> str: | |
| """Return a readable, non-empty error message for users.""" | |
| message = str(e).strip() | |
| if message: | |
| return message | |
| if isinstance(e, httpx.ReadTimeout): | |
| if read_timeout_s is not None: | |
| return f"Provider request timed out after {read_timeout_s:g}s." | |
| return "Provider request timed out." | |
| if isinstance(e, httpx.ConnectTimeout): | |
| return "Could not connect to provider." | |
| if isinstance(e, TimeoutError): | |
| if read_timeout_s is not None: | |
| return f"Provider request timed out after {read_timeout_s:g}s." | |
| return "Request timed out." | |
| if isinstance(e, (RateLimitError, openai.RateLimitError)): | |
| return "Provider rate limit reached. Please retry shortly." | |
| if isinstance(e, (AuthenticationError, openai.AuthenticationError)): | |
| return "Provider authentication failed. Check API key." | |
| if isinstance(e, (InvalidRequestError, openai.BadRequestError)): | |
| return "Invalid request sent to provider." | |
| if isinstance(e, OverloadedError): | |
| return "Provider is currently overloaded. Please retry." | |
| if isinstance(e, APIError): | |
| if e.status_code in (502, 503, 504): | |
| return "Provider is temporarily unavailable. Please retry." | |
| return "Provider API request failed." | |
| if isinstance(e, ProviderError): | |
| return "Provider request failed." | |
| return "Provider request failed unexpectedly." | |
| def append_request_id(message: str, request_id: str | None) -> str: | |
| """Append request_id suffix when available.""" | |
| base = message.strip() or "Provider request failed unexpectedly." | |
| if request_id: | |
| return f"{base} (request_id={request_id})" | |
| return base | |
| def map_error(e: Exception) -> Exception: | |
| """Map OpenAI or HTTPX exception to specific ProviderError.""" | |
| message = get_user_facing_error_message(e) | |
| # Map OpenAI Specific Errors | |
| if isinstance(e, openai.AuthenticationError): | |
| return AuthenticationError(message, raw_error=str(e)) | |
| if isinstance(e, openai.RateLimitError): | |
| # Trigger global rate limit block | |
| GlobalRateLimiter.get_instance().set_blocked(60) # Default 60s cooldown | |
| return RateLimitError(message, raw_error=str(e)) | |
| if isinstance(e, openai.BadRequestError): | |
| 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(): | |
| return OverloadedError(message, raw_error=raw_message) | |
| return APIError(message, status_code=500, raw_error=str(e)) | |
| if isinstance(e, openai.APIError): | |
| return APIError( | |
| message, status_code=getattr(e, "status_code", 500), raw_error=str(e) | |
| ) | |
| # Map raw HTTPX Errors | |
| if isinstance(e, httpx.HTTPStatusError): | |
| status = e.response.status_code | |
| if status in (401, 403): | |
| return AuthenticationError(message, raw_error=str(e)) | |
| if status == 429: | |
| GlobalRateLimiter.get_instance().set_blocked(60) | |
| return RateLimitError(message, raw_error=str(e)) | |
| if status == 400: | |
| return InvalidRequestError(message, raw_error=str(e)) | |
| if status >= 500: | |
| if status in (502, 503, 504): | |
| return OverloadedError(message, raw_error=str(e)) | |
| return APIError(message, status_code=status, raw_error=str(e)) | |
| return APIError(message, status_code=status, raw_error=str(e)) | |
| return e | |