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.
- 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/semantic_memory.py +2 -2
- 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 +230 -4
- 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.4.dist-info → kailash-0.8.5.dist-info}/METADATA +43 -27
- {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/RECORD +73 -27
- 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.4.dist-info → kailash-0.8.5.dist-info}/WHEEL +0 -0
- {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/entry_points.txt +0 -0
- {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/licenses/LICENSE +0 -0
- {kailash-0.8.4.dist-info → kailash-0.8.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,501 @@
|
|
1
|
+
"""Resource optimizer node for intelligent cost optimization.
|
2
|
+
|
3
|
+
This node integrates cost optimization capabilities into workflows,
|
4
|
+
providing multi-cloud cost analysis and optimization recommendations.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import asyncio
|
8
|
+
from datetime import datetime
|
9
|
+
from typing import Any, Dict, List, Optional
|
10
|
+
|
11
|
+
from kailash.edge.resource.cost_optimizer import (
|
12
|
+
CloudProvider,
|
13
|
+
CostMetric,
|
14
|
+
CostOptimization,
|
15
|
+
CostOptimizer,
|
16
|
+
InstanceType,
|
17
|
+
OptimizationStrategy,
|
18
|
+
)
|
19
|
+
from kailash.nodes.base import NodeParameter, register_node
|
20
|
+
from kailash.nodes.base_async import AsyncNode
|
21
|
+
|
22
|
+
|
23
|
+
@register_node()
|
24
|
+
class ResourceOptimizerNode(AsyncNode):
|
25
|
+
"""Node for cost optimization operations.
|
26
|
+
|
27
|
+
This node provides comprehensive cost analysis and optimization
|
28
|
+
for edge computing resources across multiple cloud providers.
|
29
|
+
|
30
|
+
Example:
|
31
|
+
>>> # Record cost data
|
32
|
+
>>> result = await optimizer_node.execute_async(
|
33
|
+
... operation="record_cost",
|
34
|
+
... edge_node="edge-west-1",
|
35
|
+
... resource_type="cpu",
|
36
|
+
... provider="aws",
|
37
|
+
... instance_type="on_demand",
|
38
|
+
... cost_per_hour=0.10,
|
39
|
+
... usage_hours=24
|
40
|
+
... )
|
41
|
+
|
42
|
+
>>> # Optimize costs
|
43
|
+
>>> result = await optimizer_node.execute_async(
|
44
|
+
... operation="optimize_costs",
|
45
|
+
... strategy="balance_cost_performance",
|
46
|
+
... edge_nodes=["edge-west-1", "edge-west-2"]
|
47
|
+
... )
|
48
|
+
|
49
|
+
>>> # Get spot recommendations
|
50
|
+
>>> result = await optimizer_node.execute_async(
|
51
|
+
... operation="get_spot_recommendations",
|
52
|
+
... edge_nodes=["edge-west-1"]
|
53
|
+
... )
|
54
|
+
|
55
|
+
>>> # Calculate ROI
|
56
|
+
>>> result = await optimizer_node.execute_async(
|
57
|
+
... operation="calculate_roi",
|
58
|
+
... optimization_id="opt_12345",
|
59
|
+
... implementation_cost=500.0
|
60
|
+
... )
|
61
|
+
"""
|
62
|
+
|
63
|
+
def __init__(self, **kwargs):
|
64
|
+
"""Initialize resource optimizer node."""
|
65
|
+
super().__init__(**kwargs)
|
66
|
+
|
67
|
+
# Extract configuration
|
68
|
+
cost_history_days = kwargs.get("cost_history_days", 30)
|
69
|
+
optimization_interval = kwargs.get("optimization_interval", 3600)
|
70
|
+
savings_threshold = kwargs.get("savings_threshold", 0.1)
|
71
|
+
risk_tolerance = kwargs.get("risk_tolerance", "medium")
|
72
|
+
|
73
|
+
# Initialize optimizer
|
74
|
+
self.optimizer = CostOptimizer(
|
75
|
+
cost_history_days=cost_history_days,
|
76
|
+
optimization_interval=optimization_interval,
|
77
|
+
savings_threshold=savings_threshold,
|
78
|
+
risk_tolerance=risk_tolerance,
|
79
|
+
)
|
80
|
+
|
81
|
+
self._optimizer_started = False
|
82
|
+
|
83
|
+
@property
|
84
|
+
def input_parameters(self) -> Dict[str, NodeParameter]:
|
85
|
+
"""Define input parameters."""
|
86
|
+
return {
|
87
|
+
"operation": NodeParameter(
|
88
|
+
name="operation",
|
89
|
+
type=str,
|
90
|
+
required=True,
|
91
|
+
description="Operation to perform (record_cost, optimize_costs, get_spot_recommendations, get_reservation_recommendations, calculate_roi, get_cost_forecast, start_optimizer, stop_optimizer)",
|
92
|
+
),
|
93
|
+
# For record_cost
|
94
|
+
"edge_node": NodeParameter(
|
95
|
+
name="edge_node",
|
96
|
+
type=str,
|
97
|
+
required=False,
|
98
|
+
description="Edge node identifier",
|
99
|
+
),
|
100
|
+
"resource_type": NodeParameter(
|
101
|
+
name="resource_type",
|
102
|
+
type=str,
|
103
|
+
required=False,
|
104
|
+
description="Type of resource",
|
105
|
+
),
|
106
|
+
"provider": NodeParameter(
|
107
|
+
name="provider",
|
108
|
+
type=str,
|
109
|
+
required=False,
|
110
|
+
description="Cloud provider (aws, gcp, azure, alibaba, edge_local)",
|
111
|
+
),
|
112
|
+
"instance_type": NodeParameter(
|
113
|
+
name="instance_type",
|
114
|
+
type=str,
|
115
|
+
required=False,
|
116
|
+
description="Instance pricing type (on_demand, spot, reserved, savings_plan, dedicated)",
|
117
|
+
),
|
118
|
+
"cost_per_hour": NodeParameter(
|
119
|
+
name="cost_per_hour",
|
120
|
+
type=float,
|
121
|
+
required=False,
|
122
|
+
description="Cost per hour for the resource",
|
123
|
+
),
|
124
|
+
"usage_hours": NodeParameter(
|
125
|
+
name="usage_hours",
|
126
|
+
type=float,
|
127
|
+
required=False,
|
128
|
+
description="Hours of usage",
|
129
|
+
),
|
130
|
+
"currency": NodeParameter(
|
131
|
+
name="currency",
|
132
|
+
type=str,
|
133
|
+
required=False,
|
134
|
+
default="USD",
|
135
|
+
description="Currency for costs",
|
136
|
+
),
|
137
|
+
# For optimize_costs
|
138
|
+
"strategy": NodeParameter(
|
139
|
+
name="strategy",
|
140
|
+
type=str,
|
141
|
+
required=False,
|
142
|
+
default="balance_cost_performance",
|
143
|
+
description="Optimization strategy (minimize_cost, balance_cost_performance, maximize_performance, predictable_cost, risk_averse)",
|
144
|
+
),
|
145
|
+
"edge_nodes": NodeParameter(
|
146
|
+
name="edge_nodes",
|
147
|
+
type=list,
|
148
|
+
required=False,
|
149
|
+
description="Specific edge nodes to optimize",
|
150
|
+
),
|
151
|
+
# For recommendations
|
152
|
+
"providers": NodeParameter(
|
153
|
+
name="providers",
|
154
|
+
type=list,
|
155
|
+
required=False,
|
156
|
+
description="Specific providers to analyze",
|
157
|
+
),
|
158
|
+
# For ROI calculation
|
159
|
+
"optimization_id": NodeParameter(
|
160
|
+
name="optimization_id",
|
161
|
+
type=str,
|
162
|
+
required=False,
|
163
|
+
description="Optimization ID for ROI calculation",
|
164
|
+
),
|
165
|
+
"implementation_cost": NodeParameter(
|
166
|
+
name="implementation_cost",
|
167
|
+
type=float,
|
168
|
+
required=False,
|
169
|
+
default=0.0,
|
170
|
+
description="One-time implementation cost",
|
171
|
+
),
|
172
|
+
# For forecast
|
173
|
+
"forecast_months": NodeParameter(
|
174
|
+
name="forecast_months",
|
175
|
+
type=int,
|
176
|
+
required=False,
|
177
|
+
default=12,
|
178
|
+
description="Months to forecast",
|
179
|
+
),
|
180
|
+
"include_optimizations": NodeParameter(
|
181
|
+
name="include_optimizations",
|
182
|
+
type=bool,
|
183
|
+
required=False,
|
184
|
+
default=True,
|
185
|
+
description="Include optimization impact in forecast",
|
186
|
+
),
|
187
|
+
# Configuration
|
188
|
+
"cost_history_days": NodeParameter(
|
189
|
+
name="cost_history_days",
|
190
|
+
type=int,
|
191
|
+
required=False,
|
192
|
+
default=30,
|
193
|
+
description="Days of cost history to analyze",
|
194
|
+
),
|
195
|
+
"optimization_interval": NodeParameter(
|
196
|
+
name="optimization_interval",
|
197
|
+
type=int,
|
198
|
+
required=False,
|
199
|
+
default=3600,
|
200
|
+
description="How often to run optimization (seconds)",
|
201
|
+
),
|
202
|
+
"savings_threshold": NodeParameter(
|
203
|
+
name="savings_threshold",
|
204
|
+
type=float,
|
205
|
+
required=False,
|
206
|
+
default=0.1,
|
207
|
+
description="Minimum savings percentage (0-1)",
|
208
|
+
),
|
209
|
+
"risk_tolerance": NodeParameter(
|
210
|
+
name="risk_tolerance",
|
211
|
+
type=str,
|
212
|
+
required=False,
|
213
|
+
default="medium",
|
214
|
+
description="Risk tolerance (low, medium, high)",
|
215
|
+
),
|
216
|
+
}
|
217
|
+
|
218
|
+
@property
|
219
|
+
def output_parameters(self) -> Dict[str, NodeParameter]:
|
220
|
+
"""Define output parameters."""
|
221
|
+
return {
|
222
|
+
"status": NodeParameter(
|
223
|
+
name="status", type=str, description="Operation status"
|
224
|
+
),
|
225
|
+
"optimizations": NodeParameter(
|
226
|
+
name="optimizations",
|
227
|
+
type=list,
|
228
|
+
required=False,
|
229
|
+
description="Cost optimization recommendations",
|
230
|
+
),
|
231
|
+
"spot_recommendations": NodeParameter(
|
232
|
+
name="spot_recommendations",
|
233
|
+
type=list,
|
234
|
+
required=False,
|
235
|
+
description="Spot instance recommendations",
|
236
|
+
),
|
237
|
+
"reservation_recommendations": NodeParameter(
|
238
|
+
name="reservation_recommendations",
|
239
|
+
type=list,
|
240
|
+
required=False,
|
241
|
+
description="Reserved capacity recommendations",
|
242
|
+
),
|
243
|
+
"roi_analysis": NodeParameter(
|
244
|
+
name="roi_analysis",
|
245
|
+
type=dict,
|
246
|
+
required=False,
|
247
|
+
description="ROI analysis results",
|
248
|
+
),
|
249
|
+
"cost_forecast": NodeParameter(
|
250
|
+
name="cost_forecast",
|
251
|
+
type=dict,
|
252
|
+
required=False,
|
253
|
+
description="Cost forecast with optimizations",
|
254
|
+
),
|
255
|
+
"cost_recorded": NodeParameter(
|
256
|
+
name="cost_recorded",
|
257
|
+
type=bool,
|
258
|
+
required=False,
|
259
|
+
description="Whether cost was recorded",
|
260
|
+
),
|
261
|
+
"total_savings": NodeParameter(
|
262
|
+
name="total_savings",
|
263
|
+
type=float,
|
264
|
+
required=False,
|
265
|
+
description="Total estimated savings",
|
266
|
+
),
|
267
|
+
"optimizer_active": NodeParameter(
|
268
|
+
name="optimizer_active",
|
269
|
+
type=bool,
|
270
|
+
required=False,
|
271
|
+
description="Whether optimizer is active",
|
272
|
+
),
|
273
|
+
}
|
274
|
+
|
275
|
+
def get_parameters(self) -> Dict[str, NodeParameter]:
|
276
|
+
"""Get all node parameters for compatibility."""
|
277
|
+
return self.input_parameters
|
278
|
+
|
279
|
+
async def async_run(self, **kwargs) -> Dict[str, Any]:
|
280
|
+
"""Execute cost optimization operation."""
|
281
|
+
operation = kwargs["operation"]
|
282
|
+
|
283
|
+
try:
|
284
|
+
if operation == "record_cost":
|
285
|
+
return await self._record_cost(kwargs)
|
286
|
+
elif operation == "optimize_costs":
|
287
|
+
return await self._optimize_costs(kwargs)
|
288
|
+
elif operation == "get_spot_recommendations":
|
289
|
+
return await self._get_spot_recommendations(kwargs)
|
290
|
+
elif operation == "get_reservation_recommendations":
|
291
|
+
return await self._get_reservation_recommendations(kwargs)
|
292
|
+
elif operation == "calculate_roi":
|
293
|
+
return await self._calculate_roi(kwargs)
|
294
|
+
elif operation == "get_cost_forecast":
|
295
|
+
return await self._get_cost_forecast(kwargs)
|
296
|
+
elif operation == "start_optimizer":
|
297
|
+
return await self._start_optimizer()
|
298
|
+
elif operation == "stop_optimizer":
|
299
|
+
return await self._stop_optimizer()
|
300
|
+
else:
|
301
|
+
raise ValueError(f"Unknown operation: {operation}")
|
302
|
+
|
303
|
+
except Exception as e:
|
304
|
+
self.logger.error(f"Cost optimization operation failed: {str(e)}")
|
305
|
+
return {"status": "error", "error": str(e)}
|
306
|
+
|
307
|
+
async def _record_cost(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
308
|
+
"""Record cost data."""
|
309
|
+
# Parse provider
|
310
|
+
provider_str = kwargs.get("provider", "aws")
|
311
|
+
try:
|
312
|
+
provider = CloudProvider(provider_str)
|
313
|
+
except ValueError:
|
314
|
+
provider = CloudProvider.AWS
|
315
|
+
|
316
|
+
# Parse instance type
|
317
|
+
instance_type_str = kwargs.get("instance_type", "on_demand")
|
318
|
+
try:
|
319
|
+
instance_type = InstanceType(instance_type_str)
|
320
|
+
except ValueError:
|
321
|
+
instance_type = InstanceType.ON_DEMAND
|
322
|
+
|
323
|
+
# Calculate total cost
|
324
|
+
cost_per_hour = kwargs.get("cost_per_hour", 0.0)
|
325
|
+
usage_hours = kwargs.get("usage_hours", 0.0)
|
326
|
+
total_cost = cost_per_hour * usage_hours
|
327
|
+
|
328
|
+
# Create cost metric
|
329
|
+
cost_metric = CostMetric(
|
330
|
+
timestamp=datetime.now(),
|
331
|
+
edge_node=kwargs.get("edge_node", "unknown"),
|
332
|
+
resource_type=kwargs.get("resource_type", "unknown"),
|
333
|
+
provider=provider,
|
334
|
+
instance_type=instance_type,
|
335
|
+
cost_per_hour=cost_per_hour,
|
336
|
+
usage_hours=usage_hours,
|
337
|
+
total_cost=total_cost,
|
338
|
+
currency=kwargs.get("currency", "USD"),
|
339
|
+
)
|
340
|
+
|
341
|
+
# Record cost
|
342
|
+
await self.optimizer.record_cost(cost_metric)
|
343
|
+
|
344
|
+
return {
|
345
|
+
"status": "success",
|
346
|
+
"cost_recorded": True,
|
347
|
+
"total_cost": total_cost,
|
348
|
+
"cost_metric": cost_metric.to_dict(),
|
349
|
+
}
|
350
|
+
|
351
|
+
async def _optimize_costs(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
352
|
+
"""Generate cost optimizations."""
|
353
|
+
# Parse strategy
|
354
|
+
strategy_str = kwargs.get("strategy", "balance_cost_performance")
|
355
|
+
try:
|
356
|
+
strategy = OptimizationStrategy(strategy_str)
|
357
|
+
except ValueError:
|
358
|
+
strategy = OptimizationStrategy.BALANCE_COST_PERFORMANCE
|
359
|
+
|
360
|
+
# Get optimization recommendations
|
361
|
+
optimizations = await self.optimizer.optimize_costs(
|
362
|
+
strategy=strategy, edge_nodes=kwargs.get("edge_nodes")
|
363
|
+
)
|
364
|
+
|
365
|
+
# Calculate total savings
|
366
|
+
total_savings = sum(opt.estimated_savings for opt in optimizations)
|
367
|
+
|
368
|
+
return {
|
369
|
+
"status": "success",
|
370
|
+
"optimizations": [opt.to_dict() for opt in optimizations],
|
371
|
+
"optimization_count": len(optimizations),
|
372
|
+
"total_savings": total_savings,
|
373
|
+
"average_savings_percentage": (
|
374
|
+
sum(opt.savings_percentage for opt in optimizations)
|
375
|
+
/ len(optimizations)
|
376
|
+
if optimizations
|
377
|
+
else 0
|
378
|
+
),
|
379
|
+
}
|
380
|
+
|
381
|
+
async def _get_spot_recommendations(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
382
|
+
"""Get spot instance recommendations."""
|
383
|
+
recommendations = await self.optimizer.get_spot_recommendations(
|
384
|
+
edge_nodes=kwargs.get("edge_nodes")
|
385
|
+
)
|
386
|
+
|
387
|
+
# Calculate total potential savings
|
388
|
+
total_savings = sum(rec.potential_savings for rec in recommendations)
|
389
|
+
|
390
|
+
return {
|
391
|
+
"status": "success",
|
392
|
+
"spot_recommendations": [rec.to_dict() for rec in recommendations],
|
393
|
+
"recommendation_count": len(recommendations),
|
394
|
+
"total_potential_savings": total_savings,
|
395
|
+
"average_savings_percentage": (
|
396
|
+
sum(
|
397
|
+
rec.potential_savings / rec.current_on_demand_cost * 100
|
398
|
+
for rec in recommendations
|
399
|
+
if rec.current_on_demand_cost > 0
|
400
|
+
)
|
401
|
+
/ len(recommendations)
|
402
|
+
if recommendations
|
403
|
+
else 0
|
404
|
+
),
|
405
|
+
}
|
406
|
+
|
407
|
+
async def _get_reservation_recommendations(
|
408
|
+
self, kwargs: Dict[str, Any]
|
409
|
+
) -> Dict[str, Any]:
|
410
|
+
"""Get reserved capacity recommendations."""
|
411
|
+
# Parse providers
|
412
|
+
provider_strs = kwargs.get("providers", [])
|
413
|
+
providers = []
|
414
|
+
|
415
|
+
for p_str in provider_strs:
|
416
|
+
try:
|
417
|
+
providers.append(CloudProvider(p_str))
|
418
|
+
except ValueError:
|
419
|
+
continue
|
420
|
+
|
421
|
+
recommendations = await self.optimizer.get_reservation_recommendations(
|
422
|
+
providers=providers if providers else None
|
423
|
+
)
|
424
|
+
|
425
|
+
# Calculate total savings
|
426
|
+
total_savings = sum(rec.total_savings for rec in recommendations)
|
427
|
+
|
428
|
+
return {
|
429
|
+
"status": "success",
|
430
|
+
"reservation_recommendations": [rec.to_dict() for rec in recommendations],
|
431
|
+
"recommendation_count": len(recommendations),
|
432
|
+
"total_potential_savings": total_savings,
|
433
|
+
"average_savings_percentage": (
|
434
|
+
sum(
|
435
|
+
rec.total_savings / rec.on_demand_equivalent * 100
|
436
|
+
for rec in recommendations
|
437
|
+
if rec.on_demand_equivalent > 0
|
438
|
+
)
|
439
|
+
/ len(recommendations)
|
440
|
+
if recommendations
|
441
|
+
else 0
|
442
|
+
),
|
443
|
+
}
|
444
|
+
|
445
|
+
async def _calculate_roi(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
446
|
+
"""Calculate ROI for an optimization."""
|
447
|
+
optimization_id = kwargs.get("optimization_id")
|
448
|
+
implementation_cost = kwargs.get("implementation_cost", 0.0)
|
449
|
+
|
450
|
+
if not optimization_id:
|
451
|
+
return {"status": "error", "error": "optimization_id is required"}
|
452
|
+
|
453
|
+
# Find the optimization
|
454
|
+
optimization = None
|
455
|
+
for opt in self.optimizer.optimizations:
|
456
|
+
if opt.optimization_id == optimization_id:
|
457
|
+
optimization = opt
|
458
|
+
break
|
459
|
+
|
460
|
+
if not optimization:
|
461
|
+
return {
|
462
|
+
"status": "error",
|
463
|
+
"error": f"Optimization {optimization_id} not found",
|
464
|
+
}
|
465
|
+
|
466
|
+
# Calculate ROI
|
467
|
+
roi_analysis = await self.optimizer.calculate_roi(
|
468
|
+
optimization=optimization, implementation_cost=implementation_cost
|
469
|
+
)
|
470
|
+
|
471
|
+
return {"status": "success", "roi_analysis": roi_analysis}
|
472
|
+
|
473
|
+
async def _get_cost_forecast(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
|
474
|
+
"""Get cost forecast."""
|
475
|
+
forecast = await self.optimizer.get_cost_forecast(
|
476
|
+
forecast_months=kwargs.get("forecast_months", 12),
|
477
|
+
include_optimizations=kwargs.get("include_optimizations", True),
|
478
|
+
)
|
479
|
+
|
480
|
+
return {"status": "success", "cost_forecast": forecast}
|
481
|
+
|
482
|
+
async def _start_optimizer(self) -> Dict[str, Any]:
|
483
|
+
"""Start background optimizer."""
|
484
|
+
if not self._optimizer_started:
|
485
|
+
await self.optimizer.start()
|
486
|
+
self._optimizer_started = True
|
487
|
+
|
488
|
+
return {"status": "success", "optimizer_active": True}
|
489
|
+
|
490
|
+
async def _stop_optimizer(self) -> Dict[str, Any]:
|
491
|
+
"""Stop background optimizer."""
|
492
|
+
if self._optimizer_started:
|
493
|
+
await self.optimizer.stop()
|
494
|
+
self._optimizer_started = False
|
495
|
+
|
496
|
+
return {"status": "success", "optimizer_active": False}
|
497
|
+
|
498
|
+
async def cleanup(self):
|
499
|
+
"""Clean up resources."""
|
500
|
+
if self._optimizer_started:
|
501
|
+
await self.optimizer.stop()
|