kailash 0.6.3__py3-none-any.whl → 0.6.5__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 (122) 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 +1 -1
  5. kailash/api/workflow_api.py +2 -2
  6. kailash/core/resilience/bulkhead.py +475 -0
  7. kailash/core/resilience/circuit_breaker.py +92 -10
  8. kailash/core/resilience/health_monitor.py +578 -0
  9. kailash/edge/discovery.py +86 -0
  10. kailash/mcp_server/__init__.py +309 -33
  11. kailash/mcp_server/advanced_features.py +1022 -0
  12. kailash/mcp_server/ai_registry_server.py +27 -2
  13. kailash/mcp_server/auth.py +789 -0
  14. kailash/mcp_server/client.py +645 -378
  15. kailash/mcp_server/discovery.py +1593 -0
  16. kailash/mcp_server/errors.py +673 -0
  17. kailash/mcp_server/oauth.py +1727 -0
  18. kailash/mcp_server/protocol.py +1126 -0
  19. kailash/mcp_server/registry_integration.py +587 -0
  20. kailash/mcp_server/server.py +1228 -96
  21. kailash/mcp_server/transports.py +1169 -0
  22. kailash/mcp_server/utils/__init__.py +6 -1
  23. kailash/mcp_server/utils/cache.py +250 -7
  24. kailash/middleware/auth/auth_manager.py +3 -3
  25. kailash/middleware/communication/api_gateway.py +1 -1
  26. kailash/middleware/communication/realtime.py +1 -1
  27. kailash/middleware/mcp/enhanced_server.py +1 -1
  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 +15 -15
  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 -21
  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 +2 -2
  43. kailash/nodes/ai/llm_agent.py +210 -33
  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 +10 -10
  48. kailash/nodes/api/rate_limiting.py +4 -4
  49. kailash/nodes/api/rest.py +15 -15
  50. kailash/nodes/auth/mfa.py +3 -3
  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 +8 -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 +874 -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 +194 -30
  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/async_sql.py +1956 -129
  67. kailash/nodes/data/optimistic_locking.py +906 -0
  68. kailash/nodes/data/readers.py +8 -8
  69. kailash/nodes/data/redis.py +378 -0
  70. kailash/nodes/data/sql.py +314 -3
  71. kailash/nodes/data/streaming.py +21 -0
  72. kailash/nodes/enterprise/__init__.py +8 -0
  73. kailash/nodes/enterprise/audit_logger.py +285 -0
  74. kailash/nodes/enterprise/batch_processor.py +22 -3
  75. kailash/nodes/enterprise/data_lineage.py +1 -1
  76. kailash/nodes/enterprise/mcp_executor.py +205 -0
  77. kailash/nodes/enterprise/service_discovery.py +150 -0
  78. kailash/nodes/enterprise/tenant_assignment.py +108 -0
  79. kailash/nodes/logic/async_operations.py +2 -2
  80. kailash/nodes/logic/convergence.py +1 -1
  81. kailash/nodes/logic/operations.py +1 -1
  82. kailash/nodes/monitoring/__init__.py +11 -1
  83. kailash/nodes/monitoring/health_check.py +456 -0
  84. kailash/nodes/monitoring/log_processor.py +817 -0
  85. kailash/nodes/monitoring/metrics_collector.py +627 -0
  86. kailash/nodes/monitoring/performance_benchmark.py +137 -11
  87. kailash/nodes/rag/advanced.py +7 -7
  88. kailash/nodes/rag/agentic.py +49 -2
  89. kailash/nodes/rag/conversational.py +3 -3
  90. kailash/nodes/rag/evaluation.py +3 -3
  91. kailash/nodes/rag/federated.py +3 -3
  92. kailash/nodes/rag/graph.py +3 -3
  93. kailash/nodes/rag/multimodal.py +3 -3
  94. kailash/nodes/rag/optimized.py +5 -5
  95. kailash/nodes/rag/privacy.py +3 -3
  96. kailash/nodes/rag/query_processing.py +6 -6
  97. kailash/nodes/rag/realtime.py +1 -1
  98. kailash/nodes/rag/registry.py +1 -1
  99. kailash/nodes/rag/router.py +1 -1
  100. kailash/nodes/rag/similarity.py +7 -7
  101. kailash/nodes/rag/strategies.py +4 -4
  102. kailash/nodes/security/abac_evaluator.py +6 -6
  103. kailash/nodes/security/behavior_analysis.py +5 -5
  104. kailash/nodes/security/credential_manager.py +1 -1
  105. kailash/nodes/security/rotating_credentials.py +11 -11
  106. kailash/nodes/security/threat_detection.py +8 -8
  107. kailash/nodes/testing/credential_testing.py +2 -2
  108. kailash/nodes/transform/processors.py +5 -5
  109. kailash/runtime/local.py +163 -9
  110. kailash/runtime/parameter_injection.py +425 -0
  111. kailash/runtime/parameter_injector.py +657 -0
  112. kailash/runtime/testing.py +2 -2
  113. kailash/testing/fixtures.py +2 -2
  114. kailash/workflow/builder.py +99 -14
  115. kailash/workflow/builder_improvements.py +207 -0
  116. kailash/workflow/input_handling.py +170 -0
  117. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/METADATA +22 -9
  118. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/RECORD +122 -95
  119. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/WHEEL +0 -0
  120. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/entry_points.txt +0 -0
  121. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/licenses/LICENSE +0 -0
  122. {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/top_level.txt +0 -0
@@ -145,14 +145,44 @@ class SafeCodeChecker(ast.NodeVisitor):
145
145
  "message": f"Import from module '{module_name}' is not allowed",
146
146
  }
