vnai 2.0.2__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/scope/promo.py CHANGED
@@ -1,55 +1,170 @@
1
- _G='init'
2
- _F='simple'
3
- _E='markdown'
4
- _D=True
5
- _C=None
6
- _B='terminal'
7
- _A='html'
1
+ """
2
+ Promo module for vnai: fetches and presents promotional content periodically or on demand,
3
+ using logging instead of printing to avoid polluting stdout for AI tools.
4
+ """
5
+ import logging
8
6
  import requests
9
7
  from datetime import datetime
10
- import random,threading,time,urllib.parse
8
+ import random
9
+ import threading
10
+ import time
11
+ import urllib.parse
12
+
13
+ # Module-level logger setup
14
+ logger = logging.getLogger(__name__)
15
+ if not logger.hasHandlers():
16
+ # Add a simple stream handler that only outputs the message text
17
+ handler = logging.StreamHandler()
18
+ handler.setFormatter(logging.Formatter('%(message)s'))
19
+ logger.addHandler(handler)
20
+ logger.setLevel(logging.INFO)
21
+
11
22
  class ContentManager:
12
- _instance=_C;_lock=threading.Lock()
13
- def __new__(A):
14
- with A._lock:
15
- if A._instance is _C:A._instance=super(ContentManager,A).__new__(A);A._instance._initialize()
16
- return A._instance
17
- def _initialize(A):A.last_display=0;A.display_interval=86400;A.content_base_url='https://vnstock-beam.hf.space/content-delivery';A.target_url='https://vnstocks.com/lp-khoa-hoc-python-chung-khoan';A.image_url='https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg';A._start_periodic_display()
18
- def _start_periodic_display(A):
19
- def B():
20
- while _D:
21
- B=random.randint(7200,21600);time.sleep(B);C=time.time()
22
- if C-A.last_display>=A.display_interval:A.present_content(context='periodic')
23
- C=threading.Thread(target=B,daemon=_D);C.start()
24
- def fetch_remote_content(B,context=_G,html=_D):
25
- try:
26
- C={'context':context,_A:'true'if html else'false'};D=f"{B.content_base_url}?{urllib.parse.urlencode(C)}";A=requests.get(D,timeout=3)
27
- if A.status_code==200:return A.text
28
- return
29
- except:return
30
- def present_content(C,environment=_C,context=_G):
31
- I='jupyter';H='unknown';E=context;A=environment;C.last_display=time.time()
32
- if A is _C:
33
- try:from vnai.scope.profile import inspector as J;A=J.examine().get('environment',H)
34
- except:A=H
35
- if A==I:B=C.fetch_remote_content(context=E,html=_D)
36
- else:B=C.fetch_remote_content(context=E,html=False)
37
- D=C._generate_fallback_content(E)
38
- if A==I:
39
- try:
40
- from IPython.display import display as F,HTML as G,Markdown as K
41
- if B:F(G(B))
42
- else:
43
- try:F(K(D[_E]))
44
- except:F(G(D[_A]))
45
- except:pass
46
- elif A==_B:
47
- if B:print(B)
48
- else:print(D[_B])
49
- else:print(D[_F])
50
- def _generate_fallback_content(B,context):
51
- A={_A:'',_E:'',_B:'',_F:''}
52
- if context=='loop':A[_A]=f'''
23
+ """
24
+ Singleton manager to fetch remote or fallback promotional content and
25
+ present it in different environments (Jupyter, terminal, other).
26
+
27
+ Displays content automatically at randomized intervals via a background thread.
28
+ """
29
+ _instance = None
30
+ _lock = threading.Lock()
31
+
32
+ def __new__(cls):
33
+ """
34
+ Ensure only one instance of ContentManager is created (thread-safe).
35
+ """
36
+ with cls._lock:
37
+ if cls._instance is None:
38
+ cls._instance = super(ContentManager, cls).__new__(cls)
39
+ cls._instance._initialize()
40
+ return cls._instance
41
+
42
+ def _initialize(self):
43
+ """
44
+ Internal initializer: sets up display timing, URLs, and starts the periodic display thread.
45
+ """
46
+ # Timestamp of last content display (epoch seconds)
47
+ self.last_display = 0
48
+ # Minimum interval between displays (24 hours)
49
+ self.display_interval = 24 * 3600
50
+
51
+ # Base endpoints for fetching remote content and linking
52
+ self.content_base_url = "https://vnstock-beam.hf.space/content-delivery"
53
+ self.target_url = "https://vnstocks.com/lp-khoa-hoc-python-chung-khoan"
54
+ self.image_url = (
55
+ "https://vnstocks.com/img/trang-chu-vnstock-python-api-phan-tich-giao-dich-chung-khoan.jpg"
56
+ )
57
+
58
+ # Launch the background thread to periodically present content
59
+ self._start_periodic_display()
60
+
61
+ def _start_periodic_display(self):
62
+ """
63
+ Launch a daemon thread that sleeps a random duration between 2–6 hours,
64
+ then checks if the display interval has elapsed and calls present_content.
65
+ """
66
+ def periodic_display():
67
+ while True:
68
+ # Randomize sleep to avoid synchronized requests across instances
69
+ sleep_time = random.randint(2 * 3600, 6 * 3600)
70
+ time.sleep(sleep_time)
71
+
72
+ # Present content if enough time has passed since last_display
73
+ current_time = time.time()
74
+ if current_time - self.last_display >= self.display_interval:
75
+ self.present_content(context="periodic")
76
+
77
+ thread = threading.Thread(target=periodic_display, daemon=True)
78
+ thread.start()
79
+
80
+ def fetch_remote_content(self, context: str = "init", html: bool = True) -> str:
81
+ """
82
+ Fetch promotional content from remote service with context and format flag.
83
+
84
+ Args:
85
+ context: usage context (e.g., "init", "periodic", "loop").
86
+ html: if True, request HTML; otherwise plain text.
87
+
88
+ Returns:
89
+ The content string on HTTP 200, or None on failure.
90
+ """
91
+ try:
92
+ # Build query params and URL
93
+ params = {"context": context, "html": "true" if html else "false"}
94
+ url = f"{self.content_base_url}?{urllib.parse.urlencode(params)}"
95
+
96
+ response = requests.get(url, timeout=3)
97
+ if response.status_code == 200:
98
+ return response.text
99
+ # Log non-200 responses at debug level
100
+ logger.debug(f"Non-200 response fetching content: {response.status_code}")
101
+ return None
102
+ except Exception as e:
103
+ # Log exceptions without interrupting user code
104
+ logger.debug(f"Failed to fetch remote content: {e}")
105
+ return None
106
+
107
+ def present_content(self, environment: str = None, context: str = "init") -> None:
108
+ """
109
+ Present content according to the detected environment:
110
+ - In Jupyter: use display(HTML or Markdown).
111
+ - In terminal or other: log via logger.info().
112
+
113
+ Args:
114
+ environment: override detected environment ("jupyter", "terminal", else).
115
+ context: same context flag passed to fetch_remote_content and fallback logic.
116
+ """
117
+ # Update last display timestamp
118
+ self.last_display = time.time()
119
+
120
+ # Auto-detect environment if not provided
121
+ if environment is None:
122
+ try:
123
+ from vnai.scope.profile import inspector
124
+ environment = inspector.examine().get("environment", "unknown")
125
+ except Exception:
126
+ environment = "unknown"
127
+
128
+ # Retrieve remote or HTML/text content based on environment
129
+ remote_content = self.fetch_remote_content(
130
+ context=context, html=(environment == "jupyter")
131
+ )
132
+ # Generate fallback messages if remote fetch fails
133
+ fallback = self._generate_fallback_content(context)
134
+
135
+ if environment == "jupyter":
136
+ # Rich display in Jupyter notebooks
137
+ try:
138
+ from IPython.display import display, HTML, Markdown
139
+
140
+ if remote_content:
141
+ display(HTML(remote_content))
142
+ else:
143
+ # Try Markdown, fallback to HTML
144
+ try:
145
+ display(Markdown(fallback["markdown"]))
146
+ except Exception:
147
+ display(HTML(fallback["html"]))
148
+ except Exception as e:
149
+ logger.debug(f"Jupyter display failed: {e}")
150
+
151
+ elif environment == "terminal":
152
+ # Log terminal-friendly or raw content via logger
153
+ if remote_content:
154
+ logger.info(remote_content)
155
+ else:
156
+ logger.info(fallback["terminal"])
157
+
158
+ else:
159
+ # Generic simple message for other environments
160
+ logger.info(fallback["simple"])
161
+
162
+ def _generate_fallback_content(self, context):
163
+ fallback = {"html": "", "markdown": "", "terminal": "", "simple": ""}
164
+
165
+ if context == "loop":
166
+ fallback["html"] = (
167
+ f"""
53
168
  <div style="border: 1px solid #e74c3c; padding: 15px; border-radius: 5px; margin: 10px 0;">
54
169
  <h3 style="color: #e74c3c;">⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests</h3>
55
170
  <p>Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:</p>
@@ -59,8 +174,40 @@ class ContentManager:
59
174
  <li>Tham gia gói tài trợ <a href="https://vnstocks.com/insiders-program" style="color: #3498db;">Vnstock Insider</a> để tăng 5X giới hạn API</li>
60
175
  </ul>
61
176
  </div>
62
- ''';A[_E]='\n## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests\n\nĐể tránh bị giới hạn tốc độ và tối ưu hiệu suất:\n* Thêm thời gian chờ giữa các lần gọi API\n* Sử dụng xử lý theo batch thay vì lặp liên tục\n* Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API\n ';A[_B]='\n╔═════════════════════════════════════════════════════════════════╗\n║ ║\n║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║\n║ ║\n║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║\n║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║\n║ ║\n║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║\n║ https://vnstocks.com/insiders-program ║\n║ ║\n╚═════════════════════════════════════════════════════════════════╝\n ';A[_F]='🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói Vnstock Insider: https://vnstocks.com/insiders-program'
63
- else:A[_A]=f'''
177
+ """
178
+ )
179
+ fallback["markdown"] = (
180
+ """
181
+ ## ⚠️ Bạn đang sử dụng vòng lặp với quá nhiều requests
182
+
183
+ Để tránh bị giới hạn tốc độ và tối ưu hiệu suất:
184
+ * Thêm thời gian chờ giữa các lần gọi API
185
+ * Sử dụng xử lý theo batch thay vì lặp liên tục
186
+ * Tham gia gói tài trợ [Vnstock Insider](https://vnstocks.com/insiders-program) để tăng 5X giới hạn API
187
+ """
188
+ )
189
+ fallback["terminal"] = (
190
+ """
191
+ ╔═════════════════════════════════════════════════════════════════╗
192
+ ║ ║
193
+ ║ 🚫 ĐANG BỊ CHẶN BỞI GIỚI HẠN API? GIẢI PHÁP Ở ĐÂY! ║
194
+ ║ ║
195
+ ║ ✓ Tăng ngay 500% tốc độ gọi API - Không còn lỗi RateLimit ║
196
+ ║ ✓ Tiết kiệm 85% thời gian chờ đợi giữa các request ║
197
+ ║ ║
198
+ ║ ➤ NÂNG CẤP NGAY VỚI GÓI TÀI TRỢ VNSTOCK: ║
199
+ ║ https://vnstocks.com/insiders-program ║
200
+ ║ ║
201
+ ╚═════════════════════════════════════════════════════════════════╝
202
+ """
203
+ )
204
+ fallback["simple"] = (
205
+ "🚫 Đang bị giới hạn API? Tăng tốc độ gọi API lên 500% với gói "
206
+ "Vnstock Insider: https://vnstocks.com/insiders-program"
207
+ )
208
+ else:
209
+ fallback["html"] = (
210
+ f"""
64
211
  <div style="border: 1px solid #3498db; padding: 15px; border-radius: 5px; margin: 10px 0;">
65
212
  <h3 style="color: #3498db;">👋 Chào mừng bạn đến với Vnstock!</h3>
66
213
  <p>Cảm ơn bạn đã sử dụng thư viện phân tích chứng khoán #1 tại Việt Nam cho Python</p>
@@ -70,7 +217,54 @@ class ContentManager:
70
217
  </ul>
71
218
  <p>Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.</p>
72
219
  </div>
73
- ''';A[_E]='\n## 👋 Chào mừng bạn đến với Vnstock!\n\nCảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam\n\n* Tài liệu: [vnstocks.com/docs](https://vnstocks.com/docs)\n* Cộng đồng: [vnstocks.com/community](https://vnstocks.com/community)\n\nKhám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.\n ';A[_B]='\n╔══════════════════════════════════════════════════════════╗\n║ ║\n║ 👋 Chào mừng bạn đến với Vnstock! ║\n║ ║\n║ Cảm ơn bạn đã sử dụng package phân tích ║\n║ chứng khoán #1 tại Việt Nam ║\n║ ║\n║ ✓ Tài liệu: https://vnstocks.com/docs ║\n║ ✓ Cộng đồng: https://vnstocks.com/community ║\n║ ║\n║ Khám phá các tính năng mới nhất và tham gia ║\n║ cộng đồng để nhận hỗ trợ. ║\n║ ║\n╚══════════════════════════════════════════════════════════╝\n ';A[_F]='👋 Chào mừng bạn đến với Vnstock! Tài liệu: https://vnstocks.com/docs | Cộng đồng: https://vnstocks.com/community'
74
- return A
75
- manager=ContentManager()
76
- def present(context=_G):return manager.present_content(context=context)
220
+ """
221
+ )
222
+ fallback["markdown"] = (
223
+ """
224
+ ## 👋 Chào mừng bạn đến với Vnstock!
225
+
226
+ Cảm ơn bạn đã sử dụng package phân tích chứng khoán #1 tại Việt Nam
227
+
228
+ * Tài liệu: [Sổ tay hướng dẫn](https://vnstocks.com/docs/category/s%E1%BB%95-tay-h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn)
229
+ * Cộng đồng: [Nhóm Facebook](https://www.facebook.com/groups/vnstock.official)
230
+
231
+ Khám phá các tính năng mới nhất và tham gia cộng đồng để nhận hỗ trợ.
232
+ """
233
+ )
234
+ fallback["terminal"] = (
235
+ """
236
+ ╔══════════════════════════════════════════════════════════╗
237
+ ║ ║
238
+ ║ 👋 Chào mừng bạn đến với Vnstock! ║
239
+ ║ ║
240
+ ║ Cảm ơn bạn đã sử dụng package phân tích ║
241
+ ║ chứng khoán #1 tại Việt Nam ║
242
+ ║ ║
243
+ ║ ✓ Tài liệu: https://vnstocks.com/docs/category/s%E1%BB%95-tay-h%C6%B0%E1%BB%9Bng-d%E1%BA%ABn ║
244
+ ║ ✓ Cộng đồng: https://www.facebook.com/groups/vnstock.official ║
245
+ ║ ║
246
+ ║ Khám phá các tính năng mới nhất và tham gia ║
247
+ ║ cộng đồng để nhận hỗ trợ. ║
248
+ ║ ║
249
+ ╚══════════════════════════════════════════════════════════╝
250
+ """
251
+ )
252
+ fallback["simple"] = (
253
+ "👋 Chào mừng bạn đến với Vnstock! "
254
+ "Tài liệu: https://vnstocks.com/docs/tai-lieu/huong-dan-nhanh | "
255
+ "Cộng đồng: https://www.facebook.com/groups/vnstock.official"
256
+ )
257
+ return fallback
258
+
259
+ # Singleton instance for module-level use
260
+ manager = ContentManager()
261
+
262
+
263
+ def present(context: str = "init") -> None:
264
+ """
265
+ Shortcut to ContentManager.present_content for external callers.
266
+
267
+ Args:
268
+ context: propagate context string to ContentManager.
269
+ """
270
+ return manager.present_content(context=context)
vnai/scope/state.py CHANGED
@@ -1,74 +1,223 @@
1
- _L='minimal'
2
- _K='warnings'
3
- _J='api_requests'
4
- _I='last_error_time'
5
- _H='startup_time'
6
- _G='standard'
7
- _F='function_calls'
8
- _E='peak_memory'
9
- _D='errors'
10
- _C=True
11
- _B=None
12
- _A='execution_times'
13
- import time,threading,json,os
1
+ # vnai/scope/state.py
2
+ # System state tracking
3
+
4
+ import time
5
+ import threading
6
+ import json
7
+ import os
14
8
  from datetime import datetime
15
9
  from pathlib import Path
10
+
16
11
  class Tracker:
17
- _instance=_B;_lock=threading.Lock()
18
- def __new__(A):
19
- with A._lock:
20
- if A._instance is _B:A._instance=super(Tracker,A).__new__(A);A._instance._initialize()
21
- return A._instance
22
- def _initialize(A):A.metrics={_H:datetime.now().isoformat(),_F:0,_J:0,_D:0,_K:0};A.performance_metrics={_A:[],_I:_B,_E:0};A.privacy_level=_G;A.home_dir=Path.home();A.project_dir=A.home_dir/'.vnstock';A.project_dir.mkdir(exist_ok=_C);A.data_dir=A.project_dir/'data';A.data_dir.mkdir(exist_ok=_C);A.metrics_path=A.data_dir/'usage_metrics.json';A.privacy_config_path=A.project_dir/'config'/'privacy.json';os.makedirs(os.path.dirname(A.privacy_config_path),exist_ok=_C);A._load_metrics();A._load_privacy_settings();A._start_background_collector()
23
- def _load_metrics(A):
24
- if A.metrics_path.exists():
25
- try:
26
- with open(A.metrics_path,'r')as C:D=json.load(C)
27
- for(B,E)in D.items():
28
- if B in A.metrics:A.metrics[B]=E
29
- except:pass
30
- def _save_metrics(A):
31
- try:
32
- with open(A.metrics_path,'w')as B:json.dump(A.metrics,B)
33
- except:pass
34
- def _load_privacy_settings(A):
35
- if A.privacy_config_path.exists():
36
- try:
37
- with open(A.privacy_config_path,'r')as B:C=json.load(B);A.privacy_level=C.get('level',_G)
38
- except:pass
39
- def setup_privacy(B,level=_B):
40
- A=level;C={_L:'Essential system data only',_G:'Performance metrics and errors','enhanced':'Detailed operation analytics'}
41
- if A is _B:A=_G
42
- if A not in C:raise ValueError(f"Invalid privacy level: {A}. Choose from {', '.join(C.keys())}")
43
- B.privacy_level=A
44
- with open(B.privacy_config_path,'w')as D:json.dump({'level':A},D)
45
- return A
46
- def get_privacy_level(A):return A.privacy_level
47
- def _start_background_collector(A):
48
- def B():
49
- while _C:
50
- try:
51
- import psutil as C;D=C.Process();E=D.memory_info();B=E.rss/1048576
52
- if B>A.performance_metrics[_E]:A.performance_metrics[_E]=B
53
- A._save_metrics()
54
- except:pass
55
- time.sleep(300)
56
- C=threading.Thread(target=B,daemon=_C);C.start()
57
- def record(A,event_type,data=_B):
58
- D='execution_time';C=data;B=event_type
59
- if A.privacy_level==_L and B!=_D:return _C
60
- if B in A.metrics:A.metrics[B]+=1
61
- else:A.metrics[B]=1
62
- if B==_D:A.performance_metrics[_I]=datetime.now().isoformat()
63
- if B==_F and C and D in C:
64
- A.performance_metrics[_A].append(C[D])
65
- if len(A.performance_metrics[_A])>100:A.performance_metrics[_A]=A.performance_metrics[_A][-100:]
66
- if A.metrics[_F]%100==0 or B==_D:A._save_metrics()
67
- return _C
68
- def get_metrics(A):
69
- B=0
70
- if A.performance_metrics[_A]:B=sum(A.performance_metrics[_A])/len(A.performance_metrics[_A])
71
- C=A.metrics.copy();C.update({'avg_execution_time':B,'peak_memory_mb':A.performance_metrics[_E],'uptime':(datetime.now()-datetime.fromisoformat(A.metrics[_H])).total_seconds(),'privacy_level':A.privacy_level});return C
72
- def reset(A):A.metrics={_H:datetime.now().isoformat(),_F:0,_J:0,_D:0,_K:0};A.performance_metrics={_A:[],_I:_B,_E:0};A._save_metrics();return _C
73
- tracker=Tracker()
74
- def record(event_type,data=_B):return tracker.record(event_type,data)
12
+ """Tracks system state and performance metrics"""
13
+
14
+ _instance = None
15
+ _lock = threading.Lock()
16
+
17
+ def __new__(cls):
18
+ with cls._lock:
19
+ if cls._instance is None:
20
+ cls._instance = super(Tracker, cls).__new__(cls)
21
+ cls._instance._initialize()
22
+ return cls._instance
23
+
24
+ def _initialize(self):
25
+ """Initialize tracker"""
26
+ self.metrics = {
27
+ "startup_time": datetime.now().isoformat(),
28
+ "function_calls": 0,
29
+ "api_requests": 0,
30
+ "errors": 0,
31
+ "warnings": 0
32
+ }
33
+
34
+ self.performance_metrics = {
35
+ "execution_times": [],
36
+ "last_error_time": None,
37
+ "peak_memory": 0
38
+ }
39
+
40
+ self.privacy_level = "standard"
41
+
42
+ # Setup data directory
43
+ self.home_dir = Path.home()
44
+ self.project_dir = self.home_dir / ".vnstock"
45
+ self.project_dir.mkdir(exist_ok=True)
46
+ self.data_dir = self.project_dir / 'data'
47
+ self.data_dir.mkdir(exist_ok=True)
48
+ self.metrics_path = self.data_dir / "usage_metrics.json"
49
+ self.privacy_config_path = self.project_dir / 'config' / "privacy.json"
50
+
51
+ # Create config directory if it doesn't exist
52
+ os.makedirs(os.path.dirname(self.privacy_config_path), exist_ok=True)
53
+
54
+ # Load existing metrics
55
+ self._load_metrics()
56
+
57
+ # Load privacy settings
58
+ self._load_privacy_settings()
59
+
60
+ # Start background metrics collector
61
+ self._start_background_collector()
62
+
63
+ def _load_metrics(self):
64
+ """Load metrics from file"""
65
+ if self.metrics_path.exists():
66
+ try:
67
+ with open(self.metrics_path, 'r') as f:
68
+ stored_metrics = json.load(f)
69
+
70
+ # Update metrics with stored values
71
+ for key, value in stored_metrics.items():
72
+ if key in self.metrics:
73
+ self.metrics[key] = value
74
+ except:
75
+ pass
76
+
77
+ def _save_metrics(self):
78
+ """Save metrics to file"""
79
+ try:
80
+ with open(self.metrics_path, 'w') as f:
81
+ json.dump(self.metrics, f)
82
+ except:
83
+ pass
84
+
85
+ def _load_privacy_settings(self):
86
+ """Load privacy settings"""
87
+ if self.privacy_config_path.exists():
88
+ try:
89
+ with open(self.privacy_config_path, 'r') as f:
90
+ settings = json.load(f)
91
+ self.privacy_level = settings.get("level", "standard")
92
+ except:
93
+ pass
94
+
95
+ def setup_privacy(self, level=None):
96
+ """Configure privacy level for data collection"""
97
+ privacy_levels = {
98
+ "minimal": "Essential system data only",
99
+ "standard": "Performance metrics and errors",
100
+ "enhanced": "Detailed operation analytics"
101
+ }
102
+
103
+ if level is None:
104
+ # Default level
105
+ level = "standard"
106
+
107
+ if level not in privacy_levels:
108
+ raise ValueError(f"Invalid privacy level: {level}. Choose from {', '.join(privacy_levels.keys())}")
109
+
110
+ # Store preference
111
+ self.privacy_level = level
112
+
113
+ # Store in configuration file
114
+ with open(self.privacy_config_path, "w") as f:
115
+ json.dump({"level": level}, f)
116
+
117
+ return level
118
+
119
+ def get_privacy_level(self):
120
+ """Get current privacy level"""
121
+ return self.privacy_level
122
+
123
+ def _start_background_collector(self):
124
+ """Start background metrics collection"""
125
+ def collect_metrics():
126
+ while True:
127
+ try:
128
+ import psutil
129
+
130
+ # Update peak memory
131
+ current_process = psutil.Process()
132
+ memory_info = current_process.memory_info()
133
+ memory_usage = memory_info.rss / (1024 * 1024) # MB
134
+
135
+ if memory_usage > self.performance_metrics["peak_memory"]:
136
+ self.performance_metrics["peak_memory"] = memory_usage
137
+
138
+ # Save metrics periodically
139
+ self._save_metrics()
140
+
141
+ except:
142
+ pass
143
+
144
+ time.sleep(300) # Run every 5 minutes
145
+
146
+ # Start thread
147
+ thread = threading.Thread(target=collect_metrics, daemon=True)
148
+ thread.start()
149
+
150
+ def record(self, event_type, data=None):
151
+ """Record an event"""
152
+ # Check privacy level
153
+ if self.privacy_level == "minimal" and event_type != "errors":
154
+ # In minimal mode, only track errors
155
+ return True
156
+
157
+ # Update counts
158
+ if event_type in self.metrics:
159
+ self.metrics[event_type] += 1
160
+ else:
161
+ self.metrics[event_type] = 1
162
+
163
+ # Special handling for errors
164
+ if event_type == "errors":
165
+ self.performance_metrics["last_error_time"] = datetime.now().isoformat()
166
+
167
+ # Special handling for function calls with timing data
168
+ if event_type == "function_calls" and data and "execution_time" in data:
169
+ # Keep up to 100 latest execution times
170
+ self.performance_metrics["execution_times"].append(data["execution_time"])
171
+ if len(self.performance_metrics["execution_times"]) > 100:
172
+ self.performance_metrics["execution_times"] = self.performance_metrics["execution_times"][-100:]
173
+
174
+ # Save if metrics change significantly
175
+ if self.metrics["function_calls"] % 100 == 0 or event_type == "errors":
176
+ self._save_metrics()
177
+
178
+ return True
179
+
180
+ def get_metrics(self):
181
+ """Get current metrics"""
182
+ # Calculate derived metrics
183
+ avg_execution_time = 0
184
+ if self.performance_metrics["execution_times"]:
185
+ avg_execution_time = sum(self.performance_metrics["execution_times"]) / len(self.performance_metrics["execution_times"])
186
+
187
+ # Add derived metrics to output
188
+ output = self.metrics.copy()
189
+ output.update({
190
+ "avg_execution_time": avg_execution_time,
191
+ "peak_memory_mb": self.performance_metrics["peak_memory"],
192
+ "uptime": (datetime.now() - datetime.fromisoformat(self.metrics["startup_time"])).total_seconds(),
193
+ "privacy_level": self.privacy_level
194
+ })
195
+
196
+ return output
197
+
198
+ def reset(self):
199
+ """Reset metrics"""
200
+ self.metrics = {
201
+ "startup_time": datetime.now().isoformat(),
202
+ "function_calls": 0,
203
+ "api_requests": 0,
204
+ "errors": 0,
205
+ "warnings": 0
206
+ }
207
+
208
+ self.performance_metrics = {
209
+ "execution_times": [],
210
+ "last_error_time": None,
211
+ "peak_memory": 0
212
+ }
213
+
214
+ self._save_metrics()
215
+ return True
216
+
217
+ # Create singleton instance
218
+ tracker = Tracker()
219
+
220
+
221
+ def record(event_type, data=None):
222
+ """Record an event"""
223
+ return tracker.record(event_type, data)
@@ -1,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vnai
3
- Version: 2.0.2
3
+ Version: 2.0.4
4
4
  Summary: System optimization and resource management toolkit
5
- Home-page: https://github.com/yourusername/vnai
6
- Author: Your Name
7
- Author-email: your.email@example.com
5
+ Home-page: https://github.com/vnstock-hq/initialization/new/main
6
+ Author: Vnstock HQ
7
+ Author-email: support@vnstocks.com
8
8
  License: MIT
9
9
  Classifier: Programming Language :: Python :: 3
10
10
  Classifier: Operating System :: OS Independent
@@ -17,7 +17,6 @@ Requires-Dist: requests>=2.25.0
17
17
  Requires-Dist: psutil>=5.8.0
18
18
  Provides-Extra: dev
19
19
  Requires-Dist: pytest>=6.0.0; extra == "dev"
20
- Requires-Dist: black>=21.5b2; extra == "dev"
21
20
  Dynamic: author
22
21
  Dynamic: author-email
23
22
  Dynamic: classifier
@@ -0,0 +1,16 @@
1
+ vnai/__init__.py,sha256=ClnS_1T23BjbzejrIW4KTsAVBhXuq9bjDUcRP5AIcPo,9082
2
+ vnai/beam/__init__.py,sha256=xKb_iu9aAPXCulI7dENrvqVIhelSD1mIqKE9Go3GAHw,200
3
+ vnai/beam/metrics.py,sha256=xVmVw93yhKeWzRZJurmrD9mWur16HyLJl_p1XqMwW_w,7187
4
+ vnai/beam/pulse.py,sha256=jp1YwjLaMhne2nYhM5PofveDsdrSp2YtewQ2jjE78Is,3470
5
+ vnai/beam/quota.py,sha256=yP5_Z62QJwOoCEgqqWuNkzm1Dar70UiJsu6W27LNqiw,21218
6
+ vnai/flow/__init__.py,sha256=K3OeabzAWGrdPgTAOlDqrJh2y9aQW2pgLZg8tblN3ho,147
7
+ vnai/flow/queue.py,sha256=b9YKUbiXDZRC3fVgEnA77EO0EMXAi8eCoBkHnAUI5Sc,4162
8
+ vnai/flow/relay.py,sha256=RtIPRZ3BlQd-XgTbisJg0iC1HqikAjHGyyo8aTj_fUw,15766
9
+ vnai/scope/__init__.py,sha256=overJZ_UiEfBRNcSieE1GPU_9X3oS4C5l6JeBaFFVxk,267
10
+ vnai/scope/profile.py,sha256=6LL7Djke9F1HVA9eEExud2jZ5yGUfy9_NYt68nIj2-8,30737
11
+ vnai/scope/promo.py,sha256=N4aWZdh92rVNovjCwuUjv-QdhoRwloPAn4Hydx4IcRs,12515
12
+ vnai/scope/state.py,sha256=LlcZNKBy2mcAnD765BO2Tlv3Zzbak2TOEz4RUPMCFZ8,7490
13
+ vnai-2.0.4.dist-info/METADATA,sha256=rgGwwXBeevv4rjzJ-hLADRkGnPcuTaC3rMnSgd_WMRw,988
14
+ vnai-2.0.4.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
15
+ vnai-2.0.4.dist-info/top_level.txt,sha256=4zI0qZHePCwvgSqXl4420sBcd0VzZn4MEcRsAIFae3k,5
16
+ vnai-2.0.4.dist-info/RECORD,,