| import time | |
| import functools | |
| def with_retry(max_retries: int = 5, base_delay: float = 3.0): | |
| def decorator(func): | |
| def wrapper(*args, **kwargs): | |
| last_error = None | |
| for attempt in range(max_retries + 1): | |
| try: | |
| return func(*args, **kwargs) | |
| except Exception as e: | |
| last_error = e | |
| error_str = str(e) | |
| if "429" in error_str or "Rate limit" in error_str or "FreeUsageLimitError" in error_str: | |
| delay = base_delay * (2 ** attempt) | |
| print(f" Rate limited (attempt {attempt + 1}), retrying in {delay:.0f}s...") | |
| time.sleep(delay) | |
| continue | |
| raise | |
| raise last_error | |
| return wrapper | |
| return decorator | |