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
qyro/common/config.py ADDED
@@ -0,0 +1,93 @@
1
+ """
2
+ Qyro Configuration Module
3
+ Handles configuration management for the Qyro runtime.
4
+ """
5
+
6
+ from dataclasses import dataclass
7
+ from typing import Optional
8
+ import os
9
+
10
+
11
+ @dataclass
12
+ class QyroConfig:
13
+ """Configuration for Qyro runtime."""
14
+ # Redis configuration
15
+ redis_host: str = "localhost"
16
+ redis_port: int = 6379
17
+ redis_db: int = 0
18
+ redis_password: Optional[str] = None
19
+
20
+ # Kafka configuration
21
+ kafka_bootstrap_servers: str = "localhost:9092"
22
+ kafka_topic_prefix: str = "Qyro_"
23
+
24
+ # Service configuration
25
+ service_host: str = "0.0.0.0"
26
+ service_port: int = 8765
27
+
28
+ # Debugging
29
+ debug: bool = False
30
+
31
+ # Resource limits
32
+ max_memory_mb: int = 512
33
+ max_cpu_percent: int = 80
34
+
35
+ # Process management
36
+ max_restarts: int = 5
37
+ restart_backoff: float = 2.0
38
+
39
+ def __post_init__(self):
40
+ """Load configuration from environment variables if not provided."""
41
+ # Redis configuration
42
+ if self.redis_host == "localhost":
43
+ self.redis_host = os.getenv("REDIS_HOST", "localhost")
44
+ if self.redis_port == 6379:
45
+ self.redis_port = int(os.getenv("REDIS_PORT", "6379"))
46
+ if self.redis_password is None:
47
+ self.redis_password = os.getenv("REDIS_PASSWORD")
48
+
49
+ # Kafka configuration
50
+ if self.kafka_bootstrap_servers == "localhost:9092":
51
+ self.kafka_bootstrap_servers = os.getenv("KAFKA_BOOTSTRAP_SERVERS", "localhost:9092")
52
+ if self.kafka_topic_prefix == "Qyro_":
53
+ self.kafka_topic_prefix = os.getenv("KAFKA_TOPIC_PREFIX", "Qyro_")
54
+
55
+ # Service configuration
56
+ if self.service_host == "0.0.0.0":
57
+ self.service_host = os.getenv("SERVICE_HOST", "0.0.0.0")
58
+ if self.service_port == 8765:
59
+ self.service_port = int(os.getenv("SERVICE_PORT", "8765"))
60
+
61
+ # Debug configuration
62
+ if not self.debug:
63
+ self.debug = os.getenv("DEBUG", "").lower() == "true"
64
+
65
+ # Resource limits
66
+ if self.max_memory_mb == 512:
67
+ self.max_memory_mb = int(os.getenv("MAX_MEMORY_MB", "512"))
68
+ if self.max_cpu_percent == 80:
69
+ self.max_cpu_percent = int(os.getenv("MAX_CPU_PERCENT", "80"))
70
+
71
+ # Process management
72
+ if self.max_restarts == 5:
73
+ self.max_restarts = int(os.getenv("MAX_RESTARTS", "5"))
74
+ if self.restart_backoff == 2.0:
75
+ self.restart_backoff = float(os.getenv("RESTART_BACKOFF", "2.0"))
76
+
77
+ @property
78
+ def redis_url(self) -> str:
79
+ """Get Redis URL for connection."""
80
+ if self.redis_password:
81
+ return f"redis://:{self.redis_password}@{self.redis_host}:{self.redis_port}/{self.redis_db}"
82
+ return f"redis://{self.redis_host}:{self.redis_port}/{self.redis_db}"
83
+
84
+ @property
85
+ def kafka_topics(self) -> dict:
86
+ """Get Kafka topic names."""
87
+ return {
88
+ "state_change": f"{self.kafka_topic_prefix}state_change",
89
+ "module_events": f"{self.kafka_topic_prefix}module_events",
90
+ "rpc_requests": f"{self.kafka_topic_prefix}rpc_requests",
91
+ "rpc_responses": f"{self.kafka_topic_prefix}rpc_responses",
92
+ "broadcast": f"{self.kafka_topic_prefix}broadcast"
93
+ }
@@ -0,0 +1,99 @@
1
+ """
2
+ Nexus Core Constants
3
+ Global constants shared across all language adapters for consistency.
4
+ """
5
+
6
+ # Version
7
+ NEXUS_VERSION = "2.0.0"
8
+ PROTOCOL_VERSION = 3
9
+
10
+ # File Names - MUST match across all adapters
11
+ MEM_FILE = "nexus.mem"
12
+ LOCK_FILE = ".nexus_global.lock" # UNIFIED lock file for ALL adapters
13
+
14
+ # Memory Layout - NBP v3 (Nexus Binary Protocol)
15
+ # All offsets in bytes, all integers are little-endian
16
+ HEADER_SIZE = 32
17
+
18
+ # Header offsets
19
+ OFFSET_MAGIC = 0 # [0-4]: Magic bytes "NEXS"
20
+ OFFSET_VERSION = 4 # [4-8]: Protocol version (3)
21
+ OFFSET_SEQUENCE = 8 # [8-12]: Sequence number for optimistic concurrency
22
+ OFFSET_CHECKSUM = 12 # [12-16]: CRC32 checksum of data
23
+ OFFSET_REGISTRY = 16 # [16-20]: Registry offset (function registry start)
24
+ OFFSET_LENGTH = 20 # [20-24]: Data length
25
+ OFFSET_FLAGS = 24 # [24-28]: Flags (encrypted, compressed, etc.)
26
+ OFFSET_RESERVED = 28 # [28-32]: Reserved for future use
27
+ OFFSET_DATA = 32 # [32...]: JSON/binary data
28
+
29
+ # Magic bytes
30
+ MAGIC_BYTES = b"NEXS"
31
+
32
+ # Default sizes
33
+ DEFAULT_MEMORY_SIZE = 10 * 1024 * 1024 # 10 MB
34
+ SLOT_SIZE = 1024 # 1 KB per slot
35
+ MAX_SLOTS = 1000 # Maximum number of slots
36
+
37
+ # Function Calling - Reserved memory regions
38
+ REGISTRY_OFFSET = 1024 # Function registry starts at 1KB
39
+ REGISTRY_SIZE = 1024 # 1KB for registry
40
+ CALL_QUEUE_OFFSET = 2048 # Call queue starts at 2KB
41
+ CALL_QUEUE_SIZE = 2048 # 2KB for call queue
42
+ RESULT_QUEUE_OFFSET = 4096 # Result queue starts at 4KB
43
+ RESULT_QUEUE_SIZE = 4096 # 4KB for result queue
44
+ DATA_OFFSET = 8192 # User data starts at 8KB
45
+
46
+ # Timeouts (in seconds)
47
+ DEFAULT_LOCK_TIMEOUT = 5.0
48
+ DEFAULT_CALL_TIMEOUT = 30.0
49
+ DEFAULT_POLL_INTERVAL = 0.05 # 50ms for polling mode
50
+
51
+ # Flags (bitmask)
52
+ FLAG_ENCRYPTED = 0x01
53
+ FLAG_COMPRESSED = 0x02
54
+ FLAG_BINARY = 0x04 # MessagePack instead of JSON
55
+
56
+ # Error codes - must match across all languages
57
+ class ErrorCode:
58
+ # Memory errors (1xxx)
59
+ MEMORY_NOT_FOUND = 1001
60
+ MEMORY_LOCK_TIMEOUT = 1002
61
+ MEMORY_CORRUPTION = 1003
62
+ MEMORY_FULL = 1004
63
+ SLOT_OVERFLOW = 1005
64
+ CHECKSUM_MISMATCH = 1006
65
+ VERSION_MISMATCH = 1007
66
+
67
+ # JSON errors (2xxx)
68
+ JSON_PARSE_ERROR = 2001
69
+ JSON_ENCODE_ERROR = 2002
70
+ JSON_SCHEMA_MISMATCH = 2003
71
+
72
+ # Function call errors (3xxx)
73
+ FUNCTION_NOT_FOUND = 3001
74
+ FUNCTION_TIMEOUT = 3002
75
+ FUNCTION_ERROR = 3003
76
+ INVALID_ARGUMENTS = 3004
77
+ PERMISSION_DENIED = 3005
78
+
79
+ # Auth errors (4xxx)
80
+ AUTH_REQUIRED = 4001
81
+ TOKEN_EXPIRED = 4002
82
+ TOKEN_INVALID = 4003
83
+ INSUFFICIENT_PERMISSIONS = 4004
84
+
85
+ # Security errors (8xxx)
86
+ SECURITY_VIOLATION = 8001
87
+ INPUT_VALIDATION_FAILED = 8002
88
+ RESOURCE_LIMIT_EXCEEDED = 8003
89
+ UNTRUSTED_CODE_REJECTED = 8004
90
+
91
+
92
+ # CRC32 polynomial for checksum
93
+ CRC32_POLYNOMIAL = 0xEDB88320
94
+
95
+
96
+ def calculate_crc32(data: bytes) -> int:
97
+ """Calculate CRC32 checksum matching the algorithm used by all adapters."""
98
+ import zlib
99
+ return zlib.crc32(data) & 0xFFFFFFFF
qyro/common/errors.py ADDED
@@ -0,0 +1,176 @@
1
+ """
2
+ Qyro Error Handling Framework
3
+ Standardized error codes and exception classes for cross-language consistency.
4
+ """
5
+
6
+ from enum import IntEnum
7
+ from typing import Dict, Any, Optional
8
+
9
+
10
+ class ErrorCode(IntEnum):
11
+ """Standardized error codes across all Qyro components."""
12
+
13
+ # Success
14
+ SUCCESS = 0
15
+
16
+ # Memory Errors (1xxx)
17
+ MEMORY_NOT_FOUND = 1001
18
+ MEMORY_LOCK_TIMEOUT = 1002
19
+ MEMORY_CORRUPTION = 1003
20
+ MEMORY_FULL = 1004
21
+ MEMORY_ACCESS_DENIED = 1005
22
+
23
+ # JSON/Schema Errors (2xxx)
24
+ JSON_PARSE_ERROR = 2001
25
+ JSON_SCHEMA_MISMATCH = 2002
26
+ JSON_ENCODE_ERROR = 2003
27
+ SCHEMA_NOT_FOUND = 2004
28
+ SCHEMA_INVALID = 2005
29
+
30
+ # Auth Errors (3xxx)
31
+ AUTH_FAILED = 3001
32
+ AUTH_EXPIRED = 3002
33
+ AUTH_INVALID_TOKEN = 3003
34
+ AUTH_PERMISSION_DENIED = 3004
35
+
36
+ # Slot Errors (4xxx)
37
+ SLOT_OVERFLOW = 4001
38
+ SLOT_NOT_FOUND = 4002
39
+ SLOT_LOCKED = 4003
40
+
41
+ # Process Errors (5xxx)
42
+ PROCESS_SPAWN_FAILED = 5001
43
+ PROCESS_CRASH = 5002
44
+ PROCESS_TIMEOUT = 5003
45
+
46
+ # Compile Errors (6xxx)
47
+ COMPILE_C_FAILED = 6001
48
+ COMPILE_RUST_FAILED = 6002
49
+ COMPILE_JAVA_FAILED = 6003
50
+ COMPILE_GO_FAILED = 6004
51
+
52
+ # Parse Errors (7xxx)
53
+ PARSE_FILE_NOT_FOUND = 7001
54
+ PARSE_INVALID_BLOCK = 7002
55
+ PARSE_IMPORT_FAILED = 7003
56
+
57
+ # Security Errors (8xxx)
58
+ SECURITY_VIOLATION = 8001
59
+ INPUT_VALIDATION_FAILED = 8002
60
+ RESOURCE_LIMIT_EXCEEDED = 8003
61
+ UNTRUSTED_CODE_REJECTED = 8004
62
+
63
+
64
+ class QyroError(Exception):
65
+ """Base exception for all Qyro errors."""
66
+ def __init__(self, code: ErrorCode, message: str, details: dict = None):
67
+ self.code = code
68
+ self.message = message
69
+ self.details = details or {}
70
+ super().__init__(f"[{code.value}] {message}")
71
+
72
+ def to_dict(self) -> Dict[str, Any]:
73
+ return {
74
+ "error": True,
75
+ "code": self.code.value,
76
+ "message": self.message,
77
+ "details": self.details
78
+ }
79
+
80
+
81
+ # Specific exception classes for common error types
82
+ class MemoryError(QyroError):
83
+ def __init__(self, code: ErrorCode = ErrorCode.MEMORY_NOT_FOUND, message: str = "Memory access error", **details):
84
+ super().__init__(code, message, details)
85
+
86
+
87
+ class JSONError(QyroError):
88
+ def __init__(self, code: ErrorCode = ErrorCode.JSON_PARSE_ERROR, message: str = "JSON error", **details):
89
+ super().__init__(code, message, details)
90
+
91
+
92
+ class AuthError(QyroError):
93
+ def __init__(self, code: ErrorCode = ErrorCode.AUTH_FAILED, message: str = "Authentication error", **details):
94
+ super().__init__(code, message, details)
95
+
96
+
97
+ class CompileError(QyroError):
98
+ def __init__(self, code: ErrorCode = ErrorCode.COMPILE_C_FAILED, message: str = "Compilation error", **details):
99
+ super().__init__(code, message, details)
100
+
101
+
102
+ class ParseError(QyroError):
103
+ def __init__(self, code: 'ErrorCode' = None, message: str = "Parse error", **details):
104
+ if code is None:
105
+ code = ErrorCode.PARSE_FILE_NOT_FOUND
106
+ super().__init__(code, message, details)
107
+
108
+
109
+ class SecurityError(QyroError):
110
+ def __init__(self, code: 'ErrorCode' = None, message: str = "Security violation", **details):
111
+ if code is None:
112
+ code = ErrorCode.SECURITY_VIOLATION
113
+ super().__init__(code, message, details)
114
+
115
+
116
+ class ValidationError(QyroError):
117
+ def __init__(self, code: 'ErrorCode' = None, message: str = "Input validation failed", **details):
118
+ if code is None:
119
+ code = ErrorCode.INPUT_VALIDATION_FAILED
120
+ super().__init__(code, message, details)
121
+
122
+
123
+ class ResourceLimitError(QyroError):
124
+ def __init__(self, code: 'ErrorCode' = None, message: str = "Resource limit exceeded", **details):
125
+ if code is None:
126
+ code = ErrorCode.RESOURCE_LIMIT_EXCEEDED
127
+ super().__init__(code, message, details)
128
+
129
+
130
+ # Result type for operations that can fail
131
+ class Result:
132
+ """Rust-style Result type for safe error handling."""
133
+
134
+ def __init__(self, value=None, error: QyroError = None):
135
+ self._value = value
136
+ self._error = error
137
+
138
+ @classmethod
139
+ def ok(cls, value):
140
+ return cls(value=value)
141
+
142
+ @classmethod
143
+ def err(cls, error: QyroError):
144
+ return cls(error=error)
145
+
146
+ def is_ok(self) -> bool:
147
+ return self._error is None
148
+
149
+ def is_err(self) -> bool:
150
+ return self._error is not None
151
+
152
+ def unwrap(self):
153
+ if self._error:
154
+ raise self._error
155
+ return self._value
156
+
157
+ def unwrap_or(self, default):
158
+ return self._value if self._error is None else default
159
+
160
+ def map(self, fn):
161
+ if self._error:
162
+ return self
163
+ try:
164
+ return Result.ok(fn(self._value))
165
+ except QyroError as e:
166
+ return Result.err(e)
167
+
168
+ def __repr__(self):
169
+ if self._error:
170
+ return f"Err({self._error})"
171
+ return f"Ok({self._value})"
172
+
173
+
174
+ # Qyro compatibility aliases
175
+ NexusError = QyroError
176
+ QyroErrorCode = ErrorCode
@@ -0,0 +1,74 @@
1
+ """
2
+ Nexus Frontend Compiler
3
+ Handles compilation and setup for frontend frameworks (React, Next.js).
4
+ """
5
+
6
+ import os
7
+ import shutil
8
+ from typing import Dict, Any, List
9
+ from dataclasses import dataclass
10
+ from .logging import get_logger
11
+
12
+ logger = get_logger("nexus.frontend")
13
+
14
+ @dataclass
15
+ class FrontendConfig:
16
+ framework: str = "react" # 'react' or 'nextjs'
17
+ port: int = 3000
18
+
19
+ class FrontendCompiler:
20
+ def __init__(self, output_dir: str = "nexus_frontend"):
21
+ self.output_dir = output_dir
22
+
23
+ def compile_react(self, code: str, config: FrontendConfig) -> Dict[str, Any]:
24
+ """
25
+ Compile React code block.
26
+ Since we're using Vite, "compilation" mainly means saving the file
27
+ to the correct location for Vite to pick up.
28
+ """
29
+ # Ensure output directory exists (created by orchestrator, but just in case)
30
+ if not os.path.exists(self.output_dir):
31
+ os.makedirs(self.output_dir)
32
+
33
+ src_dir = os.path.join(self.output_dir, "src")
34
+ if not os.path.exists(src_dir):
35
+ os.makedirs(src_dir)
36
+
37
+ # Save the component code
38
+ # We assume the code block contains the full component definition
39
+ # typically matching what goes into NexusComponent.tsx
40
+ component_path = os.path.join(src_dir, "NexusComponent.tsx")
41
+
42
+ # Add necessary imports if missing (simple heuristic)
43
+ final_code = code
44
+ if "import React" not in code and "import { useState" not in code:
45
+ final_code = "import React, { useState, useEffect, useRef } from 'react';\n" + code
46
+
47
+ # If the code doesn't export default, we might need to wrap or fix it.
48
+ # But for now, we assume the user provides a valid component.
49
+ # If it's a raw functional component without export, we might append the export.
50
+ if "export default" not in final_code:
51
+ final_code += "\n\nexport default NexusComponent;"
52
+
53
+ try:
54
+ with open(component_path, "w", encoding='utf-8') as f:
55
+ f.write(final_code)
56
+
57
+ logger.info(f"React component saved to {component_path}")
58
+
59
+ return {
60
+ "type": "react",
61
+ "src": component_path,
62
+ "framework": "vite"
63
+ }
64
+ except Exception as e:
65
+ logger.error(f"Failed to save React component: {e}")
66
+ raise e
67
+
68
+ def compile_nextjs(self, code: str, config: FrontendConfig) -> Dict[str, Any]:
69
+ """
70
+ Compile Next.js code block.
71
+ """
72
+ # Similar logic for Next.js pages/components
73
+ # For simplicity, we might map this to pages/index.tsx
74
+ pass