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
@@ -23,6 +23,7 @@ Example:
23
23
 
24
24
  import asyncio
25
25
  import logging
26
+ import random
26
27
  import time
27
28
  from collections import deque
28
29
  from dataclasses import dataclass, field
@@ -61,6 +62,15 @@ class CircuitBreakerConfig:
61
62
  window_size: int = 100 # Rolling window for error rate
62
63
  excluded_exceptions: List[type] = field(default_factory=list) # Don't count these
63
64
 
65
+ # Enhanced configurable thresholds
66
+ min_calls_before_evaluation: int = 10 # Min calls before evaluating error rate
67
+ slow_call_threshold: float = 5.0 # Seconds to consider a call slow
68
+ slow_call_rate_threshold: float = 0.8 # Rate of slow calls to trigger open
69
+ max_wait_duration_in_half_open: int = 60 # Max wait in half-open state
70
+ exponential_backoff_multiplier: float = 2.0 # Backoff multiplier for recovery
71
+ jitter_enabled: bool = True # Add jitter to recovery timeout
72
+ max_jitter_percentage: float = 0.1 # Maximum jitter as percentage of timeout
73
+
64
74
 
65
75
  @dataclass
66
76
  class CircuitBreakerMetrics:
@@ -70,36 +80,59 @@ class CircuitBreakerMetrics:
70
80
  successful_calls: int = 0
71
81
  failed_calls: int = 0
72
82
  rejected_calls: int = 0
83
+ slow_calls: int = 0 # New: Track slow calls
73
84
  state_transitions: List[Dict[str, Any]] = field(default_factory=list)
74
85
  last_failure_time: Optional[float] = None
75
86
  consecutive_failures: int = 0
76
87
  consecutive_successes: int = 0
88
+ avg_call_duration: float = 0.0 # New: Average call duration
89
+ total_call_duration: float = 0.0 # New: Total duration for average calculation
77
90
 
78
- def record_success(self):
91
+ def record_success(self, duration: float = 0.0):
79
92
  """Record successful call."""
80
93
  self.total_calls += 1
81
94
  self.successful_calls += 1
82
95
  self.consecutive_successes += 1
83
96
  self.consecutive_failures = 0
97
+ self._update_duration(duration)
84
98
 
85
- def record_failure(self):
99
+ def record_failure(self, duration: float = 0.0):
86
100
  """Record failed call."""
87
101
  self.total_calls += 1
88
102
  self.failed_calls += 1
89
103
  self.consecutive_failures += 1
90
104
  self.consecutive_successes = 0
91
105
  self.last_failure_time = time.time()
106
+ self._update_duration(duration)
92
107
 
93
108
  def record_rejection(self):
94
109
  """Record rejected call (circuit open)."""
95
110
  self.rejected_calls += 1
96
111
 
112
+ def record_slow_call(self):
113
+ """Record slow call."""
114
+ self.slow_calls += 1
115
+
116
+ def _update_duration(self, duration: float):
117
+ """Update duration metrics."""
118
+ if duration > 0:
119
+ self.total_call_duration += duration
120
+ # Update rolling average
121
+ if self.total_calls > 0:
122
+ self.avg_call_duration = self.total_call_duration / self.total_calls
123
+
97
124
  def get_error_rate(self) -> float:
98
125
  """Calculate current error rate."""
99
126
  if self.total_calls == 0:
100
127
  return 0.0
101
128
  return self.failed_calls / self.total_calls
102
129
 
130
+ def get_slow_call_rate(self) -> float:
131
+ """Calculate current slow call rate."""
132
+ if self.total_calls == 0:
133
+ return 0.0
134
+ return self.slow_calls / self.total_calls
135
+
103
136
 
104
137
  class ConnectionCircuitBreaker(Generic[T]):
