Spaces:
Sleeping
Sleeping
| """ | |
| Retry wrapper - executes a callable with exponential backoff and | |
| optional classification-based repair callback. | |
| """ | |
| from __future__ import annotations | |
| import time | |
| import logging | |
| from typing import Callable, Optional, Any | |
| from .classifier import classify | |
| logger = logging.getLogger("retry") | |
| def retry_call( | |
| fn: Callable[[], Any], | |
| max_attempts: int = 3, | |
| base_delay: float = 1.5, | |
| on_error: Optional[Callable[[Exception, int], None]] = None, | |
| repair_cb: Optional[Callable[[str], None]] = None, | |
| ) -> Any: | |
| last_exc: Optional[Exception] = None | |
| for attempt in range(1, max_attempts + 1): | |
| try: | |
| return fn() | |
| except Exception as e: | |
| last_exc = e | |
| logger.warning("retry attempt %s failed: %s", attempt, e) | |
| if on_error: | |
| try: | |
| on_error(e, attempt) | |
| except Exception: | |
| pass | |
| if repair_cb: | |
| try: | |
| err_class = classify(str(e)) | |
| if err_class: | |
| repair_cb(err_class.category) | |
| except Exception: | |
| pass | |
| if attempt < max_attempts: | |
| time.sleep(base_delay * (2 ** (attempt - 1))) | |
| assert last_exc is not None | |
| raise last_exc | |