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