33import logging
44import os
55import random
6+ import re
67import time
78from collections .abc import Awaitable , Callable
89from functools import wraps
@@ -33,18 +34,21 @@ def async_exponential_backoff_retry[T, **P](
3334 retryable_errors : set [str ] | None = None ,
3435) -> Callable [[Callable [P , Awaitable [T ]]], Callable [P , Awaitable [T ]]]:
3536 if not retryable_errors :
36- retryable_errors = set ( [
37- "ThrottlingException" ,
38- "throttlingException" ,
39- "ModelErrorException" ,
40- "ValidationException" ,
41- "ServiceQuotaExceededException" ,
42- "RequestLimitExceeded" ,
43- "TooManyRequestsException" ,
44- "ServiceUnavailableException" ,
45- "RequestTimeout" ,
46- "RequestTimeoutException" ,
47- ] )
37+ retryable_errors = set (
38+ [
39+ "ThrottlingException" ,
40+ "throttlingException" ,
41+ "ModelErrorException" ,
42+ "ValidationException" ,
43+ "ServiceQuotaExceededException" ,
44+ "RequestLimitExceeded" ,
45+ "TooManyRequestsException" ,
46+ "ServiceUnavailableException" ,
47+ "serviceUnavailableException" , # lowercase variant from EventStreamError
48+ "RequestTimeout" ,
49+ "RequestTimeoutException" ,
50+ ]
51+ )
4852
4953 def decorator (func : Callable [P , Awaitable [T ]]) -> Callable [P , Awaitable [T ]]:
5054 @wraps (func )
@@ -70,6 +74,15 @@ def log_bedrock_invocation_error(error: Exception, attempt_num: int):
7074 except botocore .exceptions .ClientError as e :
7175 error_code = e .response .get ("Error" , {}).get ("Code" )
7276
77+ # For EventStreamError (subclass of ClientError), the error code
78+ # may be in a different location or need to be extracted from the message
79+ if not error_code :
80+ # Try to extract error code from exception message
81+ # Format: "An error occurred (errorCode) when calling..."
82+ match = re .search (r"\((\w+)\)" , str (e ))
83+ if match :
84+ error_code = match .group (1 )
85+
7386 # Log bedrock invocation details for all errors
7487 log_bedrock_invocation_error (e , attempt + 1 )
7588
0 commit comments