kailash 0.8.4__py3-none-any.whl → 0.8.6__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 (99) hide show
  1. kailash/__init__.py +5 -11
  2. kailash/channels/__init__.py +2 -1
  3. kailash/channels/mcp_channel.py +23 -4
  4. kailash/cli/__init__.py +11 -1
  5. kailash/cli/validate_imports.py +202 -0
  6. kailash/cli/validation_audit.py +570 -0
  7. kailash/core/actors/supervisor.py +1 -1
  8. kailash/core/resilience/bulkhead.py +15 -5
  9. kailash/core/resilience/circuit_breaker.py +74 -1
  10. kailash/core/resilience/health_monitor.py +433 -33
  11. kailash/edge/compliance.py +33 -0
  12. kailash/edge/consistency.py +609 -0
  13. kailash/edge/coordination/__init__.py +30 -0
  14. kailash/edge/coordination/global_ordering.py +355 -0
  15. kailash/edge/coordination/leader_election.py +217 -0
  16. kailash/edge/coordination/partition_detector.py +296 -0
  17. kailash/edge/coordination/raft.py +485 -0
  18. kailash/edge/discovery.py +63 -1
  19. kailash/edge/migration/__init__.py +19 -0
  20. kailash/edge/migration/edge_migration_service.py +384 -0
  21. kailash/edge/migration/edge_migrator.py +832 -0
  22. kailash/edge/monitoring/__init__.py +21 -0
  23. kailash/edge/monitoring/edge_monitor.py +736 -0
  24. kailash/edge/prediction/__init__.py +10 -0
  25. kailash/edge/prediction/predictive_warmer.py +591 -0
  26. kailash/edge/resource/__init__.py +102 -0
  27. kailash/edge/resource/cloud_integration.py +796 -0
  28. kailash/edge/resource/cost_optimizer.py +949 -0
  29. kailash/edge/resource/docker_integration.py +919 -0
  30. kailash/edge/resource/kubernetes_integration.py +893 -0
  31. kailash/edge/resource/platform_integration.py +913 -0
  32. kailash/edge/resource/predictive_scaler.py +959 -0
  33. kailash/edge/resource/resource_analyzer.py +824 -0
  34. kailash/edge/resource/resource_pools.py +610 -0
  35. kailash/integrations/dataflow_edge.py +261 -0
  36. kailash/mcp_server/registry_integration.py +1 -1
  37. kailash/mcp_server/server.py +351 -8
  38. kailash/mcp_server/transports.py +305 -0
  39. kailash/middleware/gateway/event_store.py +1 -0
  40. kailash/monitoring/__init__.py +18 -0
  41. kailash/monitoring/alerts.py +646 -0
  42. kailash/monitoring/metrics.py +677 -0
  43. kailash/nodes/__init__.py +2 -0
  44. kailash/nodes/ai/semantic_memory.py +2 -2
  45. kailash/nodes/base.py +622 -1
  46. kailash/nodes/code/python.py +44 -3
  47. kailash/nodes/data/async_sql.py +42 -20
  48. kailash/nodes/edge/__init__.py +36 -0
  49. kailash/nodes/edge/base.py +240 -0
  50. kailash/nodes/edge/cloud_node.py +710 -0
  51. kailash/nodes/edge/coordination.py +239 -0
  52. kailash/nodes/edge/docker_node.py +825 -0
  53. kailash/nodes/edge/edge_data.py +582 -0
  54. kailash/nodes/edge/edge_migration_node.py +396 -0
  55. kailash/nodes/edge/edge_monitoring_node.py +421 -0
  56. kailash/nodes/edge/edge_state.py +673 -0
  57. kailash/nodes/edge/edge_warming_node.py +393 -0
  58. kailash/nodes/edge/kubernetes_node.py +652 -0
  59. kailash/nodes/edge/platform_node.py +766 -0
  60. kailash/nodes/edge/resource_analyzer_node.py +378 -0
  61. kailash/nodes/edge/resource_optimizer_node.py +501 -0
  62. kailash/nodes/edge/resource_scaler_node.py +397 -0
  63. kailash/nodes/governance.py +410 -0
  64. kailash/nodes/ports.py +676 -0
  65. kailash/nodes/rag/registry.py +1 -1
  66. kailash/nodes/transaction/distributed_transaction_manager.py +48 -1
  67. kailash/nodes/transaction/saga_state_storage.py +2 -1
  68. kailash/nodes/validation.py +8 -8
  69. kailash/runtime/local.py +374 -1
  70. kailash/runtime/validation/__init__.py +12 -0
  71. kailash/runtime/validation/connection_context.py +119 -0
  72. kailash/runtime/validation/enhanced_error_formatter.py +202 -0
  73. kailash/runtime/validation/error_categorizer.py +164 -0
  74. kailash/runtime/validation/import_validator.py +446 -0
  75. kailash/runtime/validation/metrics.py +380 -0
  76. kailash/runtime/validation/performance.py +615 -0
  77. kailash/runtime/validation/suggestion_engine.py +212 -0
  78. kailash/testing/fixtures.py +2 -2
  79. kailash/utils/data_paths.py +74 -0
  80. kailash/workflow/builder.py +413 -8
  81. kailash/workflow/contracts.py +418 -0
  82. kailash/workflow/edge_infrastructure.py +369 -0
  83. kailash/workflow/mermaid_visualizer.py +3 -1
  84. kailash/workflow/migration.py +3 -3
  85. kailash/workflow/templates.py +6 -6
  86. kailash/workflow/type_inference.py +669 -0
  87. kailash/workflow/validation.py +134 -3
  88. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/METADATA +52 -34
  89. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/RECORD +93 -42
  90. kailash/nexus/__init__.py +0 -21
  91. kailash/nexus/cli/__init__.py +0 -5
  92. kailash/nexus/cli/__main__.py +0 -6
  93. kailash/nexus/cli/main.py +0 -176
  94. kailash/nexus/factory.py +0 -413
  95. kailash/nexus/gateway.py +0 -545
  96. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/WHEEL +0 -0
  97. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/entry_points.txt +0 -0
  98. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/licenses/LICENSE +0 -0
  99. {kailash-0.8.4.dist-info → kailash-0.8.6.dist-info}/top_level.txt +0 -0
