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
@@ -0,0 +1,425 @@
1
+ """Parameter injection framework for enterprise nodes.
2
+
3
+ This module provides a framework for handling runtime parameter injection
4
+ in enterprise nodes that traditionally require initialization-time configuration.
5
+ It bridges the gap between static configuration and dynamic workflow parameters.
6
+ """
7
+
8
+ import logging
9
+ from abc import ABC, abstractmethod
10
+ from typing import Any, Dict, Optional, Union
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class ParameterInjectionMixin:
16
+ """Mixin to add parameter injection capabilities to nodes.
17
+
18
+ This mixin allows nodes to defer configuration until runtime,
19
+ enabling workflow-level parameter injection for connection parameters.
20
+ """
21
+
22
+ def __init__(self, *args, **kwargs):
23
+ """Initialize with deferred configuration support."""
24
+ # Store original kwargs for deferred initialization
25
+ self._deferred_config = kwargs.copy()
26
+ self._is_initialized = False
27
+ self._runtime_config = {}
28
+ super().__init__(*args, **kwargs)
29
+
30
+ def set_runtime_parameters(self, **runtime_params: Any) -> None:
31
+ """Set runtime parameters for deferred initialization.
32
+
33
+ Args:
34
+ **runtime_params: Runtime parameters to use for configuration
35
+ """
36
+ self._runtime_config.update(runtime_params)
37
+ logger.debug(
38
+ f"Set runtime parameters for {self.__class__.__name__}: {list(runtime_params.keys())}"
39
+ )
40
+
41
+ def get_effective_config(self) -> Dict[str, Any]:
42
+ """Get effective configuration combining init-time and runtime parameters.
43
+
44
+ Returns:
45
+ Combined configuration with runtime parameters taking precedence
46
+ """
47
+ effective_config = self._deferred_config.copy()
48
+ effective_config.update(self._runtime_config)
49
+ return effective_config
50
+
51
+ def initialize_with_runtime_config(self) -> None:
52
+ """Initialize the node with runtime configuration.
53
+
54
+ Should be called by subclasses when they need to set up connections
55
+ or other resources that depend on runtime parameters.
56
+ """
57
+ if not self._is_initialized:
58
+ self._perform_initialization(self.get_effective_config())
59
+ self._is_initialized = True
60
+
61
+ @abstractmethod
62
+ def _perform_initialization(self, config: Dict[str, Any]) -> None:
63
+ """Perform actual initialization with effective configuration.
64
+
65
+ Args:
66
+ config: Combined configuration to use for initialization
67
+ """
68
+ pass
69
+
70
+ def validate_inputs(self, **kwargs) -> Dict[str, Any]:
71
+ """Override to inject runtime parameters before validation."""
72
+ # Extract connection parameters from runtime inputs
73
+ connection_params = self._extract_connection_params(kwargs)
74
+ if connection_params:
75
+ self.set_runtime_parameters(**connection_params)
76
+
77
+ # Initialize if we have new runtime parameters
78
+ if connection_params and not self._is_initialized:
79
+ self.initialize_with_runtime_config()
80
+
81
+ return super().validate_inputs(**kwargs)
82
+
83
+ def _extract_connection_params(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
84
+ """Extract connection-related parameters from runtime inputs.
85
+
86
+ Args:
87
+ inputs: Runtime input parameters
88
+
89
+ Returns:
90
+ Dictionary of connection parameters
91
+ """
92
+ # Define connection parameter patterns
93
+ connection_param_patterns = [
94
+ "host",
95
+ "port",
96
+ "database",
97
+ "user",
98
+ "password",
99
+ "connection_string",
100
+ "token_url",
101
+ "client_id",
102
+ "client_secret",
103
+ "auth_url",
104
+ "api_key",
105
+ "database_type",
106
+ "grant_type",
107
+ "scope",
108
+ "username",
109
+ ]
110
+
111
+ connection_params = {}
112
+ for key, value in inputs.items():
113
+ if key in connection_param_patterns:
114
+ connection_params[key] = value
115
+
116
+ return connection_params
117
+
118
+
119
+ class ConfigurableOAuth2Node:
120
+ """OAuth2Node with runtime parameter injection support.
121
+
122
+ This class extends OAuth2Node to support runtime configuration of
123
+ connection parameters through workflow parameter injection.
124
+ """
125
+
126
+ def __init__(self, **kwargs):
127
+ """Initialize with deferred OAuth configuration."""
128
+ from kailash.nodes.base import Node, NodeMetadata
129
+
130
+ # Store configuration
131
+ self._deferred_config = kwargs.copy()
132
+ self._runtime_config = {}
133
+ self._is_initialized = False
134
+ self._oauth_node = None
135
+
136
+ # Initialize metadata
137
+ if "metadata" not in kwargs:
138
+ kwargs["metadata"] = NodeMetadata(
139
+ id=kwargs.get("name", "configurable_oauth2").replace(" ", "_").lower(),
140
+ name=kwargs.get("name", "ConfigurableOAuth2Node"),
141
+ description="OAuth2 node with runtime parameter injection",
142
+ tags={"auth", "oauth2", "configurable"},
143
+ version="1.0.0",
144
+ )
145
+
146
+ # Store metadata for later use
147
+ self.metadata = kwargs["metadata"]
148
+
149
+ def _perform_initialization(self, config: Dict[str, Any]) -> None:
150
+ """Initialize OAuth2Node with runtime configuration."""
151
+ from kailash.nodes.api.auth import OAuth2Node
152
+
153
+ # Filter out non-OAuth parameters
154
+ oauth_config = {}
155
+ oauth_params = [
156
+ "token_url",
157
+ "client_id",
158
+ "client_secret",
159
+ "grant_type",
160
+ "scope",
161
+ "username",
162
+ "password",
163
+ "refresh_token",
164
+ ]
165
+
166
+ for param in oauth_params:
167
+ if param in config:
168
+ oauth_config[param] = config[param]
169
+
170
+ # Create the actual OAuth2Node
171
+ self._oauth_node = OAuth2Node(**oauth_config)
172
+ logger.info(
173
+ f"Initialized OAuth2Node with runtime config: {list(oauth_config.keys())}"
174
+ )
175
+
176
+ def get_parameters(self):
177
+ """Get parameters from the underlying OAuth2Node or default set."""
178
+ if self._oauth_node:
179
+ return self._oauth_node.get_parameters()
180
+ else:
181
+ # Return default OAuth2 parameters for workflow building
182
+ from kailash.nodes.base import NodeParameter
183
+
184
+ return {
185
+ "token_url": NodeParameter(
186
+ name="token_url",
187
+ type=str,
188
+ required=True,
189
+ description="OAuth token endpoint URL",
190
+ ),
191
+ "client_id": NodeParameter(
192
+ name="client_id",
193
+ type=str,
194
+ required=True,
195
+ description="OAuth client ID",
196
+ ),
197
+ "client_secret": NodeParameter(
198
+ name="client_secret",
199
+ type=str,
200
+ required=False,
201
+ description="OAuth client secret",
202
+ ),
203
+ "grant_type": NodeParameter(
204
+ name="grant_type",
205
+ type=str,
206
+ required=False,
207
+ default="client_credentials",
208
+ description="OAuth grant type",
209
+ ),
210
+ }
211
+
212
+ def run(self, **kwargs):
213
+ """Execute OAuth2 authentication with runtime configuration."""
214
+ # Ensure initialization with current parameters
215
+ if not self._is_initialized:
216
+ self.set_runtime_parameters(**kwargs)
217
+ self.initialize_with_runtime_config()
218
+
219
+ if not self._oauth_node:
220
+ raise RuntimeError(
221
+ "OAuth2Node not initialized - missing connection parameters"
222
+ )
223
+
224
+ # Delegate to the initialized OAuth2Node
225
+ return self._oauth_node.execute(**kwargs)
226
+
227
+
228
+ class ConfigurableAsyncSQLNode(ParameterInjectionMixin):
229
+ """AsyncSQLDatabaseNode with runtime parameter injection support.
230
+
231
+ This class extends AsyncSQLDatabaseNode to support runtime configuration
232
+ of database connection parameters through workflow parameter injection.
233
+ """
234
+
235
+ def __init__(self, **kwargs):
236
+ """Initialize with deferred database configuration."""
237
+ from kailash.nodes.data.async_sql import AsyncSQLDatabaseNode
238
+
239
+ # Don't initialize AsyncSQLDatabaseNode yet - defer until runtime
240
+ self._sql_node = None
241
+ super().__init__(**kwargs)
242
+
243
+ def _perform_initialization(self, config: Dict[str, Any]) -> None:
244
+ """Initialize AsyncSQLDatabaseNode with runtime configuration."""
245
+ from kailash.nodes.data.async_sql import AsyncSQLDatabaseNode
246
+
247
+ # Filter out non-SQL parameters
248
+ sql_config = {}
249
+ sql_params = [
250
+ "database_type",
251
+ "connection_string",
252
+ "host",
253
+ "port",
254
+ "database",
255
+ "user",
256
+ "password",
257
+ "pool_size",
258
+ "max_pool_size",
259
+ "timeout",
260
+ ]
261
+
262
+ for param in sql_params:
263
+ if param in config:
264
+ sql_config[param] = config[param]
265
+
266
+ # Add any query parameters
267
+ if "query" in config:
268
+ sql_config["query"] = config["query"]
269
+ if "params" in config:
270
+ sql_config["params"] = config["params"]
271
+ if "fetch_mode" in config:
272
+ sql_config["fetch_mode"] = config["fetch_mode"]
273
+
274
+ # Create the actual AsyncSQLDatabaseNode
275
+ self._sql_node = AsyncSQLDatabaseNode(**sql_config)
276
+ logger.info(
277
+ f"Initialized AsyncSQLDatabaseNode with runtime config: {list(sql_config.keys())}"
278
+ )
279
+
280
+ def get_parameters(self):
281
+ """Get parameters from the underlying AsyncSQLDatabaseNode or default set."""
282
+ if self._sql_node:
283
+ return self._sql_node.get_parameters()
284
+ else:
285
+ # Return default SQL parameters for workflow building
286
+ from kailash.nodes.base import NodeParameter
287
+
288
+ return {
289
+ "database_type": NodeParameter(
290
+ name="database_type",
291
+ type=str,
292
+ required=True,
293
+ default="postgresql",
294
+ description="Type of database",
295
+ ),
296
+ "host": NodeParameter(
297
+ name="host", type=str, required=False, description="Database host"
298
+ ),
299
+ "database": NodeParameter(
300
+ name="database",
301
+ type=str,
302
+ required=False,
303
+ description="Database name",
304
+ ),
305
+ "user": NodeParameter(
306
+ name="user", type=str, required=False, description="Database user"
307
+ ),
308
+ "password": NodeParameter(
309
+ name="password",
310
+ type=str,
311
+ required=False,
312
+ description="Database password",
313
+ ),
314
+ "query": NodeParameter(
315
+ name="query",
316
+ type=str,
317
+ required=True,
318
+ description="SQL query to execute",
319
+ ),
320
+ }
321
+
322
+ async def async_run(self, **kwargs):
323
+ """Execute SQL query with runtime configuration."""
324
+ # Ensure initialization with current parameters
325
+ if not self._is_initialized:
326
+ self.set_runtime_parameters(**kwargs)
327
+ self.initialize_with_runtime_config()
328
+
329
+ if not self._sql_node:
330
+ raise RuntimeError(
331
+ "AsyncSQLDatabaseNode not initialized - missing connection parameters"
332
+ )
333
+
334
+ # Delegate to the initialized AsyncSQLDatabaseNode
335
+ return await self._sql_node.async_run(**kwargs)
336
+
337
+ def run(self, **kwargs):
338
+ """Synchronous wrapper for async_run."""
339
+ import asyncio
340
+
341
+ return asyncio.run(self.async_run(**kwargs))
342
+
343
+
344
+ class EnterpriseNodeFactory:
345
+ """Factory for creating enterprise nodes with parameter injection support."""
346
+
347
+ @staticmethod
348
+ def create_oauth2_node(**kwargs) -> ConfigurableOAuth2Node:
349
+ """Create an OAuth2Node with runtime parameter injection support.
350
+
351
+ Args:
352
+ **kwargs: Initial configuration parameters
353
+
354
+ Returns:
355
+ ConfigurableOAuth2Node instance
356
+ """
357
+ return ConfigurableOAuth2Node(**kwargs)
358
+
359
+ @staticmethod
360
+ def create_async_sql_node(**kwargs) -> ConfigurableAsyncSQLNode:
361
+ """Create an AsyncSQLDatabaseNode with runtime parameter injection support.
362
+
363
+ Args:
364
+ **kwargs: Initial configuration parameters
365
+
366
+ Returns:
367
+ ConfigurableAsyncSQLNode instance
368
+ """
369
+ return ConfigurableAsyncSQLNode(**kwargs)
370
+
371
+ @staticmethod
372
+ def wrap_enterprise_node(node_class, **kwargs):
373
+ """Wrap any enterprise node class with parameter injection support.
374
+
375
+ Args:
376
+ node_class: The enterprise node class to wrap
377
+ **kwargs: Initial configuration parameters
378
+
379
+ Returns:
380
+ Wrapped node instance with parameter injection support
381
+ """
382
+
383
+ # Create a dynamic wrapper class
384
+ class WrappedEnterpriseNode(ParameterInjectionMixin):
385
+ def __init__(self, **init_kwargs):
386
+ self._wrapped_node = None
387
+ self._node_class = node_class
388
+ super().__init__(**init_kwargs)
389
+
390
+ def _perform_initialization(self, config):
391
+ self._wrapped_node = self._node_class(**config)
392
+
393
+ def get_parameters(self):
394
+ if self._wrapped_node:
395
+ return self._wrapped_node.get_parameters()
396
+ else:
397
+ # Try to get parameters from class if possible
398
+ try:
399
+ temp_node = self._node_class()
400
+ return temp_node.get_parameters()
401
+ except:
402
+ return {}
403
+
404
+ def run(self, **run_kwargs):
405
+ if not self._is_initialized:
406
+ self.set_runtime_parameters(**run_kwargs)
407
+ self.initialize_with_runtime_config()
408
+
409
+ if not self._wrapped_node:
410
+ raise RuntimeError(f"{self._node_class.__name__} not initialized")
411
+
412
+ return self._wrapped_node.execute(**run_kwargs)
413
+
414
+ return WrappedEnterpriseNode(**kwargs)
415
+
416
+
417
+ # Convenience functions for workflow builders
418
+ def create_configurable_oauth2(**kwargs) -> ConfigurableOAuth2Node:
419
+ """Convenience function to create a configurable OAuth2 node."""
420
+ return EnterpriseNodeFactory.create_oauth2_node(**kwargs)
421
+
422
+
423
+ def create_configurable_sql(**kwargs) -> ConfigurableAsyncSQLNode:
424
+ """Convenience function to create a configurable SQL node."""
425
+ return EnterpriseNodeFactory.create_async_sql_node(**kwargs)