vnai 2.1.8__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/__init__.py +37 -110
- vnai/beam/__init__.py +0 -2
- vnai/beam/metrics.py +48 -99
- vnai/beam/pulse.py +24 -53
- vnai/beam/quota.py +94 -247
- vnai/flow/__init__.py +1 -4
- vnai/flow/queue.py +17 -50
- vnai/flow/relay.py +98 -204
- vnai/scope/__init__.py +1 -4
- vnai/scope/profile.py +231 -417
- vnai/scope/promo.py +41 -123
- vnai/scope/state.py +52 -119
- {vnai-2.1.8.dist-info → vnai-2.1.9.dist-info}/METADATA +1 -1
- vnai-2.1.9.dist-info/RECORD +16 -0
- vnai-2.1.8.dist-info/RECORD +0 -16
- {vnai-2.1.8.dist-info → vnai-2.1.9.dist-info}/WHEEL +0 -0
- {vnai-2.1.8.dist-info → vnai-2.1.9.dist-info}/top_level.txt +0 -0
vnai/__init__.py
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
# vnai/__init__.py
|
2
|
-
# Main entry point for vnai package
|
3
|
-
|
4
1
|
import os
|
5
2
|
import pathlib
|
6
3
|
import json
|
@@ -8,8 +5,6 @@ import time
|
|
8
5
|
import threading
|
9
6
|
import functools
|
10
7
|
from datetime import datetime
|
11
|
-
|
12
|
-
# Import core functionality
|
13
8
|
from vnai.beam.quota import guardian, optimize
|
14
9
|
from vnai.beam.metrics import collector, capture
|
15
10
|
from vnai.beam.pulse import monitor
|
@@ -19,204 +14,139 @@ from vnai.scope.profile import inspector
|
|
19
14
|
from vnai.scope.state import tracker, record
|
20
15
|
import vnai.scope.promo
|
21
16
|
from vnai.scope.promo import present
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
TC_PATH = pathlib.Path.home() / ".vnstock" / "id" / "terms_agreement.txt"
|
27
|
-
|
28
|
-
TERMS_AND_CONDITIONS = """
|
17
|
+
TC_VAR ="ACCEPT_TC"
|
18
|
+
TC_VAL ="tôi đồng ý"
|
19
|
+
TC_PATH = pathlib.Path.home() /".vnstock" /"id" /"terms_agreement.txt"
|
20
|
+
TERMS_AND_CONDITIONS ="""
|
29
21
|
Khi tiếp tục sử dụng Vnstock, bạn xác nhận rằng bạn đã đọc, hiểu và đồng ý với Chính sách quyền riêng tư và Điều khoản, điều kiện về giấy phép sử dụng Vnstock.
|
30
|
-
|
31
22
|
Chi tiết:
|
32
23
|
- Giấy phép sử dụng phần mềm: https://vnstocks.com/docs/tai-lieu/giay-phep-su-dung
|
33
24
|
- Chính sách quyền riêng tư: https://vnstocks.com/docs/tai-lieu/chinh-sach-quyen-rieng-tu
|
34
25
|
"""
|
35
26
|
|
36
27
|
class Core:
|
37
|
-
"""Core functionality for system optimization"""
|
38
|
-
|
39
28
|
def __init__(self):
|
40
|
-
"""Initialize core"""
|
41
29
|
self.initialized = False
|
42
30
|
self.webhook_url = None
|
43
31
|
self.init_time = datetime.now().isoformat()
|
44
32
|
self.home_dir = pathlib.Path.home()
|
45
|
-
self.project_dir = self.home_dir /
|
46
|
-
self.id_dir = self.project_dir /
|
33
|
+
self.project_dir = self.home_dir /".vnstock"
|
34
|
+
self.id_dir = self.project_dir /'id'
|
47
35
|
self.terms_file_path = TC_PATH
|
48
36
|
self.system_info = None
|
49
|
-
|
50
|
-
# Create necessary directories
|
51
37
|
self.project_dir.mkdir(exist_ok=True)
|
52
38
|
self.id_dir.mkdir(exist_ok=True)
|
53
|
-
|
54
|
-
# Auto-initialize
|
55
39
|
self.initialize()
|
56
40
|
|
57
41
|
def initialize(self):
|
58
|
-
"""Initialize the system"""
|
59
42
|
if self.initialized:
|
60
43
|
return True
|
61
|
-
|
62
|
-
# Check terms acceptance
|
63
44
|
if not self._check_terms():
|
64
45
|
self._accept_terms()
|
65
|
-
|
66
|
-
# Set up vnstock environment
|
67
46
|
from vnai.scope.profile import inspector
|
68
47
|
inspector.setup_vnstock_environment()
|
69
|
-
|
70
|
-
# Display content during initialization
|
71
48
|
from vnai.scope.promo import ContentManager
|
72
49
|
manager = ContentManager()
|
73
|
-
|
74
|
-
if manager.is_paid_user is False and getattr(manager, 'license_checked', False):
|
50
|
+
if manager.is_paid_user is False and getattr(manager,'license_checked', False):
|
75
51
|
present()
|
76
|
-
|
77
|
-
# Record initialization
|
78
52
|
record("initialization", {"timestamp": datetime.now().isoformat()})
|
79
|
-
|
80
|
-
# Get system information ONCE and store it in the class
|
81
53
|
self.system_info = inspector.examine()
|
82
|
-
|
83
|
-
# Queue system data with optimal structure
|
84
54
|
conduit.queue({
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
55
|
+
"type":"system_info",
|
56
|
+
"data": {
|
57
|
+
"commercial": inspector.detect_commercial_usage(),
|
58
|
+
"packages": inspector.scan_packages()
|
89
59
|
}
|
90
60
|
}, priority="high")
|
91
|
-
|
92
61
|
self.initialized = True
|
93
62
|
return True
|
94
63
|
|
95
64
|
def _check_terms(self):
|
96
|
-
"""Check if terms have been accepted"""
|
97
65
|
return os.path.exists(self.terms_file_path)
|
98
66
|
|
99
67
|
def _accept_terms(self):
|
100
|
-
"""Record terms acceptance"""
|
101
|
-
# Get system information
|
102
68
|
system_info = inspector.examine()
|
103
|
-
|
104
|
-
# Auto-accept terms
|
105
69
|
if TC_VAR in os.environ and os.environ[TC_VAR] == TC_VAL:
|
106
70
|
response = TC_VAL
|
107
71
|
else:
|
108
|
-
# For non-interactive environments, accept by default
|
109
72
|
response = TC_VAL
|
110
73
|
os.environ[TC_VAR] = TC_VAL
|
111
|
-
|
112
|
-
# Store the acceptance with hardware info
|
113
74
|
now = datetime.now()
|
114
75
|
signed_agreement = (
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
76
|
+
f"Người dùng có mã nhận dạng {system_info['machine_id']} đã chấp nhận "
|
77
|
+
f"điều khoản & điều kiện sử dụng Vnstock lúc {now}\n"
|
78
|
+
f"---\n\n"
|
79
|
+
f"THÔNG TIN THIẾT BỊ: {json.dumps(system_info, indent=2)}\n\n"
|
80
|
+
f"Đính kèm bản sao nội dung bạn đã đọc, hiểu rõ và đồng ý dưới đây:\n"
|
81
|
+
f"{TERMS_AND_CONDITIONS}"
|
121
82
|
)
|
122
|
-
|
123
|
-
# Store the acceptance
|
124
|
-
with open(self.terms_file_path, "w", encoding="utf-8") as f:
|
83
|
+
with open(self.terms_file_path,"w", encoding="utf-8") as f:
|
125
84
|
f.write(signed_agreement)
|
126
|
-
|
127
|
-
# Create the environment.json file that vnstock expects
|
128
|
-
env_file = self.id_dir / "environment.json"
|
85
|
+
env_file = self.id_dir /"environment.json"
|
129
86
|
env_data = {
|
130
|
-
|
131
|
-
|
132
|
-
|
87
|
+
"accepted_agreement": True,
|
88
|
+
"timestamp": now.isoformat(),
|
89
|
+
"machine_id": system_info['machine_id']
|
133
90
|
}
|
134
|
-
|
135
|
-
with open(env_file, "w") as f:
|
91
|
+
with open(env_file,"w") as f:
|
136
92
|
json.dump(env_data, f)
|
137
|
-
|
138
93
|
return True
|
139
94
|
|
140
95
|
def status(self):
|
141
|
-
"""Get system status"""
|
142
96
|
return {
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
# Environment information available via self.system_info
|
97
|
+
"initialized": self.initialized,
|
98
|
+
"health": monitor.report(),
|
99
|
+
"metrics": tracker.get_metrics()
|
147
100
|
}
|
148
|
-
|
101
|
+
|
149
102
|
def configure_privacy(self, level="standard"):
|
150
|
-
"""Configure privacy settings"""
|
151
103
|
from vnai.scope.state import tracker
|
152
104
|
return tracker.setup_privacy(level)
|
153
|
-
|
154
|
-
|
155
|
-
# Create singleton instance
|
156
105
|
core = Core()
|
157
106
|
|
158
|
-
# Backward support
|
159
107
|
def tc_init():
|
160
108
|
return core.initialize()
|
161
109
|
|
162
110
|
def setup():
|
163
|
-
"""Setup vnai"""
|
164
111
|
return core.initialize()
|
165
112
|
|
166
113
|
def optimize_execution(resource_type="default"):
|
167
|
-
"""Decorator for optimizing function execution"""
|
168
114
|
return optimize(resource_type)
|
169
115
|
|
170
116
|
def agg_execution(resource_type="default"):
|
171
|
-
"""Decorator for aggregating function execution"""
|
172
117
|
return optimize(resource_type, ad_cooldown=1500, content_trigger_threshold=100000)
|
173
118
|
|
174
119
|
def measure_performance(module_type="function"):
|
175
|
-
"""Decorator for measuring function performance"""
|
176
120
|
return capture(module_type)
|
177
121
|
|
178
122
|
def accept_license_terms(terms_text=None):
|
179
|
-
"""Accept license terms and conditions"""
|
180
123
|
if terms_text is None:
|
181
124
|
terms_text = TERMS_AND_CONDITIONS
|
182
|
-
|
183
|
-
# Get system information
|
184
125
|
system_info = inspector.examine()
|
185
|
-
|
186
|
-
# Record acceptance
|
187
|
-
terms_file = pathlib.Path.home() / ".vnstock" / "id" / "terms_agreement.txt"
|
126
|
+
terms_file = pathlib.Path.home() /".vnstock" /"id" /"terms_agreement.txt"
|
188
127
|
os.makedirs(os.path.dirname(terms_file), exist_ok=True)
|
189
|
-
|
190
|
-
with open(terms_file, "w", encoding="utf-8") as f:
|
128
|
+
with open(terms_file,"w", encoding="utf-8") as f:
|
191
129
|
f.write(f"Terms accepted at {datetime.now().isoformat()}\n")
|
192
130
|
f.write(f"System: {json.dumps(system_info)}\n\n")
|
193
131
|
f.write(terms_text)
|
194
|
-
|
195
132
|
return True
|
196
133
|
|
197
134
|
def accept_vnstock_terms():
|
198
|
-
"""Accept vnstock terms and create necessary files"""
|
199
|
-
# Get system information
|
200
135
|
from vnai.scope.profile import inspector
|
201
136
|
system_info = inspector.examine()
|
202
|
-
|
203
|
-
# Create necessary directories
|
204
137
|
home_dir = pathlib.Path.home()
|
205
|
-
project_dir = home_dir /
|
138
|
+
project_dir = home_dir /".vnstock"
|
206
139
|
project_dir.mkdir(exist_ok=True)
|
207
|
-
id_dir = project_dir /
|
140
|
+
id_dir = project_dir /'id'
|
208
141
|
id_dir.mkdir(exist_ok=True)
|
209
|
-
|
210
|
-
# Create environment.json file that vnstock looks for
|
211
|
-
env_file = id_dir / "environment.json"
|
142
|
+
env_file = id_dir /"environment.json"
|
212
143
|
env_data = {
|
213
|
-
|
214
|
-
|
215
|
-
|
144
|
+
"accepted_agreement": True,
|
145
|
+
"timestamp": datetime.now().isoformat(),
|
146
|
+
"machine_id": system_info['machine_id']
|
216
147
|
}
|
217
|
-
|
218
148
|
try:
|
219
|
-
with open(env_file,
|
149
|
+
with open(env_file,"w") as f:
|
220
150
|
json.dump(env_data, f)
|
221
151
|
print("Vnstock terms accepted successfully.")
|
222
152
|
return True
|
@@ -225,16 +155,13 @@ def accept_vnstock_terms():
|
|
225
155
|
return False
|
226
156
|
|
227
157
|
def configure_privacy(level="standard"):
|
228
|
-
"""Configure privacy level for analytics data"""
|
229
158
|
from vnai.scope.state import tracker
|
230
159
|
return tracker.setup_privacy(level)
|
231
160
|
|
232
161
|
def check_commercial_usage():
|
233
|
-
"""Check if running in commercial environment"""
|
234
162
|
from vnai.scope.profile import inspector
|
235
163
|
return inspector.detect_commercial_usage()
|
236
164
|
|
237
165
|
def authenticate_for_persistence():
|
238
|
-
"""Authenticate to Google Drive for persistent settings (Colab only)"""
|
239
166
|
from vnai.scope.profile import inspector
|
240
|
-
return inspector.get_or_create_user_id()
|
167
|
+
return inspector.get_or_create_user_id()
|
vnai/beam/__init__.py
CHANGED
vnai/beam/metrics.py
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# vnai/beam/metrics.py
|
2
|
-
|
3
1
|
import sys
|
4
2
|
import time
|
5
3
|
import threading
|
@@ -8,211 +6,162 @@ import hashlib
|
|
8
6
|
import json
|
9
7
|
|
10
8
|
class Collector:
|
11
|
-
"""Collects operation metrics for system optimization"""
|
12
|
-
|
13
9
|
_instance = None
|
14
10
|
_lock = threading.Lock()
|
15
|
-
|
11
|
+
|
16
12
|
def __new__(cls):
|
17
13
|
with cls._lock:
|
18
14
|
if cls._instance is None:
|
19
15
|
cls._instance = super(Collector, cls).__new__(cls)
|
20
16
|
cls._instance._initialize()
|
21
17
|
return cls._instance
|
22
|
-
|
18
|
+
|
23
19
|
def _initialize(self):
|
24
|
-
"""Initialize collector"""
|
25
|
-
# Initialize metrics storage
|
26
20
|
self.metrics = {
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
"function": [],
|
22
|
+
"rate_limit": [],
|
23
|
+
"request": [],
|
24
|
+
"error": []
|
31
25
|
}
|
32
|
-
# Configuration thresholds
|
33
26
|
self.thresholds = {
|
34
|
-
|
35
|
-
|
36
|
-
|
27
|
+
"buffer_size": 50,
|
28
|
+
"error_threshold": 0.1,
|
29
|
+
"performance_threshold": 5.0
|
37
30
|
}
|
38
|
-
# Tracking variables
|
39
31
|
self.function_count = 0
|
40
32
|
self.colab_auth_triggered = False
|
41
|
-
self.max_metric_length = 200
|
42
|
-
self._last_record_time = {}
|
43
|
-
self.min_interval_per_type = 0.5
|
44
|
-
self._recent_hashes = []
|
45
|
-
self._sending_metrics = False
|
46
|
-
|
47
|
-
def record(self, metric_type, data, priority=None):
|
48
|
-
"""Record operation metrics with deduplication and throttling"""
|
33
|
+
self.max_metric_length = 200
|
34
|
+
self._last_record_time = {}
|
35
|
+
self.min_interval_per_type = 0.5
|
36
|
+
self._recent_hashes = []
|
37
|
+
self._sending_metrics = False
|
49
38
|
|
50
|
-
|
39
|
+
def record(self, metric_type, data, priority=None):
|
51
40
|
if not isinstance(data, dict):
|
52
41
|
data = {"value": str(data)}
|
53
|
-
|
54
|
-
# Add timestamp if not present
|
55
|
-
if "timestamp" not in data:
|
42
|
+
if"timestamp" not in data:
|
56
43
|
data["timestamp"] = datetime.now().isoformat()
|
57
|
-
|
58
|
-
# For non-system info, simplify and tag machine
|
59
|
-
if metric_type != "system_info":
|
44
|
+
if metric_type !="system_info":
|
60
45
|
data.pop("system", None)
|
61
46
|
from vnai.scope.profile import inspector
|
62
47
|
data["machine_id"] = inspector.fingerprint()
|
63
|
-
|
64
|
-
# ==== THROTTLING ====
|
65
48
|
now = time.time()
|
66
49
|
last_time = self._last_record_time.get(metric_type, 0)
|
67
|
-
if now - last_time < self.min_interval_per_type and priority !=
|
68
|
-
return
|
50
|
+
if now - last_time < self.min_interval_per_type and priority !="high":
|
51
|
+
return
|
69
52
|
self._last_record_time[metric_type] = now
|
70
|
-
|
71
|
-
# ==== DEDUPLICATION ====
|
72
53
|
data_hash = hashlib.md5(json.dumps(data, sort_keys=True).encode()).hexdigest()
|
73
|
-
if data_hash in self._recent_hashes and priority !=
|
74
|
-
return
|
54
|
+
if data_hash in self._recent_hashes and priority !="high":
|
55
|
+
return
|
75
56
|
self._recent_hashes.append(data_hash)
|
76
|
-
|
77
|
-
# ==== RECORD LOGIC ====
|
78
57
|
if metric_type in self.metrics:
|
79
58
|
self.metrics[metric_type].append(data)
|
80
|
-
# Prune oldest if too long
|
81
59
|
if len(self.metrics[metric_type]) > self.max_metric_length:
|
82
60
|
self.metrics[metric_type] = self.metrics[metric_type][-self.max_metric_length:]
|
83
61
|
else:
|
84
62
|
self.metrics["function"].append(data)
|
85
|
-
|
86
|
-
# Function metric tracking (Colab trigger)
|
87
|
-
if metric_type == "function":
|
63
|
+
if metric_type =="function":
|
88
64
|
self.function_count += 1
|
89
|
-
if self.function_count > 10 and not self.colab_auth_triggered and
|
65
|
+
if self.function_count > 10 and not self.colab_auth_triggered and'google.colab' in sys.modules:
|
90
66
|
self.colab_auth_triggered = True
|
91
67
|
threading.Thread(target=self._trigger_colab_auth, daemon=True).start()
|
92
|
-
|
93
|
-
# Auto-send triggers
|
94
68
|
if sum(len(metric_list) for metric_list in self.metrics.values()) >= self.thresholds["buffer_size"]:
|
95
69
|
self._send_metrics()
|
96
|
-
if priority ==
|
70
|
+
if priority =="high" or metric_type =="error":
|
97
71
|
self._send_metrics()
|
98
|
-
|
72
|
+
|
99
73
|
def _trigger_colab_auth(self):
|
100
|
-
"""Trigger Google Colab authentication in a background thread"""
|
101
74
|
try:
|
102
75
|
from vnai.scope.profile import inspector
|
103
76
|
inspector.get_or_create_user_id()
|
104
77
|
except:
|
105
|
-
pass
|
106
|
-
|
78
|
+
pass
|
79
|
+
|
107
80
|
def _send_metrics(self):
|
108
|
-
"""Send collected metrics to data relay"""
|
109
|
-
# Prevent reentrancy
|
110
81
|
if self._sending_metrics:
|
111
82
|
return
|
112
|
-
|
113
83
|
self._sending_metrics = True
|
114
84
|
try:
|
115
|
-
# Import here to avoid circular imports
|
116
85
|
from vnai.flow.relay import track_function_call, track_rate_limit, track_api_request
|
117
86
|
except ImportError:
|
118
|
-
# If relay module is not available, clear metrics and return
|
119
87
|
for metric_type in self.metrics:
|
120
88
|
self.metrics[metric_type] = []
|
121
89
|
self._sending_metrics = False
|
122
90
|
return
|
123
|
-
|
124
|
-
# Process and send each type of metric using the appropriate tracking function
|
125
91
|
for metric_type, data_list in self.metrics.items():
|
126
92
|
if not data_list:
|
127
93
|
continue
|
128
|
-
|
129
|
-
# Process each metric by type
|
130
94
|
for data in data_list:
|
131
95
|
try:
|
132
|
-
if metric_type ==
|
133
|
-
# Use the track_function_call interface
|
96
|
+
if metric_type =="function":
|
134
97
|
track_function_call(
|
135
|
-
function_name=data.get("function",
|
136
|
-
source=data.get("source",
|
98
|
+
function_name=data.get("function","unknown"),
|
99
|
+
source=data.get("source","vnai"),
|
137
100
|
execution_time=data.get("execution_time", 0),
|
138
101
|
success=data.get("success", True),
|
139
102
|
error=data.get("error"),
|
140
103
|
args=data.get("args")
|
141
104
|
)
|
142
|
-
elif metric_type ==
|
143
|
-
# Use the track_rate_limit interface
|
105
|
+
elif metric_type =="rate_limit":
|
144
106
|
track_rate_limit(
|
145
|
-
source=data.get("source",
|
146
|
-
limit_type=data.get("limit_type",
|
107
|
+
source=data.get("source","vnai"),
|
108
|
+
limit_type=data.get("limit_type","unknown"),
|
147
109
|
limit_value=data.get("limit_value", 0),
|
148
110
|
current_usage=data.get("current_usage", 0),
|
149
111
|
is_exceeded=data.get("is_exceeded", False)
|
150
112
|
)
|
151
|
-
elif metric_type ==
|
152
|
-
# Use the track_api_request interface
|
113
|
+
elif metric_type =="request":
|
153
114
|
track_api_request(
|
154
|
-
endpoint=data.get("endpoint",
|
155
|
-
source=data.get("source",
|
156
|
-
method=data.get("method",
|
115
|
+
endpoint=data.get("endpoint","unknown"),
|
116
|
+
source=data.get("source","vnai"),
|
117
|
+
method=data.get("method","GET"),
|
157
118
|
status_code=data.get("status_code", 200),
|
158
119
|
execution_time=data.get("execution_time", 0),
|
159
120
|
request_size=data.get("request_size", 0),
|
160
121
|
response_size=data.get("response_size", 0)
|
161
122
|
)
|
162
123
|
except Exception as e:
|
163
|
-
# If tracking fails, just continue with the next item
|
164
124
|
continue
|
165
|
-
|
166
|
-
# Clear the processed metrics
|
167
125
|
self.metrics[metric_type] = []
|
168
|
-
|
169
|
-
# Reset sending flag
|
170
126
|
self._sending_metrics = False
|
171
|
-
|
127
|
+
|
172
128
|
def get_metrics_summary(self):
|
173
|
-
"""Get summary of collected metrics"""
|
174
129
|
return {
|
175
130
|
metric_type: len(data_list)
|
176
131
|
for metric_type, data_list in self.metrics.items()
|
177
132
|
}
|
178
|
-
|
179
|
-
# Create singleton instance
|
180
133
|
collector = Collector()
|
181
134
|
|
182
135
|
def capture(module_type="function"):
|
183
|
-
"""Decorator to capture metrics for any function"""
|
184
136
|
def decorator(func):
|
185
137
|
def wrapper(*args, **kwargs):
|
186
138
|
start_time = time.time()
|
187
139
|
success = False
|
188
140
|
error = None
|
189
|
-
|
190
141
|
try:
|
191
142
|
result = func(*args, **kwargs)
|
192
143
|
success = True
|
193
144
|
return result
|
194
145
|
except Exception as e:
|
195
146
|
error = str(e)
|
196
|
-
# Log the error to metrics before re-raising
|
197
147
|
collector.record("error", {
|
198
|
-
|
199
|
-
|
200
|
-
|
148
|
+
"function": func.__name__,
|
149
|
+
"error": error,
|
150
|
+
"args": str(args)[:100] if args else None
|
201
151
|
})
|
202
152
|
raise
|
203
153
|
finally:
|
204
154
|
execution_time = time.time() - start_time
|
205
|
-
|
206
155
|
collector.record(
|
207
156
|
module_type,
|
208
157
|
{
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
158
|
+
"function": func.__name__,
|
159
|
+
"execution_time": execution_time,
|
160
|
+
"success": success,
|
161
|
+
"error": error,
|
162
|
+
"timestamp": datetime.now().isoformat(),
|
163
|
+
"args": str(args)[:100] if args else None
|
215
164
|
}
|
216
165
|
)
|
217
166
|
return wrapper
|
218
|
-
return decorator
|
167
|
+
return decorator
|
vnai/beam/pulse.py
CHANGED
@@ -1,108 +1,79 @@
|
|
1
|
-
# vnai/beam/pulse.py
|
2
|
-
|
3
1
|
import threading
|
4
2
|
import time
|
5
3
|
from datetime import datetime
|
6
4
|
|
7
5
|
class Monitor:
|
8
|
-
"""Monitors system health and performance"""
|
9
|
-
|
10
6
|
_instance = None
|
11
7
|
_lock = threading.Lock()
|
12
|
-
|
8
|
+
|
13
9
|
def __new__(cls):
|
14
10
|
with cls._lock:
|
15
11
|
if cls._instance is None:
|
16
12
|
cls._instance = super(Monitor, cls).__new__(cls)
|
17
13
|
cls._instance._initialize()
|
18
14
|
return cls._instance
|
19
|
-
|
15
|
+
|
20
16
|
def _initialize(self):
|
21
|
-
|
22
|
-
self.health_status = "healthy"
|
17
|
+
self.health_status ="healthy"
|
23
18
|
self.last_check = time.time()
|
24
|
-
self.check_interval = 300
|
19
|
+
self.check_interval = 300
|
25
20
|
self.error_count = 0
|
26
21
|
self.warning_count = 0
|
27
22
|
self.status_history = []
|
28
|
-
|
29
|
-
# Start background health check thread
|
30
23
|
self._start_background_check()
|
31
|
-
|
24
|
+
|
32
25
|
def _start_background_check(self):
|
33
|
-
"""Start background health check thread"""
|
34
26
|
def check_health():
|
35
27
|
while True:
|
36
28
|
try:
|
37
29
|
self.check_health()
|
38
30
|
except:
|
39
|
-
pass
|
31
|
+
pass
|
40
32
|
time.sleep(self.check_interval)
|
41
|
-
|
42
33
|
thread = threading.Thread(target=check_health, daemon=True)
|
43
34
|
thread.start()
|
44
|
-
|
35
|
+
|
45
36
|
def check_health(self):
|
46
|
-
"""Check system health status"""
|
47
37
|
from vnai.beam.metrics import collector
|
48
38
|
from vnai.beam.quota import guardian
|
49
|
-
|
50
|
-
# Record check time
|
51
39
|
self.last_check = time.time()
|
52
|
-
|
53
|
-
# Check metrics collector health
|
54
40
|
metrics_summary = collector.get_metrics_summary()
|
55
41
|
has_errors = metrics_summary.get("error", 0) > 0
|
56
|
-
|
57
|
-
# Check resource usage
|
58
42
|
resource_usage = guardian.usage()
|
59
|
-
high_usage = resource_usage > 80
|
60
|
-
|
61
|
-
# Determine health status
|
43
|
+
high_usage = resource_usage > 80
|
62
44
|
if has_errors and high_usage:
|
63
|
-
self.health_status =
|
45
|
+
self.health_status ="critical"
|
64
46
|
self.error_count += 1
|
65
47
|
elif has_errors or high_usage:
|
66
|
-
self.health_status =
|
48
|
+
self.health_status ="warning"
|
67
49
|
self.warning_count += 1
|
68
50
|
else:
|
69
|
-
self.health_status =
|
70
|
-
|
71
|
-
# Record health status
|
51
|
+
self.health_status ="healthy"
|
72
52
|
self.status_history.append({
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
53
|
+
"timestamp": datetime.now().isoformat(),
|
54
|
+
"status": self.health_status,
|
55
|
+
"metrics": metrics_summary,
|
56
|
+
"resource_usage": resource_usage
|
77
57
|
})
|
78
|
-
|
79
|
-
# Keep history limited to last 10 entries
|
80
58
|
if len(self.status_history) > 10:
|
81
59
|
self.status_history = self.status_history[-10:]
|
82
|
-
|
83
60
|
return self.health_status
|
84
|
-
|
61
|
+
|
85
62
|
def report(self):
|
86
|
-
"""Get health report"""
|
87
|
-
# Ensure we have a fresh check if last one is old
|
88
63
|
if time.time() - self.last_check > self.check_interval:
|
89
64
|
self.check_health()
|
90
|
-
|
91
65
|
return {
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
66
|
+
"status": self.health_status,
|
67
|
+
"last_check": datetime.fromtimestamp(self.last_check).isoformat(),
|
68
|
+
"error_count": self.error_count,
|
69
|
+
"warning_count": self.warning_count,
|
70
|
+
"history": self.status_history[-3:],
|
97
71
|
}
|
98
|
-
|
72
|
+
|
99
73
|
def reset(self):
|
100
|
-
|
101
|
-
self.health_status = "healthy"
|
74
|
+
self.health_status ="healthy"
|
102
75
|
self.error_count = 0
|
103
76
|
self.warning_count = 0
|
104
77
|
self.status_history = []
|
105
78
|
self.last_check = time.time()
|
106
|
-
|
107
|
-
# Create singleton instance
|
108
|
-
monitor = Monitor()
|
79
|
+
monitor = Monitor()
|