1
1
import logging
2
2
import random
3
+ import re
3
4
import time
4
5
from functools import wraps
5
6
7
+ from botocore .exceptions import ClientError
8
+
6
9
logger = logging .getLogger (__name__ )
7
10
8
11
12
+ class CustomError (Exception ):
13
+ def __init__ (self , message ):
14
+ self .message = message
15
+
16
+ def __str__ (self ):
17
+ return self .message
18
+
19
+
9
20
def exponential_backoff_retry (
10
21
ExceptionToCheck , max_retries : int = 5 , initial_delay : float = 1.0
11
22
):
@@ -14,6 +25,7 @@ def exponential_backoff_retry(
14
25
15
26
Args:
16
27
func: Function to retry
28
+ ExceptionToCheck: Exception class to check for retrying
17
29
max_retries: Maximum number of retry attempts
18
30
initial_delay: Initial delay in seconds before first retry
19
31
"""
@@ -26,26 +38,37 @@ def wrapper(*args, **kwargs):
26
38
for attempt in range (max_retries + 1 ):
27
39
try :
28
40
return func (* args , ** kwargs )
29
-
30
- except ExceptionToCheck as e :
31
-
32
- if attempt == max_retries :
41
+ except ClientError as e :
42
+ if e .response ['Error' ]['Code' ] == 'ExpiredTokenException' :
33
43
logger .error (
34
- f"Execution failed after { attempt } attempts" )
44
+ "Expired token error. Please check/update your Security Token included in the request" )
45
+ # Do not try max_retry times
46
+ attempt = max_retries
47
+ raise CustomError (
48
+ message = "Expired Token. Please update the AWS credentials, to connect to the boto Client." )
49
+ elif e .response ['Error' ]['Code' ] == 'ThrottlingException' :
50
+ if attempt == max_retries :
51
+ logger .error (
52
+ f"Error code: { e .response ['Error' ]['Code' ]} "
53
+ f"Execution failed after { max_retries } attempts due to throttling. Try again later." )
54
+ raise CustomError (
55
+ message = f"Throttling Exception raised.. Retry limit of { max_retries } retries reached." )
56
+ logger .info (
57
+ f"Attempt { attempt + 1 } failed due to throttling. Retrying..." )
58
+ # Add jitter to avoid thundering herd problem
59
+ jitter = random .uniform (0 , 0.1 * delay )
60
+ sleep_time = delay + jitter
61
+ logger .debug (
62
+ f"Retrying in { sleep_time :.2f} seconds..."
63
+ )
64
+ time .sleep (sleep_time )
65
+ delay *= 2 # Exponential backoff
66
+ else :
67
+ logger .error (f"Client Error Raised: { e } " )
35
68
raise e
36
-
37
- # Add jitter to avoid thundering herd problem
38
- jitter = random .uniform (0 , 0.1 * delay )
39
- sleep_time = delay + jitter
40
-
41
- logger .debug (
42
- f"Attempt { attempt + 1 } /{ max_retries } failed. { str (e )} "
43
- f"Retrying in { sleep_time :.2f} seconds..."
44
- )
45
-
46
- time .sleep (sleep_time )
47
- delay *= 2 # Exponential backoff
48
-
69
+ except ExceptionToCheck as e :
70
+ logger .error (f"Error raised by { func .__name__ } : { e } " )
71
+ raise e
49
72
return wrapper
50
73
51
74
return decorator
@@ -74,3 +97,12 @@ def format_web_search(search_response, max_tokens_per_source, include_raw_conten
74
97
formatted_text += f"Full source content limited to { max_tokens_per_source } tokens: { raw_content } \n \n "
75
98
76
99
return formatted_text .strip ()
100
+
101
+
102
+ def extract_xml_content (text : str , tag_name : str ) -> str | None :
103
+ pattern = f"<{ tag_name } >(.*?)</{ tag_name } >"
104
+ match = re .search (pattern , text , re .DOTALL )
105
+ if match :
106
+ return match .group (1 ).strip ()
107
+ else :
108
+ return None
0 commit comments