vnai 2.0.1__py3-none-any.whl → 2.0.3__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/__init__.py +84 -54
- vnai/beam/__init__.py +0 -3
- vnai/beam/metrics.py +57 -32
- vnai/beam/pulse.py +36 -21
- vnai/beam/quota.py +143 -109
- vnai/flow/__init__.py +4 -2
- vnai/flow/queue.py +31 -20
- vnai/flow/relay.py +101 -64
- vnai/scope/__init__.py +4 -2
- vnai/scope/profile.py +205 -110
- vnai/scope/promo.py +96 -114
- vnai/scope/state.py +64 -38
- {vnai-2.0.1.dist-info → vnai-2.0.3.dist-info}/METADATA +3 -2
- vnai-2.0.3.dist-info/RECORD +16 -0
- {vnai-2.0.1.dist-info → vnai-2.0.3.dist-info}/WHEEL +1 -1
- vnai-2.0.1.dist-info/RECORD +0 -16
- {vnai-2.0.1.dist-info → vnai-2.0.3.dist-info}/top_level.txt +0 -0
vnai/flow/relay.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
##
|
2
|
+
|
3
|
+
##
|
4
|
+
|
3
5
|
|
4
6
|
import time
|
5
7
|
import threading
|
@@ -11,7 +13,7 @@ from pathlib import Path
|
|
11
13
|
from typing import Dict, List, Any, Optional
|
12
14
|
|
13
15
|
class Conduit:
|
14
|
-
|
16
|
+
#--
|
15
17
|
|
16
18
|
_instance = None
|
17
19
|
_lock = threading.Lock()
|
@@ -24,12 +26,13 @@ class Conduit:
|
|
24
26
|
return cls._instance
|
25
27
|
|
26
28
|
def _initialize(self, webhook_url, buffer_size, sync_interval):
|
27
|
-
|
29
|
+
#--
|
28
30
|
self.webhook_url = webhook_url
|
29
31
|
self.buffer_size = buffer_size
|
30
32
|
self.sync_interval = sync_interval
|
31
33
|
|
32
|
-
|
34
|
+
##
|
35
|
+
|
33
36
|
self.buffer = {
|
34
37
|
"function_calls": [],
|
35
38
|
"api_requests": [],
|
@@ -41,7 +44,8 @@ class Conduit:
|
|
41
44
|
self.sync_count = 0
|
42
45
|
self.failed_queue = []
|
43
46
|
|
44
|
-
|
47
|
+
##
|
48
|
+
|
45
49
|
self.home_dir = Path.home()
|
46
50
|
self.project_dir = self.home_dir / ".vnstock"
|
47
51
|
self.project_dir.mkdir(exist_ok=True)
|
@@ -49,27 +53,31 @@ class Conduit:
|
|
49
53
|
self.data_dir.mkdir(exist_ok=True)
|
50
54
|
self.config_path = self.data_dir / "relay_config.json"
|
51
55
|
|
52
|
-
|
56
|
+
##
|
57
|
+
|
53
58
|
try:
|
54
59
|
from vnai.scope.profile import inspector
|
55
60
|
self.machine_id = inspector.fingerprint()
|
56
61
|
except:
|
57
62
|
self.machine_id = self._generate_fallback_id()
|
58
63
|
|
59
|
-
|
64
|
+
##
|
65
|
+
|
60
66
|
self._load_config()
|
61
67
|
|
62
|
-
|
68
|
+
##
|
69
|
+
|
63
70
|
self._start_periodic_sync()
|
64
71
|
|
65
72
|
def _generate_fallback_id(self) -> str:
|
66
|
-
|
73
|
+
#--
|
67
74
|
try:
|
68
75
|
import platform
|
69
76
|
import hashlib
|
70
77
|
import uuid
|
71
78
|
|
72
|
-
|
79
|
+
##
|
80
|
+
|
73
81
|
system_info = platform.node() + platform.platform() + platform.processor()
|
74
82
|
return hashlib.md5(system_info.encode()).hexdigest()
|
75
83
|
except:
|
@@ -77,7 +85,7 @@ class Conduit:
|
|
77
85
|
return str(uuid.uuid4())
|
78
86
|
|
79
87
|
def _load_config(self):
|
80
|
-
|
88
|
+
#--
|
81
89
|
if self.config_path.exists():
|
82
90
|
try:
|
83
91
|
with open(self.config_path, 'r') as f:
|
@@ -97,7 +105,7 @@ class Conduit:
|
|
97
105
|
pass
|
98
106
|
|
99
107
|
def _save_config(self):
|
100
|
-
|
108
|
+
#--
|
101
109
|
config = {
|
102
110
|
'webhook_url': self.webhook_url,
|
103
111
|
'buffer_size': self.buffer_size,
|
@@ -113,7 +121,7 @@ class Conduit:
|
|
113
121
|
pass
|
114
122
|
|
115
123
|
def _start_periodic_sync(self):
|
116
|
-
|
124
|
+
#--
|
117
125
|
def periodic_sync():
|
118
126
|
while True:
|
119
127
|
time.sleep(self.sync_interval)
|
@@ -123,8 +131,9 @@ class Conduit:
|
|
123
131
|
sync_thread.start()
|
124
132
|
|
125
133
|
def add_function_call(self, record):
|
126
|
-
|
127
|
-
|
134
|
+
#--
|
135
|
+
##
|
136
|
+
|
128
137
|
if not isinstance(record, dict):
|
129
138
|
record = {"value": str(record)}
|
130
139
|
|
@@ -133,8 +142,9 @@ class Conduit:
|
|
133
142
|
self._check_triggers("function_calls")
|
134
143
|
|
135
144
|
def add_api_request(self, record):
|
136
|
-
|
137
|
-
|
145
|
+
#--
|
146
|
+
##
|
147
|
+
|
138
148
|
if not isinstance(record, dict):
|
139
149
|
record = {"value": str(record)}
|
140
150
|
|
@@ -143,8 +153,9 @@ class Conduit:
|
|
143
153
|
self._check_triggers("api_requests")
|
144
154
|
|
145
155
|
def add_rate_limit(self, record):
|
146
|
-
|
147
|
-
|
156
|
+
#--
|
157
|
+
##
|
158
|
+
|
148
159
|
if not isinstance(record, dict):
|
149
160
|
record = {"value": str(record)}
|
150
161
|
|
@@ -153,33 +164,36 @@ class Conduit:
|
|
153
164
|
self._check_triggers("rate_limits")
|
154
165
|
|
155
166
|
def _check_triggers(self, record_type: str):
|
156
|
-
|
167
|
+
#--
|
157
168
|
current_time = time.time()
|
158
169
|
should_trigger = False
|
159
170
|
trigger_reason = None
|
160
171
|
|
161
|
-
|
172
|
+
##
|
173
|
+
|
162
174
|
total_records = sum(len(buffer) for buffer in self.buffer.values())
|
163
175
|
|
164
|
-
|
176
|
+
##
|
177
|
+
|
165
178
|
if total_records >= self.buffer_size:
|
166
179
|
should_trigger = True
|
167
180
|
trigger_reason = "buffer_full"
|
168
181
|
|
169
|
-
|
170
|
-
|
171
|
-
any(item.get("is_exceeded") for item in self.buffer["rate_limits"] if isinstance(item, dict)):
|
182
|
+
##
|
183
|
+
|
184
|
+
elif record_type == "rate_limits" and self.buffer["rate_limits"] and any(item.get("is_exceeded") for item in self.buffer["rate_limits"] if isinstance(item, dict)):
|
172
185
|
should_trigger = True
|
173
186
|
trigger_reason = "rate_limit_exceeded"
|
174
|
-
elif record_type == "function_calls" and self.buffer["function_calls"] and
|
175
|
-
any(not item.get("success") for item in self.buffer["function_calls"] if isinstance(item, dict)):
|
187
|
+
elif record_type == "function_calls" and self.buffer["function_calls"] and any(not item.get("success") for item in self.buffer["function_calls"] if isinstance(item, dict)):
|
176
188
|
should_trigger = True
|
177
189
|
trigger_reason = "function_error"
|
178
190
|
|
179
|
-
|
191
|
+
##
|
192
|
+
|
180
193
|
else:
|
181
194
|
time_factor = min(1.0, (current_time - self.last_sync_time) / (self.sync_interval / 2))
|
182
|
-
if random.random() < 0.05 * time_factor:
|
195
|
+
if random.random() < 0.05 * time_factor: ##
|
196
|
+
|
183
197
|
should_trigger = True
|
184
198
|
trigger_reason = "random_time_weighted"
|
185
199
|
|
@@ -191,27 +205,32 @@ class Conduit:
|
|
191
205
|
).start()
|
192
206
|
|
193
207
|
def queue(self, package, priority=None):
|
194
|
-
|
208
|
+
#--
|
195
209
|
if not package:
|
196
210
|
return False
|
197
211
|
|
198
|
-
|
212
|
+
##
|
213
|
+
|
199
214
|
if not isinstance(package, dict):
|
200
215
|
self.add_function_call({"message": str(package)})
|
201
216
|
return True
|
202
217
|
|
203
|
-
|
218
|
+
##
|
219
|
+
|
204
220
|
if "timestamp" not in package:
|
205
221
|
package["timestamp"] = datetime.now().isoformat()
|
206
222
|
|
207
|
-
|
223
|
+
##
|
224
|
+
|
208
225
|
if "type" in package:
|
209
226
|
package_type = package["type"]
|
210
227
|
data = package.get("data", {})
|
211
228
|
|
212
|
-
|
229
|
+
##
|
230
|
+
|
213
231
|
if isinstance(data, dict) and "system" in data:
|
214
|
-
|
232
|
+
##
|
233
|
+
|
215
234
|
machine_id = data["system"].get("machine_id")
|
216
235
|
data.pop("system")
|
217
236
|
if machine_id:
|
@@ -224,8 +243,10 @@ class Conduit:
|
|
224
243
|
elif package_type == "rate_limit":
|
225
244
|
self.add_rate_limit(data)
|
226
245
|
elif package_type == "system_info":
|
227
|
-
|
228
|
-
|
246
|
+
##
|
247
|
+
|
248
|
+
##
|
249
|
+
|
229
250
|
self.add_function_call({
|
230
251
|
"type": "system_info",
|
231
252
|
"commercial": data.get("commercial"),
|
@@ -233,7 +254,8 @@ class Conduit:
|
|
233
254
|
"timestamp": package.get("timestamp")
|
234
255
|
})
|
235
256
|
elif package_type == "metrics":
|
236
|
-
|
257
|
+
##
|
258
|
+
|
237
259
|
metrics_data = data
|
238
260
|
for metric_type, metrics_list in metrics_data.items():
|
239
261
|
if isinstance(metrics_list, list):
|
@@ -247,58 +269,68 @@ class Conduit:
|
|
247
269
|
for item in metrics_list:
|
248
270
|
self.add_api_request(item)
|
249
271
|
else:
|
250
|
-
|
272
|
+
##
|
273
|
+
|
251
274
|
self.add_function_call(data)
|
252
275
|
else:
|
253
|
-
|
276
|
+
##
|
277
|
+
|
254
278
|
self.add_function_call(package)
|
255
279
|
|
256
|
-
|
280
|
+
##
|
281
|
+
|
257
282
|
if priority == "high":
|
258
283
|
self.dispatch("high_priority")
|
259
284
|
|
260
285
|
return True
|
261
286
|
|
262
287
|
def dispatch(self, reason="manual"):
|
263
|
-
|
288
|
+
#--
|
264
289
|
if not self.webhook_url:
|
265
290
|
return False
|
266
291
|
|
267
292
|
with self.lock:
|
268
|
-
|
293
|
+
##
|
294
|
+
|
269
295
|
if all(len(records) == 0 for records in self.buffer.values()):
|
270
296
|
return False
|
271
297
|
|
272
|
-
|
298
|
+
##
|
299
|
+
|
273
300
|
data_to_send = {
|
274
301
|
"function_calls": self.buffer["function_calls"].copy(),
|
275
302
|
"api_requests": self.buffer["api_requests"].copy(),
|
276
303
|
"rate_limits": self.buffer["rate_limits"].copy()
|
277
304
|
}
|
278
305
|
|
279
|
-
|
306
|
+
##
|
307
|
+
|
280
308
|
self.buffer = {
|
281
309
|
"function_calls": [],
|
282
310
|
"api_requests": [],
|
283
311
|
"rate_limits": []
|
284
312
|
}
|
285
313
|
|
286
|
-
|
314
|
+
##
|
315
|
+
|
287
316
|
self.last_sync_time = time.time()
|
288
317
|
self.sync_count += 1
|
289
318
|
self._save_config()
|
290
319
|
|
291
|
-
|
320
|
+
##
|
321
|
+
|
292
322
|
try:
|
293
323
|
from vnai.scope.profile import inspector
|
294
324
|
environment_info = inspector.examine()
|
295
325
|
machine_id = environment_info.get("machine_id", self.machine_id)
|
296
326
|
except:
|
297
|
-
|
327
|
+
##
|
328
|
+
|
298
329
|
environment_info = {"machine_id": self.machine_id}
|
299
330
|
machine_id = self.machine_id
|
300
331
|
|
301
|
-
|
332
|
+
##
|
333
|
+
|
302
334
|
payload = {
|
303
335
|
"analytics_data": data_to_send,
|
304
336
|
"metadata": {
|
@@ -315,7 +347,8 @@ class Conduit:
|
|
315
347
|
}
|
316
348
|
}
|
317
349
|
|
318
|
-
|
350
|
+
##
|
351
|
+
|
319
352
|
success = self._send_data(payload)
|
320
353
|
|
321
354
|
if not success:
|
@@ -327,7 +360,7 @@ class Conduit:
|
|
327
360
|
return success
|
328
361
|
|
329
362
|
def _send_data(self, payload):
|
330
|
-
|
363
|
+
#--
|
331
364
|
if not self.webhook_url:
|
332
365
|
return False
|
333
366
|
|
@@ -335,7 +368,8 @@ class Conduit:
|
|
335
368
|
response = requests.post(
|
336
369
|
self.webhook_url,
|
337
370
|
json=payload,
|
338
|
-
timeout=5
|
371
|
+
timeout=5 ##
|
372
|
+
|
339
373
|
)
|
340
374
|
|
341
375
|
return response.status_code == 200
|
@@ -343,7 +377,7 @@ class Conduit:
|
|
343
377
|
return False
|
344
378
|
|
345
379
|
def retry_failed(self):
|
346
|
-
|
380
|
+
#--
|
347
381
|
if not self.failed_queue:
|
348
382
|
return 0
|
349
383
|
|
@@ -362,18 +396,20 @@ class Conduit:
|
|
362
396
|
return success_count
|
363
397
|
|
364
398
|
def configure(self, webhook_url):
|
365
|
-
|
399
|
+
#--
|
366
400
|
with self.lock:
|
367
401
|
self.webhook_url = webhook_url
|
368
402
|
self._save_config()
|
369
403
|
return True
|
370
404
|
|
371
|
-
|
405
|
+
##
|
406
|
+
|
372
407
|
conduit = Conduit()
|
373
408
|
|
374
|
-
|
409
|
+
##
|
410
|
+
|
375
411
|
def track_function_call(function_name, source, execution_time, success=True, error=None, args=None):
|
376
|
-
|
412
|
+
#--
|
377
413
|
record = {
|
378
414
|
"function": function_name,
|
379
415
|
"source": source,
|
@@ -386,7 +422,8 @@ def track_function_call(function_name, source, execution_time, success=True, err
|
|
386
422
|
record["error"] = error
|
387
423
|
|
388
424
|
if args:
|
389
|
-
|
425
|
+
##
|
426
|
+
|
390
427
|
sanitized_args = {}
|
391
428
|
if isinstance(args, dict):
|
392
429
|
for key, value in args.items():
|
@@ -401,7 +438,7 @@ def track_function_call(function_name, source, execution_time, success=True, err
|
|
401
438
|
conduit.add_function_call(record)
|
402
439
|
|
403
440
|
def track_rate_limit(source, limit_type, limit_value, current_usage, is_exceeded):
|
404
|
-
|
441
|
+
#--
|
405
442
|
record = {
|
406
443
|
"source": source,
|
407
444
|
"limit_type": limit_type,
|
@@ -415,7 +452,7 @@ def track_rate_limit(source, limit_type, limit_value, current_usage, is_exceeded
|
|
415
452
|
conduit.add_rate_limit(record)
|
416
453
|
|
417
454
|
def track_api_request(endpoint, source, method, status_code, execution_time, request_size=0, response_size=0):
|
418
|
-
|
455
|
+
#--
|
419
456
|
record = {
|
420
457
|
"endpoint": endpoint,
|
421
458
|
"source": source,
|
@@ -430,13 +467,13 @@ def track_api_request(endpoint, source, method, status_code, execution_time, req
|
|
430
467
|
conduit.add_api_request(record)
|
431
468
|
|
432
469
|
def configure(webhook_url):
|
433
|
-
|
470
|
+
#--
|
434
471
|
return conduit.configure(webhook_url)
|
435
472
|
|
436
473
|
def sync_now():
|
437
|
-
|
474
|
+
#--
|
438
475
|
return conduit.dispatch("manual")
|
439
476
|
|
440
477
|
def retry_failed():
|
441
|
-
|
478
|
+
#--
|
442
479
|
return conduit.retry_failed()
|