Michael Hu
add more logs
fdc056d
"""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))