vnai 2.1.7__py3-none-any.whl → 2.1.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vnai/beam/quota.py CHANGED
@@ -1,333 +1,333 @@
1
- import time
2
- import functools
3
- import threading
4
- from collections import defaultdict
5
- from datetime import datetime
6
-
7
- class RateLimitExceeded(Exception):
8
- def __init__(self, resource_type, limit_type="min", current_usage=None, limit_value=None, retry_after=None):
9
- self.resource_type = resource_type
10
- self.limit_type = limit_type
11
- self.current_usage = current_usage
12
- self.limit_value = limit_value
13
- self.retry_after = retry_after
14
- message =f"Bạn đã gửi quá nhiều request tới {resource_type}. "
15
- if retry_after:
16
- message +=f"Vui lòng thử lại sau {round(retry_after)} giây."
17
- else:
18
- message +="Vui lòng thêm thời gian chờ giữa các lần gửi request."
19
- super().__init__(message)
20
-
21
- class Guardian:
22
- _instance = None
23
- _lock = threading.Lock()
24
-
25
- def __new__(cls):
26
- with cls._lock:
27
- if cls._instance is None:
28
- cls._instance = super(Guardian, cls).__new__(cls)
29
- cls._instance._initialize()
30
- return cls._instance
31
-
32
- def _initialize(self):
33
- self.resource_limits = defaultdict(lambda: defaultdict(int))
34
- self.usage_counters = defaultdict(lambda: defaultdict(list))
35
- self.resource_limits["default"] = {"min": 60,"hour": 3000}
36
- self.resource_limits["TCBS"] = {"min": 60,"hour": 3000}
37
- self.resource_limits["VCI"] = {"min": 60,"hour": 3000}
38
- self.resource_limits["MBK"] = {"min": 600,"hour": 36000}
39
- self.resource_limits["MAS.ext"] = {"min": 600,"hour": 36000}
40
- self.resource_limits["VCI.ext"] = {"min": 600,"hour": 36000}
41
- self.resource_limits["FMK.ext"] = {"min": 600,"hour": 36000}
42
- self.resource_limits["VND.ext"] = {"min": 600,"hour": 36000}
43
- self.resource_limits["CAF.ext"] = {"min": 600,"hour": 36000}
44
- self.resource_limits["SPL.ext"] = {"min": 600,"hour": 36000}
45
- self.resource_limits["VDS.ext"] = {"min": 600,"hour": 36000}
46
- self.resource_limits["FAD.ext"] = {"min": 600,"hour": 36000}
47
-
48
- def verify(self, operation_id, resource_type="default"):
49
- current_time = time.time()
50
- limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
51
- minute_cutoff = current_time - 60
52
- self.usage_counters[resource_type]["min"] = [
53
- t for t in self.usage_counters[resource_type]["min"]
54
- if t > minute_cutoff
55
- ]
56
- minute_usage = len(self.usage_counters[resource_type]["min"])
57
- minute_exceeded = minute_usage >= limits["min"]
58
- if minute_exceeded:
59
- from vnai.beam.metrics import collector
60
- collector.record(
61
- "rate_limit",
62
- {
63
- "resource_type": resource_type,
64
- "limit_type":"min",
65
- "limit_value": limits["min"],
66
- "current_usage": minute_usage,
67
- "is_exceeded": True
68
- },
69
- priority="high"
70
- )
71
- raise RateLimitExceeded(
72
- resource_type=resource_type,
73
- limit_type="min",
74
- current_usage=minute_usage,
75
- limit_value=limits["min"],
76
- retry_after=60 - (current_time % 60)
77
- )
78
- hour_cutoff = current_time - 3600
79
- self.usage_counters[resource_type]["hour"] = [
80
- t for t in self.usage_counters[resource_type]["hour"]
81
- if t > hour_cutoff
82
- ]
83
- hour_usage = len(self.usage_counters[resource_type]["hour"])
84
- hour_exceeded = hour_usage >= limits["hour"]
85
- from vnai.beam.metrics import collector
86
- collector.record(
87
- "rate_limit",
88
- {
89
- "resource_type": resource_type,
90
- "limit_type":"hour" if hour_exceeded else"min",
91
- "limit_value": limits["hour"] if hour_exceeded else limits["min"],
92
- "current_usage": hour_usage if hour_exceeded else minute_usage,
93
- "is_exceeded": hour_exceeded
94
- }
95
- )
96
- if hour_exceeded:
97
- raise RateLimitExceeded(
98
- resource_type=resource_type,
99
- limit_type="hour",
100
- current_usage=hour_usage,
101
- limit_value=limits["hour"],
102
- retry_after=3600 - (current_time % 3600)
103
- )
104
- self.usage_counters[resource_type]["min"].append(current_time)
105
- self.usage_counters[resource_type]["hour"].append(current_time)
106
- return True
107
-
108
- def usage(self, resource_type="default"):
109
- current_time = time.time()
110
- limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
111
- minute_cutoff = current_time - 60
112
- hour_cutoff = current_time - 3600
113
- self.usage_counters[resource_type]["min"] = [
114
- t for t in self.usage_counters[resource_type]["min"]
115
- if t > minute_cutoff
116
- ]
117
- self.usage_counters[resource_type]["hour"] = [
118
- t for t in self.usage_counters[resource_type]["hour"]
119
- if t > hour_cutoff
120
- ]
121
- minute_usage = len(self.usage_counters[resource_type]["min"])
122
- hour_usage = len(self.usage_counters[resource_type]["hour"])
123
- minute_percentage = (minute_usage / limits["min"]) * 100 if limits["min"] > 0 else 0
124
- hour_percentage = (hour_usage / limits["hour"]) * 100 if limits["hour"] > 0 else 0
125
- return max(minute_percentage, hour_percentage)
126
-
127
- def get_limit_status(self, resource_type="default"):
128
- current_time = time.time()
129
- limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
130
- minute_cutoff = current_time - 60
131
- hour_cutoff = current_time - 3600
132
- minute_usage = len([t for t in self.usage_counters[resource_type]["min"] if t > minute_cutoff])
133
- hour_usage = len([t for t in self.usage_counters[resource_type]["hour"] if t > hour_cutoff])
134
- return {
135
- "resource_type": resource_type,
136
- "minute_limit": {
137
- "usage": minute_usage,
138
- "limit": limits["min"],
139
- "percentage": (minute_usage / limits["min"]) * 100 if limits["min"] > 0 else 0,
140
- "remaining": max(0, limits["min"] - minute_usage),
141
- "reset_in_seconds": 60 - (current_time % 60)
142
- },
143
- "hour_limit": {
144
- "usage": hour_usage,
145
- "limit": limits["hour"],
146
- "percentage": (hour_usage / limits["hour"]) * 100 if limits["hour"] > 0 else 0,
147
- "remaining": max(0, limits["hour"] - hour_usage),
148
- "reset_in_seconds": 3600 - (current_time % 3600)
149
- }
150
- }
151
- guardian = Guardian()
152
-
153
- class CleanErrorContext:
154
- _last_message_time = 0
155
- _message_cooldown = 5
156
-
157
- def __enter__(self):
158
- return self
159
-
160
- def __exit__(self, exc_type, exc_val, exc_tb):
161
- if exc_type is RateLimitExceeded:
162
- current_time = time.time()
163
- if current_time - CleanErrorContext._last_message_time >= CleanErrorContext._message_cooldown:
164
- print(f"\n⚠️ {str(exc_val)}\n")
165
- CleanErrorContext._last_message_time = current_time
166
- import sys
167
- sys.exit(f"Rate limit exceeded. {str(exc_val)} Process terminated.")
168
- return False
169
- return False
170
-
171
- def optimize(resource_type='default', loop_threshold=10, time_window=5, ad_cooldown=150, content_trigger_threshold=3,
172
- max_retries=2, backoff_factor=2, debug=False):
173
- if callable(resource_type):
174
- func = resource_type
175
- return _create_wrapper(func,'default', loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
176
- max_retries, backoff_factor, debug)
177
- if loop_threshold < 2:
178
- raise ValueError(f"loop_threshold must be at least 2, got {loop_threshold}")
179
- if time_window <= 0:
180
- raise ValueError(f"time_window must be positive, got {time_window}")
181
- if content_trigger_threshold < 1:
182
- raise ValueError(f"content_trigger_threshold must be at least 1, got {content_trigger_threshold}")
183
- if max_retries < 0:
184
- raise ValueError(f"max_retries must be non-negative, got {max_retries}")
185
- if backoff_factor <= 0:
186
- raise ValueError(f"backoff_factor must be positive, got {backoff_factor}")
187
-
188
- def decorator(func):
189
- return _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
190
- max_retries, backoff_factor, debug)
191
- return decorator
192
-
193
- def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
194
- max_retries, backoff_factor, debug):
195
- call_history = []
196
- last_ad_time = 0
197
- consecutive_loop_detections = 0
198
- session_displayed = False
199
- session_start_time = time.time()
200
- session_timeout = 1800
201
- @functools.wraps(func)
202
-
203
- def wrapper(*args, **kwargs):
204
- nonlocal last_ad_time, consecutive_loop_detections, session_displayed, session_start_time
205
- current_time = time.time()
206
- content_triggered = False
207
- if current_time - session_start_time > session_timeout:
208
- session_displayed = False
209
- session_start_time = current_time
210
- retries = 0
211
- while True:
212
- call_history.append(current_time)
213
- while call_history and current_time - call_history[0] > time_window:
214
- call_history.pop(0)
215
- loop_detected = len(call_history) >= loop_threshold
216
- if debug and loop_detected:
217
- print(f"[OPTIMIZE] Đã phát hiện vòng lặp cho {func.__name__}: {len(call_history)} lần gọi trong {time_window}s")
218
- if loop_detected:
219
- consecutive_loop_detections += 1
220
- if debug:
221
- print(f"[OPTIMIZE] Số lần phát hiện vòng lặp liên tiếp: {consecutive_loop_detections}/{content_trigger_threshold}")
222
- else:
223
- consecutive_loop_detections = 0
224
- should_show_content = (consecutive_loop_detections >= content_trigger_threshold) and (current_time - last_ad_time >= ad_cooldown) and not session_displayed
225
- if should_show_content:
226
- last_ad_time = current_time
227
- consecutive_loop_detections = 0
228
- content_triggered = True
229
- session_displayed = True
230
- if debug:
231
- print(f"[OPTIMIZE] Đã kích hoạt nội dung cho {func.__name__}")
232
- try:
233
- from vnai.scope.promo import manager
234
- try:
235
- from vnai.scope.profile import inspector
236
- environment = inspector.examine().get("environment", None)
237
- manager.present_content(environment=environment, context="loop")
238
- except ImportError:
239
- manager.present_content(context="loop")
240
- except ImportError:
241
- print(f"Phát hiện vòng lặp: Hàm '{func.__name__}' đang được gọi trong một vòng lặp")
242
- except Exception as e:
243
- if debug:
244
- print(f"[OPTIMIZE] Lỗi khi hiển thị nội dung: {str(e)}")
245
- try:
246
- with CleanErrorContext():
247
- guardian.verify(func.__name__, resource_type)
248
- except RateLimitExceeded as e:
249
- from vnai.beam.metrics import collector
250
- collector.record(
251
- "error",
252
- {
253
- "function": func.__name__,
254
- "error": str(e),
255
- "context":"resource_verification",
256
- "resource_type": resource_type,
257
- "retry_attempt": retries
258
- },
259
- priority="high"
260
- )
261
- if not session_displayed:
262
- try:
263
- from vnai.scope.promo import manager
264
- try:
265
- from vnai.scope.profile import inspector
266
- environment = inspector.examine().get("environment", None)
267
- manager.present_content(environment=environment, context="loop")
268
- session_displayed = True
269
- last_ad_time = current_time
270
- except ImportError:
271
- manager.present_content(context="loop")
272
- session_displayed = True
273
- last_ad_time = current_time
274
- except Exception:
275
- pass
276
- if retries < max_retries:
277
- wait_time = backoff_factor ** retries
278
- retries += 1
279
- if hasattr(e,"retry_after") and e.retry_after:
280
- wait_time = min(wait_time, e.retry_after)
281
- if debug:
282
- print(f"[OPTIMIZE] Đã đạt giới hạn tốc độ cho {func.__name__}, thử lại sau {wait_time} giây (lần thử {retries}/{max_retries})")
283
- time.sleep(wait_time)
284
- continue
285
- else:
286
- raise
287
- start_time = time.time()
288
- success = False
289
- error = None
290
- try:
291
- result = func(*args, **kwargs)
292
- success = True
293
- return result
294
- except Exception as e:
295
- error = str(e)
296
- raise
297
- finally:
298
- execution_time = time.time() - start_time
299
- try:
300
- from vnai.beam.metrics import collector
301
- collector.record(
302
- "function",
303
- {
304
- "function": func.__name__,
305
- "resource_type": resource_type,
306
- "execution_time": execution_time,
307
- "success": success,
308
- "error": error,
309
- "in_loop": loop_detected,
310
- "loop_depth": len(call_history),
311
- "content_triggered": content_triggered,
312
- "timestamp": datetime.now().isoformat(),
313
- "retry_count": retries if retries > 0 else None
314
- }
315
- )
316
- if content_triggered:
317
- collector.record(
318
- "ad_opportunity",
319
- {
320
- "function": func.__name__,
321
- "resource_type": resource_type,
322
- "call_frequency": len(call_history) / time_window,
323
- "consecutive_loops": consecutive_loop_detections,
324
- "timestamp": datetime.now().isoformat()
325
- }
326
- )
327
- except ImportError:
328
- pass
329
- break
330
- return wrapper
331
-
332
- def rate_limit_status(resource_type="default"):
1
+ import time
2
+ import functools
3
+ import threading
4
+ from collections import defaultdict
5
+ from datetime import datetime
6
+
7
+ class RateLimitExceeded(Exception):
8
+ def __init__(self, resource_type, limit_type="min", current_usage=None, limit_value=None, retry_after=None):
9
+ self.resource_type = resource_type
10
+ self.limit_type = limit_type
11
+ self.current_usage = current_usage
12
+ self.limit_value = limit_value
13
+ self.retry_after = retry_after
14
+ message =f"Bạn đã gửi quá nhiều request tới {resource_type}. "
15
+ if retry_after:
16
+ message +=f"Vui lòng thử lại sau {round(retry_after)} giây."
17
+ else:
18
+ message +="Vui lòng thêm thời gian chờ giữa các lần gửi request."
19
+ super().__init__(message)
20
+
21
+ class Guardian:
22
+ _instance = None
23
+ _lock = threading.Lock()
24
+
25
+ def __new__(cls):
26
+ with cls._lock:
27
+ if cls._instance is None:
28
+ cls._instance = super(Guardian, cls).__new__(cls)
29
+ cls._instance._initialize()
30
+ return cls._instance
31
+
32
+ def _initialize(self):
33
+ self.resource_limits = defaultdict(lambda: defaultdict(int))
34
+ self.usage_counters = defaultdict(lambda: defaultdict(list))
35
+ self.resource_limits["default"] = {"min": 60,"hour": 3000}
36
+ self.resource_limits["TCBS"] = {"min": 60,"hour": 3000}
37
+ self.resource_limits["VCI"] = {"min": 60,"hour": 3000}
38
+ self.resource_limits["MBK"] = {"min": 600,"hour": 36000}
39
+ self.resource_limits["MAS.ext"] = {"min": 600,"hour": 36000}
40
+ self.resource_limits["VCI.ext"] = {"min": 600,"hour": 36000}
41
+ self.resource_limits["FMK.ext"] = {"min": 600,"hour": 36000}
42
+ self.resource_limits["VND.ext"] = {"min": 600,"hour": 36000}
43
+ self.resource_limits["CAF.ext"] = {"min": 600,"hour": 36000}
44
+ self.resource_limits["SPL.ext"] = {"min": 600,"hour": 36000}
45
+ self.resource_limits["VDS.ext"] = {"min": 600,"hour": 36000}
46
+ self.resource_limits["FAD.ext"] = {"min": 600,"hour": 36000}
47
+
48
+ def verify(self, operation_id, resource_type="default"):
49
+ current_time = time.time()
50
+ limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
51
+ minute_cutoff = current_time - 60
52
+ self.usage_counters[resource_type]["min"] = [
53
+ t for t in self.usage_counters[resource_type]["min"]
54
+ if t > minute_cutoff
55
+ ]
56
+ minute_usage = len(self.usage_counters[resource_type]["min"])
57
+ minute_exceeded = minute_usage >= limits["min"]
58
+ if minute_exceeded:
59
+ from vnai.beam.metrics import collector
60
+ collector.record(
61
+ "rate_limit",
62
+ {
63
+ "resource_type": resource_type,
64
+ "limit_type":"min",
65
+ "limit_value": limits["min"],
66
+ "current_usage": minute_usage,
67
+ "is_exceeded": True
68
+ },
69
+ priority="high"
70
+ )
71
+ raise RateLimitExceeded(
72
+ resource_type=resource_type,
73
+ limit_type="min",
74
+ current_usage=minute_usage,
75
+ limit_value=limits["min"],
76
+ retry_after=60 - (current_time % 60)
77
+ )
78
+ hour_cutoff = current_time - 3600
79
+ self.usage_counters[resource_type]["hour"] = [
80
+ t for t in self.usage_counters[resource_type]["hour"]
81
+ if t > hour_cutoff
82
+ ]
83
+ hour_usage = len(self.usage_counters[resource_type]["hour"])
84
+ hour_exceeded = hour_usage >= limits["hour"]
85
+ from vnai.beam.metrics import collector
86
+ collector.record(
87
+ "rate_limit",
88
+ {
89
+ "resource_type": resource_type,
90
+ "limit_type":"hour" if hour_exceeded else"min",
91
+ "limit_value": limits["hour"] if hour_exceeded else limits["min"],
92
+ "current_usage": hour_usage if hour_exceeded else minute_usage,
93
+ "is_exceeded": hour_exceeded
94
+ }
95
+ )
96
+ if hour_exceeded:
97
+ raise RateLimitExceeded(
98
+ resource_type=resource_type,
99
+ limit_type="hour",
100
+ current_usage=hour_usage,
101
+ limit_value=limits["hour"],
102
+ retry_after=3600 - (current_time % 3600)
103
+ )
104
+ self.usage_counters[resource_type]["min"].append(current_time)
105
+ self.usage_counters[resource_type]["hour"].append(current_time)
106
+ return True
107
+
108
+ def usage(self, resource_type="default"):
109
+ current_time = time.time()
110
+ limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
111
+ minute_cutoff = current_time - 60
112
+ hour_cutoff = current_time - 3600
113
+ self.usage_counters[resource_type]["min"] = [
114
+ t for t in self.usage_counters[resource_type]["min"]
115
+ if t > minute_cutoff
116
+ ]
117
+ self.usage_counters[resource_type]["hour"] = [
118
+ t for t in self.usage_counters[resource_type]["hour"]
119
+ if t > hour_cutoff
120
+ ]
121
+ minute_usage = len(self.usage_counters[resource_type]["min"])
122
+ hour_usage = len(self.usage_counters[resource_type]["hour"])
123
+ minute_percentage = (minute_usage / limits["min"]) * 100 if limits["min"] > 0 else 0
124
+ hour_percentage = (hour_usage / limits["hour"]) * 100 if limits["hour"] > 0 else 0
125
+ return max(minute_percentage, hour_percentage)
126
+
127
+ def get_limit_status(self, resource_type="default"):
128
+ current_time = time.time()
129
+ limits = self.resource_limits.get(resource_type, self.resource_limits["default"])
130
+ minute_cutoff = current_time - 60
131
+ hour_cutoff = current_time - 3600
132
+ minute_usage = len([t for t in self.usage_counters[resource_type]["min"] if t > minute_cutoff])
133
+ hour_usage = len([t for t in self.usage_counters[resource_type]["hour"] if t > hour_cutoff])
134
+ return {
135
+ "resource_type": resource_type,
136
+ "minute_limit": {
137
+ "usage": minute_usage,
138
+ "limit": limits["min"],
139
+ "percentage": (minute_usage / limits["min"]) * 100 if limits["min"] > 0 else 0,
140
+ "remaining": max(0, limits["min"] - minute_usage),
141
+ "reset_in_seconds": 60 - (current_time % 60)
142
+ },
143
+ "hour_limit": {
144
+ "usage": hour_usage,
145
+ "limit": limits["hour"],
146
+ "percentage": (hour_usage / limits["hour"]) * 100 if limits["hour"] > 0 else 0,
147
+ "remaining": max(0, limits["hour"] - hour_usage),
148
+ "reset_in_seconds": 3600 - (current_time % 3600)
149
+ }
150
+ }
151
+ guardian = Guardian()
152
+
153
+ class CleanErrorContext:
154
+ _last_message_time = 0
155
+ _message_cooldown = 5
156
+
157
+ def __enter__(self):
158
+ return self
159
+
160
+ def __exit__(self, exc_type, exc_val, exc_tb):
161
+ if exc_type is RateLimitExceeded:
162
+ current_time = time.time()
163
+ if current_time - CleanErrorContext._last_message_time >= CleanErrorContext._message_cooldown:
164
+ print(f"\n⚠️ {str(exc_val)}\n")
165
+ CleanErrorContext._last_message_time = current_time
166
+ import sys
167
+ sys.exit(f"Rate limit exceeded. {str(exc_val)} Process terminated.")
168
+ return False
169
+ return False
170
+
171
+ def optimize(resource_type='default', loop_threshold=10, time_window=5, ad_cooldown=150, content_trigger_threshold=3,
172
+ max_retries=2, backoff_factor=2, debug=False):
173
+ if callable(resource_type):
174
+ func = resource_type
175
+ return _create_wrapper(func,'default', loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
176
+ max_retries, backoff_factor, debug)
177
+ if loop_threshold < 2:
178
+ raise ValueError(f"loop_threshold must be at least 2, got {loop_threshold}")
179
+ if time_window <= 0:
180
+ raise ValueError(f"time_window must be positive, got {time_window}")
181
+ if content_trigger_threshold < 1:
182
+ raise ValueError(f"content_trigger_threshold must be at least 1, got {content_trigger_threshold}")
183
+ if max_retries < 0:
184
+ raise ValueError(f"max_retries must be non-negative, got {max_retries}")
185
+ if backoff_factor <= 0:
186
+ raise ValueError(f"backoff_factor must be positive, got {backoff_factor}")
187
+
188
+ def decorator(func):
189
+ return _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
190
+ max_retries, backoff_factor, debug)
191
+ return decorator
192
+
193
+ def _create_wrapper(func, resource_type, loop_threshold, time_window, ad_cooldown, content_trigger_threshold,
194
+ max_retries, backoff_factor, debug):
195
+ call_history = []
196
+ last_ad_time = 0
197
+ consecutive_loop_detections = 0
198
+ session_displayed = False
199
+ session_start_time = time.time()
200
+ session_timeout = 1800
201
+ @functools.wraps(func)
202
+
203
+ def wrapper(*args, **kwargs):
204
+ nonlocal last_ad_time, consecutive_loop_detections, session_displayed, session_start_time
205
+ current_time = time.time()
206
+ content_triggered = False
207
+ if current_time - session_start_time > session_timeout:
208
+ session_displayed = False
209
+ session_start_time = current_time
210
+ retries = 0
211
+ while True:
212
+ call_history.append(current_time)
213
+ while call_history and current_time - call_history[0] > time_window:
214
+ call_history.pop(0)
215
+ loop_detected = len(call_history) >= loop_threshold
216
+ if debug and loop_detected:
217
+ print(f"[OPTIMIZE] Đã phát hiện vòng lặp cho {func.__name__}: {len(call_history)} lần gọi trong {time_window}s")
218
+ if loop_detected:
219
+ consecutive_loop_detections += 1
220
+ if debug:
221
+ print(f"[OPTIMIZE] Số lần phát hiện vòng lặp liên tiếp: {consecutive_loop_detections}/{content_trigger_threshold}")
222
+ else:
223
+ consecutive_loop_detections = 0
224
+ should_show_content = (consecutive_loop_detections >= content_trigger_threshold) and (current_time - last_ad_time >= ad_cooldown) and not session_displayed
225
+ if should_show_content:
226
+ last_ad_time = current_time
227
+ consecutive_loop_detections = 0
228
+ content_triggered = True
229
+ session_displayed = True
230
+ if debug:
231
+ print(f"[OPTIMIZE] Đã kích hoạt nội dung cho {func.__name__}")
232
+ try:
233
+ from vnai.scope.promo import manager
234
+ try:
235
+ from vnai.scope.profile import inspector
236
+ environment = inspector.examine().get("environment", None)
237
+ manager.present_content(environment=environment, context="loop")
238
+ except ImportError:
239
+ manager.present_content(context="loop")
240
+ except ImportError:
241
+ print(f"Phát hiện vòng lặp: Hàm '{func.__name__}' đang được gọi trong một vòng lặp")
242
+ except Exception as e:
243
+ if debug:
244
+ print(f"[OPTIMIZE] Lỗi khi hiển thị nội dung: {str(e)}")
245
+ try:
246
+ with CleanErrorContext():
247
+ guardian.verify(func.__name__, resource_type)
248
+ except RateLimitExceeded as e:
249
+ from vnai.beam.metrics import collector
250
+ collector.record(
251
+ "error",
252
+ {
253
+ "function": func.__name__,
254
+ "error": str(e),
255
+ "context":"resource_verification",
256
+ "resource_type": resource_type,
257
+ "retry_attempt": retries
258
+ },
259
+ priority="high"
260
+ )
261
+ if not session_displayed:
262
+ try:
263
+ from vnai.scope.promo import manager
264
+ try:
265
+ from vnai.scope.profile import inspector
266
+ environment = inspector.examine().get("environment", None)
267
+ manager.present_content(environment=environment, context="loop")
268
+ session_displayed = True
269
+ last_ad_time = current_time
270
+ except ImportError:
271
+ manager.present_content(context="loop")
272
+ session_displayed = True
273
+ last_ad_time = current_time
274
+ except Exception:
275
+ pass
276
+ if retries < max_retries:
277
+ wait_time = backoff_factor ** retries
278
+ retries += 1
279
+ if hasattr(e,"retry_after") and e.retry_after:
280
+ wait_time = min(wait_time, e.retry_after)
281
+ if debug:
282
+ print(f"[OPTIMIZE] Đã đạt giới hạn tốc độ cho {func.__name__}, thử lại sau {wait_time} giây (lần thử {retries}/{max_retries})")
283
+ time.sleep(wait_time)
284
+ continue
285
+ else:
286
+ raise
287
+ start_time = time.time()
288
+ success = False
289
+ error = None
290
+ try:
291
+ result = func(*args, **kwargs)
292
+ success = True
293
+ return result
294
+ except Exception as e:
295
+ error = str(e)
296
+ raise
297
+ finally:
298
+ execution_time = time.time() - start_time
299
+ try:
300
+ from vnai.beam.metrics import collector
301
+ collector.record(
302
+ "function",
303
+ {
304
+ "function": func.__name__,
305
+ "resource_type": resource_type,
306
+ "execution_time": execution_time,
307
+ "success": success,
308
+ "error": error,
309
+ "in_loop": loop_detected,
310
+ "loop_depth": len(call_history),
311
+ "content_triggered": content_triggered,
312
+ "timestamp": datetime.now().isoformat(),
313
+ "retry_count": retries if retries > 0 else None
314
+ }
315
+ )
316
+ if content_triggered:
317
+ collector.record(
318
+ "ad_opportunity",
319
+ {
320
+ "function": func.__name__,
321
+ "resource_type": resource_type,
322
+ "call_frequency": len(call_history) / time_window,
323
+ "consecutive_loops": consecutive_loop_detections,
324
+ "timestamp": datetime.now().isoformat()
325
+ }
326
+ )
327
+ except ImportError:
328
+ pass
329
+ break
330
+ return wrapper
331
+
332
+ def rate_limit_status(resource_type="default"):
333
333
  return guardian.get_limit_status(resource_type)