105
138
  """Circuit breaker for database connections and operations.
@@ -159,14 +192,21 @@ class ConnectionCircuitBreaker(Generic[T]):
159
192
  start_time = time.time()
160
193
  try:
161
194
  result = await func(*args, **kwargs)
162
- await self._record_success()
195
+ execution_time = time.time() - start_time
196
+
197
+ # Check if this was a slow call
198
+ is_slow = execution_time > self.config.slow_call_threshold
199
+
200
+ await self._record_success(execution_time, is_slow)
163
201
  return result
164
202
  except Exception as e:
203
+ execution_time = time.time() - start_time
204
+
165
205
  # Check if this exception should be counted
166
206
  if not any(
167
207
  isinstance(e, exc_type) for exc_type in self.config.excluded_exceptions
168
208
  ):
169
- await self._record_failure(e)
209
+ await self._record_failure(e, execution_time)
170
210
  raise
171
211
 
172
212
  async def _check_state_transition(self):
@@ -191,6 +231,10 @@ class ConnectionCircuitBreaker(Generic[T]):
191
231
 
192
232
  def _should_open(self) -> bool:
193
233
  """Determine if circuit should open based on failures."""
234
+ # Only evaluate if we have minimum number of calls
235
+ if self.metrics.total_calls < self.config.min_calls_before_evaluation:
236
+ return False
237
+
194
238
  # Check consecutive failures
195
239
  if self.metrics.consecutive_failures >= self.config.failure_threshold:
196
240
  return True
@@ -202,22 +246,32 @@ class ConnectionCircuitBreaker(Generic[T]):
202
246
  if error_rate >= self.config.error_rate_threshold:
203
247
  return True
204
248
 
249
+ # Check slow call rate
250
+ slow_call_rate = self.metrics.get_slow_call_rate()
251
+ if slow_call_rate >= self.config.slow_call_rate_threshold:
252
+ return True
253
+
205
254
  return False
206
255
 
207
- async def _record_success(self):
256
+ async def _record_success(self, duration: float = 0.0, is_slow: bool = False):
208
257
  """Record successful execution."""
209
258
  async with self._lock:
210
- self.metrics.record_success()
259
+ self.metrics.record_success(duration)
260
+ if is_slow:
261
+ self.metrics.record_slow_call()
211
262
  self._rolling_window.append(True)
212
263
 
213
264
  if self.state == CircuitState.HALF_OPEN:
214
265
  if self.metrics.consecutive_successes >= self.config.success_threshold:
215
266
  await self._transition_to(CircuitState.CLOSED)
216
267
 
217
- async def _record_failure(self, error: Exception):
268
+ async def _record_failure(self, error: Exception, duration: float = 0.0):
218
269
  """Record failed execution."""
219
270
  async with self._lock:
220
- self.metrics.record_failure()
271
+ self.metrics.record_failure(duration)
272
+ # Consider slow failures as additional burden
273
+ if duration > self.config.slow_call_threshold:
274
+ self.metrics.record_slow_call()
221
275
  self._rolling_window.append(False)
222
276
 
223
277
  if self.state == CircuitState.HALF_OPEN:
@@ -280,11 +334,31 @@ class ConnectionCircuitBreaker(Generic[T]):
280
334
  return "Unknown reason"
281
335
 
282
336
  def _time_until_recovery(self) -> float:
283
- """Calculate seconds until recovery attempt."""
337
+ """Calculate seconds until recovery attempt with jitter and backoff."""
284
338
  if self.state != CircuitState.OPEN:
285
339
  return 0.0
340
+
286
341
  elapsed = time.time() - self._last_state_change
287
- remaining = self.config.recovery_timeout - elapsed
342
+
343
+ # Apply exponential backoff based on number of state transitions to OPEN
344
+ open_transitions = sum(
345
+ 1
346
+ for t in self.metrics.state_transitions
347
+ if t.get("to") == CircuitState.OPEN.value
348
+ )
349
+ backoff_multiplier = self.config.exponential_backoff_multiplier ** max(
350
+ 0, open_transitions - 1
351
+ )
352
+
353
+ base_timeout = self.config.recovery_timeout * backoff_multiplier
354
+
355
+ # Add jitter if enabled
356
+ if self.config.jitter_enabled:
357
+ jitter_range = base_timeout * self.config.max_jitter_percentage
358
+ jitter = random.uniform(-jitter_range, jitter_range)
359
+ base_timeout += jitter
360
+
361
+ remaining = base_timeout - elapsed
288
362
  return max(0.0, remaining)
289
363
 
290
364
  async def force_open(self, reason: str = "Manual override"):
@@ -331,7 +405,10 @@ class ConnectionCircuitBreaker(Generic[T]):
331
405
  "successful_calls": self.metrics.successful_calls,
332
406
  "failed_calls": self.metrics.failed_calls,
333
407
  "rejected_calls": self.metrics.rejected_calls,
408
+ "slow_calls": self.metrics.slow_calls,
334
409
  "error_rate": self.metrics.get_error_rate(),
410
+ "slow_call_rate": self.metrics.get_slow_call_rate(),
411
+ "avg_call_duration": self.metrics.avg_call_duration,
335
412
  "consecutive_failures": self.metrics.consecutive_failures,
336
413
  "consecutive_successes": self.metrics.consecutive_successes,
337
414
  },
@@ -340,6 +417,11 @@ class ConnectionCircuitBreaker(Generic[T]):
340
417
  "success_threshold": self.config.success_threshold,
341
418
  "recovery_timeout": self.config.recovery_timeout,
342
419
  "error_rate_threshold": self.config.error_rate_threshold,
420
+ "slow_call_threshold": self.config.slow_call_threshold,
421
+ "slow_call_rate_threshold": self.config.slow_call_rate_threshold,
422
+ "min_calls_before_evaluation": self.config.min_calls_before_evaluation,
423
+ "exponential_backoff_multiplier": self.config.exponential_backoff_multiplier,
424
+ "jitter_enabled": self.config.jitter_enabled,
343
425
  },
344
426
  "time_until_recovery": (
345
427
  self._time_until_recovery() if self.state == CircuitState.OPEN else None