147
147
  )
148
+ else:
149
+ # Check for dangerous function imports from allowed modules
150
+ dangerous_imports = {
151
+ "os": {"system", "popen", "execv", "execl", "spawn"},
152
+ "subprocess": {"run", "call", "check_call", "Popen"},
153
+ "__builtin__": {"eval", "exec", "compile", "__import__"},
154
+ "builtins": {"eval", "exec", "compile", "__import__"},
155
+ }
156
+
157
+ if module_name in dangerous_imports:
158
+ for alias in node.names:
159
+ import_name = alias.name
160
+ if import_name in dangerous_imports[module_name]:
161
+ self.violations.append(
162
+ {
163
+ "type": "dangerous_import",
164
+ "module": module_name,
165
+ "function": import_name,
166
+ "line": node.lineno,
167
+ "message": f"Import of dangerous function '{import_name}' from module '{module_name}' is not allowed",
168
+ }
169
+ )
148
170
  self.generic_visit(node)
149
171
 
150
172
  def visit_Call(self, node):
151
173
  """Check function calls."""
152
174
  if isinstance(node.func, ast.Name):
153
175
  func_name = node.func.id
154
- # Check for dangerous built-in functions
155
- if func_name in {"eval", "exec", "compile"}:
176
+ # Check for dangerous built-in functions and imported dangerous functions
177
+ dangerous_functions = {
178
+ "eval",
179
+ "exec",
180
+ "compile", # Built-in dangerous functions
181
+ "system",
182
+ "popen", # os module dangerous functions
183
+ "__import__", # Dynamic import function
184
+ }
185
+ if func_name in dangerous_functions:
156
186
  self.violations.append(
157
187
  {
158
188
  "type": "function_call",
@@ -236,6 +266,7 @@ class CodeExecutor:
236
266
  "zip",
237
267
  "print", # Allow print for debugging
238
268
  "hasattr", # For attribute checking
269
+ "hash", # For hashing operations
239
270
  # Exception classes for proper error handling
240
271
  "Exception",
241
272
  "ValueError",
@@ -449,15 +480,29 @@ class CodeExecutor:
449
480
  Raises:
450
481
  NodeExecutionError: If function execution fails
451
482
  """
483
+ # Sanitize inputs for security
484
+ sanitized_inputs = validate_node_parameters(inputs, self.security_config)
485
+
452
486
  try:
453
487
  # Get function signature
454
488
  sig = inspect.signature(func)
455
489
 
456
490
  # Map inputs to function parameters
457
491
  kwargs = {}
492
+ extra_kwargs = {}
493
+
494
+ # Check if function accepts **kwargs
495
+ accepts_var_keyword = any(
496
+ param.kind == inspect.Parameter.VAR_KEYWORD
497
+ for param in sig.parameters.values()
498
+ )
499
+
458
500
  for param_name, param in sig.parameters.items():
459
- if param_name in inputs:
460
- kwargs[param_name] = inputs[param_name]
501
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
502
+ # This is **kwargs parameter, skip it
503
+ continue
504
+ elif param_name in sanitized_inputs:
505
+ kwargs[param_name] = sanitized_inputs[param_name]
461
506
  elif param.default is not param.empty:
462
507
  # Use default value
463
508
  continue
@@ -466,6 +511,15 @@ class CodeExecutor:
466
511
  f"Missing required parameter: {param_name}"
467
512
  )
468
513
 
514
+ # Collect extra parameters if function accepts **kwargs
515
+ if accepts_var_keyword:
516
+ for key, value in sanitized_inputs.items():
517
+ if key not in kwargs:
518
+ extra_kwargs[key] = value
519
+
520
+ # Merge regular kwargs and extra kwargs
521
+ kwargs.update(extra_kwargs)
522
+
469
523
  # Execute function
470
524
  return func(**kwargs)
471
525
 
@@ -520,10 +574,44 @@ class FunctionWrapper:
520
574
  if param_name == "self":
521
575
  continue
522
576
 
577
+ # Skip **kwargs parameter - it's handled separately
578
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
579
+ continue
580
+
523
581
  param_type = self.type_hints.get(param_name, Any)
524
582
  input_types[param_name] = param_type
525
583
  return input_types
526
584
 
585
+ def get_parameter_info(self) -> dict[str, dict[str, Any]]:
586
+ """Extract detailed parameter information including defaults.
587
+
588
+ Returns:
589
+ Dictionary mapping parameter names to info dict with 'type' and 'has_default'
590
+ """
591
+ param_info = {}
592
+ for param_name, param in self.signature.parameters.items():
593
+ # Skip self parameter for class methods
594
+ if param_name == "self":
595
+ continue
596
+
597
+ # Skip **kwargs parameter - it's handled separately
598
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
599
+ continue
600
+
601
+ param_info[param_name] = {
602
+ "type": self.type_hints.get(param_name, Any),
603
+ "has_default": param.default is not param.empty,
604
+ "default": param.default if param.default is not param.empty else None,
605
+ }
606
+ return param_info
607
+
608
+ def accepts_var_keyword(self) -> bool:
609
+ """Check if function accepts **kwargs."""
610
+ return any(
611
+ param.kind == inspect.Parameter.VAR_KEYWORD
612
+ for param in self.signature.parameters.values()
613
+ )
614
+
527
615
  def get_output_type(self) -> type:
528
616
  """Extract output type from function signature.
529
617
 
@@ -672,6 +760,36 @@ class ClassWrapper:
672
760
  input_types[param_name] = param_type
673
761
  return input_types
674
762
 
763
+ def get_parameter_info(self) -> dict[str, dict[str, Any]]:
764
+ """Extract detailed parameter information including defaults.
765
+
766
+ Returns:
767
+ Dictionary mapping parameter names to info dict with 'type' and 'has_default'
768
+ """
769
+ param_info = {}
770
+ for param_name, param in self.signature.parameters.items():
771
+ # Skip self parameter
772
+ if param_name == "self":
773
+ continue
774
+
775
+ # Skip **kwargs parameter - it's handled separately
776
+ if param.kind == inspect.Parameter.VAR_KEYWORD:
777
+ continue
778
+
779
+ param_info[param_name] = {
780
+ "type": self.type_hints.get(param_name, Any),
781
+ "has_default": param.default is not param.empty,
782
+ "default": param.default if param.default is not param.empty else None,
783
+ }
784
+ return param_info
785
+
786
+ def accepts_var_keyword(self) -> bool:
787
+ """Check if method accepts **kwargs."""
788
+ return any(
789
+ param.kind == inspect.Parameter.VAR_KEYWORD
790
+ for param in self.signature.parameters.values()
791
+ )
792
+
675
793
  def get_output_type(self) -> type:
676
794
  """Extract output type from method signature."""
677
795
  return self.type_hints.get("return", Any)
@@ -829,6 +947,7 @@ class PythonCodeNode(Node):
829
947
  output_schema: dict[str, "NodeParameter"] | None = None,
830
948
  description: str | None = None,
831
949
  max_code_lines: int = 10,
950
+ validate_security: bool = False,
832
951
  **kwargs,
833
952
  ):
834
953
  """Initialize a Python code node.
@@ -845,6 +964,7 @@ class PythonCodeNode(Node):
845
964
  output_schema: Explicit output parameter schema for validation
846
965
  description: Node description
847
966
  max_code_lines: Maximum lines before warning (default: 10)
967
+ validate_security: If True, validate code security at creation time (default: False)
848
968
  **kwargs: Additional node parameters
849
969
  """
850
970
  # Validate inputs
@@ -889,6 +1009,10 @@ class PythonCodeNode(Node):
889
1009
  # Initialize executor
890
1010
  self.executor = CodeExecutor()
891
1011
 
1012
+ # Validate code security if requested
1013
+ if validate_security and self.code:
1014
+ self.executor.check_code_safety(self.code)
1015
+
892
1016
  # Create metadata (avoiding conflicts with kwargs)
893
1017
  if "metadata" not in kwargs:
894
1018
  kwargs["metadata"] = NodeMetadata(
@@ -938,34 +1062,47 @@ class PythonCodeNode(Node):
938
1062
  )
939
1063
 
940
1064
  # If we have a function/class, extract parameter info
1065
+ # This overrides the basic input_types to include default parameter information
941
1066
  if self.function:
942
1067
  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
- )
1068
+ for name, param_info in wrapper.get_parameter_info().items():
1069
+ # Use Any type for complex types to avoid validation issues
1070
+ param_type = param_info["type"]
1071
+ param_type = Any if hasattr(param_type, "__origin__") else param_type
1072
+
1073
+ # Override existing parameter or add new one with correct required flag
1074
+ parameters[name] = NodeParameter(
1075
+ name=name,
1076
+ type=param_type,
1077
+ required=not param_info[
1078
+ "has_default"
1079
+ ], # Fixed: respect default values
1080
+ description=f"Input parameter {name}",
1081
+ default=(
1082
+ param_info["default"] if param_info["has_default"] else None
1083
+ ),
1084
+ )
954
1085
  elif self.class_type and self.process_method:
