kailash 0.6.2__py3-none-any.whl → 0.6.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.
Files changed (131) hide show
  1. kailash/__init__.py +3 -3
  2. kailash/api/custom_nodes_secure.py +3 -3
  3. kailash/api/gateway.py +1 -1
  4. kailash/api/studio.py +2 -3
  5. kailash/api/workflow_api.py +3 -4
  6. kailash/core/resilience/bulkhead.py +460 -0
  7. kailash/core/resilience/circuit_breaker.py +92 -10
  8. kailash/edge/discovery.py +86 -0
  9. kailash/mcp_server/__init__.py +334 -0
  10. kailash/mcp_server/advanced_features.py +1022 -0
  11. kailash/{mcp → mcp_server}/ai_registry_server.py +29 -4
  12. kailash/mcp_server/auth.py +789 -0
  13. kailash/mcp_server/client.py +712 -0
  14. kailash/mcp_server/discovery.py +1593 -0
  15. kailash/mcp_server/errors.py +673 -0
  16. kailash/mcp_server/oauth.py +1727 -0
  17. kailash/mcp_server/protocol.py +1126 -0
  18. kailash/mcp_server/registry_integration.py +587 -0
  19. kailash/mcp_server/server.py +1747 -0
  20. kailash/{mcp → mcp_server}/servers/ai_registry.py +2 -2
  21. kailash/mcp_server/transports.py +1169 -0
  22. kailash/mcp_server/utils/cache.py +510 -0
  23. kailash/middleware/auth/auth_manager.py +3 -3
  24. kailash/middleware/communication/api_gateway.py +2 -9
  25. kailash/middleware/communication/realtime.py +1 -1
  26. kailash/middleware/mcp/client_integration.py +1 -1
  27. kailash/middleware/mcp/enhanced_server.py +2 -2
  28. kailash/nodes/__init__.py +2 -0
  29. kailash/nodes/admin/audit_log.py +6 -6
  30. kailash/nodes/admin/permission_check.py +8 -8
  31. kailash/nodes/admin/role_management.py +32 -28
  32. kailash/nodes/admin/schema.sql +6 -1
  33. kailash/nodes/admin/schema_manager.py +13 -13
  34. kailash/nodes/admin/security_event.py +16 -20
  35. kailash/nodes/admin/tenant_isolation.py +3 -3
  36. kailash/nodes/admin/transaction_utils.py +3 -3
  37. kailash/nodes/admin/user_management.py +21 -22
  38. kailash/nodes/ai/a2a.py +11 -11
  39. kailash/nodes/ai/ai_providers.py +9 -12
  40. kailash/nodes/ai/embedding_generator.py +13 -14
  41. kailash/nodes/ai/intelligent_agent_orchestrator.py +19 -19
  42. kailash/nodes/ai/iterative_llm_agent.py +3 -3
  43. kailash/nodes/ai/llm_agent.py +213 -36
  44. kailash/nodes/ai/self_organizing.py +2 -2
  45. kailash/nodes/alerts/discord.py +4 -4
  46. kailash/nodes/api/graphql.py +6 -6
  47. kailash/nodes/api/http.py +12 -17
  48. kailash/nodes/api/rate_limiting.py +4 -4
  49. kailash/nodes/api/rest.py +15 -15
  50. kailash/nodes/auth/mfa.py +3 -4
  51. kailash/nodes/auth/risk_assessment.py +2 -2
  52. kailash/nodes/auth/session_management.py +5 -5
  53. kailash/nodes/auth/sso.py +143 -0
  54. kailash/nodes/base.py +6 -2
  55. kailash/nodes/base_async.py +16 -2
  56. kailash/nodes/base_with_acl.py +2 -2
  57. kailash/nodes/cache/__init__.py +9 -0
  58. kailash/nodes/cache/cache.py +1172 -0
  59. kailash/nodes/cache/cache_invalidation.py +870 -0
  60. kailash/nodes/cache/redis_pool_manager.py +595 -0
  61. kailash/nodes/code/async_python.py +2 -1
  62. kailash/nodes/code/python.py +196 -35
  63. kailash/nodes/compliance/data_retention.py +6 -6
  64. kailash/nodes/compliance/gdpr.py +5 -5
  65. kailash/nodes/data/__init__.py +10 -0
  66. kailash/nodes/data/optimistic_locking.py +906 -0
  67. kailash/nodes/data/readers.py +8 -8
  68. kailash/nodes/data/redis.py +349 -0
  69. kailash/nodes/data/sql.py +314 -3
  70. kailash/nodes/data/streaming.py +21 -0
  71. kailash/nodes/enterprise/__init__.py +8 -0
  72. kailash/nodes/enterprise/audit_logger.py +285 -0
  73. kailash/nodes/enterprise/batch_processor.py +22 -3
  74. kailash/nodes/enterprise/data_lineage.py +1 -1
  75. kailash/nodes/enterprise/mcp_executor.py +205 -0
  76. kailash/nodes/enterprise/service_discovery.py +150 -0
  77. kailash/nodes/enterprise/tenant_assignment.py +108 -0
  78. kailash/nodes/logic/async_operations.py +2 -2
  79. kailash/nodes/logic/convergence.py +1 -1
  80. kailash/nodes/logic/operations.py +1 -1
  81. kailash/nodes/monitoring/__init__.py +11 -1
  82. kailash/nodes/monitoring/health_check.py +456 -0
  83. kailash/nodes/monitoring/log_processor.py +817 -0
  84. kailash/nodes/monitoring/metrics_collector.py +627 -0
  85. kailash/nodes/monitoring/performance_benchmark.py +137 -11
  86. kailash/nodes/rag/advanced.py +7 -7
  87. kailash/nodes/rag/agentic.py +49 -2
  88. kailash/nodes/rag/conversational.py +3 -3
  89. kailash/nodes/rag/evaluation.py +3 -3
  90. kailash/nodes/rag/federated.py +3 -3
  91. kailash/nodes/rag/graph.py +3 -3
  92. kailash/nodes/rag/multimodal.py +3 -3
  93. kailash/nodes/rag/optimized.py +5 -5
  94. kailash/nodes/rag/privacy.py +3 -3
  95. kailash/nodes/rag/query_processing.py +6 -6
  96. kailash/nodes/rag/realtime.py +1 -1
  97. kailash/nodes/rag/registry.py +2 -6
  98. kailash/nodes/rag/router.py +1 -1
  99. kailash/nodes/rag/similarity.py +7 -7
  100. kailash/nodes/rag/strategies.py +4 -4
  101. kailash/nodes/security/abac_evaluator.py +6 -6
  102. kailash/nodes/security/behavior_analysis.py +5 -6
  103. kailash/nodes/security/credential_manager.py +1 -1
  104. kailash/nodes/security/rotating_credentials.py +11 -11
  105. kailash/nodes/security/threat_detection.py +8 -8
  106. kailash/nodes/testing/credential_testing.py +2 -2
  107. kailash/nodes/transform/processors.py +5 -5
  108. kailash/runtime/local.py +162 -14
  109. kailash/runtime/parameter_injection.py +425 -0
  110. kailash/runtime/parameter_injector.py +657 -0
  111. kailash/runtime/testing.py +2 -2
  112. kailash/testing/fixtures.py +2 -2
  113. kailash/workflow/builder.py +99 -18
  114. kailash/workflow/builder_improvements.py +207 -0
  115. kailash/workflow/input_handling.py +170 -0
  116. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/METADATA +21 -8
  117. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/RECORD +126 -101
  118. kailash/mcp/__init__.py +0 -53
  119. kailash/mcp/client.py +0 -445
  120. kailash/mcp/server.py +0 -292
  121. kailash/mcp/server_enhanced.py +0 -449
  122. kailash/mcp/utils/cache.py +0 -267
  123. /kailash/{mcp → mcp_server}/client_new.py +0 -0
  124. /kailash/{mcp → mcp_server}/utils/__init__.py +0 -0
  125. /kailash/{mcp → mcp_server}/utils/config.py +0 -0
  126. /kailash/{mcp → mcp_server}/utils/formatters.py +0 -0
  127. /kailash/{mcp → mcp_server}/utils/metrics.py +0 -0
  128. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/WHEEL +0 -0
  129. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/entry_points.txt +0 -0
  130. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/licenses/LICENSE +0 -0
  131. {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/top_level.txt +0 -0
@@ -57,11 +57,7 @@ from pathlib import Path
57
57
  from typing import Any, get_type_hints
58
58
 
59
59
  from kailash.nodes.base import Node, NodeMetadata, NodeParameter, register_node
60
- from kailash.sdk_exceptions import (
61
- NodeConfigurationError,
62
- NodeExecutionError,
63
- SafetyViolationError,
64
- )
60
+ from kailash.sdk_exceptions import NodeConfigurationError, NodeExecutionError, SafetyViolationError
65
61
  from kailash.security import (
66
62
  ExecutionTimeoutError,
67
63
  MemoryLimitError,
@@ -145,14 +141,44 @@ class SafeCodeChecker(ast.NodeVisitor):
145
141
  "message": f"Import from module '{module_name}' is not allowed",
146
142
  }
147
143
  )
