""" Error Handling for Conversation Memory System. Provides retry logic for Pinecone operations. """ import asyncio import logging from typing import Optional, Any, Callable from pathlib import Path import importlib.util # Load config explicitly from same directory _config_path = Path(__file__).parent / "config.py" _spec = importlib.util.spec_from_file_location("memory_config", _config_path) config = importlib.util.module_from_spec(_spec) _spec.loader.exec_module(config) # Configure logging logger = logging.getLogger("conversation_memory") if not logger.handlers: handler = logging.StreamHandler() handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) logger.addHandler(handler) logger.setLevel(logging.INFO) # ============================================================================ # RETRY LOGIC # ============================================================================ async def with_retry( async_func: Callable, *args, max_retries: int = None, retry_delay: float = None, **kwargs ) -> Optional[Any]: """ Execute async function with exponential backoff retry. """ max_retries = max_retries or config.MAX_RETRIES retry_delay = retry_delay or config.RETRY_DELAY for attempt in range(max_retries): try: return await async_func(*args, **kwargs) except Exception as e: wait_time = retry_delay * (2 ** attempt) logger.warning( f"Attempt {attempt + 1}/{max_retries} failed: {e}. " f"Retrying in {wait_time}s..." ) if attempt < max_retries - 1: await asyncio.sleep(wait_time) else: logger.error(f"All retries exhausted for {async_func.__name__}") return None # ============================================================================ # SAFE PINECONE UPSERT # ============================================================================ async def safe_upsert( index, vectors: list, namespace: str ) -> bool: """ Safely upsert to Pinecone with retry logic. Args: index: Pinecone index object vectors: List of (id, embedding, metadata) tuples namespace: Pinecone namespace Returns: True on success, False on failure """ async def _upsert(): loop = asyncio.get_event_loop() return await loop.run_in_executor( None, lambda: index.upsert(vectors=vectors, namespace=namespace) ) result = await with_retry(_upsert) return result is not None