omnimancer-cli 0.1.0__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 (126) hide show
  1. omnimancer/__init__.py +42 -0
  2. omnimancer/__main__.py +9 -0
  3. omnimancer/cli/__init__.py +8 -0
  4. omnimancer/cli/agent_manager.py +826 -0
  5. omnimancer/cli/agent_wizard.py +944 -0
  6. omnimancer/cli/approval_formatter.py +734 -0
  7. omnimancer/cli/approval_integration.py +734 -0
  8. omnimancer/cli/approval_prompt.py +1286 -0
  9. omnimancer/cli/batch_approval_display.py +617 -0
  10. omnimancer/cli/batch_approval_filters.py +729 -0
  11. omnimancer/cli/batch_operation_monitor.py +775 -0
  12. omnimancer/cli/commands/performance_cmd.py +624 -0
  13. omnimancer/cli/commands.py +757 -0
  14. omnimancer/cli/completion_handler.py +281 -0
  15. omnimancer/cli/config_setup_wizard.py +783 -0
  16. omnimancer/cli/dynamic_commands.py +371 -0
  17. omnimancer/cli/handlers/__init__.py +18 -0
  18. omnimancer/cli/handlers/agent_handler.py +865 -0
  19. omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
  20. omnimancer/cli/handlers/custom_agent_handler.py +352 -0
  21. omnimancer/cli/handlers/permissions_handler.py +1609 -0
  22. omnimancer/cli/interface.py +6695 -0
  23. omnimancer/cli/performance_dashboard.py +612 -0
  24. omnimancer/core/__init__.py +34 -0
  25. omnimancer/core/agent/__init__.py +256 -0
  26. omnimancer/core/agent/agent_config.py +715 -0
  27. omnimancer/core/agent/agent_switcher.py +668 -0
  28. omnimancer/core/agent/approval_context.py +460 -0
  29. omnimancer/core/agent/approval_dialog.py +902 -0
  30. omnimancer/core/agent/approval_interface.py +628 -0
  31. omnimancer/core/agent/approval_manager.py +760 -0
  32. omnimancer/core/agent/cli_integration.py +470 -0
  33. omnimancer/core/agent/config.py +845 -0
  34. omnimancer/core/agent/custom_agent_integration.py +654 -0
  35. omnimancer/core/agent/diff_renderer.py +706 -0
  36. omnimancer/core/agent/file_content_display.py +503 -0
  37. omnimancer/core/agent/file_modification_workflow.py +658 -0
  38. omnimancer/core/agent/file_system_manager.py +1790 -0
  39. omnimancer/core/agent/input_handler.py +614 -0
  40. omnimancer/core/agent/metrics_collector.py +771 -0
  41. omnimancer/core/agent/optimization_engine.py +729 -0
  42. omnimancer/core/agent/performance_monitor.py +717 -0
  43. omnimancer/core/agent/persona.py +870 -0
  44. omnimancer/core/agent/persona_validator.py +672 -0
  45. omnimancer/core/agent/program_executor.py +748 -0
  46. omnimancer/core/agent/proposed_changes_integration.py +663 -0
  47. omnimancer/core/agent/read_before_write_errors.py +492 -0
  48. omnimancer/core/agent/read_before_write_ui.py +647 -0
  49. omnimancer/core/agent/rich_renderer.py +763 -0
  50. omnimancer/core/agent/status_config.py +238 -0
  51. omnimancer/core/agent/status_core.py +446 -0
  52. omnimancer/core/agent/status_integrated_engine.py +540 -0
  53. omnimancer/core/agent/status_manager.py +878 -0
  54. omnimancer/core/agent/status_ui_components.py +535 -0
  55. omnimancer/core/agent/template_applicator.py +548 -0
  56. omnimancer/core/agent/terminal_status_listener.py +394 -0
  57. omnimancer/core/agent/token_tracker.py +679 -0
  58. omnimancer/core/agent/types.py +60 -0
  59. omnimancer/core/agent/web_client.py +745 -0
  60. omnimancer/core/agent/workflow_orchestrator.py +826 -0
  61. omnimancer/core/agent_engine.py +1625 -0
  62. omnimancer/core/agent_mode_manager.py +706 -0
  63. omnimancer/core/agent_progress_ui.py +545 -0
  64. omnimancer/core/chat_manager.py +141 -0
  65. omnimancer/core/config_manager.py +2199 -0
  66. omnimancer/core/config_migration.py +772 -0
  67. omnimancer/core/config_provider.py +1974 -0
  68. omnimancer/core/config_security_validator.py +393 -0
  69. omnimancer/core/config_validator.py +768 -0
  70. omnimancer/core/conversation_manager.py +352 -0
  71. omnimancer/core/engine.py +787 -0
  72. omnimancer/core/enhanced_input.py +523 -0
  73. omnimancer/core/env_loader.py +83 -0
  74. omnimancer/core/fallback_manager.py +568 -0
  75. omnimancer/core/health_monitor.py +462 -0
  76. omnimancer/core/history_manager.py +462 -0
  77. omnimancer/core/mcp_integration_layer.py +878 -0
  78. omnimancer/core/models.py +1889 -0
  79. omnimancer/core/provider_capabilities.py +403 -0
  80. omnimancer/core/provider_initializer.py +450 -0
  81. omnimancer/core/provider_registry.py +432 -0
  82. omnimancer/core/security/__init__.py +16 -0
  83. omnimancer/core/security/approval_workflow.py +406 -0
  84. omnimancer/core/security/audit_logger.py +511 -0
  85. omnimancer/core/security/permission_controller.py +511 -0
  86. omnimancer/core/security/sandbox_manager.py +379 -0
  87. omnimancer/core/security/security_manager.py +530 -0
  88. omnimancer/core/setup_wizard.py +345 -0
  89. omnimancer/core/setup_wizard_core.py +449 -0
  90. omnimancer/core/setup_wizard_guide_generator.py +376 -0
  91. omnimancer/core/setup_wizard_provider_setup.py +454 -0
  92. omnimancer/core/setup_wizard_ui.py +317 -0
  93. omnimancer/core/setup_wizard_validation.py +371 -0
  94. omnimancer/core/signal_handler.py +174 -0
  95. omnimancer/mcp/__init__.py +11 -0
  96. omnimancer/mcp/client.py +725 -0
  97. omnimancer/mcp/manager.py +649 -0
  98. omnimancer/providers/__init__.py +24 -0
  99. omnimancer/providers/azure.py +608 -0
  100. omnimancer/providers/base.py +262 -0
  101. omnimancer/providers/bedrock.py +711 -0
  102. omnimancer/providers/claude.py +426 -0
  103. omnimancer/providers/claude_code.py +406 -0
  104. omnimancer/providers/cohere.py +412 -0
  105. omnimancer/providers/factory.py +988 -0
  106. omnimancer/providers/gemini.py +708 -0
  107. omnimancer/providers/mistral.py +554 -0
  108. omnimancer/providers/ollama.py +510 -0
  109. omnimancer/providers/openai.py +362 -0
  110. omnimancer/providers/openrouter.py +709 -0
  111. omnimancer/providers/perplexity.py +476 -0
  112. omnimancer/providers/vertex.py +617 -0
  113. omnimancer/providers/xai.py +484 -0
  114. omnimancer/ui/__init__.py +6 -0
  115. omnimancer/ui/cancellation_handler.py +235 -0
  116. omnimancer/ui/progress_indicator.py +318 -0
  117. omnimancer/utils/__init__.py +22 -0
  118. omnimancer/utils/error_handler.py +589 -0
  119. omnimancer/utils/errors.py +667 -0
  120. omnimancer/utils/retry.py +238 -0
  121. omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
  122. omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
  123. omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
  124. omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
  125. omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
  126. omnimancer_cli-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,729 @@