@@ -1084,6 +1084,310 @@ class WebSocketTransport(BaseTransport):
1084
1084
  self.websocket = None
1085
1085
 
1086
1086
 
1087
+ class WebSocketServerTransport(BaseTransport):
1088
+ """WebSocket server transport for accepting MCP connections."""
1089
+
1090
+ def __init__(
1091
+ self,
1092
+ host: str = "0.0.0.0",
1093
+ port: int = 3001,
1094
+ message_handler: Optional[
1095
+ Callable[[Dict[str, Any], str], Dict[str, Any]]
1096
+ ] = None,
1097
+ ping_interval: float = 20.0,
1098
+ ping_timeout: float = 20.0,
1099
+ max_message_size: int = 10 * 1024 * 1024, # 10MB
1100
+ **kwargs,
1101
+ ):
1102
+ """Initialize WebSocket server transport.
1103
+
1104
+ Args:
1105
+ host: Host to bind to
1106
+ port: Port to listen on
1107
+ message_handler: Handler for incoming messages
1108
+ ping_interval: Ping interval in seconds
1109
+ ping_timeout: Ping timeout in seconds
1110
+ max_message_size: Maximum message size in bytes
1111
+ **kwargs: Base transport arguments
1112
+ """
1113
+ super().__init__("websocket_server", **kwargs)
1114
+
1115
+ self.host = host
1116
+ self.port = port
1117
+ self.message_handler = message_handler
1118
+ self.ping_interval = ping_interval
1119
+ self.ping_timeout = ping_timeout
1120
+ self.max_message_size = max_message_size
1121
+
1122
+ # Server state
1123
+ self.server: Optional[websockets.WebSocketServer] = None
1124
+ self._clients: Dict[str, Any] = {} # websockets.WebSocketServerProtocol
1125
+ self._client_sessions: Dict[str, Dict[str, Any]] = {}
1126
+ self._server_task: Optional[asyncio.Task] = None
1127
+
1128
+ async def connect(self) -> None:
1129
+ """Start the WebSocket server."""
1130
+ if self._connected:
1131
+ return
1132
+
1133
+ try:
1134
+ # Create handler that works with new websockets API
1135
+ async def connection_handler(websocket):
1136
+ # Get path from the websocket's request path
1137
+ path = websocket.path if hasattr(websocket, "path") else "/"
1138
+ await self.handle_client(websocket, path)
1139
+
1140
+ # Start WebSocket server
1141
+ self.server = await websockets.serve(
1142
+ connection_handler,
1143
+ self.host,
1144
+ self.port,
1145
+ ping_interval=self.ping_interval,
1146
+ ping_timeout=self.ping_timeout,
1147
+ max_size=self.max_message_size,
1148
+ )
1149
+
1150
+ self._connected = True
1151
+ self._update_metrics("connections_total")
1152
+
1153
+ logger.info(f"WebSocket server listening on {self.host}:{self.port}")
1154
+
1155
+ except Exception as e:
1156
+ self._update_metrics("connections_failed")
1157
+ raise TransportError(
1158
+ f"Failed to start WebSocket server: {e}",
1159
+ transport_type="websocket_server",
1160
+ )
1161
+
1162
+ async def disconnect(self) -> None:
1163
+ """Stop the WebSocket server."""
1164
+ if not self._connected:
1165
+ return
1166
+
1167
+ self._connected = False
1168
+
1169
+ # Close all client connections
1170
+ clients = list(self._clients.values())
1171
+ for client in clients:
1172
+ await client.close()
1173
+
1174
+ # Stop server
1175
+ if self.server:
1176
+ self.server.close()
1177
+ await self.server.wait_closed()
1178
+ self.server = None
1179
+
1180
+ # Clear client tracking
1181
+ self._clients.clear()
1182
+ self._client_sessions.clear()
1183
+
1184
+ logger.info("WebSocket server stopped")
1185
+
1186
+ async def send_message(
1187
+ self, message: Dict[str, Any], client_id: Optional[str] = None
1188
+ ) -> None:
1189
+ """Send message to specific client or broadcast to all.
1190
+
1191
+ Args:
1192
+ message: Message to send
1193
+ client_id: Target client ID (None for broadcast)
1194
+ """
1195
+ if not self._connected:
1196
+ raise TransportError(
1197
+ "Transport not connected", transport_type="websocket_server"
1198
+ )
1199
+
1200
+ message_data = json.dumps(message)
1201
+
1202
+ try:
1203
+ if client_id:
1204
+ # Send to specific client
1205
+ if client_id in self._clients:
1206
+ await self._clients[client_id].send(message_data)
1207
+ self._update_metrics("messages_sent")
1208
+ self._update_metrics("bytes_sent", len(message_data))
1209
+ else:
1210
+ raise TransportError(
1211
+ f"Client {client_id} not found",
1212
+ transport_type="websocket_server",
1213
+ )
1214
+ else:
1215
+ # Broadcast to all clients
1216
+ if self._clients:
1217
+ await asyncio.gather(
1218
+ *[
1219
+ client.send(message_data)
1220
+ for client in self._clients.values()
1221
+ ],
1222
+ return_exceptions=True,
1223
+ )
1224
+ self._update_metrics("messages_sent", len(self._clients))
1225
+ self._update_metrics(
1226
+ "bytes_sent", len(message_data) * len(self._clients)
1227
+ )
1228
+
1229
+ except Exception as e:
1230
+ self._update_metrics("errors_total")
1231
+ raise TransportError(
1232
+ f"Failed to send message: {e}", transport_type="websocket_server"
1233
+ )
1234
+
1235
+ async def receive_message(self) -> Dict[str, Any]:
1236
+ """Not implemented for server transport."""
1237
+ raise NotImplementedError(
1238
+ "Server transport doesn't support receive_message. "
1239
+ "Messages are handled via handle_client callback."
1240
+ )
1241
+
1242
+ async def handle_client(self, websocket, path: str):
1243
+ """Handle a client connection.
1244
+
1245
+ Args:
1246
+ websocket: WebSocket connection
1247
+ path: Request path
1248
+ """
1249
+ client_id = str(uuid.uuid4())
1250
+ self._clients[client_id] = websocket
1251
+ self._client_sessions[client_id] = {
1252
+ "connected_at": time.time(),
1253
+ "path": path,
1254
+ "remote_address": websocket.remote_address,
1255
+ }
1256
+
1257
+ logger.info(f"Client {client_id} connected from {websocket.remote_address}")
1258
+ self._update_metrics("connections_total")
1259
+
1260
+ try:
1261
+ async for message in websocket:
1262
+ try:
1263
+ # Parse message
1264
+ request = json.loads(message)
1265
+
1266
+ # Update metrics
1267
+ self._update_metrics("messages_received")
1268
+ self._update_metrics("bytes_received", len(message))
1269
+
1270
+ # Handle message
1271
+ if self.message_handler:
1272
+ response = await self._handle_message_safely(request, client_id)
1273
+ else:
1274
+ response = {
1275
+ "jsonrpc": "2.0",
1276
+ "error": {
1277
+ "code": -32601,
1278
+ "message": "No message handler configured",
1279
+ },
1280
+ "id": request.get("id"),
1281
+ }
1282
+
1283
+ # Send response
1284
+ await websocket.send(json.dumps(response))
1285
+ self._update_metrics("messages_sent")
1286
+ self._update_metrics("bytes_sent", len(json.dumps(response)))
1287
+
1288
+ except json.JSONDecodeError as e:
1289
+ logger.error(f"Invalid JSON from client {client_id}: {e}")
1290
+ self._update_metrics("errors_total")
1291
+
1292
+ error_response = {
1293
+ "jsonrpc": "2.0",
1294
+ "error": {
1295
+ "code": -32700,
1296
+ "message": "Parse error: Invalid JSON",
1297
+ },
1298
+ "id": None,
1299
+ }
1300
+ await websocket.send(json.dumps(error_response))
1301
+
1302
+ except Exception as e:
1303
+ logger.error(f"Error handling message from client {client_id}: {e}")
1304
+ self._update_metrics("errors_total")
1305
+
1306
+ except websockets.exceptions.ConnectionClosed:
1307
+ logger.info(f"Client {client_id} disconnected")
1308
+ except Exception as e:
1309
+ logger.error(f"Error in client handler for {client_id}: {e}")
1310
+ finally:
1311
+ # Clean up client
1312
+ del self._clients[client_id]
1313
+ del self._client_sessions[client_id]
1314
+
1315
+ async def _handle_message_safely(
1316
+ self, request: Dict[str, Any], client_id: str
1317
+ ) -> Dict[str, Any]:
1318
+ """Handle message with error handling.
1319
+
1320
+ Args:
1321
+ request: JSON-RPC request
1322
+ client_id: Client identifier
1323
+
1324
+ Returns:
1325
+ JSON-RPC response
1326
+ """
1327
+ try:
1328
+ if asyncio.iscoroutinefunction(self.message_handler):
1329
+ return await self.message_handler(request, client_id)
1330
+ else:
1331
+ return self.message_handler(request, client_id)
1332
+ except Exception as e:
1333
+ logger.error(f"Message handler error: {e}")
1334
+ return {
1335
+ "jsonrpc": "2.0",
1336
+ "error": {
1337
+ "code": -32603,
1338
+ "message": f"Internal error: {str(e)}",
1339
+ },
1340
+ "id": request.get("id"),
1341
+ }
1342
+
1343
+ def get_client_info(self, client_id: str) -> Optional[Dict[str, Any]]:
1344
+ """Get information about a connected client.
1345
+
1346
+ Args:
1347
+ client_id: Client identifier
1348
+
1349
+ Returns:
1350
+ Client information or None
1351
+ """
1352
+ if client_id not in self._client_sessions:
1353
+ return None
1354
+
1355
+ session = self._client_sessions[client_id]
1356
+ return {
1357
+ "client_id": client_id,
1358
+ "connected_at": session["connected_at"],
1359
+ "connection_duration": time.time() - session["connected_at"],
1360
+ "path": session["path"],
1361
+ "remote_address": session["remote_address"],
1362
+ }
1363
+
1364
+ def list_clients(self) -> List[Dict[str, Any]]:
1365
+ """List all connected clients.
1366
+
1367
+ Returns:
1368
+ List of client information
1369
+ """
1370
+ return [self.get_client_info(client_id) for client_id in self._client_sessions]
1371
+
1372
+ async def close_client(
1373
+ self, client_id: str, code: int = 1000, reason: str = ""
1374
+ ) -> bool:
1375
+ """Close a specific client connection.
1376
+
1377
+ Args:
1378
+ client_id: Client to disconnect
1379
+ code: WebSocket close code
1380
+ reason: Close reason
1381
+
1382
+ Returns:
1383
+ True if client was closed
1384
+ """
1385
+ if client_id in self._clients:
1386
+ await self._clients[client_id].close(code, reason)
1387
+ return True
1388
+ return False
1389
+
1390
+
1087
1391
  class TransportManager:
