Spaces:
Sleeping
Sleeping
File size: 8,006 Bytes
79d4fd5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | # Error handling for integration layer with fallback mechanisms
import logging
import traceback
from dataclasses import dataclass
from enum import Enum
from typing import Any, Callable, Dict, Optional, TypeVar
logger = logging.getLogger(__name__)
T = TypeVar("T")
class ErrorType(Enum):
"""Types of errors that can occur during query processing."""
DATABASE_CONNECTION = "database_connection"
DATABASE_QUERY = "database_query"
SQL_VALIDATION = "sql_validation"
SQL_INJECTION = "sql_injection"
DOCUMENT_RETRIEVAL = "document_retrieval"
LLM_GENERATION = "llm_generation"
INTENT_CLASSIFICATION = "intent_classification"
AGENT_EXECUTION = "agent_execution"
TIMEOUT = "timeout"
UNKNOWN = "unknown"
@dataclass
class ErrorContext:
"""Context information for error handling."""
error_type: ErrorType
message: str
original_error: Optional[Exception] = None
query: Optional[str] = None
additional_info: Optional[Dict[str, Any]] = None
def to_dict(self) -> Dict[str, Any]:
"""Convert error context to dictionary."""
return {
"error_type": self.error_type.value,
"message": self.message,
"query": self.query,
"additional_info": self.additional_info,
"original_error": str(self.original_error) if self.original_error else None,
}
class ErrorHandler:
"""Handles errors with fallback mechanisms and user-friendly messages."""
# Turkish user-friendly error messages
ERROR_MESSAGES = {
ErrorType.DATABASE_CONNECTION: "Veritabanı bağlantısı kurulamadı. Lütfen daha sonra tekrar deneyin.",
ErrorType.DATABASE_QUERY: "Veritabanı sorgusu başarısız oldu. Lütfen sorunuzu farklı şekilde sormayı deneyin.",
ErrorType.SQL_VALIDATION: "Oluşturulan sorgu geçersiz. Lütfen sorunuzu daha açık ifade edin.",
ErrorType.SQL_INJECTION: "Güvenlik nedeniyle bu sorgu işlenemedi.",
ErrorType.DOCUMENT_RETRIEVAL: "Dokümanlar aranamadı. Lütfen daha sonra tekrar deneyin.",
ErrorType.LLM_GENERATION: "Yanıt oluşturulamadı. Lütfen daha sonra tekrar deneyin.",
ErrorType.INTENT_CLASSIFICATION: "Sorgunuz anlaşılamadı. Lütfen farklı kelimeler kullanarak tekrar deneyin.",
ErrorType.AGENT_EXECUTION: "İşlem tamamlanamadı. Lütfen daha sonra tekrar deneyin.",
ErrorType.TIMEOUT: "İşlem zaman aşımına uğradı. Lütfen daha kısa bir sorgu deneyin.",
ErrorType.UNKNOWN: "Beklenmeyen bir hata oluştu. Lütfen daha sonra tekrar deneyin.",
}
def __init__(self, enable_fallback: bool = True):
"""Initialize error handler with fallback configuration."""
self._enable_fallback = enable_fallback
self._error_count: Dict[ErrorType, int] = {et: 0 for et in ErrorType}
self._fallback_handlers: Dict[ErrorType, Callable[..., Any]] = {}
@property
def enable_fallback(self) -> bool:
"""Check if fallback is enabled."""
return self._enable_fallback
@enable_fallback.setter
def enable_fallback(self, value: bool) -> None:
"""Set fallback enabled state."""
self._enable_fallback = value
@property
def error_counts(self) -> Dict[ErrorType, int]:
"""Get error counts by type."""
return self._error_count.copy()
def reset_counts(self) -> None:
"""Reset all error counts."""
self._error_count = {et: 0 for et in ErrorType}
def register_fallback(
self,
error_type: ErrorType,
handler: Callable[..., Any],
) -> None:
"""Register a fallback handler for specific error type."""
self._fallback_handlers[error_type] = handler
def handle(
self,
error: Exception,
error_type: Optional[ErrorType] = None,
query: Optional[str] = None,
fallback_value: Optional[T] = None,
) -> ErrorContext:
"""Handle an error and return context with user-friendly message."""
detected_type = error_type or self._detect_error_type(error)
self._error_count[detected_type] += 1
context = ErrorContext(
error_type=detected_type,
message=self.ERROR_MESSAGES.get(detected_type, self.ERROR_MESSAGES[ErrorType.UNKNOWN]),
original_error=error,
query=query,
additional_info={"traceback": traceback.format_exc()},
)
logger.error(
f"Error handled: type={detected_type.value}, query={query}, error={str(error)}",
exc_info=True,
)
return context
def execute_with_fallback(
self,
func: Callable[..., T],
error_type: ErrorType,
fallback_value: T,
query: Optional[str] = None,
*args: Any,
**kwargs: Any,
) -> T:
"""Execute function with fallback on error."""
try:
return func(*args, **kwargs)
except Exception as e:
self.handle(e, error_type, query)
if self._enable_fallback and error_type in self._fallback_handlers:
try:
return self._fallback_handlers[error_type](query, e, *args, **kwargs)
except Exception:
pass
return fallback_value
def _detect_error_type(self, error: Exception) -> ErrorType:
"""Detect error type from exception."""
error_str = str(error).lower()
error_type_name = type(error).__name__.lower()
if "connection" in error_str or "connect" in error_type_name:
return ErrorType.DATABASE_CONNECTION
elif "timeout" in error_str or "timed out" in error_str:
return ErrorType.TIMEOUT
elif "injection" in error_str or "forbidden" in error_str:
return ErrorType.SQL_INJECTION
elif "sql" in error_str or "query" in error_str:
return ErrorType.DATABASE_QUERY
elif "validation" in error_str or "invalid" in error_str:
return ErrorType.SQL_VALIDATION
elif "retriev" in error_str or "document" in error_str:
return ErrorType.DOCUMENT_RETRIEVAL
elif "llm" in error_str or "generat" in error_str:
return ErrorType.LLM_GENERATION
elif "intent" in error_str or "classif" in error_str:
return ErrorType.INTENT_CLASSIFICATION
elif "agent" in error_str or "execution" in error_str:
return ErrorType.AGENT_EXECUTION
return ErrorType.UNKNOWN
def get_user_message(self, error_type: ErrorType) -> str:
"""Get user-friendly message for error type."""
return self.ERROR_MESSAGES.get(error_type, self.ERROR_MESSAGES[ErrorType.UNKNOWN])
def get_most_frequent_error(self) -> Optional[ErrorType]:
"""Get the most frequently occurring error type."""
if all(count == 0 for count in self._error_count.values()):
return None
return max(self._error_count, key=lambda k: self._error_count[k])
_error_handler_instance: Optional[ErrorHandler] = None
def get_error_handler(enable_fallback: bool = True) -> ErrorHandler:
"""Get or create singleton error handler instance."""
global _error_handler_instance
if _error_handler_instance is None:
_error_handler_instance = ErrorHandler(enable_fallback=enable_fallback)
return _error_handler_instance
def reset_error_handler() -> None:
"""Reset the singleton error handler instance."""
global _error_handler_instance
_error_handler_instance = None
def handle_error(
error: Exception,
error_type: Optional[ErrorType] = None,
query: Optional[str] = None,
) -> ErrorContext:
"""Convenience function to handle errors using singleton handler."""
handler = get_error_handler()
return handler.handle(error, error_type, query)
|