955
1086
  wrapper = ClassWrapper(
956
1087
  self.class_type, self.process_method or "process", self.executor
957
1088
  )
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
- )
1089
+ for name, param_info in wrapper.get_parameter_info().items():
1090
+ # Use Any type for complex types to avoid validation issues
1091
+ param_type = param_info["type"]
1092
+ param_type = Any if hasattr(param_type, "__origin__") else param_type
1093
+
1094
+ # Override existing parameter or add new one with correct required flag
1095
+ parameters[name] = NodeParameter(
1096
+ name=name,
1097
+ type=param_type,
1098
+ required=not param_info[
1099
+ "has_default"
1100
+ ], # Fixed: respect default values
1101
+ description=f"Input parameter {name}",
1102
+ default=(
1103
+ param_info["default"] if param_info["has_default"] else None
1104
+ ),
1105
+ )
969
1106
 
970
1107
  return parameters
971
1108
 
@@ -985,6 +1122,21 @@ class PythonCodeNode(Node):
985
1122
  if self.code:
986
1123
  return kwargs
987
1124
 
1125
+ # Check if function/class accepts **kwargs
1126
+ accepts_var_keyword = False
1127
+ if self.function:
1128
+ wrapper = FunctionWrapper(self.function, self.executor)
1129
+ accepts_var_keyword = wrapper.accepts_var_keyword()
1130
+ elif self.class_type:
1131
+ wrapper = ClassWrapper(
1132
+ self.class_type, self.process_method or "process", self.executor
1133
+ )
1134
+ accepts_var_keyword = wrapper.accepts_var_keyword()
1135
+
1136
+ # If function accepts **kwargs, pass through all inputs
1137
+ if accepts_var_keyword:
1138
+ return kwargs
1139
+
988
1140
  # Otherwise use standard validation for function/class nodes
