qyro 2.0.0__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.
Files changed (45) hide show
  1. qyro/__init__.py +17 -0
  2. qyro/adapters/__init__.py +4 -0
  3. qyro/adapters/language_adapters/__init__.py +4 -0
  4. qyro/adapters/language_adapters/c/__init__.py +4 -0
  5. qyro/adapters/language_adapters/python/__init__.py +4 -0
  6. qyro/adapters/language_adapters/python/python_adapter.py +584 -0
  7. qyro/cli/__init__.py +8 -0
  8. qyro/cli/__main__.py +5 -0
  9. qyro/cli/cli.py +392 -0
  10. qyro/cli/interactive.py +297 -0
  11. qyro/common/__init__.py +37 -0
  12. qyro/common/animation.py +82 -0
  13. qyro/common/builder.py +434 -0
  14. qyro/common/compiler.py +895 -0
  15. qyro/common/config.py +93 -0
  16. qyro/common/constants.py +99 -0
  17. qyro/common/errors.py +176 -0
  18. qyro/common/frontend.py +74 -0
  19. qyro/common/health.py +358 -0
  20. qyro/common/kafka_manager.py +192 -0
  21. qyro/common/logging.py +149 -0
  22. qyro/common/memory.py +147 -0
  23. qyro/common/metrics.py +301 -0
  24. qyro/common/monitoring.py +468 -0
  25. qyro/common/parser.py +91 -0
  26. qyro/common/platform.py +609 -0
  27. qyro/common/redis_memory.py +1108 -0
  28. qyro/common/rpc.py +287 -0
  29. qyro/common/sandbox.py +191 -0
  30. qyro/common/schema_loader.py +33 -0
  31. qyro/common/secure_sandbox.py +490 -0
  32. qyro/common/toolchain_validator.py +617 -0
  33. qyro/common/type_generator.py +176 -0
  34. qyro/common/validation.py +401 -0
  35. qyro/common/validator.py +204 -0
  36. qyro/gateway/__init__.py +8 -0
  37. qyro/gateway/gateway.py +303 -0
  38. qyro/orchestrator/__init__.py +8 -0
  39. qyro/orchestrator/orchestrator.py +1223 -0
  40. qyro-2.0.0.dist-info/METADATA +244 -0
  41. qyro-2.0.0.dist-info/RECORD +45 -0
  42. qyro-2.0.0.dist-info/WHEEL +5 -0
  43. qyro-2.0.0.dist-info/entry_points.txt +2 -0
  44. qyro-2.0.0.dist-info/licenses/LICENSE +21 -0
  45. qyro-2.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,468 @@
