kailash 0.8.3__py3-none-any.whl → 0.8.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 +1 -7
- kailash/cli/__init__.py +11 -1
- kailash/cli/validation_audit.py +570 -0
- kailash/core/actors/supervisor.py +1 -1
- kailash/core/resilience/circuit_breaker.py +71 -1
- kailash/core/resilience/health_monitor.py +172 -0
- kailash/edge/compliance.py +33 -0
- kailash/edge/consistency.py +609 -0
- kailash/edge/coordination/__init__.py +30 -0
- kailash/edge/coordination/global_ordering.py +355 -0
- kailash/edge/coordination/leader_election.py +217 -0
- kailash/edge/coordination/partition_detector.py +296 -0
- kailash/edge/coordination/raft.py +485 -0
- kailash/edge/discovery.py +63 -1
- kailash/edge/migration/__init__.py +19 -0
- kailash/edge/migration/edge_migrator.py +832 -0
- kailash/edge/monitoring/__init__.py +21 -0
- kailash/edge/monitoring/edge_monitor.py +736 -0
- kailash/edge/prediction/__init__.py +10 -0
- kailash/edge/prediction/predictive_warmer.py +591 -0
- kailash/edge/resource/__init__.py +102 -0
- kailash/edge/resource/cloud_integration.py +796 -0
- kailash/edge/resource/cost_optimizer.py +949 -0
- kailash/edge/resource/docker_integration.py +919 -0
- kailash/edge/resource/kubernetes_integration.py +893 -0
- kailash/edge/resource/platform_integration.py +913 -0
- kailash/edge/resource/predictive_scaler.py +959 -0
- kailash/edge/resource/resource_analyzer.py +824 -0
- kailash/edge/resource/resource_pools.py +610 -0
- kailash/integrations/dataflow_edge.py +261 -0
- kailash/mcp_server/registry_integration.py +1 -1
- kailash/monitoring/__init__.py +18 -0
- kailash/monitoring/alerts.py +646 -0
- kailash/monitoring/metrics.py +677 -0
- kailash/nodes/__init__.py +2 -0
- kailash/nodes/ai/__init__.py +17 -0
- kailash/nodes/ai/a2a.py +1914 -43
- kailash/nodes/ai/a2a_backup.py +1807 -0
- kailash/nodes/ai/hybrid_search.py +972 -0
- kailash/nodes/ai/semantic_memory.py +558 -0
- kailash/nodes/ai/streaming_analytics.py +947 -0
- kailash/nodes/base.py +545 -0
- kailash/nodes/edge/__init__.py +36 -0
- kailash/nodes/edge/base.py +240 -0
- kailash/nodes/edge/cloud_node.py +710 -0
- kailash/nodes/edge/coordination.py +239 -0
- kailash/nodes/edge/docker_node.py +825 -0
- kailash/nodes/edge/edge_data.py +582 -0
- kailash/nodes/edge/edge_migration_node.py +392 -0
- kailash/nodes/edge/edge_monitoring_node.py +421 -0
- kailash/nodes/edge/edge_state.py +673 -0
- kailash/nodes/edge/edge_warming_node.py +393 -0
- kailash/nodes/edge/kubernetes_node.py +652 -0
- kailash/nodes/edge/platform_node.py +766 -0
- kailash/nodes/edge/resource_analyzer_node.py +378 -0
- kailash/nodes/edge/resource_optimizer_node.py +501 -0
- kailash/nodes/edge/resource_scaler_node.py +397 -0
- kailash/nodes/ports.py +676 -0
- kailash/runtime/local.py +344 -1
- kailash/runtime/validation/__init__.py +20 -0
- kailash/runtime/validation/connection_context.py +119 -0
- kailash/runtime/validation/enhanced_error_formatter.py +202 -0
- kailash/runtime/validation/error_categorizer.py +164 -0
- kailash/runtime/validation/metrics.py +380 -0
- kailash/runtime/validation/performance.py +615 -0
- kailash/runtime/validation/suggestion_engine.py +212 -0
- kailash/testing/fixtures.py +2 -2
- kailash/workflow/builder.py +234 -8
- kailash/workflow/contracts.py +418 -0
- kailash/workflow/edge_infrastructure.py +369 -0
- kailash/workflow/migration.py +3 -3
- kailash/workflow/type_inference.py +669 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/METADATA +44 -27
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/RECORD +78 -28
- kailash/nexus/__init__.py +0 -21
- kailash/nexus/cli/__init__.py +0 -5
- kailash/nexus/cli/__main__.py +0 -6
- kailash/nexus/cli/main.py +0 -176
- kailash/nexus/factory.py +0 -413
- kailash/nexus/gateway.py +0 -545
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/WHEEL +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/entry_points.txt +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.8.3.dist-info → kailash-0.8.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,380 @@
|
|
1
|
+
"""
|
2
|
+
Performance monitoring and metrics collection for connection validation.
|
3
|
+
|
4
|
+
Tracks validation performance, security violations, and provides insights
|
5
|
+
into validation effectiveness and potential performance bottlenecks.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import logging
|
9
|
+
import time
|
10
|
+
from collections import defaultdict
|
11
|
+
from dataclasses import dataclass, field
|
12
|
+
from datetime import UTC, datetime
|
13
|
+
from enum import Enum
|
14
|
+
from typing import Any, Dict, List, Optional
|
15
|
+
|
16
|
+
from kailash.runtime.validation.error_categorizer import ErrorCategory
|
17
|
+
|
18
|
+
|
19
|
+
class ValidationEventType(Enum):
|
20
|
+
"""Types of validation events to track."""
|
21
|
+
|
22
|
+
VALIDATION_STARTED = "validation_started"
|
23
|
+
VALIDATION_COMPLETED = "validation_completed"
|
24
|
+
VALIDATION_FAILED = "validation_failed"
|
25
|
+
SECURITY_VIOLATION = "security_violation"
|
26
|
+
CACHE_HIT = "cache_hit"
|
27
|
+
CACHE_MISS = "cache_miss"
|
28
|
+
TYPE_COERCION = "type_coercion"
|
29
|
+
MODE_BYPASS = "mode_bypass"
|
30
|
+
|
31
|
+
|
32
|
+
@dataclass
|
33
|
+
class ValidationMetric:
|
34
|
+
"""Single validation metric entry."""
|
35
|
+
|
36
|
+
timestamp: datetime
|
37
|
+
event_type: ValidationEventType
|
38
|
+
node_id: str
|
39
|
+
node_type: str
|
40
|
+
duration_ms: Optional[float] = None
|
41
|
+
error_category: Optional[ErrorCategory] = None
|
42
|
+
validation_mode: Optional[str] = None
|
43
|
+
connection_source: Optional[str] = None
|
44
|
+
connection_target: Optional[str] = None
|
45
|
+
additional_data: Dict[str, Any] = field(default_factory=dict)
|
46
|
+
|
47
|
+
|
48
|
+
class ValidationMetricsCollector:
|
49
|
+
"""Collects and aggregates validation performance metrics."""
|
50
|
+
|
51
|
+
def __init__(self, enable_detailed_logging: bool = False):
|
52
|
+
"""Initialize metrics collector.
|
53
|
+
|
54
|
+
Args:
|
55
|
+
enable_detailed_logging: Whether to log detailed metrics to logger
|
56
|
+
"""
|
57
|
+
self.metrics: List[ValidationMetric] = []
|
58
|
+
self.node_validation_times: Dict[str, List[float]] = defaultdict(list)
|
59
|
+
self.error_counts: Dict[ErrorCategory, int] = defaultdict(int)
|
60
|
+
self.security_violations: List[ValidationMetric] = []
|
61
|
+
self.enable_detailed_logging = enable_detailed_logging
|
62
|
+
self.logger = logging.getLogger("kailash.validation.metrics")
|
63
|
+
|
64
|
+
# Performance tracking
|
65
|
+
self._active_validations: Dict[str, float] = {}
|
66
|
+
self._cache_stats = {"hits": 0, "misses": 0}
|
67
|
+
|
68
|
+
def start_validation(
|
69
|
+
self, node_id: str, node_type: str, validation_mode: str
|
70
|
+
) -> None:
|
71
|
+
"""Record the start of a validation operation.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
node_id: ID of the node being validated
|
75
|
+
node_type: Type of the node
|
76
|
+
validation_mode: Validation mode (off, warn, strict)
|
77
|
+
"""
|
78
|
+
self._active_validations[node_id] = time.time()
|
79
|
+
|
80
|
+
metric = ValidationMetric(
|
81
|
+
timestamp=datetime.now(UTC),
|
82
|
+
event_type=ValidationEventType.VALIDATION_STARTED,
|
83
|
+
node_id=node_id,
|
84
|
+
node_type=node_type,
|
85
|
+
validation_mode=validation_mode,
|
86
|
+
)
|
87
|
+
self.metrics.append(metric)
|
88
|
+
|
89
|
+
if self.enable_detailed_logging:
|
90
|
+
self.logger.debug(
|
91
|
+
f"Validation started for {node_id} ({node_type}) in {validation_mode} mode"
|
92
|
+
)
|
93
|
+
|
94
|
+
def end_validation(
|
95
|
+
self,
|
96
|
+
node_id: str,
|
97
|
+
node_type: str,
|
98
|
+
success: bool,
|
99
|
+
error_category: Optional[ErrorCategory] = None,
|
100
|
+
connection_info: Optional[Dict[str, str]] = None,
|
101
|
+
) -> None:
|
102
|
+
"""Record the end of a validation operation.
|
103
|
+
|
104
|
+
Args:
|
105
|
+
node_id: ID of the node that was validated
|
106
|
+
node_type: Type of the node
|
107
|
+
success: Whether validation succeeded
|
108
|
+
error_category: Category of error if validation failed
|
109
|
+
connection_info: Optional connection source/target information
|
110
|
+
"""
|
111
|
+
# Calculate duration
|
112
|
+
duration_ms = None
|
113
|
+
if node_id in self._active_validations:
|
114
|
+
start_time = self._active_validations.pop(node_id)
|
115
|
+
duration_ms = (time.time() - start_time) * 1000 # Convert to milliseconds
|
116
|
+
self.node_validation_times[node_type].append(duration_ms)
|
117
|
+
|
118
|
+
# Record appropriate event
|
119
|
+
event_type = (
|
120
|
+
ValidationEventType.VALIDATION_COMPLETED
|
121
|
+
if success
|
122
|
+
else ValidationEventType.VALIDATION_FAILED
|
123
|
+
)
|
124
|
+
|
125
|
+
metric = ValidationMetric(
|
126
|
+
timestamp=datetime.now(UTC),
|
127
|
+
event_type=event_type,
|
128
|
+
node_id=node_id,
|
129
|
+
node_type=node_type,
|
130
|
+
duration_ms=duration_ms,
|
131
|
+
error_category=error_category,
|
132
|
+
connection_source=(
|
133
|
+
connection_info.get("source") if connection_info else None
|
134
|
+
),
|
135
|
+
connection_target=(
|
136
|
+
connection_info.get("target") if connection_info else None
|
137
|
+
),
|
138
|
+
)
|
139
|
+
self.metrics.append(metric)
|
140
|
+
|
141
|
+
# Track error counts
|
142
|
+
if error_category:
|
143
|
+
self.error_counts[error_category] += 1
|
144
|
+
|
145
|
+
if self.enable_detailed_logging:
|
146
|
+
status = "succeeded" if success else "failed"
|
147
|
+
duration_str = f" in {duration_ms:.2f}ms" if duration_ms else ""
|
148
|
+
self.logger.debug(
|
149
|
+
f"Validation {status} for {node_id} ({node_type}){duration_str}"
|
150
|
+
)
|
151
|
+
|
152
|
+
def record_security_violation(
|
153
|
+
self,
|
154
|
+
node_id: str,
|
155
|
+
node_type: str,
|
156
|
+
violation_details: Dict[str, Any],
|
157
|
+
connection_info: Optional[Dict[str, str]] = None,
|
158
|
+
) -> None:
|
159
|
+
"""Record a security violation event.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
node_id: ID of the node where violation occurred
|
163
|
+
node_type: Type of the node
|
164
|
+
violation_details: Details about the security violation
|
165
|
+
connection_info: Optional connection source/target information
|
166
|
+
"""
|
167
|
+
metric = ValidationMetric(
|
168
|
+
timestamp=datetime.now(UTC),
|
169
|
+
event_type=ValidationEventType.SECURITY_VIOLATION,
|
170
|
+
node_id=node_id,
|
171
|
+
node_type=node_type,
|
172
|
+
error_category=ErrorCategory.SECURITY_VIOLATION,
|
173
|
+
connection_source=(
|
174
|
+
connection_info.get("source") if connection_info else None
|
175
|
+
),
|
176
|
+
connection_target=(
|
177
|
+
connection_info.get("target") if connection_info else None
|
178
|
+
),
|
179
|
+
additional_data=violation_details,
|
180
|
+
)
|
181
|
+
|
182
|
+
self.metrics.append(metric)
|
183
|
+
self.security_violations.append(metric)
|
184
|
+
self.error_counts[ErrorCategory.SECURITY_VIOLATION] += 1
|
185
|
+
|
186
|
+
# Always log security violations regardless of detailed logging setting
|
187
|
+
self.logger.warning(
|
188
|
+
f"SECURITY VIOLATION in {node_id} ({node_type}): {violation_details.get('message', 'Unknown')}"
|
189
|
+
)
|
190
|
+
|
191
|
+
def record_cache_hit(self, node_type: str) -> None:
|
192
|
+
"""Record a validation cache hit."""
|
193
|
+
self._cache_stats["hits"] += 1
|
194
|
+
|
195
|
+
metric = ValidationMetric(
|
196
|
+
timestamp=datetime.now(UTC),
|
197
|
+
event_type=ValidationEventType.CACHE_HIT,
|
198
|
+
node_id="cache",
|
199
|
+
node_type=node_type,
|
200
|
+
)
|
201
|
+
self.metrics.append(metric)
|
202
|
+
|
203
|
+
def record_cache_miss(self, node_type: str) -> None:
|
204
|
+
"""Record a validation cache miss."""
|
205
|
+
self._cache_stats["misses"] += 1
|
206
|
+
|
207
|
+
metric = ValidationMetric(
|
208
|
+
timestamp=datetime.now(UTC),
|
209
|
+
event_type=ValidationEventType.CACHE_MISS,
|
210
|
+
node_id="cache",
|
211
|
+
node_type=node_type,
|
212
|
+
)
|
213
|
+
self.metrics.append(metric)
|
214
|
+
|
215
|
+
def record_mode_bypass(self, node_id: str, node_type: str, mode: str) -> None:
|
216
|
+
"""Record when validation is bypassed due to mode setting.
|
217
|
+
|
218
|
+
Args:
|
219
|
+
node_id: ID of the node
|
220
|
+
node_type: Type of the node
|
221
|
+
mode: Validation mode that caused bypass
|
222
|
+
"""
|
223
|
+
metric = ValidationMetric(
|
224
|
+
timestamp=datetime.now(UTC),
|
225
|
+
event_type=ValidationEventType.MODE_BYPASS,
|
226
|
+
node_id=node_id,
|
227
|
+
node_type=node_type,
|
228
|
+
validation_mode=mode,
|
229
|
+
)
|
230
|
+
self.metrics.append(metric)
|
231
|
+
|
232
|
+
def get_performance_summary(self) -> Dict[str, Any]:
|
233
|
+
"""Get a summary of validation performance metrics.
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
Dictionary containing performance statistics
|
237
|
+
"""
|
238
|
+
total_validations = sum(
|
239
|
+
1
|
240
|
+
for m in self.metrics
|
241
|
+
if m.event_type
|
242
|
+
in [
|
243
|
+
ValidationEventType.VALIDATION_COMPLETED,
|
244
|
+
ValidationEventType.VALIDATION_FAILED,
|
245
|
+
]
|
246
|
+
)
|
247
|
+
|
248
|
+
failed_validations = sum(
|
249
|
+
1
|
250
|
+
for m in self.metrics
|
251
|
+
if m.event_type == ValidationEventType.VALIDATION_FAILED
|
252
|
+
)
|
253
|
+
|
254
|
+
# Calculate average validation times by node type
|
255
|
+
avg_times = {}
|
256
|
+
for node_type, times in self.node_validation_times.items():
|
257
|
+
if times:
|
258
|
+
avg_times[node_type] = {
|
259
|
+
"avg_ms": sum(times) / len(times),
|
260
|
+
"min_ms": min(times),
|
261
|
+
"max_ms": max(times),
|
262
|
+
"count": len(times),
|
263
|
+
}
|
264
|
+
|
265
|
+
# Cache effectiveness
|
266
|
+
total_cache_ops = self._cache_stats["hits"] + self._cache_stats["misses"]
|
267
|
+
cache_hit_rate = (
|
268
|
+
self._cache_stats["hits"] / total_cache_ops * 100
|
269
|
+
if total_cache_ops > 0
|
270
|
+
else 0
|
271
|
+
)
|
272
|
+
|
273
|
+
return {
|
274
|
+
"total_validations": total_validations,
|
275
|
+
"failed_validations": failed_validations,
|
276
|
+
"failure_rate": (
|
277
|
+
failed_validations / total_validations * 100
|
278
|
+
if total_validations > 0
|
279
|
+
else 0
|
280
|
+
),
|
281
|
+
"security_violations": len(self.security_violations),
|
282
|
+
"error_breakdown": dict(self.error_counts),
|
283
|
+
"performance_by_node_type": avg_times,
|
284
|
+
"cache_stats": {
|
285
|
+
"hits": self._cache_stats["hits"],
|
286
|
+
"misses": self._cache_stats["misses"],
|
287
|
+
"hit_rate": cache_hit_rate,
|
288
|
+
},
|
289
|
+
"mode_bypasses": sum(
|
290
|
+
1
|
291
|
+
for m in self.metrics
|
292
|
+
if m.event_type == ValidationEventType.MODE_BYPASS
|
293
|
+
),
|
294
|
+
}
|
295
|
+
|
296
|
+
def get_security_report(self) -> Dict[str, Any]:
|
297
|
+
"""Get a detailed security violations report.
|
298
|
+
|
299
|
+
Returns:
|
300
|
+
Dictionary containing security violation details
|
301
|
+
"""
|
302
|
+
violations_by_node = defaultdict(list)
|
303
|
+
for violation in self.security_violations:
|
304
|
+
violations_by_node[violation.node_type].append(
|
305
|
+
{
|
306
|
+
"timestamp": violation.timestamp.isoformat(),
|
307
|
+
"node_id": violation.node_id,
|
308
|
+
"connection": f"{violation.connection_source} → {violation.connection_target}",
|
309
|
+
"details": violation.additional_data,
|
310
|
+
}
|
311
|
+
)
|
312
|
+
|
313
|
+
return {
|
314
|
+
"total_violations": len(self.security_violations),
|
315
|
+
"violations_by_node_type": dict(violations_by_node),
|
316
|
+
"most_recent_violations": [
|
317
|
+
{
|
318
|
+
"timestamp": v.timestamp.isoformat(),
|
319
|
+
"node": f"{v.node_id} ({v.node_type})",
|
320
|
+
"details": v.additional_data,
|
321
|
+
}
|
322
|
+
for v in sorted(
|
323
|
+
self.security_violations, key=lambda x: x.timestamp, reverse=True
|
324
|
+
)[:10]
|
325
|
+
],
|
326
|
+
}
|
327
|
+
|
328
|
+
def reset_metrics(self) -> None:
|
329
|
+
"""Reset all collected metrics."""
|
330
|
+
self.metrics.clear()
|
331
|
+
self.node_validation_times.clear()
|
332
|
+
self.error_counts.clear()
|
333
|
+
self.security_violations.clear()
|
334
|
+
self._cache_stats = {"hits": 0, "misses": 0}
|
335
|
+
self._active_validations.clear()
|
336
|
+
|
337
|
+
def export_metrics(self) -> List[Dict[str, Any]]:
|
338
|
+
"""Export all metrics as a list of dictionaries.
|
339
|
+
|
340
|
+
Returns:
|
341
|
+
List of metric dictionaries for external processing
|
342
|
+
"""
|
343
|
+
return [
|
344
|
+
{
|
345
|
+
"timestamp": m.timestamp.isoformat(),
|
346
|
+
"event_type": m.event_type.value,
|
347
|
+
"node_id": m.node_id,
|
348
|
+
"node_type": m.node_type,
|
349
|
+
"duration_ms": m.duration_ms,
|
350
|
+
"error_category": m.error_category.value if m.error_category else None,
|
351
|
+
"validation_mode": m.validation_mode,
|
352
|
+
"connection_source": m.connection_source,
|
353
|
+
"connection_target": m.connection_target,
|
354
|
+
"additional_data": m.additional_data,
|
355
|
+
}
|
356
|
+
for m in self.metrics
|
357
|
+
]
|
358
|
+
|
359
|
+
|
360
|
+
# Global metrics collector instance
|
361
|
+
_global_metrics_collector = None
|
362
|
+
|
363
|
+
|
364
|
+
def get_metrics_collector() -> ValidationMetricsCollector:
|
365
|
+
"""Get the global metrics collector instance.
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
The global ValidationMetricsCollector instance
|
369
|
+
"""
|
370
|
+
global _global_metrics_collector
|
371
|
+
if _global_metrics_collector is None:
|
372
|
+
_global_metrics_collector = ValidationMetricsCollector()
|
373
|
+
return _global_metrics_collector
|
374
|
+
|
375
|
+
|
376
|
+
def reset_global_metrics() -> None:
|
377
|
+
"""Reset the global metrics collector."""
|
378
|
+
global _global_metrics_collector
|
379
|
+
if _global_metrics_collector:
|
380
|
+
_global_metrics_collector.reset_metrics()
|