144
+ else:
145
+ # Check for dangerous function imports from allowed modules
146
+ dangerous_imports = {
147
+ "os": {"system", "popen", "execv", "execl", "spawn"},
148
+ "subprocess": {"run", "call", "check_call", "Popen"},
149
+ "__builtin__": {"eval", "exec", "compile", "__import__"},
150
+ "builtins": {"eval", "exec", "compile", "__import__"},
151
+ }
152
+
153
+ if module_name in dangerous_imports:
154
+ for alias in node.names:
155
+ import_name = alias.name
156
+ if import_name in dangerous_imports[module_name]:
157
+ self.violations.append(
158
+ {
159
+ "type": "dangerous_import",
160
+ "module": module_name,
161
+ "function": import_name,
162
+ "line": node.lineno,
163
+ "message": f"Import of dangerous function '{import_name}' from module '{module_name}' is not allowed",
164
+ }
165
+ )
148
166
  self.generic_visit(node)
149
167
 
150
168
  def visit_Call(self, node):
151
169
  """Check function calls."""
152
170
  if isinstance(node.func, ast.Name):
153
171
  func_name = node.func.id
154
- # Check for dangerous built-in functions
155
- if func_name in {"eval", "exec", "compile"}:
172
+ # Check for dangerous built-in functions and imported dangerous functions
173
+ dangerous_functions = {
174
+ "eval",
175
+ "exec",
176
+ "compile", # Built-in dangerous functions
177
+ "system",
178
+ "popen", # os module dangerous functions
179
+ "__import__", # Dynamic import function
180
+ }
181
+ if func_name in dangerous_functions:
156
182
  self.violations.append(
157
183
  {
158
184
  "type": "function_call",
@@ -236,6 +262,7 @@ class CodeExecutor:
236
262
  "zip",
237
263
  "print", # Allow print for debugging
238
264
  "hasattr", # For attribute checking
265
+ "hash", # For hashing operations
239
266
  # Exception classes for proper error handling
240
267
  "Exception",
241
268
  "ValueError",
@@ -375,6 +402,7 @@ class CodeExecutor:
375
402
 
376
403
  # Sanitize inputs
377
404
  sanitized_inputs = validate_node_parameters(inputs, self.security_config)
405
+
378
406
 
379
407
  # Create isolated namespace
380
408
  import builtins
@@ -449,15 +477,29 @@ class CodeExecutor:
449
477
  Raises:
450
478
  NodeExecutionError: If function execution fails
451
479
  """
480
+ # Sanitize inputs for security
481
+ sanitized_inputs = validate_node_parameters(inputs, self.security_config)
482
+
452
483
  try:
453
484
  # Get function signature
454
485
  sig = inspect.signature(func)
455
486
 
456
487
  # Map inputs to function parameters
457
488
  kwargs = {}
489
+ extra_kwargs = {}
490
+
491
+ # Check if function accepts **kwargs
492
+ accepts_var_keyword = any(
493
+ param.kind == inspect.Parameter.VAR_KEYWORD
494
+ for param in sig.parameters.values()
495
+ )
496
+
458
497
  for param_name, param in sig.parameters.items():
459
- if param_name in inputs:
460
- kwargs[param_name] = inputs[param_name]
498
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
499
+ # This is **kwargs parameter, skip it
500
+ continue
501
+ elif param_name in sanitized_inputs:
502
+ kwargs[param_name] = sanitized_inputs[param_name]
461
503
  elif param.default is not param.empty:
462
504
  # Use default value
463
505
  continue
@@ -466,6 +508,15 @@ class CodeExecutor:
466
508
  f"Missing required parameter: {param_name}"
467
509
  )
468
510
 
511
+ # Collect extra parameters if function accepts **kwargs
512
+ if accepts_var_keyword:
513
+ for key, value in sanitized_inputs.items():
514
+ if key not in kwargs:
515
+ extra_kwargs[key] = value
516
+
517
+ # Merge regular kwargs and extra kwargs
518
+ kwargs.update(extra_kwargs)
519
+
469
520
  # Execute function
470
521
  return func(**kwargs)
471
522
 
@@ -520,10 +571,44 @@ class FunctionWrapper:
520
571
  if param_name == "self":
521
572
  continue
522
573
 
574
+ # Skip **kwargs parameter - it's handled separately
575
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
576
+ continue
577
+
523
578
  param_type = self.type_hints.get(param_name, Any)
524
579
  input_types[param_name] = param_type
525
580
  return input_types
526
581
 
582
+ def get_parameter_info(self) -> dict[str, dict[str, Any]]:
583
+ """Extract detailed parameter information including defaults.
584
+
585
+ Returns:
586
+ Dictionary mapping parameter names to info dict with 'type' and 'has_default'
587
+ """
588
+ param_info = {}
589
+ for param_name, param in self.signature.parameters.items():
590
+ # Skip self parameter for class methods
591
+ if param_name == "self":
592
+ continue
593
+
594
+ # Skip **kwargs parameter - it's handled separately
595
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
596
+ continue
597
+
598
+ param_info[param_name] = {
599
+ "type": self.type_hints.get(param_name, Any),
600
+ "has_default": param.default is not param.empty,
601
+ "default": param.default if param.default is not param.empty else None,
602
+ }
603
+ return param_info
604
+
605
+ def accepts_var_keyword(self) -> bool:
606
+ """Check if function accepts **kwargs."""
607
+ return any(
608
+ param.kind == inspect.Parameter.VAR_KEYWORD
609
+ for param in self.signature.parameters.values()
610
+ )
611
+
527
612
  def get_output_type(self) -> type:
528
613
  """Extract output type from function signature.
529
614
 
@@ -672,6 +757,36 @@ class ClassWrapper:
672
757
  input_types[param_name] = param_type
673
758
  return input_types
674
759
 
760
+ def get_parameter_info(self) -> dict[str, dict[str, Any]]:
761
+ """Extract detailed parameter information including defaults.
762
+
763
+ Returns:
764
+ Dictionary mapping parameter names to info dict with 'type' and 'has_default'
765
+ """
766
+ param_info = {}
767
+ for param_name, param in self.signature.parameters.items():
768
+ # Skip self parameter
769
+ if param_name == "self":
770
+ continue
771
+
772
+ # Skip **kwargs parameter - it's handled separately
773
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
774
+ continue
775
+
776
+ param_info[param_name] = {
777
+ "type": self.type_hints.get(param_name, Any),
778
+ "has_default": param.default is not param.empty,
779
+ "default": param.default if param.default is not param.empty else None,
780
+ }
781
+ return param_info
782
+
783
+ def accepts_var_keyword(self) -> bool:
784
+ """Check if method accepts **kwargs."""
785
+ return any(
786
+ param.kind == inspect.Parameter.VAR_KEYWORD
787
+ for param in self.signature.parameters.values()
788
+ )
789
+
675
790
  def get_output_type(self) -> type:
676
791
  """Extract output type from method signature."""
677
792
  return self.type_hints.get("return", Any)
@@ -829,6 +944,7 @@ class PythonCodeNode(Node):
829
944
  output_schema: dict[str, "NodeParameter"] | None = None,
830
945
  description: str | None = None,
831
946
  max_code_lines: int = 10,
947
+ validate_security: bool = False,
832
948
  **kwargs,
833
949
  ):
834
950
  """Initialize a Python code node.
@@ -845,6 +961,7 @@ class PythonCodeNode(Node):
845
961
  output_schema: Explicit output parameter schema for validation
846
962
  description: Node description
847
963
  max_code_lines: Maximum lines before warning (default: 10)
964
+ validate_security: If True, validate code security at creation time (default: False)
848
965
  **kwargs: Additional node parameters
849
966
  """
850
967
  # Validate inputs
@@ -889,6 +1006,10 @@ class PythonCodeNode(Node):
889
1006
  # Initialize executor
890
1007
  self.executor = CodeExecutor()
891
1008
 
1009
+ # Validate code security if requested
1010
+ if validate_security and self.code:
1011
+ self.executor.check_code_safety(self.code)
1012
+
892
1013
  # Create metadata (avoiding conflicts with kwargs)
893
1014
  if "metadata" not in kwargs:
894
1015
  kwargs["metadata"] = NodeMetadata(
@@ -938,34 +1059,47 @@ class PythonCodeNode(Node):
938
1059
  )
939
1060
 
940
1061
  # If we have a function/class, extract parameter info
1062
+ # This overrides the basic input_types to include default parameter information
941
1063
  if self.function:
942
1064
  wrapper = FunctionWrapper(self.function, self.executor)
943
- for name, type_ in wrapper.get_input_types().items():
944
- if name not in parameters:
945
- # Use Any type for complex types to avoid validation issues
946
- param_type = Any if hasattr(type_, "__origin__") else type_
947
-
948
- parameters[name] = NodeParameter(
949
- name=name,
950
- type=param_type,
951
- required=True,
952
- description=f"Input parameter {name}",
953
- )
1065
+ for name, param_info in wrapper.get_parameter_info().items():
1066
+ # Use Any type for complex types to avoid validation issues
1067
+ param_type = param_info["type"]
1068
+ param_type = Any if hasattr(param_type, "__origin__") else param_type
1069
+
1070
+ # Override existing parameter or add new one with correct required flag
1071
+ parameters[name] = NodeParameter(
1072
+ name=name,
1073
+ type=param_type,
1074
+ required=not param_info[
1075
+ "has_default"
1076
+ ], # Fixed: respect default values
1077
+ description=f"Input parameter {name}",
1078
+ default=(
1079
+ param_info["default"] if param_info["has_default"] else None
1080
+ ),
1081
+ )
954
1082
  elif self.class_type and self.process_method:
955
1083
  wrapper = ClassWrapper(
956
1084
  self.class_type, self.process_method or "process", self.executor
957
1085
  )
958
- for name, type_ in wrapper.get_input_types().items():
959
- if name not in parameters:
960
- # Use Any type for complex types to avoid validation issues
961
- param_type = Any if hasattr(type_, "__origin__") else type_
962
-
963
- parameters[name] = NodeParameter(
964
- name=name,
965
- type=param_type,
966
- required=True,
967
- description=f"Input parameter {name}",
968
- )
1086
+ for name, param_info in wrapper.get_parameter_info().items():
1087
+ # Use Any type for complex types to avoid validation issues
1088
+ param_type = param_info["type"]
1089
+ param_type = Any if hasattr(param_type, "__origin__") else param_type
1090
+
1091
+ # Override existing parameter or add new one with correct required flag
1092
+ parameters[name] = NodeParameter(
1093
+ name=name,
1094
+ type=param_type,
1095
+ required=not param_info[
1096
+ "has_default"
1097
+ ], # Fixed: respect default values
1098
+ description=f"Input parameter {name}",
1099
+ default=(
1100
+ param_info["default"] if param_info["has_default"] else None
1101
+ ),
1102
+ )
969
1103
 
970
1104
  return parameters
971
1105
 
@@ -985,6 +1119,21 @@ class PythonCodeNode(Node):
985
1119
  if self.code:
986
1120
  return kwargs
987
1121
 
1122
+ # Check if function/class accepts **kwargs
1123
+ accepts_var_keyword = False
1124
+ if self.function:
1125
+ wrapper = FunctionWrapper(self.function, self.executor)
1126
+ accepts_var_keyword = wrapper.accepts_var_keyword()
1127
+ elif self.class_type:
1128
+ wrapper = ClassWrapper(
1129
+ self.class_type, self.process_method or "process", self.executor
1130
+ )
1131
+ accepts_var_keyword = wrapper.accepts_var_keyword()
1132
+
1133
+ # If function accepts **kwargs, pass through all inputs
1134
+ if accepts_var_keyword:
1135
+ return kwargs
1136
+
988
1137
  # Otherwise use standard validation for function/class nodes
989
1138
  return super().validate_inputs(**kwargs)
990
1139
 
@@ -1429,12 +1578,24 @@ class PythonCodeNode(Node):
1429
1578
 
1430
1579
  # Add suggestions from violations
1431
1580
  for violation in violations:
1432
- if violation["type"] in ["import", "import_from"]:
1433
- module_info = self.check_module_availability(
1434
- violation["module"]
1435
- )
1581
+ if violation["type"] in [
1582
+ "import",
1583
+ "import_from",
1584
+ "dangerous_import",
1585
+ ]:
1586
+ module = violation.get("module", "unknown")
1587
+ module_info = self.check_module_availability(module)
1436
1588
  result["suggestions"].extend(module_info["suggestions"])
1437
1589
 
1590
+ except SafetyViolationError as e:
1591
+ # Safety violations should mark code as invalid, not just warnings
1592
+ result["valid"] = False
1593
+ result["safety_violations"].append(
1594
+ {"type": "safety_error", "message": str(e), "line": 1}
1595
+ )
1596
+ result["suggestions"].append(
1597
+ "Fix security violations before using this code."
1598
+ )
1438
1599
  except Exception as e:
1439
1600
  result["warnings"].append(f"Could not complete safety check: {e}")
1440
1601
 
@@ -122,7 +122,7 @@ class DataRetentionPolicyNode(SecurityMixin, PerformanceMixin, LoggingMixin, Nod
122
122
  ... {"id": "session_456", "type": "session_logs", "created": "2022-01-01", "size": 512}
123
123
  ... ]
124
124
  >>>
125
- >>> result = retention_node.run(
125
+ >>> result = retention_node.execute(
126
126
  ... action="apply_policy",
127
127
  ... data_type="user_data",
128
128
  ... data_records=data_records
@@ -130,7 +130,7 @@ class DataRetentionPolicyNode(SecurityMixin, PerformanceMixin, LoggingMixin, Nod
130
130
  >>> print(f"Actions taken: {result['actions_taken']}")
131
131
  >>>
132
132
  >>> # Scan for expired data
133
- >>> scan_result = retention_node.run(
133
+ >>> scan_result = retention_node.execute(
134
134
  ... action="scan_expired",
135
135
  ... data_types=["user_data", "session_logs"]
136
136
  ... )
@@ -430,11 +430,11 @@ class DataRetentionPolicyNode(SecurityMixin, PerformanceMixin, LoggingMixin, Nod
430
430
 
431
431
  async def async_run(self, **kwargs) -> Dict[str, Any]:
432
432
  """Async wrapper for run method."""
433
- return self.run(**kwargs)
433
+ return self.execute(**kwargs)
434
434
 
435
435
  async def execute_async(self, **kwargs) -> Dict[str, Any]:
436
436
  """Async execution method for test compatibility."""
437
- return self.run(**kwargs)
437
+ return self.execute(**kwargs)
438
438
 
439
439
  def _apply_retention_policy(
440
440
  self, data_type: str, data_records: List[Dict[str, Any]]
@@ -1351,7 +1351,7 @@ class DataRetentionPolicyNode(SecurityMixin, PerformanceMixin, LoggingMixin, Nod
1351
1351
  }
1352
1352
 
1353
1353
  try:
1354
- self.audit_log_node.run(**audit_entry)
1354
+ self.audit_log_node.execute(**audit_entry)
1355
1355
  except Exception as e:
1356
1356
  self.log_with_context("WARNING", f"Failed to audit retention action: {e}")
1357
1357
 
@@ -1375,7 +1375,7 @@ class DataRetentionPolicyNode(SecurityMixin, PerformanceMixin, LoggingMixin, Nod
1375
1375
  }
1376
1376
 
1377
1377
  try:
1378
- self.security_event_node.run(**security_event)
1378
+ self.security_event_node.execute(**security_event)
1379
1379
  except Exception as e:
1380
1380
  self.log_with_context("WARNING", f"Failed to log security event: {e}")
1381
1381
 
@@ -152,7 +152,7 @@ class GDPRComplianceNode(SecurityMixin, PerformanceMixin, LoggingMixin, Node):
152
152
  ... "address": "123 Main St"
153
153
  ... }
154
154
  >>>
155
- >>> result = gdpr_node.run(
155
+ >>> result = gdpr_node.execute(
156
156
  ... action="check_compliance",
157
157
  ... data_type="user_profile",
158
158
  ... data=data
@@ -160,7 +160,7 @@ class GDPRComplianceNode(SecurityMixin, PerformanceMixin, LoggingMixin, Node):
160
160
  >>> print(f"Compliance: {result['compliant']}")
161
161
  >>>
162
162
  >>> # Process data subject request
163
- >>> request_result = gdpr_node.run(
163
+ >>> request_result = gdpr_node.execute(
164
164
  ... action="process_data_subject_request",
165
165
  ... request_type="erasure",
166
166
  ... user_id="user123"
@@ -515,7 +515,7 @@ class GDPRComplianceNode(SecurityMixin, PerformanceMixin, LoggingMixin, Node):
515
515
 
516
516
  async def execute_async(self, **kwargs) -> Dict[str, Any]:
517
517
  """Async execution method for test compatibility."""
518
- return self.run(**kwargs)
518
+ return self.execute(**kwargs)
519
519
 
520
520
  def _check_data_compliance(
521
521
  self, data_type: str, data: Dict[str, Any]
@@ -1523,7 +1523,7 @@ RESPONSE FORMAT:
1523
1523
  """
1524
1524
 
1525
1525
  # Run AI analysis
1526
- ai_response = self.ai_agent.run(
1526
+ ai_response = self.ai_agent.execute(
1527
1527
  provider="ollama",
1528
1528
  model=self.ai_model.replace("ollama:", ""),
1529
1529
  messages=[{"role": "user", "content": prompt}],
@@ -1749,7 +1749,7 @@ RESPONSE FORMAT:
1749
1749
  }
1750
1750
 
1751
1751
  try:
1752
- self.audit_log_node.run(**audit_entry)
1752
+ self.audit_log_node.execute(**audit_entry)
1753
1753
  except Exception as e:
1754
1754
  self.log_with_context(
1755
1755
  "WARNING", f"Failed to audit data subject request: {e}"
@@ -91,12 +91,16 @@ from kailash.nodes.data.async_vector import AsyncPostgreSQLVectorNode
91
91
  from kailash.nodes.data.directory import DirectoryReaderNode
92
92
  from kailash.nodes.data.event_generation import EventGeneratorNode
93
93
  from kailash.nodes.data.file_discovery import FileDiscoveryNode
94
+ from kailash.nodes.data.query_router import QueryRouterNode
94
95
  from kailash.nodes.data.readers import (
95
96
  CSVReaderNode,
96
97
  DocumentProcessorNode,
97
98
  JSONReaderNode,
98
99
  TextReaderNode,
99
100
  )
101
+
102
+ # Redis
103
+ from kailash.nodes.data.redis import RedisNode
100
104
  from kailash.nodes.data.retrieval import HybridRetrieverNode, RelevanceScorerNode
101
105
  from kailash.nodes.data.sharepoint_graph import (
102
106
  SharePointGraphReader,
@@ -115,6 +119,7 @@ from kailash.nodes.data.vector_db import (
115
119
  TextSplitterNode,
116
120
  VectorDatabaseNode,
117
121
  )
122
+ from kailash.nodes.data.workflow_connection_pool import WorkflowConnectionPool
118
123
  from kailash.nodes.data.writers import CSVWriterNode, JSONWriterNode, TextWriterNode
119
124
 
120
125
  __all__ = [
@@ -143,6 +148,8 @@ __all__ = [
143
148
  "HybridRetrieverNode",
144
149
  # SQL
145
150
  "SQLDatabaseNode",
151
+ # Redis
152
+ "RedisNode",
146
153
  # Vector DB
147
154
  "EmbeddingNode",
148
155
  "VectorDatabaseNode",
@@ -157,4 +164,7 @@ __all__ = [
157
164
  "AsyncConnectionManager",
158
165
  "get_connection_manager",
159
166
  "AsyncPostgreSQLVectorNode",
167
+ # Connection Pool & Query Routing
168
+ "WorkflowConnectionPool",
169
+ "QueryRouterNode",
160
170
  ]