1088
1392
  """Manager for MCP transport instances."""
1089
1393
 
@@ -1095,6 +1399,7 @@ class TransportManager:
1095
1399
  "sse": SSETransport,
1096
1400
  "streamable_http": StreamableHTTPTransport,
1097
1401
  "websocket": WebSocketTransport,
1402
+ "websocket_server": WebSocketServerTransport,
1098
1403
  }
1099
1404
 
1100
1405
  def register_transport_factory(self, transport_type: str, factory: Callable):
@@ -125,6 +125,7 @@ class EventStore:
125
125
  self._flush_task = asyncio.create_task(self._flush_loop())
126
126
  except RuntimeError:
127
127
  # If no event loop is running, defer task creation
128
+ # Don't create the coroutine here as it will never be awaited
128
129
  self._flush_task = None
129
130
 
130
131
  async def _ensure_flush_task(self):
@@ -0,0 +1,18 @@
1
+ """
2
+ Monitoring and alerting system for Kailash SDK.
3
+
4
+ Provides comprehensive monitoring for validation failures, security violations,
5
+ performance metrics, and alerting for critical events.
6
+ """
7
+
8
+ from .alerts import AlertManager, AlertRule, AlertSeverity
9
+ from .metrics import PerformanceMetrics, SecurityMetrics, ValidationMetrics
10
+
11
+ __all__ = [
12
+ "ValidationMetrics",
13
+ "SecurityMetrics",
14
+ "PerformanceMetrics",
15
+ "AlertManager",
16
+ "AlertRule",
17
+ "AlertSeverity",
18
+ ]