"""Error message mapping for user-friendly error handling.""" import logging from typing import Dict, Optional, Any from enum import Enum from ...domain.exceptions import ( DomainException, InvalidAudioFormatException, InvalidTextContentException, TranslationFailedException, SpeechRecognitionException, SpeechSynthesisException, InvalidVoiceSettingsException, AudioProcessingException ) logger = logging.getLogger(__name__) class ErrorSeverity(Enum): """Error severity levels.""" LOW = "low" MEDIUM = "medium" HIGH = "high" CRITICAL = "critical" class ErrorCategory(Enum): """Error categories for classification.""" VALIDATION = "validation" PROCESSING = "processing" SYSTEM = "system" EXTERNAL = "external" CONFIGURATION = "configuration" class ErrorMapping: """Error mapping configuration.""" def __init__(self, user_message: str, error_code: str, severity: ErrorSeverity, category: ErrorCategory, recovery_suggestions: Optional[list] = None, technical_details: Optional[str] = None): self.user_message = user_message self.error_code = error_code self.severity = severity self.category = category self.recovery_suggestions = recovery_suggestions or [] self.technical_details = technical_details class ErrorMapper: """Maps domain exceptions to user-friendly error messages.""" def __init__(self): """Initialize error mapper with predefined mappings.""" self._mappings: Dict[type, ErrorMapping] = { # Audio format errors InvalidAudioFormatException: ErrorMapping( user_message="The uploaded audio file format is not supported. Please use WAV, MP3, FLAC, or OGG format.", error_code="INVALID_AUDIO_FORMAT", severity=ErrorSeverity.MEDIUM, category=ErrorCategory.VALIDATION, recovery_suggestions=[ "Convert your audio file to a supported format (WAV, MP3, FLAC, or OGG)", "Check that your file is not corrupted", "Ensure the file has a proper audio extension" ] ), # Text content errors InvalidTextContentException: ErrorMapping( user_message="The text content is invalid or too long. Please check your input.", error_code="INVALID_TEXT_CONTENT", severity=ErrorSeverity.MEDIUM, category=ErrorCategory.VALIDATION, recovery_suggestions=[ "Ensure text is not empty", "Check that text length is within limits (max 10,000 characters)", "Verify text encoding is valid" ] ), # Translation errors TranslationFailedException: ErrorMapping( user_message="Translation failed. This might be due to unsupported language pairs or service issues.", error_code="TRANSLATION_FAILED", severity=ErrorSeverity.HIGH, category=ErrorCategory.PROCESSING, recovery_suggestions=[ "Try a different target language", "Check if the source language is correctly detected", "Retry the operation after a few moments", "Ensure the text is in a supported language" ] ), # Speech recognition errors SpeechRecognitionException: ErrorMapping( user_message="Speech recognition failed. The audio might be unclear or in an unsupported language.", error_code="SPEECH_RECOGNITION_FAILED", severity=ErrorSeverity.HIGH, category=ErrorCategory.PROCESSING, recovery_suggestions=[ "Ensure audio quality is good (clear speech, minimal background noise)", "Try a different speech recognition model", "Check that the audio contains speech in a supported language", "Verify audio file is not corrupted" ] ), # Speech synthesis errors SpeechSynthesisException: ErrorMapping( user_message="Text-to-speech generation failed. This might be due to voice availability or text issues.", error_code="SPEECH_SYNTHESIS_FAILED", severity=ErrorSeverity.HIGH, category=ErrorCategory.PROCESSING, recovery_suggestions=[ "Try a different voice", "Check if the text contains unsupported characters", "Reduce text length if it's very long", "Retry with a different TTS provider" ] ), # Voice settings errors InvalidVoiceSettingsException: ErrorMapping( user_message="Voice settings are invalid. Please check speed, voice selection, and language settings.", error_code="INVALID_VOICE_SETTINGS", severity=ErrorSeverity.MEDIUM, category=ErrorCategory.VALIDATION, recovery_suggestions=[ "Ensure speed is between 0.5 and 2.0", "Select a valid voice from the available options", "Check that the language is supported", "Verify voice is available for the selected language" ] ), # General audio processing errors AudioProcessingException: ErrorMapping( user_message="Audio processing failed. There was an issue processing your audio file.", error_code="AUDIO_FORMAT_ERROR", # Use existing valid error code severity=ErrorSeverity.HIGH, category=ErrorCategory.PROCESSING, recovery_suggestions=[ "Check that your audio file is valid and not corrupted", "Try uploading a different audio file", "Ensure file size is within limits", "Retry the operation" ] ), # Validation errors ValueError: ErrorMapping( user_message="Invalid input provided. Please check your request parameters.", error_code="VALIDATION_ERROR", severity=ErrorSeverity.MEDIUM, category=ErrorCategory.VALIDATION, recovery_suggestions=[ "Check all required fields are provided", "Verify parameter values are within valid ranges", "Ensure file formats are supported" ] ), # File size errors PermissionError: ErrorMapping( user_message="Permission denied. Unable to access required files or directories.", error_code="PERMISSION_ERROR", severity=ErrorSeverity.HIGH, category=ErrorCategory.SYSTEM, recovery_suggestions=[ "Check file permissions", "Ensure temporary directory is writable", "Contact system administrator if issue persists" ] ), # Memory errors MemoryError: ErrorMapping( user_message="Insufficient memory to process the request. Try with a smaller file.", error_code="MEMORY_ERROR", severity=ErrorSeverity.HIGH, category=ErrorCategory.SYSTEM, recovery_suggestions=[ "Use a smaller audio file", "Try processing shorter audio segments", "Retry the operation later" ] ), # Timeout errors TimeoutError: ErrorMapping( user_message="Processing timed out. The operation took too long to complete.", error_code="TIMEOUT_ERROR", severity=ErrorSeverity.HIGH, category=ErrorCategory.SYSTEM, recovery_suggestions=[ "Try with a shorter audio file", "Retry the operation", "Check system load and try again later" ] ), # Type errors TypeError: ErrorMapping( user_message="Invalid data type provided. This is likely a configuration or implementation issue.", error_code="TYPE_ERROR", severity=ErrorSeverity.HIGH, category=ErrorCategory.SYSTEM, recovery_suggestions=[ "Retry the operation", "Try a different voice or model", "Contact support if the issue persists" ] ) } # Default mapping for unknown errors self._default_mapping = ErrorMapping( user_message="An unexpected error occurred. Please try again or contact support.", error_code="UNKNOWN_ERROR", severity=ErrorSeverity.CRITICAL, category=ErrorCategory.SYSTEM, recovery_suggestions=[ "Retry the operation", "Check your input parameters", "Contact support if the issue persists" ] ) def map_exception(self, exception: Exception, context: Optional[Dict[str, Any]] = None) -> ErrorMapping: """ Map an exception to user-friendly error information. Args: exception: The exception to map context: Optional context information Returns: ErrorMapping: Mapped error information """ try: # Get mapping for exception type mapping = self._mappings.get(type(exception)) if mapping is None: # Try parent classes for domain exceptions for exc_type in type(exception).__mro__: if exc_type in self._mappings: mapping = self._mappings[exc_type] break if mapping is None: # Use default mapping mapping = self._default_mapping logger.warning(f"No mapping found for exception type: {type(exception).__name__}") # Add context-specific information if available if context: mapping = self._enhance_mapping_with_context(mapping, exception, context) logger.info(f"Mapped {type(exception).__name__} to {mapping.error_code}") return mapping except Exception as e: logger.error(f"Error mapping exception {type(exception).__name__}: {e}") return self._default_mapping def _enhance_mapping_with_context(self, mapping: ErrorMapping, exception: Exception, context: Dict[str, Any]) -> ErrorMapping: """ Enhance error mapping with context-specific information. Args: mapping: Base error mapping exception: Original exception context: Context information Returns: ErrorMapping: Enhanced error mapping """ try: # Create a copy of the mapping to avoid modifying the original enhanced_mapping = ErrorMapping( user_message=mapping.user_message, error_code=mapping.error_code, severity=mapping.severity, category=mapping.category, recovery_suggestions=mapping.recovery_suggestions.copy(), technical_details=mapping.technical_details ) # Add context-specific enhancements if 'file_name' in context: enhanced_mapping.user_message += f" (File: {context['file_name']})" if 'operation' in context: enhanced_mapping.technical_details = f"Failed during {context['operation']}: {str(exception)}" if 'correlation_id' in context: enhanced_mapping.technical_details = ( f"{enhanced_mapping.technical_details or str(exception)} " f"[ID: {context['correlation_id']}]" ) # Add provider-specific suggestions if 'provider' in context: provider = context['provider'] if isinstance(exception, (SpeechRecognitionException, SpeechSynthesisException, TranslationFailedException)): enhanced_mapping.recovery_suggestions.append(f"Try switching from {provider} to an alternative provider") # Add file size specific suggestions if 'file_size' in context and context['file_size'] > 50 * 1024 * 1024: # 50MB enhanced_mapping.recovery_suggestions.insert(0, "Try with a smaller file (under 50MB)") return enhanced_mapping except Exception as e: logger.error(f"Error enhancing mapping with context: {e}") return mapping def get_error_code_from_exception(self, exception: Exception) -> str: """ Get error code from exception. Args: exception: Exception to get code for Returns: str: Error code """ mapping = self.map_exception(exception) return mapping.error_code def get_user_message_from_exception(self, exception: Exception, context: Optional[Dict[str, Any]] = None) -> str: """ Get user-friendly message from exception. Args: exception: Exception to get message for context: Optional context information Returns: str: User-friendly error message """ mapping = self.map_exception(exception, context) return mapping.user_message def get_recovery_suggestions(self, exception: Exception, context: Optional[Dict[str, Any]] = None) -> list: """ Get recovery suggestions for an exception. Args: exception: Exception to get suggestions for context: Optional context information Returns: list: List of recovery suggestions """ mapping = self.map_exception(exception, context) return mapping.recovery_suggestions def add_custom_mapping(self, exception_type: type, mapping: ErrorMapping) -> None: """ Add a custom error mapping. Args: exception_type: Exception type to map mapping: Error mapping configuration """ self._mappings[exception_type] = mapping logger.info(f"Added custom mapping for {exception_type.__name__}") def get_all_error_codes(self) -> list: """ Get all available error codes. Returns: list: List of all error codes """ codes = [mapping.error_code for mapping in self._mappings.values()] codes.append(self._default_mapping.error_code) return list(set(codes))