vnai 0.1.4__py3-none-any.whl → 2.0.1__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/metrics.py ADDED
@@ -0,0 +1,184 @@
1
+ # vnai/beam/metrics.py
2
+ # System performance metrics collection (formerly analytics)
3
+
4
+ import sys
5
+ import time
6
+ import threading
7
+ from datetime import datetime
8
+
9
+ class Collector:
10
+ """Collects operation metrics for system optimization"""
11
+
12
+ _instance = None
13
+ _lock = threading.Lock()
14
+
15
+ def __new__(cls):
16
+ with cls._lock:
17
+ if cls._instance is None:
18
+ cls._instance = super(Collector, cls).__new__(cls)
19
+ cls._instance._initialize()
20
+ return cls._instance
21
+
22
+ def _initialize(self):
23
+ """Initialize collector"""
24
+ self.metrics = {
25
+ "function": [],
26
+ "rate_limit": [],
27
+ "request": [],
28
+ "error": []
29
+ }
30
+ self.thresholds = {
31
+ "buffer_size": 50,
32
+ "error_threshold": 0.1,
33
+ "performance_threshold": 5.0
34
+ }
35
+ self.function_count = 0
36
+ self.colab_auth_triggered = False
37
+
38
+ def record(self, metric_type, data, priority=None):
39
+ """Record operation metrics"""
40
+ # Ensure data is a dictionary
41
+ if not isinstance(data, dict):
42
+ data = {"value": str(data)}
43
+
44
+ # Add timestamp if not present
45
+ if "timestamp" not in data:
46
+ data["timestamp"] = datetime.now().isoformat()
47
+
48
+ # For system_info type, keep full data
49
+ # For other types, only include machine_id reference
50
+ if metric_type != "system_info" and isinstance(data, dict):
51
+ # Remove any system info and just reference machine_id
52
+ if "system" in data:
53
+ del data["system"]
54
+
55
+ # Get machine_id for reference
56
+ from vnai.scope.profile import inspector
57
+ data["machine_id"] = inspector.fingerprint()
58
+
59
+ # Add to appropriate metrics collection
60
+ if metric_type in self.metrics:
61
+ self.metrics[metric_type].append(data)
62
+ else:
63
+ self.metrics["function"].append(data)
64
+
65
+ # Keep track of function call count for Colab auth trigger
66
+ if metric_type == "function":
67
+ self.function_count += 1
68
+
69
+ # Check if we should trigger Colab authentication
70
+ if self.function_count > 10 and not self.colab_auth_triggered and 'google.colab' in sys.modules:
71
+ self.colab_auth_triggered = True
72
+ # Trigger in a separate thread to avoid blocking
73
+ threading.Thread(
74
+ target=self._trigger_colab_auth,
75
+ daemon=True
76
+ ).start()
77
+
78
+ # Check buffer size and send if threshold reached
79
+ if sum(len(metric_list) for metric_list in self.metrics.values()) >= self.thresholds["buffer_size"]:
80
+ self._send_metrics()
81
+
82
+ # Send immediately for high priority metrics
83
+ if priority == "high" or (metric_type == "error"):
84
+ self._send_metrics()
85
+
86
+ def _trigger_colab_auth(self):
87
+ """Trigger Google Colab authentication in a background thread"""
88
+ try:
89
+ from vnai.scope.profile import inspector
90
+ inspector.get_or_create_user_id()
91
+ except:
92
+ pass # Silently fail if there's an issue
93
+
94
+ def _send_metrics(self):
95
+ """Send collected metrics to data relay"""
96
+ # Import here to avoid circular imports
97
+ from vnai.flow.relay import track_function_call, track_rate_limit, track_api_request
98
+
99
+ # Process and send each type of metric using the appropriate tracking function
100
+ for metric_type, data_list in self.metrics.items():
101
+ if not data_list:
102
+ continue
103
+
104
+ # Process each metric by type
105
+ for data in data_list:
106
+ try:
107
+ if metric_type == "function":
108
+ # Use the track_function_call interface
109
+ track_function_call(
110
+ function_name=data.get("function", "unknown"),
111
+ source=data.get("source", "vnai"),
112
+ execution_time=data.get("execution_time", 0),
113
+ success=data.get("success", True),
114
+ error=data.get("error"),
115
+ args=data.get("args")
116
+ )
117
+ elif metric_type == "rate_limit":
118
+ # Use the track_rate_limit interface
119
+ track_rate_limit(
120
+ source=data.get("source", "vnai"),
121
+ limit_type=data.get("limit_type", "unknown"),
122
+ limit_value=data.get("limit_value", 0),
123
+ current_usage=data.get("current_usage", 0),
124
+ is_exceeded=data.get("is_exceeded", False)
125
+ )
126
+ elif metric_type == "request":
127
+ # Use the track_api_request interface
128
+ track_api_request(
129
+ endpoint=data.get("endpoint", "unknown"),
130
+ source=data.get("source", "vnai"),
131
+ method=data.get("method", "GET"),
132
+ status_code=data.get("status_code", 200),
133
+ execution_time=data.get("execution_time", 0),
134
+ request_size=data.get("request_size", 0),
135
+ response_size=data.get("response_size", 0)
136
+ )
137
+ except Exception as e:
138
+ # If tracking fails, just continue with the next item
139
+ continue
140
+
141
+ # Clear the processed metrics
142
+ self.metrics[metric_type] = []
143
+
144
+ def get_metrics_summary(self):
145
+ """Get summary of collected metrics"""
146
+ return {
147
+ metric_type: len(data_list)
148
+ for metric_type, data_list in self.metrics.items()
149
+ }
150
+
151
+ # Create singleton instance
152
+ collector = Collector()
153
+
154
+ def capture(module_type="function"):
155
+ """Decorator to capture metrics for any function"""
156
+ def decorator(func):
157
+ def wrapper(*args, **kwargs):
158
+ start_time = time.time()
159
+ success = False
160
+ error = None
161
+
162
+ try:
163
+ result = func(*args, **kwargs)
164
+ success = True
165
+ return result
166
+ except Exception as e:
167
+ error = str(e)
168
+ raise
169
+ finally:
170
+ execution_time = time.time() - start_time
171
+
172
+ collector.record(
173
+ module_type,
174
+ {
175
+ "function": func.__name__,
176
+ "execution_time": execution_time,
177
+ "success": success,
178
+ "error": error,
179
+ "timestamp": datetime.now().isoformat(),
180
+ "args": str(args)[:100] if args else None # Truncate for privacy
181
+ }
182
+ )
183
+ return wrapper
184
+ return decorator
vnai/beam/pulse.py ADDED
@@ -0,0 +1,109 @@
1
+ # vnai/beam/pulse.py
2
+ # System health and performance monitoring
3
+
4
+ import threading
5
+ import time
6
+ from datetime import datetime
7
+
8
+ class Monitor:
9
+ """Monitors system health and performance"""
10
+
11
+ _instance = None
12
+ _lock = threading.Lock()
13
+
14
+ def __new__(cls):
15
+ with cls._lock:
16
+ if cls._instance is None:
17
+ cls._instance = super(Monitor, cls).__new__(cls)
18
+ cls._instance._initialize()
19
+ return cls._instance
20
+
21
+ def _initialize(self):
22
+ """Initialize monitor"""
23
+ self.health_status = "healthy"
24
+ self.last_check = time.time()
25
+ self.check_interval = 300 # seconds
26
+ self.error_count = 0
27
+ self.warning_count = 0
28
+ self.status_history = []
29
+
30
+ # Start background health check thread
31
+ self._start_background_check()
32
+
33
+ def _start_background_check(self):
34
+ """Start background health check thread"""
35
+ def check_health():
36
+ while True:
37
+ try:
38
+ self.check_health()
39
+ except:
40
+ pass # Don't let errors stop the monitor
41
+ time.sleep(self.check_interval)
42
+
43
+ thread = threading.Thread(target=check_health, daemon=True)
44
+ thread.start()
45
+
46
+ def check_health(self):
47
+ """Check system health status"""
48
+ from vnai.beam.metrics import collector
49
+ from vnai.beam.quota import guardian
50
+
51
+ # Record check time
52
+ self.last_check = time.time()
53
+
54
+ # Check metrics collector health
55
+ metrics_summary = collector.get_metrics_summary()
56
+ has_errors = metrics_summary.get("error", 0) > 0
57
+
58
+ # Check resource usage
59
+ resource_usage = guardian.usage()
60
+ high_usage = resource_usage > 80 # Over 80% of rate limits
61
+
62
+ # Determine health status
63
+ if has_errors and high_usage:
64
+ self.health_status = "critical"
65
+ self.error_count += 1
66
+ elif has_errors or high_usage:
67
+ self.health_status = "warning"
68
+ self.warning_count += 1
69
+ else:
70
+ self.health_status = "healthy"
71
+
72
+ # Record health status
73
+ self.status_history.append({
74
+ "timestamp": datetime.now().isoformat(),
75
+ "status": self.health_status,
76
+ "metrics": metrics_summary,
77
+ "resource_usage": resource_usage
78
+ })
79
+
80
+ # Keep history limited to last 10 entries
81
+ if len(self.status_history) > 10:
82
+ self.status_history = self.status_history[-10:]
83
+
84
+ return self.health_status
85
+
86
+ def report(self):
87
+ """Get health report"""
88
+ # Ensure we have a fresh check if last one is old
89
+ if time.time() - self.last_check > self.check_interval:
90
+ self.check_health()
91
+
92
+ return {
93
+ "status": self.health_status,
94
+ "last_check": datetime.fromtimestamp(self.last_check).isoformat(),
95
+ "error_count": self.error_count,
96
+ "warning_count": self.warning_count,
97
+ "history": self.status_history[-3:], # Last 3 entries
98
+ }
99
+
100
+ def reset(self):
101
+ """Reset health monitor"""
102
+ self.health_status = "healthy"
103
+ self.error_count = 0
104
+ self.warning_count = 0
105
+ self.status_history = []
106
+ self.last_check = time.time()
107
+
108
+ # Create singleton instance
109
+ monitor = Monitor()