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,624 @@
1
+ """
2
+ Performance monitoring CLI command for Omnimancer.
3
+
4
+ Provides command-line access to performance metrics, dashboards, and optimization suggestions.
5
+ """
6
+
7
+ import json
8
+ import sys
9
+ from datetime import datetime, timedelta
10
+ from pathlib import Path
11
+ from typing import Any, Dict, List
12
+
13
+ import click
14
+ from rich.console import Console
15
+ from rich.table import Table
16
+
17
+ from ...core.agent.metrics_collector import (
18
+ get_metrics_collector,
19
+ initialize_metrics_collection,
20
+ )
21
+ from ...core.agent.optimization_engine import (
22
+ AlertLevel,
23
+ OptimizationCategory,
24
+ get_optimization_engine,
25
+ initialize_optimization_engine,
26
+ )
27
+ from ...core.agent.performance_monitor import (
28
+ get_performance_monitor,
29
+ initialize_performance_monitoring,
30
+ )
31
+ from ...core.agent.token_tracker import get_token_tracker
32
+ from ..performance_dashboard import (
33
+ create_performance_dashboard,
34
+ )
35
+
36
+
37
+ @click.group()
38
+ @click.option("--debug", is_flag=True, help="Enable debug output")
39
+ @click.pass_context
40
+ def performance(ctx, debug):
41
+ """Performance monitoring and optimization commands."""
42
+ ctx.ensure_object(dict)
43
+ ctx.obj["debug"] = debug
44
+
45
+ if debug:
46
+ import logging
47
+
48
+ logging.getLogger("omnimancer.core.agent").setLevel(logging.DEBUG)
49
+
50
+
51
+ @performance.command()
52
+ @click.option(
53
+ "--start-monitoring",
54
+ is_flag=True,
55
+ help="Start performance monitoring services",
56
+ )
57
+ @click.option(
58
+ "--format",
59
+ "output_format",
60
+ type=click.Choice(["table", "json"]),
61
+ default="table",
62
+ help="Output format",
63
+ )
64
+ @click.pass_context
65
+ def status(ctx, start_monitoring, output_format):
66
+ """Show performance monitoring status."""
67
+ console = Console()
68
+
69
+ try:
70
+ if start_monitoring:
71
+ console.print("[yellow]Initializing performance monitoring...[/yellow]")
72
+
73
+ # Initialize all components
74
+ performance_monitor = initialize_performance_monitoring(auto_start=True)
75
+ metrics_collector = initialize_metrics_collection(auto_start=True)
76
+ optimization_engine = initialize_optimization_engine(auto_start=True)
77
+
78
+ console.print("[green]✓ Performance monitoring started[/green]")
79
+ else:
80
+ performance_monitor = get_performance_monitor()
81
+ metrics_collector = get_metrics_collector()
82
+ optimization_engine = get_optimization_engine()
83
+
84
+ # Gather status information
85
+ status_info = {
86
+ "monitoring_active": (
87
+ performance_monitor.is_monitoring
88
+ if hasattr(performance_monitor, "is_monitoring")
89
+ else False
90
+ ),
91
+ "metrics_collecting": (
92
+ metrics_collector.is_collecting
93
+ if hasattr(metrics_collector, "is_collecting")
94
+ else False
95
+ ),
96
+ "optimization_running": (
97
+ optimization_engine.is_running
98
+ if hasattr(optimization_engine, "is_running")
99
+ else False
100
+ ),
101
+ "timestamp": datetime.now().isoformat(),
102
+ }
103
+
104
+ # Get recent statistics
105
+ dashboard_data = performance_monitor.get_performance_dashboard_data()
106
+ status_info.update(
107
+ {
108
+ "active_alerts": len(dashboard_data.get("active_alerts", [])),
109
+ "suggestions_count": len(dashboard_data.get("suggestions", [])),
110
+ "recent_snapshot": dashboard_data.get("current_snapshot", {}),
111
+ }
112
+ )
113
+
114
+ # Display results
115
+ if output_format == "json":
116
+ console.print(json.dumps(status_info, indent=2, default=str))
117
+ else:
118
+ _display_status_table(console, status_info)
119
+
120
+ except Exception as e:
121
+ console.print(f"[red]Error checking performance status: {e}[/red]")
122
+ if ctx.obj.get("debug"):
123
+ console.print_exception()
124
+ sys.exit(1)
125
+
126
+
127
+ @performance.command()
128
+ @click.option(
129
+ "--view",
130
+ type=click.Choice(
131
+ [
132
+ "overview",
133
+ "tokens",
134
+ "performance",
135
+ "resources",
136
+ "alerts",
137
+ "suggestions",
138
+ ]
139
+ ),
140
+ default="overview",
141
+ help="Dashboard view to display",
142
+ )
143
+ @click.option("--live", is_flag=True, help="Start live updating dashboard")
144
+ @click.option("--export", type=click.Path(), help="Export performance report to file")
145
+ @click.pass_context
146
+ def dashboard(ctx, view, live, export):
147
+ """Show performance dashboard."""
148
+ console = Console()
149
+
150
+ try:
151
+ dashboard = create_performance_dashboard()
152
+
153
+ if export:
154
+ export_path = Path(export)
155
+ console.print(f"[yellow]Exporting performance report...[/yellow]")
156
+ result_path = dashboard.export_report(export_path)
157
+ console.print(f"[green]Report exported to {result_path}[/green]")
158
+ return
159
+
160
+ if live:
161
+ console.print(f"[yellow]Starting live dashboard ({view} view)...[/yellow]")
162
+ dashboard.start_live_dashboard(view)
163
+ else:
164
+ if view == "overview":
165
+ dashboard.show_overview()
166
+ else:
167
+ dashboard.show_detailed_view(view)
168
+
169
+ except KeyboardInterrupt:
170
+ console.print("\n[yellow]Dashboard stopped.[/yellow]")
171
+ except Exception as e:
172
+ console.print(f"[red]Error displaying dashboard: {e}[/red]")
173
+ if ctx.obj.get("debug"):
174
+ console.print_exception()
175
+ sys.exit(1)
176
+
177
+
178
+ @performance.command()
179
+ @click.option("--hours", type=int, default=1, help="Time window in hours")
180
+ @click.option("--provider", help="Filter by specific provider")
181
+ @click.option(
182
+ "--format",
183
+ "output_format",
184
+ type=click.Choice(["table", "json"]),
185
+ default="table",
186
+ help="Output format",
187
+ )
188
+ @click.pass_context
189
+ def tokens(ctx, hours, provider, output_format):
190
+ """Show token usage statistics."""
191
+ console = Console()
192
+
193
+ try:
194
+ token_tracker = get_token_tracker()
195
+
196
+ # Get usage summary
197
+ time_window = timedelta(hours=hours)
198
+ summary = token_tracker.get_usage_summary(time_window)
199
+
200
+ if output_format == "json":
201
+ console.print(json.dumps(summary, indent=2, default=str))
202
+ else:
203
+ _display_token_usage_table(console, summary, hours)
204
+
205
+ except Exception as e:
206
+ console.print(f"[red]Error retrieving token usage: {e}[/red]")
207
+ if ctx.obj.get("debug"):
208
+ console.print_exception()
209
+ sys.exit(1)
210
+
211
+
212
+ @performance.command()
213
+ @click.option(
214
+ "--category",
215
+ type=click.Choice(
216
+ [
217
+ "cost_reduction",
218
+ "performance_improvement",
219
+ "resource_efficiency",
220
+ "reliability_enhancement",
221
+ "user_experience",
222
+ ]
223
+ ),
224
+ help="Filter by optimization category",
225
+ )
226
+ @click.option(
227
+ "--format",
228
+ "output_format",
229
+ type=click.Choice(["table", "json"]),
230
+ default="table",
231
+ help="Output format",
232
+ )
233
+ @click.pass_context
234
+ def suggestions(ctx, category, output_format):
235
+ """Show optimization suggestions."""
236
+ console = Console()
237
+
238
+ try:
239
+ optimization_engine = get_optimization_engine()
240
+
241
+ # Get current suggestions
242
+ if category:
243
+ category_enum = OptimizationCategory(category)
244
+ current_suggestions = optimization_engine.get_current_suggestions(
245
+ category_enum
246
+ )
247
+ else:
248
+ current_suggestions = optimization_engine.get_current_suggestions()
249
+
250
+ # Also get token tracker suggestions
251
+ token_tracker = get_token_tracker()
252
+ token_suggestions = token_tracker.get_cost_optimization_suggestions()
253
+
254
+ if output_format == "json":
255
+ data = {
256
+ "optimization_suggestions": [s.__dict__ for s in current_suggestions],
257
+ "token_suggestions": token_suggestions,
258
+ }
259
+ console.print(json.dumps(data, indent=2, default=str))
260
+ else:
261
+ _display_suggestions_table(console, current_suggestions, token_suggestions)
262
+
263
+ except Exception as e:
264
+ console.print(f"[red]Error retrieving suggestions: {e}[/red]")
265
+ if ctx.obj.get("debug"):
266
+ console.print_exception()
267
+ sys.exit(1)
268
+
269
+
270
+ @performance.command()
271
+ @click.option(
272
+ "--level",
273
+ type=click.Choice(["info", "warning", "critical", "emergency"]),
274
+ help="Filter by alert level",
275
+ )
276
+ @click.option(
277
+ "--format",
278
+ "output_format",
279
+ type=click.Choice(["table", "json"]),
280
+ default="table",
281
+ help="Output format",
282
+ )
283
+ @click.pass_context
284
+ def alerts(ctx, level, output_format):
285
+ """Show performance alerts."""
286
+ console = Console()
287
+
288
+ try:
289
+ optimization_engine = get_optimization_engine()
290
+
291
+ # Get active alerts
292
+ if level:
293
+ level_enum = AlertLevel(level)
294
+ active_alerts = optimization_engine.get_active_alerts(level_enum)
295
+ else:
296
+ active_alerts = optimization_engine.get_active_alerts()
297
+
298
+ if output_format == "json":
299
+ data = [alert.__dict__ for alert in active_alerts]
300
+ console.print(json.dumps(data, indent=2, default=str))
301
+ else:
302
+ _display_alerts_table(console, active_alerts)
303
+
304
+ except Exception as e:
305
+ console.print(f"[red]Error retrieving alerts: {e}[/red]")
306
+ if ctx.obj.get("debug"):
307
+ console.print_exception()
308
+ sys.exit(1)
309
+
310
+
311
+ @performance.command()
312
+ @click.option("--metric", help="Specific metric to analyze")
313
+ @click.option("--days", type=int, default=7, help="Analysis period in days")
314
+ @click.option(
315
+ "--format",
316
+ "output_format",
317
+ type=click.Choice(["table", "json"]),
318
+ default="table",
319
+ help="Output format",
320
+ )
321
+ @click.pass_context
322
+ def analyze(ctx, metric, days, output_format):
323
+ """Analyze performance patterns and trends."""
324
+ console = Console()
325
+
326
+ try:
327
+ token_tracker = get_token_tracker()
328
+ metrics_collector = get_metrics_collector()
329
+
330
+ # Analyze usage patterns
331
+ usage_patterns = token_tracker.analyze_usage_patterns(days)
332
+
333
+ analysis_results = {
334
+ "usage_patterns": usage_patterns.__dict__,
335
+ "analysis_period_days": days,
336
+ "timestamp": datetime.now().isoformat(),
337
+ }
338
+
339
+ # Add metric-specific analysis if requested
340
+ if metric:
341
+ # Get anomalies for specific metric
342
+ anomalies = metrics_collector.detect_anomalies(metric)
343
+ analysis_results["anomalies"] = anomalies
344
+
345
+ if output_format == "json":
346
+ console.print(json.dumps(analysis_results, indent=2, default=str))
347
+ else:
348
+ _display_analysis_results(console, analysis_results)
349
+
350
+ except Exception as e:
351
+ console.print(f"[red]Error performing analysis: {e}[/red]")
352
+ if ctx.obj.get("debug"):
353
+ console.print_exception()
354
+ sys.exit(1)
355
+
356
+
357
+ @performance.command()
358
+ @click.option("--output", "-o", type=click.Path(), help="Output file path")
359
+ @click.option(
360
+ "--format",
361
+ "output_format",
362
+ type=click.Choice(["json", "csv"]),
363
+ default="json",
364
+ help="Export format",
365
+ )
366
+ @click.option("--days", type=int, default=7, help="Data period to export (days)")
367
+ @click.pass_context
368
+ def export(ctx, output, output_format, days):
369
+ """Export performance metrics to file."""
370
+ console = Console()
371
+
372
+ try:
373
+ if not output:
374
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
375
+ output = f"omnimancer_performance_{timestamp}.{output_format}"
376
+
377
+ output_path = Path(output)
378
+ time_window = timedelta(days=days)
379
+
380
+ console.print(f"[yellow]Exporting {days} days of performance data...[/yellow]")
381
+
382
+ # Export metrics
383
+ metrics_collector = get_metrics_collector()
384
+ metrics_path = metrics_collector.export_metrics(
385
+ time_window, f"metrics_{output_path.stem}.json"
386
+ )
387
+
388
+ # Export token usage
389
+ token_tracker = get_token_tracker()
390
+ token_tracker.save_usage_history(f"tokens_{output_path.stem}.json")
391
+
392
+ # Create combined report
393
+ dashboard = create_performance_dashboard()
394
+ report_path = dashboard.export_report(output_path)
395
+
396
+ console.print(f"[green]✓ Performance data exported to {report_path}[/green]")
397
+ console.print(f"[green]✓ Metrics data exported to {metrics_path}[/green]")
398
+
399
+ except Exception as e:
400
+ console.print(f"[red]Error exporting data: {e}[/red]")
401
+ if ctx.obj.get("debug"):
402
+ console.print_exception()
403
+ sys.exit(1)
404
+
405
+
406
+ # Helper functions for display
407
+ def _display_status_table(console: Console, status_info: Dict[str, Any]) -> None:
408
+ """Display status information in table format."""
409
+ table = Table(title="Performance Monitoring Status")
410
+ table.add_column("Component", style="cyan")
411
+ table.add_column("Status", justify="center")
412
+ table.add_column("Details")
413
+
414
+ # Monitoring status
415
+ monitoring_status = (
416
+ "🟢 Active" if status_info.get("monitoring_active") else "🔴 Inactive"
417
+ )
418
+ table.add_row("Performance Monitor", monitoring_status, "Real-time monitoring")
419
+
420
+ # Metrics collection
421
+ metrics_status = (
422
+ "🟢 Active" if status_info.get("metrics_collecting") else "🔴 Inactive"
423
+ )
424
+ table.add_row("Metrics Collection", metrics_status, "System metrics gathering")
425
+
426
+ # Optimization engine
427
+ optimization_status = (
428
+ "🟢 Running" if status_info.get("optimization_running") else "🔴 Stopped"
429
+ )
430
+ table.add_row(
431
+ "Optimization Engine",
432
+ optimization_status,
433
+ "Alert and suggestion generation",
434
+ )
435
+
436
+ # Statistics
437
+ alerts_count = status_info.get("active_alerts", 0)
438
+ suggestions_count = status_info.get("suggestions_count", 0)
439
+
440
+ table.add_row("Active Alerts", f"{alerts_count}", "Performance warnings")
441
+ table.add_row("Suggestions", f"{suggestions_count}", "Optimization recommendations")
442
+
443
+ console.print(table)
444
+
445
+
446
+ def _display_token_usage_table(
447
+ console: Console, summary: Dict[str, Any], hours: int
448
+ ) -> None:
449
+ """Display token usage in table format."""
450
+ table = Table(title=f"Token Usage (Last {hours} hours)")
451
+ table.add_column("Metric", style="cyan")
452
+ table.add_column("Value", justify="right")
453
+ table.add_column("Rate", justify="right")
454
+
455
+ total_tokens = summary.get("total_tokens", 0)
456
+ total_requests = summary.get("total_requests", 0)
457
+ total_cost = summary.get("total_cost", 0.0)
458
+
459
+ table.add_row(
460
+ "Total Tokens",
461
+ f"{total_tokens:,}",
462
+ f"{total_tokens/(hours*60):.0f}/min",
463
+ )
464
+ table.add_row(
465
+ "Total Requests",
466
+ f"{total_requests:,}",
467
+ f"{total_requests/(hours*60):.1f}/min",
468
+ )
469
+ table.add_row(
470
+ "Total Cost", f"${total_cost:.4f}", f"${total_cost/(hours*60):.5f}/min"
471
+ )
472
+
473
+ if total_requests > 0:
474
+ table.add_row("Avg Tokens/Request", f"{total_tokens/total_requests:.0f}", "-")
475
+ table.add_row("Avg Cost/Request", f"${total_cost/total_requests:.5f}", "-")
476
+
477
+ console.print(table)
478
+
479
+ # Provider breakdown
480
+ provider_breakdown = summary.get("provider_breakdown", {})
481
+ if provider_breakdown:
482
+ provider_table = Table(title="Provider Breakdown")
483
+ provider_table.add_column("Provider", style="cyan")
484
+ provider_table.add_column("Tokens", justify="right")
485
+ provider_table.add_column("Requests", justify="right")
486
+ provider_table.add_column("Cost", justify="right")
487
+
488
+ for provider, stats in provider_breakdown.items():
489
+ provider_table.add_row(
490
+ provider,
491
+ f"{stats['tokens']:,}",
492
+ f"{stats['requests']:,}",
493
+ f"${stats['cost']:.4f}",
494
+ )
495
+
496
+ console.print()
497
+ console.print(provider_table)
498
+
499
+
500
+ def _display_suggestions_table(
501
+ console: Console,
502
+ optimization_suggestions: List[Any],
503
+ token_suggestions: List[Dict[str, Any]],
504
+ ) -> None:
505
+ """Display optimization suggestions in table format."""
506
+ if not optimization_suggestions and not token_suggestions:
507
+ console.print("[green]No optimization suggestions at this time[/green]")
508
+ return
509
+
510
+ table = Table(title="Optimization Suggestions")
511
+ table.add_column("Type", style="cyan")
512
+ table.add_column("Title")
513
+ table.add_column("Impact")
514
+ table.add_column("Difficulty")
515
+
516
+ # Add optimization suggestions
517
+ for suggestion in optimization_suggestions:
518
+ table.add_row(
519
+ (
520
+ suggestion.optimization_type.value
521
+ if hasattr(suggestion, "optimization_type")
522
+ else "General"
523
+ ),
524
+ suggestion.title if hasattr(suggestion, "title") else "N/A",
525
+ (
526
+ suggestion.potential_improvement
527
+ if hasattr(suggestion, "potential_improvement")
528
+ else "Unknown"
529
+ ),
530
+ (
531
+ suggestion.implementation_difficulty
532
+ if hasattr(suggestion, "implementation_difficulty")
533
+ else "Unknown"
534
+ ),
535
+ )
536
+
537
+ # Add token suggestions
538
+ for suggestion in token_suggestions:
539
+ table.add_row(
540
+ suggestion.get("type", "token"),
541
+ suggestion.get("title", "Token Optimization"),
542
+ suggestion.get("potential_savings", "Unknown"),
543
+ "Easy",
544
+ )
545
+
546
+ console.print(table)
547
+
548
+
549
+ def _display_alerts_table(console: Console, alerts: List[Any]) -> None:
550
+ """Display performance alerts in table format."""
551
+ if not alerts:
552
+ console.print("[green]No active alerts[/green]")
553
+ return
554
+
555
+ table = Table(title="Performance Alerts")
556
+ table.add_column("Level", style="cyan")
557
+ table.add_column("Title")
558
+ table.add_column("Description")
559
+ table.add_column("Time")
560
+
561
+ for alert in alerts:
562
+ level_color = {
563
+ "info": "blue",
564
+ "warning": "yellow",
565
+ "critical": "red",
566
+ "emergency": "bright_red",
567
+ }.get(alert.level.value if hasattr(alert, "level") else "info", "white")
568
+
569
+ table.add_row(
570
+ f"[{level_color}]{alert.level.value if hasattr(alert, 'level') else 'Unknown'}[/{level_color}]",
571
+ alert.title if hasattr(alert, "title") else "Unknown Alert",
572
+ (alert.description if hasattr(alert, "description") else "No description"),
573
+ (
574
+ alert.timestamp.strftime("%H:%M:%S")
575
+ if hasattr(alert, "timestamp")
576
+ else "Unknown"
577
+ ),
578
+ )
579
+
580
+ console.print(table)
581
+
582
+
583
+ def _display_analysis_results(console: Console, results: Dict[str, Any]) -> None:
584
+ """Display analysis results in readable format."""
585
+ usage_patterns = results.get("usage_patterns", {})
586
+
587
+ # Summary table
588
+ table = Table(
589
+ title=f"Performance Analysis ({results.get('analysis_period_days', 7)} days)"
590
+ )
591
+ table.add_column("Metric", style="cyan")
592
+ table.add_column("Value")
593
+
594
+ table.add_row("Total Tokens", f"{usage_patterns.get('total_tokens', 0):,}")
595
+ table.add_row("Total Requests", f"{usage_patterns.get('total_requests', 0):,}")
596
+ table.add_row("Total Cost", f"${usage_patterns.get('total_cost', 0.0):.4f}")
597
+ table.add_row(
598
+ "Avg Tokens/Request",
599
+ f"{usage_patterns.get('average_tokens_per_request', 0):.0f}",
600
+ )
601
+ table.add_row(
602
+ "Peak Usage Time",
603
+ str(usage_patterns.get("peak_usage_time", "Unknown")),
604
+ )
605
+
606
+ console.print(table)
607
+
608
+ # Efficiency metrics
609
+ if usage_patterns.get("average_context_utilization", 0) > 0:
610
+ efficiency_table = Table(title="Efficiency Metrics")
611
+ efficiency_table.add_column("Metric", style="cyan")
612
+ efficiency_table.add_column("Score")
613
+
614
+ efficiency_table.add_row(
615
+ "Context Utilization",
616
+ f"{usage_patterns.get('average_context_utilization', 0)*100:.1f}%",
617
+ )
618
+ efficiency_table.add_row(
619
+ "Response Efficiency",
620
+ f"{usage_patterns.get('average_response_efficiency', 0)*100:.1f}%",
621
+ )
622
+
623
+ console.print()
624
+ console.print(efficiency_table)