Spaces:
Sleeping
Sleeping
| """ | |
| Error classification utility for LLM provider errors. | |
| Maps raw exceptions from AI providers/Esperanto/LangChain to user-friendly | |
| error messages and appropriate exception types. | |
| """ | |
| from loguru import logger | |
| from open_notebook.exceptions import ( | |
| AuthenticationError, | |
| ConfigurationError, | |
| ExternalServiceError, | |
| NetworkError, | |
| OpenNotebookError, | |
| RateLimitError, | |
| ) | |
| # Classification rules: (keywords, exception_class, user_message or None to pass through) | |
| _CLASSIFICATION_RULES: list[tuple[list[str], type[OpenNotebookError], str | None]] = [ | |
| # Authentication errors | |
| ( | |
| ["authentication", "unauthorized", "invalid api key", "invalid_api_key", "401"], | |
| AuthenticationError, | |
| "Authentication failed. Please check your API key in Settings -> Credentials.", | |
| ), | |
| # Rate limit errors | |
| ( | |
| ["rate limit", "rate_limit", "429", "too many requests", "quota exceeded"], | |
| RateLimitError, | |
| "Rate limit exceeded. Please wait a moment and try again.", | |
| ), | |
| # Model not found (pass through original message) | |
| ( | |
| ["model not found", "does not exist", "model_not_found"], | |
| ConfigurationError, | |
| None, | |
| ), | |
| # Configuration errors from provision.py (pass through) | |
| ( | |
| ["no model configured", "please go to settings"], | |
| ConfigurationError, | |
| None, | |
| ), | |
| # Network errors | |
| ( | |
| ["connecterror", "timeoutexception", "connection refused", "connection error", "timed out", "timeout"], | |
| NetworkError, | |
| "Could not connect to the AI provider. Please check your network connection and provider URL.", | |
| ), | |
| # Context length errors | |
| ( | |
| ["context length", "token limit", "maximum context", "context_length_exceeded", "max_tokens"], | |
| ExternalServiceError, | |
| "Content too large for the selected model. Try using a smaller selection or a model with a larger context window.", | |
| ), | |
| # Payload too large errors | |
| ( | |
| ["413", "payload too large", "request entity too large"], | |
| ExternalServiceError, | |
| "The request payload is too large for the AI provider. Try reducing the content size or using a different model.", | |
| ), | |
| # Provider availability errors | |
| ( | |
| ["500", "502", "503", "service unavailable", "overloaded", "internal server error"], | |
| ExternalServiceError, | |
| "The AI provider is temporarily unavailable. Please try again in a few minutes.", | |
| ), | |
| ] | |
| def classify_error(exception: BaseException) -> tuple[type[OpenNotebookError], str]: | |
| """ | |
| Classify a raw exception into a user-friendly error type and message. | |
| Args: | |
| exception: Any exception from LLM providers/Esperanto/LangChain | |
| Returns: | |
| Tuple of (exception_class, user_friendly_message) | |
| """ | |
| error_str = str(exception).lower() | |
| error_type_name = type(exception).__name__.lower() | |
| combined = f"{error_type_name}: {error_str}" | |
| for keywords, exc_class, message in _CLASSIFICATION_RULES: | |
| for keyword in keywords: | |
| if keyword in combined: | |
| user_message = message if message is not None else _truncate(str(exception)) | |
| return exc_class, user_message | |
| # Unclassified error - log for future improvement | |
| logger.warning( | |
| f"Unclassified LLM error ({type(exception).__name__}): {exception}" | |
| ) | |
| return ExternalServiceError, f"AI service error: {_truncate(str(exception))}" | |
| def _truncate(text: str, max_length: int = 200) -> str: | |
| """Truncate text to max_length to avoid leaking verbose internal details.""" | |
| if len(text) <= max_length: | |
| return text | |
| return text[:max_length] + "..." | |