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,612 @@
1
+ """
2
+ Performance Dashboard for Omnimancer Agent Monitoring.
3
+
4
+ This module provides a rich CLI interface for viewing agent performance metrics,
5
+ token usage, resource utilization, and optimization suggestions.
6
+ """
7
+
8
+ import json
9
+ import logging
10
+ import time
11
+ from datetime import datetime, timedelta
12
+ from pathlib import Path
13
+ from typing import Dict, List, Optional
14
+
15
+ from rich import box
16
+ from rich.align import Align
17
+ from rich.console import Console
18
+ from rich.layout import Layout
19
+ from rich.live import Live
20
+ from rich.panel import Panel
21
+ from rich.rule import Rule
22
+ from rich.table import Table
23
+ from rich.text import Text
24
+
25
+ from ..core.agent.metrics_collector import (
26
+ get_metrics_collector,
27
+ )
28
+ from ..core.agent.performance_monitor import (
29
+ get_performance_monitor,
30
+ )
31
+ from ..core.agent.token_tracker import (
32
+ get_token_tracker,
33
+ )
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class PerformanceDashboard:
39
+ """Interactive performance dashboard for Omnimancer agents."""
40
+
41
+ def __init__(self):
42
+ """Initialize the dashboard."""
43
+ self.console = Console()
44
+ self.performance_monitor = get_performance_monitor()
45
+ self.token_tracker = get_token_tracker()
46
+ self.metrics_collector = get_metrics_collector()
47
+
48
+ # Dashboard state
49
+ self.is_running = False
50
+ self.refresh_interval = 5.0 # seconds
51
+ self.current_view = "overview"
52
+
53
+ # Views
54
+ self.available_views = {
55
+ "overview": self._render_overview,
56
+ "tokens": self._render_token_usage,
57
+ "performance": self._render_performance_metrics,
58
+ "resources": self._render_resource_usage,
59
+ "alerts": self._render_alerts,
60
+ "suggestions": self._render_suggestions,
61
+ }
62
+
63
+ def show_overview(self) -> None:
64
+ """Show overview dashboard (non-interactive)."""
65
+ try:
66
+ self.console.clear()
67
+ self._print_header("Omnimancer Performance Overview")
68
+
69
+ # Get current data
70
+ dashboard_data = self.performance_monitor.get_performance_dashboard_data()
71
+ metrics_data = self.metrics_collector.get_dashboard_metrics()
72
+ token_summary = self.token_tracker.get_usage_summary(timedelta(hours=1))
73
+
74
+ # Create layout
75
+ layout = self._create_overview_layout(
76
+ dashboard_data, metrics_data, token_summary
77
+ )
78
+ self.console.print(layout)
79
+
80
+ # Show recent alerts
81
+ recent_alerts = dashboard_data.get("active_alerts", [])[
82
+ -3:
83
+ ] # Last 3 alerts
84
+ if recent_alerts:
85
+ self.console.print(Rule("Recent Alerts"))
86
+ self._display_alerts_summary(recent_alerts)
87
+
88
+ # Show suggestions
89
+ suggestions = dashboard_data.get("suggestions", [])[
90
+ -2:
91
+ ] # Last 2 suggestions
92
+ if suggestions:
93
+ self.console.print(Rule("Optimization Suggestions"))
94
+ self._display_suggestions_summary(suggestions)
95
+
96
+ except Exception as e:
97
+ self.console.print(f"[red]Error displaying overview: {e}[/red]")
98
+ logger.error(f"Dashboard overview error: {e}")
99
+
100
+ def show_detailed_view(self, view_name: str = "overview") -> None:
101
+ """Show detailed view for specific metrics."""
102
+ if view_name not in self.available_views:
103
+ self.console.print(f"[red]Unknown view: {view_name}[/red]")
104
+ self.console.print(
105
+ f"Available views: {', '.join(self.available_views.keys())}"
106
+ )
107
+ return
108
+
109
+ try:
110
+ self.console.clear()
111
+ self.available_views[view_name]()
112
+ except Exception as e:
113
+ self.console.print(f"[red]Error displaying {view_name} view: {e}[/red]")
114
+ logger.error(f"Dashboard view error: {e}")
115
+
116
+ def start_live_dashboard(self, view: str = "overview") -> None:
117
+ """Start live updating dashboard."""
118
+ if view not in self.available_views:
119
+ self.console.print(f"[red]Unknown view: {view}[/red]")
120
+ return
121
+
122
+ self.current_view = view
123
+ self.is_running = True
124
+
125
+ try:
126
+ with Live(
127
+ self._generate_live_layout(),
128
+ console=self.console,
129
+ refresh_per_second=1 / self.refresh_interval,
130
+ screen=True,
131
+ ) as live:
132
+ self.console.print(
133
+ "[yellow]Live dashboard started. Press Ctrl+C to exit.[/yellow]"
134
+ )
135
+
136
+ while self.is_running:
137
+ try:
138
+ live.update(self._generate_live_layout())
139
+ time.sleep(self.refresh_interval)
140
+ except KeyboardInterrupt:
141
+ break
142
+
143
+ except KeyboardInterrupt:
144
+ pass
145
+ finally:
146
+ self.is_running = False
147
+ self.console.print("\n[yellow]Dashboard stopped.[/yellow]")
148
+
149
+ def _generate_live_layout(self) -> Layout:
150
+ """Generate live updating layout."""
151
+ if self.current_view == "overview":
152
+ dashboard_data = self.performance_monitor.get_performance_dashboard_data()
153
+ metrics_data = self.metrics_collector.get_dashboard_metrics()
154
+ token_summary = self.token_tracker.get_usage_summary(timedelta(hours=1))
155
+ return self._create_overview_layout(
156
+ dashboard_data, metrics_data, token_summary
157
+ )
158
+ else:
159
+ # For other views, just render statically
160
+ layout = Layout()
161
+ layout.split_column(
162
+ Layout(self._render_header(), size=3),
163
+ Layout(
164
+ Panel(
165
+ self.available_views[self.current_view](),
166
+ title=f"{self.current_view.title()} View",
167
+ )
168
+ ),
169
+ )
170
+ return layout
171
+
172
+ def _create_overview_layout(
173
+ self, dashboard_data: Dict, metrics_data: Dict, token_summary: Dict
174
+ ) -> Layout:
175
+ """Create overview dashboard layout."""
176
+ # Main layout
177
+ layout = Layout()
178
+
179
+ # Split into header and body
180
+ layout.split_column(Layout(self._render_header(), size=3), Layout(name="body"))
181
+
182
+ # Split body into sections
183
+ layout["body"].split_row(Layout(name="left"), Layout(name="right"))
184
+
185
+ # Left column: performance and resources
186
+ layout["left"].split_column(
187
+ Layout(self._create_performance_panel(dashboard_data, metrics_data)),
188
+ Layout(self._create_resources_panel(metrics_data)),
189
+ )
190
+
191
+ # Right column: tokens and status
192
+ layout["right"].split_column(
193
+ Layout(self._create_token_usage_panel(token_summary)),
194
+ Layout(self._create_status_panel(dashboard_data)),
195
+ )
196
+
197
+ return layout
198
+
199
+ def _render_header(self) -> Panel:
200
+ """Render dashboard header."""
201
+ title_text = Text("Omnimancer Agent Performance Dashboard", style="bold blue")
202
+ subtitle_text = Text(
203
+ f"Last updated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
204
+ style="dim",
205
+ )
206
+ header_content = Align.center(f"{title_text}\n{subtitle_text}")
207
+
208
+ return Panel(header_content, style="blue", box=box.ROUNDED)
209
+
210
+ def _create_performance_panel(
211
+ self, dashboard_data: Dict, metrics_data: Dict
212
+ ) -> Panel:
213
+ """Create performance metrics panel."""
214
+ current_snapshot = dashboard_data.get("current_snapshot", {})
215
+ performance_metrics = metrics_data.get("performance", {})
216
+
217
+ # Performance table
218
+ table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE)
219
+ table.add_column("Metric", style="cyan")
220
+ table.add_column("Value", justify="right")
221
+ table.add_column("Status", justify="center")
222
+
223
+ # Response time
224
+ response_time = performance_metrics.get("avg_response_time_ms", 0)
225
+ response_status = self._get_status_indicator(
226
+ response_time, 1000, 5000
227
+ ) # Good < 1s, Warning < 5s
228
+ table.add_row("Avg Response Time", f"{response_time:.0f} ms", response_status)
229
+
230
+ # Success rate
231
+ success_rate = performance_metrics.get("success_rate_percent", 100)
232
+ success_status = self._get_status_indicator(
233
+ success_rate, 95, 90, reverse=True
234
+ ) # Good > 95%
235
+ table.add_row("Success Rate", f"{success_rate:.1f}%", success_status)
236
+
237
+ # Active operations
238
+ active_ops = current_snapshot.get("active_operations", 0)
239
+ ops_status = self._get_status_indicator(
240
+ active_ops, 5, 15
241
+ ) # Good < 5, Warning < 15
242
+ table.add_row("Active Operations", str(active_ops), ops_status)
243
+
244
+ return Panel(table, title="Performance Metrics", border_style="green")
245
+
246
+ def _create_resources_panel(self, metrics_data: Dict) -> Panel:
247
+ """Create system resources panel."""
248
+ resources = metrics_data.get("resources", {})
249
+
250
+ # Resources table
251
+ table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE)
252
+ table.add_column("Resource", style="cyan")
253
+ table.add_column("Usage", justify="right")
254
+ table.add_column("Status", justify="center")
255
+
256
+ # CPU usage
257
+ cpu_percent = resources.get("cpu_percent", 0)
258
+ cpu_status = self._get_status_indicator(
259
+ cpu_percent, 50, 80
260
+ ) # Good < 50%, Warning < 80%
261
+ table.add_row("CPU Usage", f"{cpu_percent:.1f}%", cpu_status)
262
+
263
+ # Memory usage
264
+ memory_mb = resources.get("memory_mb", 0)
265
+ memory_gb = memory_mb / 1024
266
+ memory_status = self._get_status_indicator(
267
+ memory_mb, 512, 1024
268
+ ) # Good < 512MB, Warning < 1GB
269
+ table.add_row("Memory Usage", f"{memory_gb:.1f} GB", memory_status)
270
+
271
+ return Panel(table, title="System Resources", border_style="yellow")
272
+
273
+ def _create_token_usage_panel(self, token_summary: Dict) -> Panel:
274
+ """Create token usage panel."""
275
+ # Token usage table
276
+ table = Table(show_header=True, header_style="bold magenta", box=box.SIMPLE)
277
+ table.add_column("Metric", style="cyan")
278
+ table.add_column("Last Hour", justify="right")
279
+ table.add_column("Rate", justify="right")
280
+
281
+ # Token totals
282
+ total_tokens = token_summary.get("total_tokens", 0)
283
+ total_requests = token_summary.get("total_requests", 0)
284
+ avg_tokens = token_summary.get("average_tokens_per_request", 0)
285
+
286
+ table.add_row("Total Tokens", f"{total_tokens:,}", f"{total_tokens/60:.0f}/min")
287
+ table.add_row("Requests", f"{total_requests:,}", f"{total_requests/60:.1f}/min")
288
+ table.add_row("Avg Tokens/Request", f"{avg_tokens:.0f}", "-")
289
+
290
+ # Cost information
291
+ total_cost = token_summary.get("total_cost", 0.0)
292
+ avg_cost = token_summary.get("average_cost_per_request", 0.0)
293
+
294
+ table.add_row("Total Cost", f"${total_cost:.3f}", f"${total_cost/60:.4f}/min")
295
+ table.add_row("Avg Cost/Request", f"${avg_cost:.4f}", "-")
296
+
297
+ return Panel(table, title="Token Usage (Last Hour)", border_style="blue")
298
+
299
+ def _create_status_panel(self, dashboard_data: Dict) -> Panel:
300
+ """Create monitoring status panel."""
301
+ monitoring_status = dashboard_data.get("monitoring_status", "unknown")
302
+ active_alerts = len(dashboard_data.get("active_alerts", []))
303
+ suggestions = len(dashboard_data.get("suggestions", []))
304
+
305
+ # Status information
306
+ status_lines = [
307
+ f"Monitoring: {monitoring_status.title()}",
308
+ f"Active Alerts: {active_alerts}",
309
+ f"Suggestions: {suggestions}",
310
+ "",
311
+ f"Uptime: {self._get_uptime()}",
312
+ ]
313
+
314
+ status_text = "\n".join(status_lines)
315
+ status_color = "green" if monitoring_status == "active" else "red"
316
+
317
+ return Panel(status_text, title="System Status", border_style=status_color)
318
+
319
+ def _get_status_indicator(
320
+ self,
321
+ value: float,
322
+ good_threshold: float,
323
+ warning_threshold: float,
324
+ reverse: bool = False,
325
+ ) -> str:
326
+ """Get colored status indicator based on thresholds."""
327
+ if reverse:
328
+ # Higher values are better (e.g., success rate)
329
+ if value >= good_threshold:
330
+ return "[green]●[/green]"
331
+ elif value >= warning_threshold:
332
+ return "[yellow]●[/yellow]"
333
+ else:
334
+ return "[red]●[/red]"
335
+ else:
336
+ # Lower values are better (e.g., response time)
337
+ if value <= good_threshold:
338
+ return "[green]●[/green]"
339
+ elif value <= warning_threshold:
340
+ return "[yellow]●[/yellow]"
341
+ else:
342
+ return "[red]●[/red]"
343
+
344
+ def _get_uptime(self) -> str:
345
+ """Get system uptime string."""
346
+ # This is a placeholder - in reality you'd track when monitoring started
347
+ return "Running"
348
+
349
+ def _print_header(self, title: str) -> None:
350
+ """Print dashboard header."""
351
+ self.console.print(Panel(title, style="bold blue"))
352
+ self.console.print()
353
+
354
+ def _render_overview(self) -> None:
355
+ """Render overview page."""
356
+ self._print_header("Performance Overview")
357
+
358
+ # Get data and display
359
+ dashboard_data = self.performance_monitor.get_performance_dashboard_data()
360
+ self.console.print(json.dumps(dashboard_data, indent=2, default=str))
361
+
362
+ def _render_token_usage(self) -> None:
363
+ """Render token usage page."""
364
+ self._print_header("Token Usage Analysis")
365
+
366
+ # Recent usage summary
367
+ summary_1h = self.token_tracker.get_usage_summary(timedelta(hours=1))
368
+ summary_24h = self.token_tracker.get_usage_summary(timedelta(hours=24))
369
+
370
+ # Create comparison table
371
+ table = Table(
372
+ title="Token Usage Summary",
373
+ show_header=True,
374
+ header_style="bold magenta",
375
+ )
376
+ table.add_column("Metric", style="cyan")
377
+ table.add_column("Last Hour", justify="right")
378
+ table.add_column("Last 24 Hours", justify="right")
379
+
380
+ table.add_row(
381
+ "Total Tokens",
382
+ f"{summary_1h.get('total_tokens', 0):,}",
383
+ f"{summary_24h.get('total_tokens', 0):,}",
384
+ )
385
+ table.add_row(
386
+ "Total Requests",
387
+ f"{summary_1h.get('total_requests', 0):,}",
388
+ f"{summary_24h.get('total_requests', 0):,}",
389
+ )
390
+ table.add_row(
391
+ "Total Cost",
392
+ f"${summary_1h.get('total_cost', 0):.3f}",
393
+ f"${summary_24h.get('total_cost', 0):.3f}",
394
+ )
395
+
396
+ self.console.print(table)
397
+ self.console.print()
398
+
399
+ # Provider breakdown
400
+ if summary_24h.get("provider_breakdown"):
401
+ provider_table = Table(
402
+ title="Provider Breakdown (24h)",
403
+ show_header=True,
404
+ header_style="bold magenta",
405
+ )
406
+ provider_table.add_column("Provider", style="cyan")
407
+ provider_table.add_column("Tokens", justify="right")
408
+ provider_table.add_column("Requests", justify="right")
409
+ provider_table.add_column("Cost", justify="right")
410
+
411
+ for provider, stats in summary_24h["provider_breakdown"].items():
412
+ provider_table.add_row(
413
+ provider,
414
+ f"{stats['tokens']:,}",
415
+ f"{stats['requests']:,}",
416
+ f"${stats['cost']:.3f}",
417
+ )
418
+
419
+ self.console.print(provider_table)
420
+
421
+ def _render_performance_metrics(self) -> None:
422
+ """Render performance metrics page."""
423
+ self._print_header("Performance Metrics")
424
+
425
+ metrics_data = self.metrics_collector.get_dashboard_metrics()
426
+
427
+ # Performance summary
428
+ performance = metrics_data.get("performance", {})
429
+
430
+ perf_table = Table(
431
+ title="Performance Summary",
432
+ show_header=True,
433
+ header_style="bold magenta",
434
+ )
435
+ perf_table.add_column("Metric", style="cyan")
436
+ perf_table.add_column("Value", justify="right")
437
+ perf_table.add_column("Status", justify="center")
438
+
439
+ response_time = performance.get("avg_response_time_ms", 0)
440
+ success_rate = performance.get("success_rate_percent", 100)
441
+
442
+ perf_table.add_row(
443
+ "Average Response Time",
444
+ f"{response_time:.0f} ms",
445
+ self._get_status_indicator(response_time, 1000, 5000),
446
+ )
447
+ perf_table.add_row(
448
+ "Success Rate",
449
+ f"{success_rate:.1f}%",
450
+ self._get_status_indicator(success_rate, 95, 90, reverse=True),
451
+ )
452
+
453
+ self.console.print(perf_table)
454
+
455
+ def _render_resource_usage(self) -> None:
456
+ """Render resource usage page."""
457
+ self._print_header("System Resource Usage")
458
+
459
+ metrics_data = self.metrics_collector.get_dashboard_metrics()
460
+ resources = metrics_data.get("resources", {})
461
+
462
+ resource_table = Table(
463
+ title="Current Resource Usage",
464
+ show_header=True,
465
+ header_style="bold magenta",
466
+ )
467
+ resource_table.add_column("Resource", style="cyan")
468
+ resource_table.add_column("Current Usage", justify="right")
469
+ resource_table.add_column("Status", justify="center")
470
+
471
+ cpu_percent = resources.get("cpu_percent", 0)
472
+ memory_mb = resources.get("memory_mb", 0)
473
+
474
+ resource_table.add_row(
475
+ "CPU",
476
+ f"{cpu_percent:.1f}%",
477
+ self._get_status_indicator(cpu_percent, 50, 80),
478
+ )
479
+ resource_table.add_row(
480
+ "Memory",
481
+ f"{memory_mb:.0f} MB",
482
+ self._get_status_indicator(memory_mb, 512, 1024),
483
+ )
484
+
485
+ self.console.print(resource_table)
486
+
487
+ def _render_alerts(self) -> None:
488
+ """Render alerts page."""
489
+ self._print_header("Performance Alerts")
490
+
491
+ dashboard_data = self.performance_monitor.get_performance_dashboard_data()
492
+ alerts = dashboard_data.get("active_alerts", [])
493
+
494
+ if not alerts:
495
+ self.console.print("[green]No active alerts[/green]")
496
+ return
497
+
498
+ for alert in alerts[-10:]: # Show last 10 alerts
499
+ self._display_alert_detail(alert)
500
+
501
+ def _render_suggestions(self) -> None:
502
+ """Render optimization suggestions page."""
503
+ self._print_header("Optimization Suggestions")
504
+
505
+ dashboard_data = self.performance_monitor.get_performance_dashboard_data()
506
+ suggestions = dashboard_data.get("suggestions", [])
507
+
508
+ # Also get token usage suggestions
509
+ token_suggestions = self.token_tracker.get_cost_optimization_suggestions()
510
+
511
+ all_suggestions = suggestions + token_suggestions
512
+
513
+ if not all_suggestions:
514
+ self.console.print("[green]No optimization suggestions available[/green]")
515
+ return
516
+
517
+ for suggestion in all_suggestions:
518
+ self._display_suggestion_detail(suggestion)
519
+
520
+ def _display_alerts_summary(self, alerts: List[Dict]) -> None:
521
+ """Display alerts summary."""
522
+ for alert in alerts:
523
+ level_color = {
524
+ "info": "blue",
525
+ "warning": "yellow",
526
+ "critical": "red",
527
+ "emergency": "bright_red",
528
+ }.get(alert.get("level", "info"), "white")
529
+
530
+ self.console.print(
531
+ f"[{level_color}]● {alert.get('title', 'Unknown Alert')}[/{level_color}]"
532
+ )
533
+
534
+ def _display_suggestions_summary(self, suggestions: List[Dict]) -> None:
535
+ """Display suggestions summary."""
536
+ for suggestion in suggestions:
537
+ self.console.print(
538
+ f"[yellow]💡 {suggestion.get('title', 'Optimization Available')}[/yellow]"
539
+ )
540
+
541
+ def _display_alert_detail(self, alert: Dict) -> None:
542
+ """Display detailed alert information."""
543
+ level_color = {
544
+ "info": "blue",
545
+ "warning": "yellow",
546
+ "critical": "red",
547
+ "emergency": "bright_red",
548
+ }.get(alert.get("level", "info"), "white")
549
+
550
+ alert_panel = Panel(
551
+ f"[bold]{alert.get('description', 'No description')}[/bold]\n\n"
552
+ + f"Threshold: {alert.get('threshold_value', 'N/A')}\n"
553
+ + f"Current: {alert.get('current_value', 'N/A')}\n"
554
+ + f"Time: {alert.get('timestamp', 'Unknown')}",
555
+ title=f"[{level_color}]{alert.get('title', 'Alert')}[/{level_color}]",
556
+ border_style=level_color,
557
+ )
558
+
559
+ self.console.print(alert_panel)
560
+ self.console.print()
561
+
562
+ def _display_suggestion_detail(self, suggestion: Dict) -> None:
563
+ """Display detailed suggestion information."""
564
+ suggestion_panel = Panel(
565
+ f"[bold]{suggestion.get('description', 'No description')}[/bold]\n\n"
566
+ + f"Suggestion: {suggestion.get('suggestion', 'N/A')}\n"
567
+ + f"Potential Savings: {suggestion.get('potential_savings', 'Unknown')}",
568
+ title=f"[yellow]💡 {suggestion.get('title', 'Optimization Suggestion')}[/yellow]",
569
+ border_style="yellow",
570
+ )
571
+
572
+ self.console.print(suggestion_panel)
573
+ self.console.print()
574
+
575
+ def export_report(self, output_path: Optional[Path] = None) -> Path:
576
+ """Export performance report to file."""
577
+ if not output_path:
578
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
579
+ output_path = Path(f"performance_report_{timestamp}.json")
580
+
581
+ # Collect all data
582
+ report_data = {
583
+ "metadata": {
584
+ "generated_at": datetime.now().isoformat(),
585
+ "report_type": "performance_dashboard",
586
+ "version": "1.0",
587
+ },
588
+ "dashboard_data": self.performance_monitor.get_performance_dashboard_data(),
589
+ "metrics_data": self.metrics_collector.get_dashboard_metrics(),
590
+ "token_summary_1h": self.token_tracker.get_usage_summary(
591
+ timedelta(hours=1)
592
+ ),
593
+ "token_summary_24h": self.token_tracker.get_usage_summary(
594
+ timedelta(hours=24)
595
+ ),
596
+ "usage_patterns": asdict(self.token_tracker.analyze_usage_patterns(7)),
597
+ "optimization_suggestions": self.token_tracker.get_cost_optimization_suggestions(),
598
+ }
599
+
600
+ # Write report
601
+ with open(output_path, "w") as f:
602
+ json.dump(report_data, f, indent=2, default=str)
603
+
604
+ self.console.print(
605
+ f"[green]Performance report exported to {output_path}[/green]"
606
+ )
607
+ return output_path
608
+
609
+
610
+ def create_performance_dashboard() -> PerformanceDashboard:
611
+ """Create a performance dashboard instance."""
612
+ return PerformanceDashboard()
@@ -0,0 +1,34 @@
1
+ """
2
+ Core module for Omnimancer.
3
+
4
+ This module contains the core business logic including the chat engine,
5
+ configuration management, and session handling.
6
+ """
7
+
8
+ from ..providers.base import BaseProvider
9
+ from ..utils.errors import (
10
+ OmnimancerError,
11
+ ProviderError,
12
+ )
13
+ from .models import (
14
+ ChatContext,
15
+ ChatMessage,
16
+ ChatResponse,
17
+ Config,
18
+ EnhancedModelInfo,
19
+ ModelInfo,
20
+ )
21
+ from .provider_registry import ProviderRegistry
22
+
23
+ __all__ = [
24
+ "BaseProvider",
25
+ "ChatContext",
26
+ "ChatMessage",
27
+ "ChatResponse",
28
+ "Config",
29
+ "ModelInfo",
30
+ "EnhancedModelInfo",
31
+ "ProviderRegistry",
32
+ "OmnimancerError",
33
+ "ProviderError",
34
+ ]