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.
- kailash/__init__.py +3 -3
- kailash/api/custom_nodes_secure.py +3 -3
- kailash/api/gateway.py +1 -1
- kailash/api/studio.py +1 -1
- kailash/api/workflow_api.py +2 -2
- kailash/core/resilience/bulkhead.py +475 -0
- kailash/core/resilience/circuit_breaker.py +92 -10
- kailash/core/resilience/health_monitor.py +578 -0
- kailash/edge/discovery.py +86 -0
- kailash/mcp_server/__init__.py +309 -33
- kailash/mcp_server/advanced_features.py +1022 -0
- kailash/mcp_server/ai_registry_server.py +27 -2
- kailash/mcp_server/auth.py +789 -0
- kailash/mcp_server/client.py +645 -378
- 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 +1228 -96
- kailash/mcp_server/transports.py +1169 -0
- kailash/mcp_server/utils/__init__.py +6 -1
- kailash/mcp_server/utils/cache.py +250 -7
- kailash/middleware/auth/auth_manager.py +3 -3
- kailash/middleware/communication/api_gateway.py +1 -1
- kailash/middleware/communication/realtime.py +1 -1
- kailash/middleware/mcp/enhanced_server.py +1 -1
- 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 +15 -15
- kailash/nodes/admin/tenant_isolation.py +3 -3
- kailash/nodes/admin/transaction_utils.py +3 -3
- kailash/nodes/admin/user_management.py +21 -21
- 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 +2 -2
- kailash/nodes/ai/llm_agent.py +210 -33
- 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 +10 -10
- kailash/nodes/api/rate_limiting.py +4 -4
- kailash/nodes/api/rest.py +15 -15
- kailash/nodes/auth/mfa.py +3 -3
- 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 +8 -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 +874 -0
- kailash/nodes/cache/redis_pool_manager.py +595 -0
- kailash/nodes/code/async_python.py +2 -1
- kailash/nodes/code/python.py +194 -30
- 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/async_sql.py +1956 -129
- kailash/nodes/data/optimistic_locking.py +906 -0
- kailash/nodes/data/readers.py +8 -8
- kailash/nodes/data/redis.py +378 -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 +1 -1
- 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 -5
- 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 +163 -9
- 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 -14
- kailash/workflow/builder_improvements.py +207 -0
- kailash/workflow/input_handling.py +170 -0
- {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/METADATA +22 -9
- {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/RECORD +122 -95
- {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/WHEEL +0 -0
- {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/entry_points.txt +0 -0
- {kailash-0.6.3.dist-info → kailash-0.6.5.dist-info}/licenses/LICENSE +0 -0
- {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
|
-
|
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
|
-
|
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
|