989
1141
  return super().validate_inputs(**kwargs)
990
1142
 
@@ -1429,12 +1581,24 @@ class PythonCodeNode(Node):
1429
1581
 
1430
1582
  # Add suggestions from violations
1431
1583
  for violation in violations:
1432
- if violation["type"] in ["import", "import_from"]:
1433
- module_info = self.check_module_availability(
1434
- violation["module"]
1435
- )
1584
+ if violation["type"] in [
1585
+ "import",
1586
+ "import_from",
1587
+ "dangerous_import",
1588
+ ]:
1589
+ module = violation.get("module", "unknown")
1590
+ module_info = self.check_module_availability(module)
1436
1591
  result["suggestions"].extend(module_info["suggestions"])
1437
1592
 
1593
+ except SafetyViolationError as e:
1594
+ # Safety violations should mark code as invalid, not just warnings
1595
+ result["valid"] = False
1596
+ result["safety_violations"].append(
1597
+ {"type": "safety_error", "message": str(e), "line": 1}
1598
+ )
1599
+ result["suggestions"].append(
1600
+ "Fix security violations before using this code."
1601
+ )
1438
1602
  except Exception as e:
1439
1603
  result["warnings"].append(f"Could not complete safety check: {e}")
1440
1604
 
@@ -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
  ]