1
+ """
2
+ Qyro Monitoring and Observability System
3
+ Production-grade metrics, health checks, and observability for the Qyro runtime.
4
+ """
5
+
6
+ import time
7
+ import threading
8
+ import json
9
+ from typing import Dict, Any, List, Optional, Callable
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime
12
+ from enum import Enum
13
+ import psutil
14
+ import os
15
+ import logging
16
+ from collections import deque
17
+ from contextlib import contextmanager
18
+
19
+
20
+ class Severity(Enum):
21
+ """Log severity levels."""
22
+ DEBUG = 10
23
+ INFO = 20
24
+ WARNING = 30
25
+ ERROR = 40
26
+ CRITICAL = 50
27
+
28
+
29
+ @dataclass
30
+ class LogEntry:
31
+ """A single log entry."""
32
+ timestamp: float
33
+ level: Severity
34
+ message: str
35
+ module: str
36
+ details: Dict[str, Any] = field(default_factory=dict)
37
+
38
+
39
+ @dataclass
40
+ class MetricPoint:
41
+ """A single metric measurement."""
42
+ name: str
43
+ value: float
44
+ timestamp: float
45
+ labels: Dict[str, str] = field(default_factory=dict)
46
+
47
+
48
+ class MetricsCollector:
49
+ """Collects and stores metrics for the Qyro system."""
50
+
51
+ def __init__(self, retention_minutes: int = 60):
52
+ self.retention_seconds = retention_minutes * 60
53
+ self.metrics: Dict[str, deque] = {}
54
+ self._lock = threading.Lock()
55
+ self.start_time = time.time()
56
+
57
+ def record_metric(self, name: str, value: float, labels: Dict[str, str] = None):
58
+ """Record a metric value."""
59
+ labels = labels or {}
60
+ key = f"{name}:{json.dumps(labels, sort_keys=True)}"
61
+
62
+ with self._lock:
63
+ if key not in self.metrics:
64
+ self.metrics[key] = deque()
65
+
66
+ # Remove old metrics beyond retention period
67
+ now = time.time()
68
+ while self.metrics[key] and (now - self.metrics[key][0].timestamp) > self.retention_seconds:
69
+ self.metrics[key].popleft()
70
+
71
+ metric_point = MetricPoint(
72
+ name=name,
73
+ value=value,
74
+ timestamp=now,
75
+ labels=labels
76
+ )
77
+ self.metrics[key].append(metric_point)
78
+
79
+ def get_latest(self, name: str, labels: Dict[str, str] = None) -> Optional[MetricPoint]:
80
+ """Get the latest value for a metric."""
81
+ labels = labels or {}
82
+ key = f"{name}:{json.dumps(labels, sort_keys=True)}"
83
+
84
+ with self._lock:
85
+ if key in self.metrics and self.metrics[key]:
86
+ return self.metrics[key][-1]
87
+ return None
88
+
89
+ def get_values(self, name: str, labels: Dict[str, str] = None,
90
+ since: float = None) -> List[MetricPoint]:
91
+ """Get metric values, optionally filtered by time."""
92
+ labels = labels or {}
93
+ key = f"{name}:{json.dumps(labels, sort_keys=True)}"
94
+
95
+ with self._lock:
96
+ if key not in self.metrics:
97
+ return []
98
+
99
+ values = list(self.metrics[key])
100
+ if since is not None:
101
+ values = [v for v in values if v.timestamp >= since]
102
+
103
+ return values
104
+
105
+ def get_system_metrics(self) -> Dict[str, float]:
106
+ """Get system-level metrics."""
107
+ return {
108
+ 'cpu_percent': psutil.cpu_percent(interval=0.1),
109
+ 'memory_percent': psutil.virtual_memory().percent,
110
+ 'disk_percent': psutil.disk_usage('/').percent,
111
+ 'process_count': len(psutil.pids()),
112
+ 'uptime_seconds': time.time() - self.start_time
113
+ }
114
+
115
+
116
+ class Logger:
117
+ """Production-grade logger with structured logging."""
118
+
119
+ def __init__(self, name: str = "qyro", level: Severity = Severity.INFO):
120
+ self.name = name
121
+ self.level = level
122
+ self.handlers: List[Callable[[LogEntry], None]] = []
123
+ self._lock = threading.Lock()
124
+
125
+ # Add default console handler
126
+ self.add_handler(self._console_handler)
127
+
128
+ def add_handler(self, handler: Callable[[LogEntry], None]):
129
+ """Add a log handler."""
130
+ with self._lock:
131
+ self.handlers.append(handler)
132
+
133
+ def _console_handler(self, entry: LogEntry):
134
+ """Default console handler."""
135
+ timestamp = datetime.fromtimestamp(entry.timestamp).strftime('%Y-%m-%d %H:%M:%S')
136
+ level_name = entry.level.name
137
+ print(f"[{timestamp}] [{level_name}] [{entry.module}] {entry.message}")
138
+ if entry.details:
139
+ print(f" Details: {entry.details}")
140
+
141
+ def _log(self, level: Severity, message: str, module: str = "", **details):
142
+ """Internal logging method."""
143
+ if level.value < self.level.value:
144
+ return
145
+
146
+ entry = LogEntry(
147
+ timestamp=time.time(),
148
+ level=level,
149
+ message=message,
150
+ module=module or self.name,
151
+ details=details
152
+ )
153
+
154
+ with self._lock:
155
+ for handler in self.handlers:
156
+ try:
157
+ handler(entry)
158
+ except Exception:
159
+ # Never let logging errors break the application
160
+ pass
161
+
162
+ def debug(self, message: str, module: str = "", **details):
163
+ self._log(Severity.DEBUG, message, module, **details)
164
+
165
+ def info(self, message: str, module: str = "", **details):
166
+ self._log(Severity.INFO, message, module, **details)
167
+
168
+ def warning(self, message: str, module: str = "", **details):
169
+ self._log(Severity.WARNING, message, module, **details)
170
+
171
+ def error(self, message: str, module: str = "", **details):
172
+ self._log(Severity.ERROR, message, module, **details)
173
+
174
+ def critical(self, message: str, module: str = "", **details):
175
+ self._log(Severity.CRITICAL, message, module, **details)
176
+
177
+
178
+ class HealthChecker:
179
+ """Health checking system for the Qyro runtime."""
180
+
181
+ def __init__(self, metrics_collector: MetricsCollector, logger: Logger):
182
+ self.metrics = metrics_collector
183
+ self.logger = logger
184
+ self.checks: Dict[str, Callable[[], Dict[str, Any]]] = {}
185
+ self._lock = threading.Lock()
186
+
187
+ def register_check(self, name: str, check_func: Callable[[], Dict[str, Any]]):
188
+ """Register a health check function."""
189
+ with self._lock:
190
+ self.checks[name] = check_func
191
+
192
+ def run_checks(self) -> Dict[str, Dict[str, Any]]:
193
+ """Run all registered health checks."""
194
+ results = {}
195
+
196
+ for name, check_func in self.checks.items():
197
+ try:
198
+ result = check_func()
199
+ results[name] = result
200
+ except Exception as e:
201
+ results[name] = {
202
+ 'status': 'error',
203
+ 'error': str(e),
204
+ 'timestamp': time.time()
205
+ }
206
+
207
+ return results
208
+
209
+ def get_health_status(self) -> str:
210
+ """Get overall health status."""
211
+ results = self.run_checks()
212
+
213
+ # If any check failed, return unhealthy
214
+ for result in results.values():
215
+ if result.get('status') == 'error' or result.get('healthy') is False:
216
+ return 'unhealthy'
217
+
218
+ return 'healthy'
219
+
220
+
221
+ class QyroMonitor:
222
+ """Main monitoring system for Qyro runtime."""
223
+
224
+ def __init__(self):
225
+ self.metrics = MetricsCollector()
226
+ self.logger = Logger()
227
+ self.health = HealthChecker(self.metrics, self.logger)
228
+ self._monitoring_thread: Optional[threading.Thread] = None
229
+ self._stop_event = threading.Event()
230
+ self._running = False
231
+
232
+ # Register default health checks
233
+ self._register_default_checks()
234
+
235
+ def _register_default_checks(self):
236
+ """Register default health checks."""
237
+ def check_system_resources():
238
+ cpu_percent = psutil.cpu_percent(interval=0.1)
239
+ memory_percent = psutil.virtual_memory().percent
240
+ disk_percent = psutil.disk_usage('/').percent
241
+
242
+ healthy = cpu_percent < 90 and memory_percent < 90 and disk_percent < 90
243
+
244
+ return {
245
+ 'status': 'healthy' if healthy else 'degraded',
246
+ 'healthy': healthy,
247
+ 'cpu_percent': cpu_percent,
248
+ 'memory_percent': memory_percent,
249
+ 'disk_percent': disk_percent,
250
+ 'timestamp': time.time()
251
+ }
252
+
253
+ def check_process_count():
254
+ proc_count = len(psutil.pids())
255
+ healthy = proc_count < 1000 # Arbitrary limit
256
+
257
+ return {
258
+ 'status': 'healthy' if healthy else 'warning',
259
+ 'healthy': healthy,
260
+ 'process_count': proc_count,
261
+ 'timestamp': time.time()
262
+ }
263
+
264
+ self.health.register_check('system_resources', check_system_resources)
265
+ self.health.register_check('process_count', check_process_count)
266
+
267
+ def start_monitoring(self, interval: float = 30.0):
268
+ """Start background monitoring."""
269
+ if self._running:
270
+ return
271
+
272
+ self._running = True
273
+
274
+ def monitor_loop():
275
+ while not self._stop_event.is_set():
276
+ try:
277
+ # Collect system metrics
278
+ sys_metrics = self.metrics.get_system_metrics()
279
+ for name, value in sys_metrics.items():
280
+ self.metrics.record_metric(f"system.{name}", value)
281
+
282
+ # Run health checks
283
+ health_results = self.health.run_checks()
284
+
285
+ # Log health status
286
+ overall_status = self.health.get_health_status()
287
+ self.logger.info(f"Health check completed - Overall: {overall_status}",
288
+ module="monitor", health_results=health_results)
289
+
290
+ # Wait for next interval or stop event
291
+ if self._stop_event.wait(timeout=interval):
292
+ break
293
+
294
+ except Exception as e:
295
+ self.logger.error(f"Monitoring error: {e}", module="monitor")
296
+ # Wait a bit before retrying
297
+ if self._stop_event.wait(timeout=5.0):
298
+ break
299
+
300
+ self._monitoring_thread = threading.Thread(target=monitor_loop, daemon=True)
301
+ self._monitoring_thread.start()
302
+
303
+ def stop_monitoring(self):
304
+ """Stop background monitoring."""
305
+ if not self._running:
306
+ return
307
+
308
+ self._running = False
309
+ self._stop_event.set()
310
+
311
+ if self._monitoring_thread:
312
+ self._monitoring_thread.join(timeout=5.0) # Wait up to 5 seconds
313
+
314
+ self._stop_event.clear()
315
+
316
+ def get_metrics_summary(self) -> Dict[str, Any]:
317
+ """Get a summary of current metrics."""
318
+ return {
319
+ 'system': self.metrics.get_system_metrics(),
320
+ 'health_status': self.health.get_health_status(),
321
+ 'health_details': self.health.run_checks(),
322
+ 'uptime': time.time() - self.metrics.start_time
323
+ }
324
+
325
+ def record_custom_metric(self, name: str, value: float, labels: Dict[str, str] = None):
326
+ """Record a custom metric."""
327
+ self.metrics.record_metric(name, value, labels)
328
+
329
+ def log_event(self, level: Severity, message: str, module: str = "", **details):
330
+ """Log an event."""
331
+ if level == Severity.DEBUG:
332
+ self.logger.debug(message, module, **details)
333
+ elif level == Severity.INFO:
334
+ self.logger.info(message, module, **details)
335
+ elif level == Severity.WARNING:
336
+ self.logger.warning(message, module, **details)
337
+ elif level == Severity.ERROR:
338
+ self.logger.error(message, module, **details)
339
+ elif level == Severity.CRITICAL:
340
+ self.logger.critical(message, module, **details)
341
+
342
+
343
+ # Global monitor instance
344
+ _qyro_monitor: Optional[QyroMonitor] = None
345
+
346
+
347
+ def get_monitor() -> QyroMonitor:
348
+ """Get the global monitor instance."""
349
+ global _qyro_monitor
350
+ if _qyro_monitor is None:
351
+ _qyro_monitor = QyroMonitor()
352
+ return _qyro_monitor
353
+
354
+
355
+ def start_monitoring(interval: float = 30.0):
356
+ """Start the monitoring system."""
357
+ monitor = get_monitor()
358
+ monitor.start_monitoring(interval)
359
+
360
+
361
+ def stop_monitoring():
362
+ """Stop the monitoring system."""
363
+ monitor = get_monitor()
364
+ monitor.stop_monitoring()
365
+
366
+
367
+ @contextmanager
368
+ def monitor_performance(metric_name: str, labels: Dict[str, str] = None):
369
+ """Context manager to monitor performance of a code block."""
370
+ start_time = time.time()
371
+ try:
372
+ yield
373
+ finally:
374
+ duration = time.time() - start_time
375
+ monitor = get_monitor()
376
+ monitor.record_custom_metric(metric_name, duration, labels)
377
+
378
+
379
+ # Example usage and health check endpoints for integration
380
+ def create_health_endpoints(app):
381
+ """Add health check endpoints to a web application (FastAPI/Flask)."""
382
+ monitor = get_monitor()
383
+
384
+ try:
385
+ # Try FastAPI
386
+ from fastapi import APIRouter
387
+ router = APIRouter()
388
+
389
+ @router.get("/health")
390
+ def health_check():
391
+ return {
392
+ "status": monitor.health.get_health_status(),
393
+ "checks": monitor.health.run_checks(),
394
+ "timestamp": time.time()
395
+ }
396
+
397
+ @router.get("/metrics")
398
+ def metrics_endpoint():
399
+ return monitor.get_metrics_summary()
400
+
401
+ @router.get("/live")
402
+ def liveness():
403
+ # Liveness probe - just check if the service is running
404
+ return {"status": "alive", "timestamp": time.time()}
405
+
406
+ @router.get("/ready")
407
+ def readiness():
408
+ # Readiness probe - check if the service is ready to serve requests
409
+ health_status = monitor.health.get_health_status()
410
+ return {
411
+ "status": "ready" if health_status == "healthy" else "not_ready",
412
+ "health": health_status,
413
+ "timestamp": time.time()
414
+ }
415
+
416
+ app.include_router(router)
417
+ return router
418
+
419
+ except ImportError:
420
+ pass
421
+
422
+ try:
423
+ # Try Flask
424
+ from flask import jsonify
425
+
426
+ @app.route("/health")
427
+ def health_check():
428
+ return jsonify({
429
+ "status": monitor.health.get_health_status(),
430
+ "checks": monitor.health.run_checks(),
431
+ "timestamp": time.time()
432
+ })
433
+
434
+ @app.route("/metrics")
435
+ def metrics_endpoint():
436
+ return jsonify(monitor.get_metrics_summary())
437
+
438
+ @app.route("/live")
439
+ def liveness():
440
+ return jsonify({"status": "alive", "timestamp": time.time()})
441
+
442
+ @app.route("/ready")
443
+ def readiness():
444
+ health_status = monitor.health.get_health_status()
445
+ return jsonify({
446
+ "status": "ready" if health_status == "healthy" else "not_ready",
447
+ "health": health_status,
448
+ "timestamp": time.time()
449
+ })
450
+
451
+ except ImportError:
452
+ pass
453
+
454
+
455
+ # Initialize monitoring when module is loaded
456
+ def _init_monitoring():
457
+ """Initialize monitoring system."""
458
+ monitor = get_monitor()
459
+
460
+ # Start monitoring in background
461
+ monitor.start_monitoring()
462
+
463
+ # Log system startup
464
+ monitor.log_event(Severity.INFO, "Qyro monitoring system initialized", "monitor")
465
+
466
+
467
+ # Initialize monitoring
468
+ _init_monitoring()
qyro/common/parser.py ADDED
@@ -0,0 +1,91 @@
1
+ import re
2
+
3
+ class NexusParser:
4
+ def __init__(self):
5
+ self.blocks = {
6
+ 'py': [],
7
+ 'js': [],
8
+ 'rs': [],
9
+ 'c': [],
10
+ 'java': [],
11
+ 'node': [],
12
+ 'ts': [],
13
+ 'go': [],
14
+ 'web': [],
15
+ 'schema': [],
16
+ 'react': [],
17
+ 'nextjs': [],
18
+ }
19
+
20
+ def parse_file(self, filepath: str):
21
+ try:
22
+ with open(filepath, 'r', encoding='utf-8') as f:
23
+ content = f.read()
24
+ # Pass the directory of the file as base_path
25
+ import os
26
+ base_path = os.path.dirname(filepath) or "."
27
+ self.parse_string(content, base_path=base_path)
28
+ except FileNotFoundError:
29
+ print(f"[QYRO] Error: File {filepath} not found.")
30
+
31
+ def parse_string(self, content: str, base_path="."):
32
+ # Import handling: >>>import "path/to/file.qyro"
33
+ import_pattern = re.compile(r'^>>>import\s+"([^"]+)"', re.MULTILINE)
34
+ for match in import_pattern.finditer(content):
35
+ rel_path = match.group(1)
36
+ import os
37
+ # Resolve relative to base_path
38
+ full_path = os.path.join(base_path, rel_path)
39
+ print(f"[QYRO] Importing {full_path}...")
40
+ self.parse_file(full_path)
41
+
42
+ # Split by block markers: >>>py, >>>c, >>>java, etc.
43
+ parts = re.split(r'(?m)^>>>(\S+)\s*$', content)
44
+
45
+ if len(parts) < 2:
46
+ return
47
+
48
+ for i in range(1, len(parts), 2):
49
+ raw_type = parts[i].strip()
50
+ block_content = parts[i+1].strip()
51
+
52
+ # Handle type:name syntax (e.g. py:server -> type=py, name=server)
53
+ block_name = f"module_{i}"
54
+ if ':' in raw_type:
55
+ parts_type = raw_type.split(':', 1)
56
+ block_type = parts_type[0].lower()
57
+ block_name = parts_type[1]
58
+ else:
59
+ block_type = raw_type.lower()
60
+
61
+ if block_type == 'import':
62
+ continue
63
+
64
+ # Store in legacy string list (Backward Compatibility)
65
+ if block_type not in self.blocks:
66
+ self.blocks[block_type] = []
67
+ self.blocks[block_type].append(block_content)
68
+
69
+ # Store in unified named blocks list (For Build System)
70
+ if not hasattr(self, 'named_blocks'):
71
+ self.named_blocks = {}
72
+
73
+ if block_type not in self.named_blocks:
74
+ self.named_blocks[block_type] = []
75
+
76
+ self.named_blocks[block_type].append({
77
+ "name": block_name,
78
+ "content": block_content,
79
+ "original_type": raw_type
80
+ })
81
+
82
+ def get_blocks(self, block_type: str):
83
+ return self.blocks.get(block_type, [])
84
+
85
+ def get_named_blocks(self):
86
+ """Return all blocks with their metadata (name, type)."""
87
+ return getattr(self, 'named_blocks', {})
88
+
89
+
90
+ # Qyro compatibility alias
91
+ QyroParser = NexusParser