kailash 0.8.4__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.
Files changed (79) hide show
  1. kailash/__init__.py +1 -7
  2. kailash/cli/__init__.py +11 -1
  3. kailash/cli/validation_audit.py +570 -0
  4. kailash/core/actors/supervisor.py +1 -1
  5. kailash/core/resilience/circuit_breaker.py +71 -1
  6. kailash/core/resilience/health_monitor.py +172 -0
  7. kailash/edge/compliance.py +33 -0
  8. kailash/edge/consistency.py +609 -0
  9. kailash/edge/coordination/__init__.py +30 -0
  10. kailash/edge/coordination/global_ordering.py +355 -0
  11. kailash/edge/coordination/leader_election.py +217 -0
  12. kailash/edge/coordination/partition_detector.py +296 -0
  13. kailash/edge/coordination/raft.py +485 -0
  14. kailash/edge/discovery.py +63 -1
  15. kailash/edge/migration/__init__.py +19 -0
  16. kailash/edge/migration/edge_migrator.py +832 -0
  17. kailash/edge/monitoring/__init__.py +21 -0
  18. kailash/edge/monitoring/edge_monitor.py +736 -0
  19. kailash/edge/prediction/__init__.py +10 -0
  20. kailash/edge/prediction/predictive_warmer.py +591 -0
  21. kailash/edge/resource/__init__.py +102 -0
  22. kailash/edge/resource/cloud_integration.py +796 -0
  23. kailash/edge/resource/cost_optimizer.py +949 -0
  24. kailash/edge/resource/docker_integration.py +919 -0
  25. kailash/edge/resource/kubernetes_integration.py +893 -0
  26. kailash/edge/resource/platform_integration.py +913 -0
  27. kailash/edge/resource/predictive_scaler.py +959 -0
  28. kailash/edge/resource/resource_analyzer.py +824 -0
  29. kailash/edge/resource/resource_pools.py +610 -0
  30. kailash/integrations/dataflow_edge.py +261 -0
  31. kailash/mcp_server/registry_integration.py +1 -1
  32. kailash/monitoring/__init__.py +18 -0
  33. kailash/monitoring/alerts.py +646 -0
  34. kailash/monitoring/metrics.py +677 -0
  35. kailash/nodes/__init__.py +2 -0
  36. kailash/nodes/ai/semantic_memory.py +2 -2
  37. kailash/nodes/base.py +545 -0
  38. kailash/nodes/edge/__init__.py +36 -0
  39. kailash/nodes/edge/base.py +240 -0
  40. kailash/nodes/edge/cloud_node.py +710 -0
  41. kailash/nodes/edge/coordination.py +239 -0
  42. kailash/nodes/edge/docker_node.py +825 -0
  43. kailash/nodes/edge/edge_data.py +582 -0
  44. kailash/nodes/edge/edge_migration_node.py +392 -0
  45. kailash/nodes/edge/edge_monitoring_node.py +421 -0
  46. kailash/nodes/edge/edge_state.py +673 -0
  47. kailash/nodes/edge/edge_warming_node.py +393 -0
  48. kailash/nodes/edge/kubernetes_node.py +652 -0
  49. kailash/nodes/edge/platform_node.py +766 -0
  50. kailash/nodes/edge/resource_analyzer_node.py +378 -0
  51. kailash/nodes/edge/resource_optimizer_node.py +501 -0
  52. kailash/nodes/edge/resource_scaler_node.py +397 -0
  53. kailash/nodes/ports.py +676 -0
  54. kailash/runtime/local.py +344 -1
  55. kailash/runtime/validation/__init__.py +20 -0
  56. kailash/runtime/validation/connection_context.py +119 -0
  57. kailash/runtime/validation/enhanced_error_formatter.py +202 -0
  58. kailash/runtime/validation/error_categorizer.py +164 -0
  59. kailash/runtime/validation/metrics.py +380 -0
  60. kailash/runtime/validation/performance.py +615 -0
  61. kailash/runtime/validation/suggestion_engine.py +212 -0
  62. kailash/testing/fixtures.py +2 -2
  63. kailash/workflow/builder.py +230 -4
  64. kailash/workflow/contracts.py +418 -0
  65. kailash/workflow/edge_infrastructure.py +369 -0
  66. kailash/workflow/migration.py +3 -3
  67. kailash/workflow/type_inference.py +669 -0
  68. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/METADATA +43 -27
  69. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/RECORD +73 -27
  70. kailash/nexus/__init__.py +0 -21
  71. kailash/nexus/cli/__init__.py +0 -5
  72. kailash/nexus/cli/__main__.py +0 -6
  73. kailash/nexus/cli/main.py +0 -176
  74. kailash/nexus/factory.py +0 -413
  75. kailash/nexus/gateway.py +0 -545
  76. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/WHEEL +0 -0
  77. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/entry_points.txt +0 -0
  78. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/licenses/LICENSE +0 -0
  79. {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,10 @@
1
+ """Edge prediction and warming module."""
2
+
3
+ from .predictive_warmer import (
4
+ PredictionStrategy,
5
+ PredictiveWarmer,
6
+ UsagePattern,
7
+ WarmingDecision,
8
+ )
9
+
10
+ __all__ = ["PredictiveWarmer", "PredictionStrategy", "UsagePattern", "WarmingDecision"]
@@ -0,0 +1,591 @@
1
+ """Predictive edge warming service for anticipating and pre-warming edge nodes.
2
+
3
+ This service analyzes usage patterns, predicts future needs, and pre-warms
4
+ edge nodes to reduce cold start latency.
5
+ """
6
+
7
+ import asyncio
8
+ import json
9
+ import time
10
+ from collections import defaultdict, deque
11
+ from dataclasses import dataclass
12
+ from datetime import datetime, timedelta
13
+ from enum import Enum
14
+ from typing import Any, Dict, List, Optional, Set, Tuple
15
+
16
+ import numpy as np
17
+ from sklearn.linear_model import LinearRegression
18
+ from sklearn.preprocessing import StandardScaler
19
+
20
+
21
+ class PredictionStrategy(Enum):
22
+ """Prediction strategies for edge warming."""
23
+
24
+ TIME_SERIES = "time_series" # Historical time-based patterns
25
+ GEOGRAPHIC = "geographic" # Location-based predictions
26
+ USER_BEHAVIOR = "user_behavior" # User-specific patterns
27
+ WORKLOAD = "workload" # Workload type patterns
28
+ HYBRID = "hybrid" # Combination of strategies
29
+
30
+
31
+ @dataclass
32
+ class UsagePattern:
33
+ """Represents a usage pattern for prediction."""
34
+
35
+ timestamp: datetime
36
+ edge_node: str
37
+ user_id: Optional[str]
38
+ location: Optional[Tuple[float, float]] # lat, lon
39
+ workload_type: str
40
+ response_time: float
41
+ resource_usage: Dict[str, float] # cpu, memory, etc.
42
+
43
+ def to_features(self) -> List[float]:
44
+ """Convert pattern to feature vector for ML."""
45
+ features = [
46
+ self.timestamp.hour,
47
+ self.timestamp.weekday(),
48
+ self.response_time,
49
+ self.resource_usage.get("cpu", 0),
50
+ self.resource_usage.get("memory", 0),
51
+ ]
52
+
53
+ if self.location:
54
+ features.extend(self.location)
55
+
56
+ return features
57
+
58
+
59
+ @dataclass
60
+ class WarmingDecision:
61
+ """Represents a decision to warm an edge node."""
62
+
63
+ edge_node: str
64
+ confidence: float
65
+ predicted_time: datetime
66
+ resources_needed: Dict[str, float]
67
+ strategy_used: PredictionStrategy
68
+ reasoning: str
69
+
70
+
71
+ class PredictiveWarmer:
72
+ """Predictive edge warming service.
73
+
74
+ Analyzes usage patterns and pre-warms edge nodes to reduce latency.
75
+ """
76
+
77
+ def __init__(
78
+ self,
79
+ history_window: int = 7 * 24 * 60 * 60, # 7 days in seconds
80
+ prediction_horizon: int = 300, # 5 minutes ahead
81
+ confidence_threshold: float = 0.7,
82
+ max_prewarmed_nodes: int = 10,
83
+ ):
84
+ """Initialize predictive warmer.
85
+
86
+ Args:
87
+ history_window: Time window for historical analysis
88
+ prediction_horizon: How far ahead to predict (seconds)
89
+ confidence_threshold: Minimum confidence for warming
90
+ max_prewarmed_nodes: Maximum nodes to keep warm
91
+ """
92
+ self.history_window = history_window
93
+ self.prediction_horizon = prediction_horizon
94
+ self.confidence_threshold = confidence_threshold
95
+ self.max_prewarmed_nodes = max_prewarmed_nodes
96
+
97
+ # Pattern storage
98
+ self.usage_history: deque = deque(maxlen=10000)
99
+ self.pattern_cache: Dict[str, List[UsagePattern]] = defaultdict(list)
100
+
101
+ # ML models for prediction
102
+ self.time_series_model = LinearRegression()
103
+ self.scaler = StandardScaler()
104
+ self.model_trained = False
105
+
106
+ # Current state
107
+ self.warmed_nodes: Set[str] = set()
108
+ self.warming_decisions: List[WarmingDecision] = []
109
+
110
+ # Metrics
111
+ self.predictions_made = 0
112
+ self.successful_predictions = 0
113
+ self.false_positives = 0
114
+ self.missed_predictions = 0
115
+
116
+ self._running = False
117
+ self._prediction_task = None
118
+
119
+ async def start(self):
120
+ """Start the predictive warming service."""
121
+ self._running = True
122
+ self._prediction_task = asyncio.create_task(self._prediction_loop())
123
+
124
+ async def stop(self):
125
+ """Stop the predictive warming service."""
126
+ self._running = False
127
+ if self._prediction_task:
128
+ self._prediction_task.cancel()
129
+ try:
130
+ await self._prediction_task
131
+ except asyncio.CancelledError:
132
+ pass
133
+
134
+ async def record_usage(self, pattern: UsagePattern):
135
+ """Record a usage pattern for analysis.
136
+
137
+ Args:
138
+ pattern: Usage pattern to record
139
+ """
140
+ self.usage_history.append(pattern)
141
+
142
+ # Cache by different dimensions
143
+ self.pattern_cache[pattern.edge_node].append(pattern)
144
+ if pattern.user_id:
145
+ self.pattern_cache[f"user_{pattern.user_id}"].append(pattern)
146
+ self.pattern_cache[pattern.workload_type].append(pattern)
147
+
148
+ # Retrain model periodically
149
+ if len(self.usage_history) % 100 == 0:
150
+ await self._train_models()
151
+
152
+ async def predict_warming_needs(
153
+ self, strategy: PredictionStrategy = PredictionStrategy.HYBRID
154
+ ) -> List[WarmingDecision]:
155
+ """Predict which edge nodes need warming.
156
+
157
+ Args:
158
+ strategy: Prediction strategy to use
159
+
160
+ Returns:
161
+ List of warming decisions
162
+ """
163
+ decisions = []
164
+
165
+ if strategy == PredictionStrategy.TIME_SERIES:
166
+ decisions.extend(await self._predict_time_series())
167
+ elif strategy == PredictionStrategy.GEOGRAPHIC:
168
+ decisions.extend(await self._predict_geographic())
169
+ elif strategy == PredictionStrategy.USER_BEHAVIOR:
170
+ decisions.extend(await self._predict_user_behavior())
171
+ elif strategy == PredictionStrategy.WORKLOAD:
172
+ decisions.extend(await self._predict_workload())
173
+ elif strategy == PredictionStrategy.HYBRID:
174
+ # Combine all strategies
175
+ all_decisions = []
176
+ all_decisions.extend(await self._predict_time_series())
177
+ all_decisions.extend(await self._predict_geographic())
178
+ all_decisions.extend(await self._predict_user_behavior())
179
+ all_decisions.extend(await self._predict_workload())
180
+
181
+ # Aggregate and rank decisions
182
+ decisions = self._aggregate_decisions(all_decisions)
183
+
184
+ # Filter by confidence and limit
185
+ decisions = [d for d in decisions if d.confidence >= self.confidence_threshold]
186
+ decisions.sort(key=lambda d: d.confidence, reverse=True)
187
+
188
+ return decisions[: self.max_prewarmed_nodes]
189
+
190
+ async def _prediction_loop(self):
191
+ """Main prediction loop."""
192
+ while self._running:
193
+ try:
194
+ # Make predictions
195
+ decisions = await self.predict_warming_needs()
196
+ self.warming_decisions = decisions
197
+
198
+ # Execute warming decisions
199
+ for decision in decisions:
200
+ await self._execute_warming(decision)
201
+
202
+ # Wait before next prediction
203
+ await asyncio.sleep(0.1) # Fast prediction for tests
204
+
205
+ except Exception as e:
206
+ print(f"Prediction error: {e}")
207
+ await asyncio.sleep(0.1) # Fast retry for tests
208
+
209
+ async def _train_models(self):
210
+ """Train ML models on historical data."""
211
+ if len(self.usage_history) < 100:
212
+ return # Not enough data
213
+
214
+ # Prepare training data
215
+ X = []
216
+ y = []
217
+
218
+ for i in range(len(self.usage_history) - 1):
219
+ pattern = self.usage_history[i]
220
+ next_pattern = self.usage_history[i + 1]
221
+
222
+ # Features from current pattern
223
+ features = pattern.to_features()
224
+ X.append(features)
225
+
226
+ # Target: will this edge be used in next time window?
227
+ time_diff = (next_pattern.timestamp - pattern.timestamp).total_seconds()
228
+ y.append(1 if time_diff < self.prediction_horizon else 0)
229
+
230
+ if X and y:
231
+ # Normalize features
232
+ X_scaled = self.scaler.fit_transform(X)
233
+
234
+ # Train model
235
+ self.time_series_model.fit(X_scaled, y)
236
+ self.model_trained = True
237
+
238
+ async def _predict_time_series(self) -> List[WarmingDecision]:
239
+ """Predict based on time series patterns."""
240
+ decisions = []
241
+
242
+ if not self.model_trained or len(self.usage_history) < 10:
243
+ return decisions
244
+
245
+ # Analyze patterns for each edge node
246
+ edge_patterns = defaultdict(list)
247
+ for pattern in self.usage_history:
248
+ edge_patterns[pattern.edge_node].append(pattern)
249
+
250
+ current_time = datetime.now()
251
+
252
+ for edge_node, patterns in edge_patterns.items():
253
+ if len(patterns) < 5:
254
+ continue
255
+
256
+ # Extract time-based features
257
+ hourly_usage = defaultdict(int)
258
+ daily_usage = defaultdict(int)
259
+
260
+ for pattern in patterns[-100:]: # Last 100 patterns
261
+ hourly_usage[pattern.timestamp.hour] += 1
262
+ daily_usage[pattern.timestamp.weekday()] += 1
263
+
264
+ # Predict if node will be needed
265
+ current_hour = current_time.hour
266
+ current_day = current_time.weekday()
267
+
268
+ # Simple heuristic: high usage in current hour/day
269
+ hour_score = (
270
+ hourly_usage[current_hour] / max(hourly_usage.values())
271
+ if hourly_usage
272
+ else 0
273
+ )
274
+ day_score = (
275
+ daily_usage[current_day] / max(daily_usage.values())
276
+ if daily_usage
277
+ else 0
278
+ )
279
+
280
+ confidence = (hour_score + day_score) / 2
281
+
282
+ if confidence > self.confidence_threshold:
283
+ decisions.append(
284
+ WarmingDecision(
285
+ edge_node=edge_node,
286
+ confidence=confidence,
287
+ predicted_time=current_time
288
+ + timedelta(seconds=self.prediction_horizon),
289
+ resources_needed=self._estimate_resources(patterns),
290
+ strategy_used=PredictionStrategy.TIME_SERIES,
291
+ reasoning=f"High usage at hour {current_hour} (score: {hour_score:.2f})",
292
+ )
293
+ )
294
+
295
+ return decisions
296
+
297
+ async def _predict_geographic(self) -> List[WarmingDecision]:
298
+ """Predict based on geographic patterns."""
299
+ decisions = []
300
+
301
+ # Group patterns by location proximity
302
+ location_patterns = defaultdict(list)
303
+
304
+ for pattern in self.usage_history:
305
+ if pattern.location:
306
+ # Simple grid-based grouping
307
+ lat_grid = int(pattern.location[0] * 10) / 10
308
+ lon_grid = int(pattern.location[1] * 10) / 10
309
+ location_patterns[(lat_grid, lon_grid)].append(pattern)
310
+
311
+ # Find active locations
312
+ current_time = datetime.now()
313
+ active_locations = []
314
+
315
+ for location, patterns in location_patterns.items():
316
+ recent_patterns = [
317
+ p
318
+ for p in patterns
319
+ if (current_time - p.timestamp).total_seconds() < 3600 # Last hour
320
+ ]
321
+
322
+ if len(recent_patterns) > 5:
323
+ active_locations.append(location)
324
+
325
+ # Predict edge nodes for active locations
326
+ for location in active_locations:
327
+ patterns = location_patterns[location]
328
+
329
+ # Find most used edge node for this location
330
+ edge_usage = defaultdict(int)
331
+ for pattern in patterns:
332
+ edge_usage[pattern.edge_node] += 1
333
+
334
+ if edge_usage:
335
+ best_edge = max(edge_usage, key=edge_usage.get)
336
+ confidence = edge_usage[best_edge] / len(patterns)
337
+
338
+ decisions.append(
339
+ WarmingDecision(
340
+ edge_node=best_edge,
341
+ confidence=confidence,
342
+ predicted_time=current_time
343
+ + timedelta(seconds=self.prediction_horizon),
344
+ resources_needed=self._estimate_resources(patterns),
345
+ strategy_used=PredictionStrategy.GEOGRAPHIC,
346
+ reasoning=f"Active location {location} typically uses {best_edge}",
347
+ )
348
+ )
349
+
350
+ return decisions
351
+
352
+ async def _predict_user_behavior(self) -> List[WarmingDecision]:
353
+ """Predict based on user behavior patterns."""
354
+ decisions = []
355
+
356
+ # Analyze per-user patterns
357
+ user_patterns = defaultdict(list)
358
+
359
+ for pattern in self.usage_history:
360
+ if pattern.user_id:
361
+ user_patterns[pattern.user_id].append(pattern)
362
+
363
+ current_time = datetime.now()
364
+
365
+ for user_id, patterns in user_patterns.items():
366
+ if len(patterns) < 10:
367
+ continue
368
+
369
+ # Find user's typical usage times
370
+ usage_times = [p.timestamp.hour for p in patterns]
371
+ avg_hour = sum(usage_times) / len(usage_times)
372
+
373
+ # Check if current time matches user's pattern
374
+ if abs(current_time.hour - avg_hour) < 2:
375
+ # Find user's preferred edge nodes
376
+ edge_usage = defaultdict(int)
377
+ for pattern in patterns:
378
+ edge_usage[pattern.edge_node] += 1
379
+
380
+ if edge_usage:
381
+ best_edge = max(edge_usage, key=edge_usage.get)
382
+ confidence = edge_usage[best_edge] / len(patterns)
383
+
384
+ decisions.append(
385
+ WarmingDecision(
386
+ edge_node=best_edge,
387
+ confidence=confidence
388
+ * 0.8, # Slightly lower confidence for user-based
389
+ predicted_time=current_time
390
+ + timedelta(seconds=self.prediction_horizon),
391
+ resources_needed=self._estimate_resources(patterns),
392
+ strategy_used=PredictionStrategy.USER_BEHAVIOR,
393
+ reasoning=f"User {user_id} typically active at this time",
394
+ )
395
+ )
396
+
397
+ return decisions
398
+
399
+ async def _predict_workload(self) -> List[WarmingDecision]:
400
+ """Predict based on workload patterns."""
401
+ decisions = []
402
+
403
+ # Analyze workload type patterns
404
+ workload_patterns = defaultdict(list)
405
+
406
+ for pattern in self.usage_history:
407
+ workload_patterns[pattern.workload_type].append(pattern)
408
+
409
+ current_time = datetime.now()
410
+
411
+ for workload_type, patterns in workload_patterns.items():
412
+ # Find recent surge in this workload type
413
+ recent_patterns = [
414
+ p
415
+ for p in patterns
416
+ if (current_time - p.timestamp).total_seconds() < 600 # Last 10 minutes
417
+ ]
418
+
419
+ if len(recent_patterns) > 5:
420
+ # Increasing trend suggests more coming
421
+ edge_usage = defaultdict(int)
422
+ for pattern in recent_patterns:
423
+ edge_usage[pattern.edge_node] += 1
424
+
425
+ for edge_node, count in edge_usage.items():
426
+ confidence = count / len(recent_patterns)
427
+
428
+ decisions.append(
429
+ WarmingDecision(
430
+ edge_node=edge_node,
431
+ confidence=confidence,
432
+ predicted_time=current_time
433
+ + timedelta(seconds=self.prediction_horizon),
434
+ resources_needed=self._estimate_resources(patterns),
435
+ strategy_used=PredictionStrategy.WORKLOAD,
436
+ reasoning=f"Surge in {workload_type} workload detected",
437
+ )
438
+ )
439
+
440
+ return decisions
441
+
442
+ def _aggregate_decisions(
443
+ self, decisions: List[WarmingDecision]
444
+ ) -> List[WarmingDecision]:
445
+ """Aggregate decisions from multiple strategies."""
446
+ # Group by edge node
447
+ node_decisions = defaultdict(list)
448
+ for decision in decisions:
449
+ node_decisions[decision.edge_node].append(decision)
450
+
451
+ # Combine confidences
452
+ aggregated = []
453
+ for edge_node, node_decisions_list in node_decisions.items():
454
+ # Weight different strategies
455
+ weights = {
456
+ PredictionStrategy.TIME_SERIES: 0.3,
457
+ PredictionStrategy.GEOGRAPHIC: 0.25,
458
+ PredictionStrategy.USER_BEHAVIOR: 0.25,
459
+ PredictionStrategy.WORKLOAD: 0.2,
460
+ }
461
+
462
+ total_confidence = 0
463
+ total_weight = 0
464
+ resources = {}
465
+ reasons = []
466
+
467
+ for decision in node_decisions_list:
468
+ weight = weights.get(decision.strategy_used, 0.25)
469
+ total_confidence += decision.confidence * weight
470
+ total_weight += weight
471
+
472
+ # Merge resource estimates
473
+ for resource, value in decision.resources_needed.items():
474
+ resources[resource] = max(resources.get(resource, 0), value)
475
+
476
+ reasons.append(f"{decision.strategy_used.value}: {decision.reasoning}")
477
+
478
+ if total_weight > 0:
479
+ aggregated.append(
480
+ WarmingDecision(
481
+ edge_node=edge_node,
482
+ confidence=total_confidence / total_weight,
483
+ predicted_time=datetime.now()
484
+ + timedelta(seconds=self.prediction_horizon),
485
+ resources_needed=resources,
486
+ strategy_used=PredictionStrategy.HYBRID,
487
+ reasoning="; ".join(reasons),
488
+ )
489
+ )
490
+
491
+ return aggregated
492
+
493
+ def _estimate_resources(self, patterns: List[UsagePattern]) -> Dict[str, float]:
494
+ """Estimate resource needs based on patterns."""
495
+ if not patterns:
496
+ return {"cpu": 0.1, "memory": 128}
497
+
498
+ # Average resource usage
499
+ cpu_usage = []
500
+ memory_usage = []
501
+
502
+ for pattern in patterns[-20:]: # Last 20 patterns
503
+ cpu_usage.append(pattern.resource_usage.get("cpu", 0))
504
+ memory_usage.append(pattern.resource_usage.get("memory", 0))
505
+
506
+ return {
507
+ "cpu": np.percentile(cpu_usage, 75) if cpu_usage else 0.1,
508
+ "memory": np.percentile(memory_usage, 75) if memory_usage else 128,
509
+ }
510
+
511
+ async def _execute_warming(self, decision: WarmingDecision):
512
+ """Execute a warming decision."""
513
+ if decision.edge_node not in self.warmed_nodes:
514
+ # Simulate warming the edge node
515
+ print(
516
+ f"Warming edge node {decision.edge_node} "
517
+ f"(confidence: {decision.confidence:.2f}, "
518
+ f"reason: {decision.reasoning})"
519
+ )
520
+
521
+ self.warmed_nodes.add(decision.edge_node)
522
+ self.predictions_made += 1
523
+
524
+ # TODO: Actual edge node warming implementation
525
+ # This would involve:
526
+ # 1. Pre-allocating resources
527
+ # 2. Loading necessary data
528
+ # 3. Establishing connections
529
+ # 4. Running health checks
530
+
531
+ def evaluate_prediction(self, edge_node: str, was_used: bool):
532
+ """Evaluate a prediction after the fact.
533
+
534
+ Args:
535
+ edge_node: Edge node that was predicted
536
+ was_used: Whether the node was actually used
537
+ """
538
+ if edge_node in self.warmed_nodes:
539
+ if was_used:
540
+ self.successful_predictions += 1
541
+ else:
542
+ self.false_positives += 1
543
+ elif was_used:
544
+ self.missed_predictions += 1
545
+
546
+ def get_metrics(self) -> Dict[str, Any]:
547
+ """Get prediction metrics."""
548
+ total_evaluated = (
549
+ self.successful_predictions + self.false_positives + self.missed_predictions
550
+ )
551
+
552
+ if total_evaluated == 0:
553
+ precision = recall = f1_score = 0
554
+ else:
555
+ precision = (
556
+ self.successful_predictions
557
+ / (self.successful_predictions + self.false_positives)
558
+ if (self.successful_predictions + self.false_positives) > 0
559
+ else 0
560
+ )
561
+ recall = (
562
+ self.successful_predictions
563
+ / (self.successful_predictions + self.missed_predictions)
564
+ if (self.successful_predictions + self.missed_predictions) > 0
565
+ else 0
566
+ )
567
+ f1_score = (
568
+ 2 * precision * recall / (precision + recall)
569
+ if (precision + recall) > 0
570
+ else 0
571
+ )
572
+
573
+ return {
574
+ "predictions_made": self.predictions_made,
575
+ "successful_predictions": self.successful_predictions,
576
+ "false_positives": self.false_positives,
577
+ "missed_predictions": self.missed_predictions,
578
+ "precision": precision,
579
+ "recall": recall,
580
+ "f1_score": f1_score,
581
+ "warmed_nodes": list(self.warmed_nodes),
582
+ "current_decisions": [
583
+ {
584
+ "edge_node": d.edge_node,
585
+ "confidence": d.confidence,
586
+ "strategy": d.strategy_used.value,
587
+ "reasoning": d.reasoning,
588
+ }
589
+ for d in self.warming_decisions
590
+ ],
591
+ }
@@ -0,0 +1,102 @@
1
+ """Edge resource management components."""
2
+
3
+ from .cloud_integration import (
4
+ CloudInstance,
5
+ CloudIntegration,
6
+ CloudMetrics,
7
+ )
8
+ from .cloud_integration import CloudProvider as CloudProviderType
9
+ from .cloud_integration import (
10
+ InstanceSpec,
11
+ InstanceState,
12
+ )
13
+ from .cost_optimizer import (
14
+ CloudProvider,
15
+ CostMetric,
16
+ CostOptimization,
17
+ CostOptimizer,
18
+ InstanceType,
19
+ OptimizationStrategy,
20
+ )
21
+ from .docker_integration import (
22
+ ContainerMetrics,
23
+ ContainerSpec,
24
+ ContainerState,
25
+ DockerIntegration,
26
+ NetworkMode,
27
+ RestartPolicyType,
28
+ ServiceSpec,
29
+ )
30
+
31
+ # Phase 4.4 Integration & Testing components
32
+ from .kubernetes_integration import (
33
+ KubernetesIntegration,
34
+ KubernetesResource,
35
+ KubernetesResourceType,
36
+ PodScalingSpec,
37
+ ScalingPolicy,
38
+ )
39
+ from .platform_integration import (
40
+ PlatformConfig,
41
+ PlatformIntegration,
42
+ PlatformType,
43
+ ResourceAllocation,
44
+ )
45
+ from .platform_integration import ResourceRequest as PlatformResourceRequest
46
+ from .platform_integration import (
47
+ ResourceScope,
48
+ )
49
+ from .predictive_scaler import (
50
+ PredictionHorizon,
51
+ PredictiveScaler,
52
+ ScalingDecision,
53
+ ScalingPrediction,
54
+ ScalingStrategy,
55
+ )
56
+ from .resource_analyzer import ResourceAnalyzer, ResourceMetric, ResourceType
57
+ from .resource_pools import AllocationResult, ResourcePool, ResourceRequest
58
+
59
+ __all__ = [
60
+ "ResourceAnalyzer",
61
+ "ResourceMetric",
62
+ "ResourceType",
63
+ "ResourcePool",
64
+ "ResourceRequest",
65
+ "AllocationResult",
66
+ "PredictiveScaler",
67
+ "ScalingStrategy",
68
+ "PredictionHorizon",
69
+ "ScalingPrediction",
70
+ "ScalingDecision",
71
+ "CostOptimizer",
72
+ "CloudProvider",
73
+ "InstanceType",
74
+ "OptimizationStrategy",
75
+ "CostMetric",
76
+ "CostOptimization",
77
+ # Phase 4.4 components
78
+ "KubernetesIntegration",
79
+ "KubernetesResource",
80
+ "KubernetesResourceType",
81
+ "PodScalingSpec",
82
+ "ScalingPolicy",
83
+ "DockerIntegration",
84
+ "ContainerSpec",
85
+ "ServiceSpec",
86
+ "ContainerState",
87
+ "RestartPolicyType",
88
+ "NetworkMode",
89
+ "ContainerMetrics",
90
+ "CloudIntegration",
91
+ "CloudProviderType",
92
+ "InstanceSpec",
93
+ "InstanceState",
94
+ "CloudInstance",
95
+ "CloudMetrics",
96
+ "PlatformIntegration",
97
+ "PlatformType",
98
+ "ResourceScope",
99
+ "PlatformResourceRequest",
100
+ "ResourceAllocation",
101
+ "PlatformConfig",
102
+ ]