1
+ """
2
+ Optimization Engine and Alert System for Omnimancer Agent Performance.
3
+
4
+ This module provides intelligent optimization suggestions and proactive alerting
5
+ based on performance patterns, resource usage, and cost analysis.
6
+ """
7
+
8
+ import logging
9
+ import statistics
10
+ import threading
11
+ import time
12
+ from dataclasses import dataclass
13
+ from datetime import datetime, timedelta
14
+ from enum import Enum
15
+ from typing import Any, Callable, Dict, List, Optional
16
+
17
+ from .metrics_collector import (
18
+ AggregationMethod,
19
+ PerformanceMetricsCollector,
20
+ get_metrics_collector,
21
+ )
22
+ from .performance_monitor import (
23
+ AlertLevel,
24
+ OptimizationSuggestion,
25
+ OptimizationType,
26
+ PerformanceAlert,
27
+ PerformanceMonitor,
28
+ get_performance_monitor,
29
+ )
30
+ from .persona import get_persona_manager
31
+ from .token_tracker import TokenUsageTracker, get_token_tracker
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class OptimizationCategory(Enum):
37
+ """Categories of optimizations."""
38
+
39
+ COST_REDUCTION = "cost_reduction"
40
+ PERFORMANCE_IMPROVEMENT = "performance_improvement"
41
+ RESOURCE_EFFICIENCY = "resource_efficiency"
42
+ RELIABILITY_ENHANCEMENT = "reliability_enhancement"
43
+ USER_EXPERIENCE = "user_experience"
44
+
45
+
46
+ class AlertTrigger(Enum):
47
+ """Types of alert triggers."""
48
+
49
+ THRESHOLD_EXCEEDED = "threshold_exceeded"
50
+ TREND_DETECTION = "trend_detection"
51
+ ANOMALY_DETECTION = "anomaly_detection"
52
+ PATTERN_RECOGNITION = "pattern_recognition"
53
+ COST_SPIKE = "cost_spike"
54
+
55
+
56
+ @dataclass
57
+ class OptimizationRule:
58
+ """Rule for generating optimization suggestions."""
59
+
60
+ rule_id: str
61
+ name: str
62
+ category: OptimizationCategory
63
+ description: str
64
+ condition_check: Callable[[Dict[str, Any]], bool]
65
+ suggestion_generator: Callable[[Dict[str, Any]], OptimizationSuggestion]
66
+ priority: int = 50 # 0-100, higher is more important
67
+ enabled: bool = True
68
+
69
+ def evaluate(self, context: Dict[str, Any]) -> Optional[OptimizationSuggestion]:
70
+ """Evaluate rule and generate suggestion if conditions are met."""
71
+ if not self.enabled:
72
+ return None
73
+
74
+ try:
75
+ if self.condition_check(context):
76
+ return self.suggestion_generator(context)
77
+ except Exception as e:
78
+ logger.error(f"Error evaluating optimization rule {self.rule_id}: {e}")
79
+
80
+ return None
81
+
82
+
83
+ @dataclass
84
+ class AlertRule:
85
+ """Rule for generating performance alerts."""
86
+
87
+ rule_id: str
88
+ name: str
89
+ metric_name: str
90
+ trigger_type: AlertTrigger
91
+ threshold_value: float
92
+ comparison_operator: str # "gt", "lt", "eq", "gte", "lte"
93
+ alert_level: AlertLevel
94
+ time_window: timedelta
95
+ min_samples: int = 1
96
+ enabled: bool = True
97
+ cooldown_period: timedelta = timedelta(minutes=15)
98
+ last_triggered: Optional[datetime] = None
99
+
100
+ def check_condition(self, current_value: float, context: Dict[str, Any]) -> bool:
101
+ """Check if alert condition is met."""
102
+ if not self.enabled:
103
+ return False
104
+
105
+ # Check cooldown period
106
+ if (
107
+ self.last_triggered
108
+ and datetime.now() - self.last_triggered < self.cooldown_period
109
+ ):
110
+ return False
111
+
112
+ # Evaluate condition
113
+ if self.comparison_operator == "gt":
114
+ return current_value > self.threshold_value
115
+ elif self.comparison_operator == "lt":
116
+ return current_value < self.threshold_value
117
+ elif self.comparison_operator == "eq":
118
+ return abs(current_value - self.threshold_value) < 0.001
119
+ elif self.comparison_operator == "gte":
120
+ return current_value >= self.threshold_value
121
+ elif self.comparison_operator == "lte":
122
+ return current_value <= self.threshold_value
123
+
124
+ return False
125
+
126
+ def trigger_alert(
127
+ self, current_value: float, context: Dict[str, Any]
128
+ ) -> PerformanceAlert:
129
+ """Generate alert when rule is triggered."""
130
+ self.last_triggered = datetime.now()
131
+
132
+ return PerformanceAlert(
133
+ alert_id=f"alert_{self.rule_id}_{int(time.time() * 1000)}",
134
+ level=self.alert_level,
135
+ title=self.name,
136
+ description=f"Metric '{self.metric_name}' {self.comparison_operator} {self.threshold_value}",
137
+ metric_type=context.get("metric_type", "unknown"),
138
+ threshold_value=self.threshold_value,
139
+ current_value=current_value,
140
+ provider_name=context.get("provider_name"),
141
+ persona_name=context.get("persona_name"),
142
+ suggested_actions=self._generate_suggested_actions(current_value, context),
143
+ )
144
+
145
+ def _generate_suggested_actions(
146
+ self, current_value: float, context: Dict[str, Any]
147
+ ) -> List[str]:
148
+ """Generate suggested actions based on the alert."""
149
+ actions = []
150
+
151
+ if "token" in self.metric_name.lower():
152
+ actions.extend(
153
+ [
154
+ "Review recent token usage patterns",
155
+ "Consider switching to a more efficient model",
156
+ "Implement response caching for repeated queries",
157
+ ]
158
+ )
159
+ elif "response_time" in self.metric_name.lower():
160
+ actions.extend(
161
+ [
162
+ "Check network connectivity",
163
+ "Consider switching to a faster provider",
164
+ "Review context size and complexity",
165
+ ]
166
+ )
167
+ elif "cpu" in self.metric_name.lower() or "memory" in self.metric_name.lower():
168
+ actions.extend(
169
+ [
170
+ "Monitor system resource usage",
171
+ "Consider reducing concurrent operations",
172
+ "Review background processes",
173
+ ]
174
+ )
175
+
176
+ return actions
177
+
178
+
179
+ class TrendAnalyzer:
180
+ """Analyzes trends in performance metrics."""
181
+
182
+ def __init__(self, min_data_points: int = 10):
183
+ """Initialize trend analyzer."""
184
+ self.min_data_points = min_data_points
185
+
186
+ def detect_trend(
187
+ self, values: List[float], timestamps: List[datetime]
188
+ ) -> Dict[str, Any]:
189
+ """Detect trend in time series data."""
190
+ if len(values) < self.min_data_points:
191
+ return {"trend": "insufficient_data", "confidence": 0.0}
192
+
193
+ # Simple linear regression to detect trend
194
+ try:
195
+ # Convert timestamps to numeric values (seconds since first timestamp)
196
+ time_numeric = [(t - timestamps[0]).total_seconds() for t in timestamps]
197
+
198
+ # Calculate trend using linear regression
199
+ n = len(values)
200
+ sum_x = sum(time_numeric)
201
+ sum_y = sum(values)
202
+ sum_xy = sum(x * y for x, y in zip(time_numeric, values))
203
+ sum_x_squared = sum(x * x for x in time_numeric)
204
+
205
+ # Calculate slope (trend direction)
206
+ slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x_squared - sum_x * sum_x)
207
+
208
+ # Calculate correlation coefficient for confidence
209
+ mean_x = sum_x / n
210
+ mean_y = sum_y / n
211
+
212
+ numerator = sum(
213
+ (x - mean_x) * (y - mean_y) for x, y in zip(time_numeric, values)
214
+ )
215
+ denom_x = sum((x - mean_x) ** 2 for x in time_numeric)
216
+ denom_y = sum((y - mean_y) ** 2 for y in values)
217
+
218
+ correlation = (
219
+ numerator / (denom_x * denom_y) ** 0.5 if denom_x * denom_y > 0 else 0
220
+ )
221
+
222
+ # Determine trend direction
223
+ if abs(slope) < 0.001: # Threshold for "no trend"
224
+ trend = "stable"
225
+ elif slope > 0:
226
+ trend = "increasing"
227
+ else:
228
+ trend = "decreasing"
229
+
230
+ return {
231
+ "trend": trend,
232
+ "slope": slope,
233
+ "confidence": abs(correlation),
234
+ "correlation": correlation,
235
+ }
236
+
237
+ except (ValueError, ZeroDivisionError):
238
+ return {"trend": "unknown", "confidence": 0.0}
239
+
240
+ def detect_anomalies(
241
+ self, values: List[float], sensitivity: float = 2.0
242
+ ) -> List[int]:
243
+ """Detect anomalies using statistical methods."""
244
+ if len(values) < 3:
245
+ return []
246
+
247
+ # Use simple statistical outlier detection
248
+ mean_val = statistics.mean(values)
249
+ std_dev = statistics.stdev(values) if len(values) > 1 else 0
250
+
251
+ if std_dev == 0:
252
+ return []
253
+
254
+ anomalies = []
255
+ for i, value in enumerate(values):
256
+ z_score = abs(value - mean_val) / std_dev
257
+ if z_score > sensitivity:
258
+ anomalies.append(i)
259
+
260
+ return anomalies
261
+
262
+
263
+ class OptimizationEngine:
264
+ """Main optimization engine with rule-based suggestions and alerting."""
265
+
266
+ def __init__(
267
+ self,
268
+ performance_monitor: Optional[PerformanceMonitor] = None,
269
+ token_tracker: Optional[TokenUsageTracker] = None,
270
+ metrics_collector: Optional[PerformanceMetricsCollector] = None,
271
+ ):
272
+ """Initialize optimization engine."""
273
+ self.performance_monitor = performance_monitor or get_performance_monitor()
274
+ self.token_tracker = token_tracker or get_token_tracker()
275
+ self.metrics_collector = metrics_collector or get_metrics_collector()
276
+ self.persona_manager = get_persona_manager()
277
+
278
+ # Rule engines
279
+ self.optimization_rules: Dict[str, OptimizationRule] = {}
280
+ self.alert_rules: Dict[str, AlertRule] = {}
281
+
282
+ # Analysis components
283
+ self.trend_analyzer = TrendAnalyzer()
284
+
285
+ # State
286
+ self.is_running = False
287
+ self.analysis_thread: Optional[threading.Thread] = None
288
+ self.analysis_interval = 60.0 # seconds
289
+
290
+ # Event handlers
291
+ self.optimization_handlers: List[Callable] = []
292
+ self.alert_handlers: List[Callable] = []
293
+
294
+ self._initialize_default_rules()
295
+ logger.info("Optimization engine initialized")
296
+
297
+ def _initialize_default_rules(self) -> None:
298
+ """Initialize default optimization and alert rules."""
299
+ # Cost optimization rules
300
+ self.add_optimization_rule(
301
+ OptimizationRule(
302
+ rule_id="high_cost_provider",
303
+ name="High Cost Provider Usage",
304
+ category=OptimizationCategory.COST_REDUCTION,
305
+ description="Detects heavy usage of expensive providers",
306
+ condition_check=self._check_high_cost_provider,
307
+ suggestion_generator=self._suggest_cost_optimization,
308
+ priority=80,
309
+ )
310
+ )
311
+
312
+ self.add_optimization_rule(
313
+ OptimizationRule(
314
+ rule_id="inefficient_token_usage",
315
+ name="Inefficient Token Usage",
316
+ category=OptimizationCategory.COST_REDUCTION,
317
+ description="Detects patterns of inefficient token usage",
318
+ condition_check=self._check_inefficient_tokens,
319
+ suggestion_generator=self._suggest_token_optimization,
320
+ priority=70,
321
+ )
322
+ )
323
+
324
+ # Performance optimization rules
325
+ self.add_optimization_rule(
326
+ OptimizationRule(
327
+ rule_id="slow_response_times",
328
+ name="Slow Response Times",
329
+ category=OptimizationCategory.PERFORMANCE_IMPROVEMENT,
330
+ description="Detects consistently slow response times",
331
+ condition_check=self._check_slow_responses,
332
+ suggestion_generator=self._suggest_performance_improvement,
333
+ priority=75,
334
+ )
335
+ )
336
+
337
+ # Alert rules
338
+ self.add_alert_rule(
339
+ AlertRule(
340
+ rule_id="excessive_token_usage",
341
+ name="Excessive Token Usage",
342
+ metric_name="agent.tokens_per_minute",
343
+ trigger_type=AlertTrigger.THRESHOLD_EXCEEDED,
344
+ threshold_value=5000,
345
+ comparison_operator="gt",
346
+ alert_level=AlertLevel.WARNING,
347
+ time_window=timedelta(minutes=5),
348
+ )
349
+ )
350
+
351
+ self.add_alert_rule(
352
+ AlertRule(
353
+ rule_id="high_cost_rate",
354
+ name="High Cost Rate",
355
+ metric_name="agent.cost_per_minute",
356
+ trigger_type=AlertTrigger.THRESHOLD_EXCEEDED,
357
+ threshold_value=1.0,
358
+ comparison_operator="gt",
359
+ alert_level=AlertLevel.CRITICAL,
360
+ time_window=timedelta(minutes=5),
361
+ )
362
+ )
363
+
364
+ self.add_alert_rule(
365
+ AlertRule(
366
+ rule_id="slow_response_alert",
367
+ name="Slow Response Times",
368
+ metric_name="operation.duration_ms",
369
+ trigger_type=AlertTrigger.THRESHOLD_EXCEEDED,
370
+ threshold_value=10000,
371
+ comparison_operator="gt",
372
+ alert_level=AlertLevel.WARNING,
373
+ time_window=timedelta(minutes=10),
374
+ )
375
+ )
376
+
377
+ def add_optimization_rule(self, rule: OptimizationRule) -> None:
378
+ """Add optimization rule."""
379
+ self.optimization_rules[rule.rule_id] = rule
380
+ logger.debug(f"Added optimization rule: {rule.name}")
381
+
382
+ def add_alert_rule(self, rule: AlertRule) -> None:
383
+ """Add alert rule."""
384
+ self.alert_rules[rule.rule_id] = rule
385
+ logger.debug(f"Added alert rule: {rule.name}")
386
+
387
+ def start_analysis(self) -> None:
388
+ """Start continuous optimization analysis."""
389
+ if self.is_running:
390
+ logger.warning("Optimization engine is already running")
391
+ return
392
+
393
+ self.is_running = True
394
+ self.analysis_thread = threading.Thread(
395
+ target=self._analysis_loop,
396
+ name="OptimizationAnalysis",
397
+ daemon=True,
398
+ )
399
+ self.analysis_thread.start()
400
+ logger.info("Optimization analysis started")
401
+
402
+ def stop_analysis(self) -> None:
403
+ """Stop continuous optimization analysis."""
404
+ if not self.is_running:
405
+ return
406
+
407
+ self.is_running = False
408
+ if self.analysis_thread and self.analysis_thread.is_alive():
409
+ self.analysis_thread.join(timeout=5.0)
410
+ logger.info("Optimization analysis stopped")
411
+
412
+ def _analysis_loop(self) -> None:
413
+ """Main analysis loop."""
414
+ while self.is_running:
415
+ try:
416
+ self._run_analysis_cycle()
417
+ time.sleep(self.analysis_interval)
418
+ except Exception as e:
419
+ logger.error(f"Error in optimization analysis loop: {e}")
420
+ time.sleep(self.analysis_interval)
421
+
422
+ def _run_analysis_cycle(self) -> None:
423
+ """Run a single analysis cycle."""
424
+ # Gather context data
425
+ context = self._gather_analysis_context()
426
+
427
+ # Check optimization rules
428
+ suggestions = self._evaluate_optimization_rules(context)
429
+
430
+ # Check alert rules
431
+ alerts = self._evaluate_alert_rules(context)
432
+
433
+ # Process suggestions
434
+ for suggestion in suggestions:
435
+ self._handle_optimization_suggestion(suggestion)
436
+
437
+ # Process alerts
438
+ for alert in alerts:
439
+ self._handle_performance_alert(alert)
440
+
441
+ def _gather_analysis_context(self) -> Dict[str, Any]:
442
+ """Gather context data for analysis."""
443
+ context = {}
444
+
445
+ try:
446
+ # Performance dashboard data
447
+ if self.performance_monitor:
448
+ context["dashboard_data"] = (
449
+ self.performance_monitor.get_performance_dashboard_data()
450
+ )
451
+
452
+ # Token usage data
453
+ if self.token_tracker:
454
+ context["token_summary_1h"] = self.token_tracker.get_usage_summary(
455
+ timedelta(hours=1)
456
+ )
457
+ context["token_summary_24h"] = self.token_tracker.get_usage_summary(
458
+ timedelta(hours=24)
459
+ )
460
+ context["usage_patterns"] = self.token_tracker.analyze_usage_patterns(7)
461
+
462
+ # Metrics data
463
+ if self.metrics_collector:
464
+ context["metrics_data"] = self.metrics_collector.get_dashboard_metrics()
465
+
466
+ # Current timestamp
467
+ context["analysis_time"] = datetime.now()
468
+
469
+ except Exception as e:
470
+ logger.error(f"Error gathering analysis context: {e}")
471
+
472
+ return context
473
+
474
+ def _evaluate_optimization_rules(
475
+ self, context: Dict[str, Any]
476
+ ) -> List[OptimizationSuggestion]:
477
+ """Evaluate all optimization rules."""
478
+ suggestions = []
479
+
480
+ for rule in self.optimization_rules.values():
481
+ try:
482
+ suggestion = rule.evaluate(context)
483
+ if suggestion:
484
+ suggestions.append(suggestion)
485
+ except Exception as e:
486
+ logger.error(f"Error evaluating optimization rule {rule.rule_id}: {e}")
487
+
488
+ return suggestions
489
+
490
+ def _evaluate_alert_rules(self, context: Dict[str, Any]) -> List[PerformanceAlert]:
491
+ """Evaluate all alert rules."""
492
+ alerts = []
493
+
494
+ for rule in self.alert_rules.values():
495
+ try:
496
+ # Get current metric value
497
+ metric_summary = self.metrics_collector.get_metric_summary(
498
+ rule.metric_name,
499
+ rule.time_window,
500
+ AggregationMethod.AVERAGE,
501
+ )
502
+
503
+ if metric_summary and rule.check_condition(
504
+ metric_summary.value, context
505
+ ):
506
+ alert = rule.trigger_alert(metric_summary.value, context)
507
+ alerts.append(alert)
508
+
509
+ except Exception as e:
510
+ logger.error(f"Error evaluating alert rule {rule.rule_id}: {e}")
511
+
512
+ return alerts
513
+
514
+ def _handle_optimization_suggestion(
515
+ self, suggestion: OptimizationSuggestion
516
+ ) -> None:
517
+ """Handle optimization suggestion."""
518
+ # Add to performance monitor
519
+ if self.performance_monitor:
520
+ self.performance_monitor.suggestions.append(suggestion)
521
+
522
+ # Notify handlers
523
+ for handler in self.optimization_handlers:
524
+ try:
525
+ handler(suggestion)
526
+ except Exception as e:
527
+ logger.error(f"Error in optimization handler: {e}")
528
+
529
+ logger.info(f"Generated optimization suggestion: {suggestion.title}")
530
+
531
+ def _handle_performance_alert(self, alert: PerformanceAlert) -> None:
532
+ """Handle performance alert."""
533
+ # Add to performance monitor
534
+ if self.performance_monitor:
535
+ self.performance_monitor.alerts.append(alert)
536
+
537
+ # Notify handlers
538
+ for handler in self.alert_handlers:
539
+ try:
540
+ handler(alert)
541
+ except Exception as e:
542
+ logger.error(f"Error in alert handler: {e}")
543
+
544
+ logger.warning(f"Generated performance alert: {alert.title}")
545
+
546
+ # Optimization rule condition checkers
547
+ def _check_high_cost_provider(self, context: Dict[str, Any]) -> bool:
548
+ """Check for high cost provider usage."""
549
+ token_summary = context.get("token_summary_24h", {})
550
+ provider_breakdown = token_summary.get("provider_breakdown", {})
551
+
552
+ for provider, stats in provider_breakdown.items():
553
+ if stats.get("cost", 0) > 5.0: # More than $5 in 24 hours
554
+ return True
555
+
556
+ return False
557
+
558
+ def _check_inefficient_tokens(self, context: Dict[str, Any]) -> bool:
559
+ """Check for inefficient token usage patterns."""
560
+ token_summary = context.get("token_summary_1h", {})
561
+ avg_tokens_per_request = token_summary.get("average_tokens_per_request", 0)
562
+
563
+ # Alert if average request uses more than 2000 tokens
564
+ return avg_tokens_per_request > 2000
565
+
566
+ def _check_slow_responses(self, context: Dict[str, Any]) -> bool:
567
+ """Check for slow response times."""
568
+ metrics_data = context.get("metrics_data", {})
569
+ performance = metrics_data.get("performance", {})
570
+ avg_response_time = performance.get("avg_response_time_ms", 0)
571
+
572
+ # Alert if average response time is over 5 seconds
573
+ return avg_response_time > 5000
574
+
575
+ # Optimization suggestion generators
576
+ def _suggest_cost_optimization(
577
+ self, context: Dict[str, Any]
578
+ ) -> OptimizationSuggestion:
579
+ """Generate cost optimization suggestion."""
580
+ token_summary = context.get("token_summary_24h", {})
581
+ total_cost = token_summary.get("total_cost", 0)
582
+
583
+ return OptimizationSuggestion(
584
+ suggestion_id=f"cost_opt_{int(time.time())}",
585
+ optimization_type=OptimizationType.COST_OPTIMIZATION,
586
+ title="Reduce Provider Costs",
587
+ description=f"High daily cost of ${total_cost:.2f} detected",
588
+ potential_improvement="30-50% cost reduction",
589
+ confidence=0.8,
590
+ implementation_difficulty="medium",
591
+ estimated_impact={"cost_savings": total_cost * 0.4},
592
+ required_actions=[
593
+ "Switch to more cost-effective models for routine tasks",
594
+ "Implement response caching",
595
+ "Optimize context sizes",
596
+ ],
597
+ )
598
+
599
+ def _suggest_token_optimization(
600
+ self, context: Dict[str, Any]
601
+ ) -> OptimizationSuggestion:
602
+ """Generate token usage optimization suggestion."""
603
+ token_summary = context.get("token_summary_1h", {})
604
+ avg_tokens = token_summary.get("average_tokens_per_request", 0)
605
+
606
+ return OptimizationSuggestion(
607
+ suggestion_id=f"token_opt_{int(time.time())}",
608
+ optimization_type=OptimizationType.CONTEXT_REDUCTION,
609
+ title="Optimize Token Usage",
610
+ description=f"High average of {avg_tokens:.0f} tokens per request",
611
+ potential_improvement="20-40% token reduction",
612
+ confidence=0.75,
613
+ implementation_difficulty="easy",
614
+ estimated_impact={"token_savings": avg_tokens * 0.3},
615
+ required_actions=[
616
+ "Review and reduce context sizes",
617
+ "Implement context compression",
618
+ "Use more efficient prompting strategies",
619
+ ],
620
+ )
621
+
622
+ def _suggest_performance_improvement(
623
+ self, context: Dict[str, Any]
624
+ ) -> OptimizationSuggestion:
625
+ """Generate performance improvement suggestion."""
626
+ metrics_data = context.get("metrics_data", {})
627
+ performance = metrics_data.get("performance", {})
628
+ response_time = performance.get("avg_response_time_ms", 0)
629
+
630
+ return OptimizationSuggestion(
631
+ suggestion_id=f"perf_opt_{int(time.time())}",
632
+ optimization_type=OptimizationType.PROVIDER_SWITCHING,
633
+ title="Improve Response Times",
634
+ description=f"Slow average response time of {response_time:.0f}ms",
635
+ potential_improvement="40-60% faster responses",
636
+ confidence=0.7,
637
+ implementation_difficulty="medium",
638
+ estimated_impact={"response_time_improvement": response_time * 0.5},
639
+ required_actions=[
640
+ "Switch to faster providers",
641
+ "Optimize network connectivity",
642
+ "Reduce context complexity",
643
+ ],
644
+ )
645
+
646
+ def add_optimization_handler(
647
+ self, handler: Callable[[OptimizationSuggestion], None]
648
+ ) -> None:
649
+ """Add optimization suggestion handler."""
650
+ self.optimization_handlers.append(handler)
651
+
652
+ def add_alert_handler(self, handler: Callable[[PerformanceAlert], None]) -> None:
653
+ """Add alert handler."""
654
+ self.alert_handlers.append(handler)
655
+
656
+ def get_current_suggestions(
657
+ self, category: Optional[OptimizationCategory] = None
658
+ ) -> List[OptimizationSuggestion]:
659
+ """Get current optimization suggestions."""
660
+ if not self.performance_monitor:
661
+ return []
662
+
663
+ suggestions = self.performance_monitor.suggestions
664
+
665
+ if category:
666
+ # Filter suggestions would require additional logic since OptimizationSuggestion
667
+ # doesn't have category field in the original design
668
+ pass
669
+
670
+ # Return recent suggestions (last 24 hours)
671
+ cutoff_time = datetime.now() - timedelta(hours=24)
672
+ return [s for s in suggestions if s.timestamp >= cutoff_time]
673
+
674
+ def get_active_alerts(
675
+ self, level: Optional[AlertLevel] = None
676
+ ) -> List[PerformanceAlert]:
677
+ """Get active performance alerts."""
678
+ if not self.performance_monitor:
679
+ return []
680
+
681
+ alerts = self.performance_monitor.alerts
682
+
683
+ if level:
684
+ alerts = [a for a in alerts if a.level == level]
685
+
686
+ # Return recent alerts (last 6 hours)
687
+ cutoff_time = datetime.now() - timedelta(hours=6)
688
+ return [a for a in alerts if a.timestamp >= cutoff_time]
689
+
690
+
691
+ # Global optimization engine instance
692
+ _optimization_engine: Optional[OptimizationEngine] = None
693
+ _engine_lock = threading.Lock()
694
+
695
+
696
+ def get_optimization_engine() -> OptimizationEngine:
697
+ """Get the global optimization engine instance."""
698
+ global _optimization_engine
699
+ if _optimization_engine is None:
700
+ with _engine_lock:
701
+ if _optimization_engine is None:
702
+ _optimization_engine = OptimizationEngine()
703
+ return _optimization_engine
704
+
705
+
706
+ def set_optimization_engine(engine: OptimizationEngine) -> None:
707
+ """Set the global optimization engine instance."""
708
+ global _optimization_engine
709
+ with _engine_lock:
710
+ _optimization_engine = engine
711
+
712
+
713
+ def initialize_optimization_engine(
714
+ auto_start: bool = True,
715
+ ) -> OptimizationEngine:
716
+ """Initialize optimization engine."""
717
+ engine = get_optimization_engine()
718
+ if auto_start:
719
+ engine.start_analysis()
720
+ return engine
721
+
722
+
723
+ def shutdown_optimization_engine() -> None:
724
+ """Shutdown optimization engine."""
725
+ global _optimization_engine
726
+ if _optimization_engine:
727
+ _optimization_engine.stop_analysis()
728
+ with _engine_lock:
729
+ _optimization_engine = None