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.
- kailash/__init__.py +3 -3
- kailash/api/custom_nodes_secure.py +3 -3
- kailash/api/gateway.py +1 -1
- kailash/api/studio.py +2 -3
- kailash/api/workflow_api.py +3 -4
- kailash/core/resilience/bulkhead.py +460 -0
- kailash/core/resilience/circuit_breaker.py +92 -10
- kailash/edge/discovery.py +86 -0
- kailash/mcp_server/__init__.py +334 -0
- kailash/mcp_server/advanced_features.py +1022 -0
- kailash/{mcp → mcp_server}/ai_registry_server.py +29 -4
- kailash/mcp_server/auth.py +789 -0
- kailash/mcp_server/client.py +712 -0
- kailash/mcp_server/discovery.py +1593 -0
- kailash/mcp_server/errors.py +673 -0
- kailash/mcp_server/oauth.py +1727 -0
- kailash/mcp_server/protocol.py +1126 -0
- kailash/mcp_server/registry_integration.py +587 -0
- kailash/mcp_server/server.py +1747 -0
- kailash/{mcp → mcp_server}/servers/ai_registry.py +2 -2
- kailash/mcp_server/transports.py +1169 -0
- kailash/mcp_server/utils/cache.py +510 -0
- kailash/middleware/auth/auth_manager.py +3 -3
- kailash/middleware/communication/api_gateway.py +2 -9
- kailash/middleware/communication/realtime.py +1 -1
- kailash/middleware/mcp/client_integration.py +1 -1
- kailash/middleware/mcp/enhanced_server.py +2 -2
- kailash/nodes/__init__.py +2 -0
- kailash/nodes/admin/audit_log.py +6 -6
- kailash/nodes/admin/permission_check.py +8 -8
- kailash/nodes/admin/role_management.py +32 -28
- kailash/nodes/admin/schema.sql +6 -1
- kailash/nodes/admin/schema_manager.py +13 -13
- kailash/nodes/admin/security_event.py +16 -20
- kailash/nodes/admin/tenant_isolation.py +3 -3
- kailash/nodes/admin/transaction_utils.py +3 -3
- kailash/nodes/admin/user_management.py +21 -22
- kailash/nodes/ai/a2a.py +11 -11
- kailash/nodes/ai/ai_providers.py +9 -12
- kailash/nodes/ai/embedding_generator.py +13 -14
- kailash/nodes/ai/intelligent_agent_orchestrator.py +19 -19
- kailash/nodes/ai/iterative_llm_agent.py +3 -3
- kailash/nodes/ai/llm_agent.py +213 -36
- kailash/nodes/ai/self_organizing.py +2 -2
- kailash/nodes/alerts/discord.py +4 -4
- kailash/nodes/api/graphql.py +6 -6
- kailash/nodes/api/http.py +12 -17
- kailash/nodes/api/rate_limiting.py +4 -4
- kailash/nodes/api/rest.py +15 -15
- kailash/nodes/auth/mfa.py +3 -4
- kailash/nodes/auth/risk_assessment.py +2 -2
- kailash/nodes/auth/session_management.py +5 -5
- kailash/nodes/auth/sso.py +143 -0
- kailash/nodes/base.py +6 -2
- kailash/nodes/base_async.py +16 -2
- kailash/nodes/base_with_acl.py +2 -2
- kailash/nodes/cache/__init__.py +9 -0
- kailash/nodes/cache/cache.py +1172 -0
- kailash/nodes/cache/cache_invalidation.py +870 -0
- kailash/nodes/cache/redis_pool_manager.py +595 -0
- kailash/nodes/code/async_python.py +2 -1
- kailash/nodes/code/python.py +196 -35
- kailash/nodes/compliance/data_retention.py +6 -6
- kailash/nodes/compliance/gdpr.py +5 -5
- kailash/nodes/data/__init__.py +10 -0
- kailash/nodes/data/optimistic_locking.py +906 -0
- kailash/nodes/data/readers.py +8 -8
- kailash/nodes/data/redis.py +349 -0
- kailash/nodes/data/sql.py +314 -3
- kailash/nodes/data/streaming.py +21 -0
- kailash/nodes/enterprise/__init__.py +8 -0
- kailash/nodes/enterprise/audit_logger.py +285 -0
- kailash/nodes/enterprise/batch_processor.py +22 -3
- kailash/nodes/enterprise/data_lineage.py +1 -1
- kailash/nodes/enterprise/mcp_executor.py +205 -0
- kailash/nodes/enterprise/service_discovery.py +150 -0
- kailash/nodes/enterprise/tenant_assignment.py +108 -0
- kailash/nodes/logic/async_operations.py +2 -2
- kailash/nodes/logic/convergence.py +1 -1
- kailash/nodes/logic/operations.py +1 -1
- kailash/nodes/monitoring/__init__.py +11 -1
- kailash/nodes/monitoring/health_check.py +456 -0
- kailash/nodes/monitoring/log_processor.py +817 -0
- kailash/nodes/monitoring/metrics_collector.py +627 -0
- kailash/nodes/monitoring/performance_benchmark.py +137 -11
- kailash/nodes/rag/advanced.py +7 -7
- kailash/nodes/rag/agentic.py +49 -2
- kailash/nodes/rag/conversational.py +3 -3
- kailash/nodes/rag/evaluation.py +3 -3
- kailash/nodes/rag/federated.py +3 -3
- kailash/nodes/rag/graph.py +3 -3
- kailash/nodes/rag/multimodal.py +3 -3
- kailash/nodes/rag/optimized.py +5 -5
- kailash/nodes/rag/privacy.py +3 -3
- kailash/nodes/rag/query_processing.py +6 -6
- kailash/nodes/rag/realtime.py +1 -1
- kailash/nodes/rag/registry.py +2 -6
- kailash/nodes/rag/router.py +1 -1
- kailash/nodes/rag/similarity.py +7 -7
- kailash/nodes/rag/strategies.py +4 -4
- kailash/nodes/security/abac_evaluator.py +6 -6
- kailash/nodes/security/behavior_analysis.py +5 -6
- kailash/nodes/security/credential_manager.py +1 -1
- kailash/nodes/security/rotating_credentials.py +11 -11
- kailash/nodes/security/threat_detection.py +8 -8
- kailash/nodes/testing/credential_testing.py +2 -2
- kailash/nodes/transform/processors.py +5 -5
- kailash/runtime/local.py +162 -14
- kailash/runtime/parameter_injection.py +425 -0
- kailash/runtime/parameter_injector.py +657 -0
- kailash/runtime/testing.py +2 -2
- kailash/testing/fixtures.py +2 -2
- kailash/workflow/builder.py +99 -18
- kailash/workflow/builder_improvements.py +207 -0
- kailash/workflow/input_handling.py +170 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/METADATA +21 -8
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/RECORD +126 -101
- kailash/mcp/__init__.py +0 -53
- kailash/mcp/client.py +0 -445
- kailash/mcp/server.py +0 -292
- kailash/mcp/server_enhanced.py +0 -449
- kailash/mcp/utils/cache.py +0 -267
- /kailash/{mcp → mcp_server}/client_new.py +0 -0
- /kailash/{mcp → mcp_server}/utils/__init__.py +0 -0
- /kailash/{mcp → mcp_server}/utils/config.py +0 -0
- /kailash/{mcp → mcp_server}/utils/formatters.py +0 -0
- /kailash/{mcp → mcp_server}/utils/metrics.py +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/WHEEL +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/entry_points.txt +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.6.2.dist-info → kailash-0.6.4.dist-info}/top_level.txt +0 -0
kailash/nodes/code/python.py
CHANGED
@@ -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
|
-
|
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
|
460
|
-
kwargs
|
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,
|
944
|
-
|
945
|
-
|
946
|
-
|
947
|
-
|
948
|
-
|
949
|
-
|
950
|
-
|
951
|
-
|
952
|
-
|
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,
|
959
|
-
|
960
|
-
|
961
|
-
|
962
|
-
|
963
|
-
|
964
|
-
|
965
|
-
|
966
|
-
|
967
|
-
|
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 [
|
1433
|
-
|
1434
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
|
kailash/nodes/compliance/gdpr.py
CHANGED
@@ -152,7 +152,7 @@ class GDPRComplianceNode(SecurityMixin, PerformanceMixin, LoggingMixin, Node):
|
|
152
152
|
... "address": "123 Main St"
|
153
153
|
... }
|
154
154
|
>>>
|
155
|
-
>>> result = gdpr_node.
|
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.
|
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.
|
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.
|
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.
|
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}"
|
kailash/nodes/data/__init__.py
CHANGED
@@ -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
|
]
|