Spaces:
Running
Running
| """ | |
| Error Handler Module - Professional error handling and logging | |
| Provides comprehensive error handling, logging, and user-friendly error messages | |
| """ | |
| import logging | |
| import traceback | |
| from typing import Optional, Dict, Any, Callable | |
| from enum import Enum | |
| from datetime import datetime | |
| import os | |
| class ErrorSeverity(Enum): | |
| """Error severity levels""" | |
| LOW = "low" # Minor issues, can continue | |
| MEDIUM = "medium" # Significant issues, degraded service | |
| HIGH = "high" # Critical issues, major features broken | |
| CRITICAL = "critical" # System failure, cannot continue | |
| class ErrorCategory(Enum): | |
| """Error categories for better organization""" | |
| API = "api" # API-related errors (Gemini, MCP) | |
| VALIDATION = "validation" # Input validation errors | |
| FILESYSTEM = "filesystem" # File I/O errors | |
| NETWORK = "network" # Network/connectivity errors | |
| PARSING = "parsing" # JSON/data parsing errors | |
| CONFIGURATION = "configuration" # Configuration/setup errors | |
| UNKNOWN = "unknown" # Unknown errors | |
| class MVPAgentError(Exception): | |
| """Base exception for MVP Agent""" | |
| def __init__( | |
| self, | |
| message: str, | |
| category: ErrorCategory = ErrorCategory.UNKNOWN, | |
| severity: ErrorSeverity = ErrorSeverity.MEDIUM, | |
| details: Optional[Dict[str, Any]] = None, | |
| user_message: Optional[str] = None | |
| ): | |
| super().__init__(message) | |
| self.message = message | |
| self.category = category | |
| self.severity = severity | |
| self.details = details or {} | |
| self.user_message = user_message or self._generate_user_message() | |
| self.timestamp = datetime.now() | |
| def _generate_user_message(self) -> str: | |
| """Generate user-friendly error message""" | |
| category_messages = { | |
| ErrorCategory.API: "We're having trouble connecting to our AI services. Please try again in a moment.", | |
| ErrorCategory.VALIDATION: "There's an issue with your input. Please check and try again.", | |
| ErrorCategory.FILESYSTEM: "We couldn't save your files. Please check disk space and permissions.", | |
| ErrorCategory.NETWORK: "Network connection issue. Please check your internet connection.", | |
| ErrorCategory.PARSING: "We received an unexpected response. Trying alternative approach...", | |
| ErrorCategory.CONFIGURATION: "Configuration issue detected. Please check your API keys and settings.", | |
| ErrorCategory.UNKNOWN: "An unexpected error occurred. We're working to resolve it." | |
| } | |
| return category_messages.get(self.category, self.message) | |
| def to_dict(self) -> Dict[str, Any]: | |
| """Convert error to dictionary for logging""" | |
| return { | |
| "message": self.message, | |
| "user_message": self.user_message, | |
| "category": self.category.value, | |
| "severity": self.severity.value, | |
| "details": self.details, | |
| "timestamp": self.timestamp.isoformat() | |
| } | |
| class ErrorLogger: | |
| """Centralized error logging""" | |
| def __init__(self, log_dir: str = "logs"): | |
| """Initialize error logger""" | |
| self.log_dir = log_dir | |
| self._ensure_log_dir() | |
| self._setup_logging() | |
| def _ensure_log_dir(self): | |
| """Ensure log directory exists""" | |
| if not os.path.exists(self.log_dir): | |
| os.makedirs(self.log_dir) | |
| def _setup_logging(self): | |
| """Set up logging configuration""" | |
| log_file = os.path.join( | |
| self.log_dir, | |
| f"mvp_agent_{datetime.now().strftime('%Y%m%d')}.log" | |
| ) | |
| logging.basicConfig( | |
| level=logging.INFO, | |
| format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', | |
| handlers=[ | |
| logging.FileHandler(log_file), | |
| logging.StreamHandler() # Also print to console | |
| ] | |
| ) | |
| self.logger = logging.getLogger('MVPAgent') | |
| def log_error(self, error: Exception, context: Optional[Dict[str, Any]] = None): | |
| """ | |
| Log an error with context | |
| Args: | |
| error: The exception that occurred | |
| context: Additional context information | |
| """ | |
| if isinstance(error, MVPAgentError): | |
| error_dict = error.to_dict() | |
| error_dict['context'] = context or {} | |
| self.logger.error(f"MVP Agent Error: {error_dict}") | |
| else: | |
| self.logger.error( | |
| f"Unexpected error: {str(error)}\n" | |
| f"Context: {context}\n" | |
| f"Traceback: {traceback.format_exc()}" | |
| ) | |
| def log_warning(self, message: str, context: Optional[Dict[str, Any]] = None): | |
| """Log a warning""" | |
| self.logger.warning(f"{message} | Context: {context}") | |
| def log_info(self, message: str): | |
| """Log informational message""" | |
| self.logger.info(message) | |
| class ErrorHandler: | |
| """Professional error handling with fallbacks and recovery""" | |
| def __init__(self, logger: Optional[ErrorLogger] = None): | |
| """Initialize error handler""" | |
| self.logger = logger or ErrorLogger() | |
| self.error_history = [] | |
| def handle_api_error( | |
| self, | |
| error: Exception, | |
| operation: str, | |
| fallback: Optional[Callable] = None | |
| ) -> Any: | |
| """ | |
| Handle API errors with fallback strategy | |
| Args: | |
| error: The API error | |
| operation: Description of the operation that failed | |
| fallback: Optional fallback function to try | |
| Returns: | |
| Result from fallback if successful, or raises | |
| """ | |
| agent_error = MVPAgentError( | |
| message=f"API error during {operation}: {str(error)}", | |
| category=ErrorCategory.API, | |
| severity=ErrorSeverity.HIGH, | |
| details={"operation": operation, "original_error": str(error)} | |
| ) | |
| self.logger.log_error(agent_error, {"operation": operation}) | |
| self.error_history.append(agent_error) | |
| if fallback: | |
| try: | |
| self.logger.log_info(f"Attempting fallback for {operation}") | |
| return fallback() | |
| except Exception as fallback_error: | |
| self.logger.log_error( | |
| fallback_error, | |
| {"operation": f"{operation}_fallback"} | |
| ) | |
| raise agent_error | |
| raise agent_error | |
| def handle_validation_error(self, error: Exception, field: str) -> MVPAgentError: | |
| """ | |
| Handle validation errors | |
| Args: | |
| error: The validation error | |
| field: The field that failed validation | |
| Returns: | |
| MVPAgentError instance | |
| """ | |
| agent_error = MVPAgentError( | |
| message=f"Validation error for {field}: {str(error)}", | |
| category=ErrorCategory.VALIDATION, | |
| severity=ErrorSeverity.LOW, | |
| details={"field": field, "original_error": str(error)}, | |
| user_message=f"Please check your {field} and try again." | |
| ) | |
| self.logger.log_error(agent_error, {"field": field}) | |
| self.error_history.append(agent_error) | |
| return agent_error | |
| def handle_filesystem_error( | |
| self, | |
| error: Exception, | |
| operation: str, | |
| path: str | |
| ) -> MVPAgentError: | |
| """ | |
| Handle filesystem errors | |
| Args: | |
| error: The filesystem error | |
| operation: Description of file operation | |
| path: File path involved | |
| Returns: | |
| MVPAgentError instance | |
| """ | |
| agent_error = MVPAgentError( | |
| message=f"Filesystem error during {operation}: {str(error)}", | |
| category=ErrorCategory.FILESYSTEM, | |
| severity=ErrorSeverity.MEDIUM, | |
| details={"operation": operation, "path": path, "original_error": str(error)}, | |
| user_message="We couldn't save your files. Please check disk space and try again." | |
| ) | |
| self.logger.log_error(agent_error, {"operation": operation, "path": path}) | |
| self.error_history.append(agent_error) | |
| return agent_error | |
| def handle_parsing_error( | |
| self, | |
| error: Exception, | |
| data_type: str, | |
| fallback: Optional[Callable] = None | |
| ) -> Any: | |
| """ | |
| Handle parsing errors (e.g., JSON parsing) | |
| Args: | |
| error: The parsing error | |
| data_type: Type of data being parsed | |
| fallback: Optional fallback parser | |
| Returns: | |
| Result from fallback or raises | |
| """ | |
| agent_error = MVPAgentError( | |
| message=f"Parsing error for {data_type}: {str(error)}", | |
| category=ErrorCategory.PARSING, | |
| severity=ErrorSeverity.MEDIUM, | |
| details={"data_type": data_type, "original_error": str(error)} | |
| ) | |
| self.logger.log_error(agent_error, {"data_type": data_type}) | |
| self.error_history.append(agent_error) | |
| if fallback: | |
| try: | |
| self.logger.log_info(f"Attempting fallback parser for {data_type}") | |
| return fallback() | |
| except Exception as fallback_error: | |
| self.logger.log_error( | |
| fallback_error, | |
| {"data_type": f"{data_type}_fallback"} | |
| ) | |
| raise agent_error | |
| raise agent_error | |
| def safe_execute( | |
| self, | |
| func: Callable, | |
| operation: str, | |
| fallback: Optional[Callable] = None, | |
| *args, | |
| **kwargs | |
| ) -> Any: | |
| """ | |
| Safely execute a function with error handling | |
| Args: | |
| func: Function to execute | |
| operation: Description of the operation | |
| fallback: Optional fallback function | |
| *args, **kwargs: Arguments for func | |
| Returns: | |
| Result from func or fallback | |
| """ | |
| try: | |
| return func(*args, **kwargs) | |
| except MVPAgentError: | |
| raise # Re-raise our own errors | |
| except Exception as e: | |
| self.logger.log_error(e, {"operation": operation}) | |
| if fallback: | |
| try: | |
| self.logger.log_warning( | |
| f"Using fallback for {operation}", | |
| {"original_error": str(e)} | |
| ) | |
| return fallback(*args, **kwargs) | |
| except Exception as fallback_error: | |
| self.logger.log_error( | |
| fallback_error, | |
| {"operation": f"{operation}_fallback"} | |
| ) | |
| raise MVPAgentError( | |
| message=f"Both primary and fallback failed for {operation}", | |
| category=ErrorCategory.UNKNOWN, | |
| severity=ErrorSeverity.HIGH, | |
| details={ | |
| "primary_error": str(e), | |
| "fallback_error": str(fallback_error) | |
| } | |
| ) | |
| raise MVPAgentError( | |
| message=f"Error during {operation}: {str(e)}", | |
| category=ErrorCategory.UNKNOWN, | |
| severity=ErrorSeverity.MEDIUM, | |
| details={"original_error": str(e)} | |
| ) | |
| def get_error_summary(self) -> Dict[str, Any]: | |
| """Get summary of errors encountered""" | |
| return { | |
| "total_errors": len(self.error_history), | |
| "by_category": self._count_by_category(), | |
| "by_severity": self._count_by_severity(), | |
| "recent_errors": [e.to_dict() for e in self.error_history[-5:]] | |
| } | |
| def _count_by_category(self) -> Dict[str, int]: | |
| """Count errors by category""" | |
| counts = {} | |
| for error in self.error_history: | |
| category = error.category.value | |
| counts[category] = counts.get(category, 0) + 1 | |
| return counts | |
| def _count_by_severity(self) -> Dict[str, int]: | |
| """Count errors by severity""" | |
| counts = {} | |
| for error in self.error_history: | |
| severity = error.severity.value | |
| counts[severity] = counts.get(severity, 0) + 1 | |
| return counts | |
| # Singleton instances | |
| _error_logger = None | |
| _error_handler = None | |
| def get_error_logger() -> ErrorLogger: | |
| """Get or create error logger singleton""" | |
| global _error_logger | |
| if _error_logger is None: | |
| _error_logger = ErrorLogger() | |
| return _error_logger | |
| def get_error_handler() -> ErrorHandler: | |
| """Get or create error handler singleton""" | |
| global _error_handler | |
| if _error_handler is None: | |
| _error_handler = ErrorHandler(get_error_logger()) | |
| return _error_handler | |