(*args, **kwargs)
| 14 | def decorator(func): |
| 15 | @wraps(func) |
| 16 | def wrapper(*args, **kwargs): |
| 17 | last_error = None |
| 18 | for attempt in range(max_retries): |
| 19 | try: |
| 20 | return func(*args, **kwargs) |
| 21 | except Exception as e: |
| 22 | last_error = e |
| 23 | error_str = str(e).lower() |
| 24 | |
| 25 | # 不可重试的错误类型 |
| 26 | non_retryable = [ |
| 27 | "401", "unauthenticated", # 认证错误 |
| 28 | "403", "permission_denied", "forbidden", # 权限错误 |
| 29 | "404", "not_found", # 资源不存在 |
| 30 | "invalid_argument", # 参数错误 |
| 31 | "safety", "blocked", "filter", # 安全过滤 |
| 32 | ] |
| 33 | |
| 34 | should_retry = True |
| 35 | for keyword in non_retryable: |
| 36 | if keyword in error_str: |
| 37 | should_retry = False |
| 38 | break |
| 39 | |
| 40 | if not should_retry: |
| 41 | # 直接抛出,不重试 |
| 42 | raise Exception(parse_genai_error(e)) |
| 43 | |
| 44 | # 可重试的错误 |
| 45 | if attempt < max_retries - 1: |
| 46 | if "429" in error_str or "resource_exhausted" in error_str: |
| 47 | wait_time = (base_delay ** attempt) + random.uniform(0, 1) |
| 48 | print(f"[重试] 遇到资源限制,{wait_time:.1f}秒后重试 (尝试 {attempt + 2}/{max_retries})") |
| 49 | else: |
| 50 | wait_time = min(2 ** attempt, 10) + random.uniform(0, 1) |
| 51 | print(f"[重试] 请求失败,{wait_time:.1f}秒后重试 (尝试 {attempt + 2}/{max_retries})") |
| 52 | time.sleep(wait_time) |
| 53 | continue |
| 54 | |
| 55 | # 重试次数耗尽 |
| 56 | raise Exception(parse_genai_error(e)) |
| 57 | |
| 58 | # 理论上不会到这里,但保险起见 |
| 59 | raise Exception(parse_genai_error(last_error)) |
| 60 | return wrapper |
| 61 | return decorator |
| 62 |
nothing calls this directly
no test coverage detected