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.
- omnimancer/__init__.py +42 -0
- omnimancer/__main__.py +9 -0
- omnimancer/cli/__init__.py +8 -0
- omnimancer/cli/agent_manager.py +826 -0
- omnimancer/cli/agent_wizard.py +944 -0
- omnimancer/cli/approval_formatter.py +734 -0
- omnimancer/cli/approval_integration.py +734 -0
- omnimancer/cli/approval_prompt.py +1286 -0
- omnimancer/cli/batch_approval_display.py +617 -0
- omnimancer/cli/batch_approval_filters.py +729 -0
- omnimancer/cli/batch_operation_monitor.py +775 -0
- omnimancer/cli/commands/performance_cmd.py +624 -0
- omnimancer/cli/commands.py +757 -0
- omnimancer/cli/completion_handler.py +281 -0
- omnimancer/cli/config_setup_wizard.py +783 -0
- omnimancer/cli/dynamic_commands.py +371 -0
- omnimancer/cli/handlers/__init__.py +18 -0
- omnimancer/cli/handlers/agent_handler.py +865 -0
- omnimancer/cli/handlers/agent_persona_handler.py +1048 -0
- omnimancer/cli/handlers/custom_agent_handler.py +352 -0
- omnimancer/cli/handlers/permissions_handler.py +1609 -0
- omnimancer/cli/interface.py +6695 -0
- omnimancer/cli/performance_dashboard.py +612 -0
- omnimancer/core/__init__.py +34 -0
- omnimancer/core/agent/__init__.py +256 -0
- omnimancer/core/agent/agent_config.py +715 -0
- omnimancer/core/agent/agent_switcher.py +668 -0
- omnimancer/core/agent/approval_context.py +460 -0
- omnimancer/core/agent/approval_dialog.py +902 -0
- omnimancer/core/agent/approval_interface.py +628 -0
- omnimancer/core/agent/approval_manager.py +760 -0
- omnimancer/core/agent/cli_integration.py +470 -0
- omnimancer/core/agent/config.py +845 -0
- omnimancer/core/agent/custom_agent_integration.py +654 -0
- omnimancer/core/agent/diff_renderer.py +706 -0
- omnimancer/core/agent/file_content_display.py +503 -0
- omnimancer/core/agent/file_modification_workflow.py +658 -0
- omnimancer/core/agent/file_system_manager.py +1790 -0
- omnimancer/core/agent/input_handler.py +614 -0
- omnimancer/core/agent/metrics_collector.py +771 -0
- omnimancer/core/agent/optimization_engine.py +729 -0
- omnimancer/core/agent/performance_monitor.py +717 -0
- omnimancer/core/agent/persona.py +870 -0
- omnimancer/core/agent/persona_validator.py +672 -0
- omnimancer/core/agent/program_executor.py +748 -0
- omnimancer/core/agent/proposed_changes_integration.py +663 -0
- omnimancer/core/agent/read_before_write_errors.py +492 -0
- omnimancer/core/agent/read_before_write_ui.py +647 -0
- omnimancer/core/agent/rich_renderer.py +763 -0
- omnimancer/core/agent/status_config.py +238 -0
- omnimancer/core/agent/status_core.py +446 -0
- omnimancer/core/agent/status_integrated_engine.py +540 -0
- omnimancer/core/agent/status_manager.py +878 -0
- omnimancer/core/agent/status_ui_components.py +535 -0
- omnimancer/core/agent/template_applicator.py +548 -0
- omnimancer/core/agent/terminal_status_listener.py +394 -0
- omnimancer/core/agent/token_tracker.py +679 -0
- omnimancer/core/agent/types.py +60 -0
- omnimancer/core/agent/web_client.py +745 -0
- omnimancer/core/agent/workflow_orchestrator.py +826 -0
- omnimancer/core/agent_engine.py +1625 -0
- omnimancer/core/agent_mode_manager.py +706 -0
- omnimancer/core/agent_progress_ui.py +545 -0
- omnimancer/core/chat_manager.py +141 -0
- omnimancer/core/config_manager.py +2199 -0
- omnimancer/core/config_migration.py +772 -0
- omnimancer/core/config_provider.py +1974 -0
- omnimancer/core/config_security_validator.py +393 -0
- omnimancer/core/config_validator.py +768 -0
- omnimancer/core/conversation_manager.py +352 -0
- omnimancer/core/engine.py +787 -0
- omnimancer/core/enhanced_input.py +523 -0
- omnimancer/core/env_loader.py +83 -0
- omnimancer/core/fallback_manager.py +568 -0
- omnimancer/core/health_monitor.py +462 -0
- omnimancer/core/history_manager.py +462 -0
- omnimancer/core/mcp_integration_layer.py +878 -0
- omnimancer/core/models.py +1889 -0
- omnimancer/core/provider_capabilities.py +403 -0
- omnimancer/core/provider_initializer.py +450 -0
- omnimancer/core/provider_registry.py +432 -0
- omnimancer/core/security/__init__.py +16 -0
- omnimancer/core/security/approval_workflow.py +406 -0
- omnimancer/core/security/audit_logger.py +511 -0
- omnimancer/core/security/permission_controller.py +511 -0
- omnimancer/core/security/sandbox_manager.py +379 -0
- omnimancer/core/security/security_manager.py +530 -0
- omnimancer/core/setup_wizard.py +345 -0
- omnimancer/core/setup_wizard_core.py +449 -0
- omnimancer/core/setup_wizard_guide_generator.py +376 -0
- omnimancer/core/setup_wizard_provider_setup.py +454 -0
- omnimancer/core/setup_wizard_ui.py +317 -0
- omnimancer/core/setup_wizard_validation.py +371 -0
- omnimancer/core/signal_handler.py +174 -0
- omnimancer/mcp/__init__.py +11 -0
- omnimancer/mcp/client.py +725 -0
- omnimancer/mcp/manager.py +649 -0
- omnimancer/providers/__init__.py +24 -0
- omnimancer/providers/azure.py +608 -0
- omnimancer/providers/base.py +262 -0
- omnimancer/providers/bedrock.py +711 -0
- omnimancer/providers/claude.py +426 -0
- omnimancer/providers/claude_code.py +406 -0
- omnimancer/providers/cohere.py +412 -0
- omnimancer/providers/factory.py +988 -0
- omnimancer/providers/gemini.py +708 -0
- omnimancer/providers/mistral.py +554 -0
- omnimancer/providers/ollama.py +510 -0
- omnimancer/providers/openai.py +362 -0
- omnimancer/providers/openrouter.py +709 -0
- omnimancer/providers/perplexity.py +476 -0
- omnimancer/providers/vertex.py +617 -0
- omnimancer/providers/xai.py +484 -0
- omnimancer/ui/__init__.py +6 -0
- omnimancer/ui/cancellation_handler.py +235 -0
- omnimancer/ui/progress_indicator.py +318 -0
- omnimancer/utils/__init__.py +22 -0
- omnimancer/utils/error_handler.py +589 -0
- omnimancer/utils/errors.py +667 -0
- omnimancer/utils/retry.py +238 -0
- omnimancer_cli-0.1.0.dist-info/METADATA +413 -0
- omnimancer_cli-0.1.0.dist-info/RECORD +126 -0
- omnimancer_cli-0.1.0.dist-info/WHEEL +5 -0
- omnimancer_cli-0.1.0.dist-info/entry_points.txt +4 -0
- omnimancer_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- 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
|
+
]
|