Extract retry-after seconds from various error message formats. Handles common formats: - "Retry-After: 60" - "retry after 60 seconds" - "try again in 5 seconds" - "30 seconds remaining" Args: error_message: The error message to parse Returns: Seco
(error_message: str)
| 31 | |
| 32 | |
| 33 | def parse_retry_after(error_message: str) -> Optional[int]: |
| 34 | """ |
| 35 | Extract retry-after seconds from various error message formats. |
| 36 | |
| 37 | Handles common formats: |
| 38 | - "Retry-After: 60" |
| 39 | - "retry after 60 seconds" |
| 40 | - "try again in 5 seconds" |
| 41 | - "30 seconds remaining" |
| 42 | |
| 43 | Args: |
| 44 | error_message: The error message to parse |
| 45 | |
| 46 | Returns: |
| 47 | Seconds to wait, or None if not parseable. |
| 48 | """ |
| 49 | # Patterns require explicit "seconds" or "s" unit, OR no unit at all (end of string/sentence) |
| 50 | # This prevents matching "30 minutes" or "1 hour" since those have non-seconds units |
| 51 | patterns = [ |
| 52 | r"retry.?after[:\s]+(\d+)\s*(?:seconds?|s\b)", # Requires seconds unit |
| 53 | r"retry.?after[:\s]+(\d+)(?:\s*$|\s*[,.])", # Or end of string/sentence |
| 54 | r"try again in\s+(\d+)\s*(?:seconds?|s\b)", # Requires seconds unit |
| 55 | r"try again in\s+(\d+)(?:\s*$|\s*[,.])", # Or end of string/sentence |
| 56 | r"(\d+)\s*seconds?\s*(?:remaining|left|until)", |
| 57 | ] |
| 58 | |
| 59 | for pattern in patterns: |
| 60 | match = re.search(pattern, error_message, re.IGNORECASE) |
| 61 | if match: |
| 62 | return int(match.group(1)) |
| 63 | |
| 64 | return None |
| 65 | |
| 66 | |
| 67 | def is_rate_limit_error(error_message: str) -> bool: |
no outgoing calls