crackerjack 0.29.0__py3-none-any.whl ā 0.31.4__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.
Potentially problematic release.
This version of crackerjack might be problematic. Click here for more details.
- crackerjack/CLAUDE.md +1005 -0
- crackerjack/RULES.md +380 -0
- crackerjack/__init__.py +42 -13
- crackerjack/__main__.py +225 -253
- crackerjack/agents/__init__.py +41 -0
- crackerjack/agents/architect_agent.py +281 -0
- crackerjack/agents/base.py +169 -0
- crackerjack/agents/coordinator.py +512 -0
- crackerjack/agents/documentation_agent.py +498 -0
- crackerjack/agents/dry_agent.py +388 -0
- crackerjack/agents/formatting_agent.py +245 -0
- crackerjack/agents/import_optimization_agent.py +281 -0
- crackerjack/agents/performance_agent.py +669 -0
- crackerjack/agents/proactive_agent.py +104 -0
- crackerjack/agents/refactoring_agent.py +788 -0
- crackerjack/agents/security_agent.py +529 -0
- crackerjack/agents/test_creation_agent.py +652 -0
- crackerjack/agents/test_specialist_agent.py +486 -0
- crackerjack/agents/tracker.py +212 -0
- crackerjack/api.py +560 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/facade.py +104 -0
- crackerjack/cli/handlers.py +267 -0
- crackerjack/cli/interactive.py +471 -0
- crackerjack/cli/options.py +401 -0
- crackerjack/cli/utils.py +18 -0
- crackerjack/code_cleaner.py +670 -0
- crackerjack/config/__init__.py +19 -0
- crackerjack/config/hooks.py +218 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +406 -0
- crackerjack/core/autofix_coordinator.py +200 -0
- crackerjack/core/container.py +104 -0
- crackerjack/core/enhanced_container.py +542 -0
- crackerjack/core/performance.py +243 -0
- crackerjack/core/phase_coordinator.py +561 -0
- crackerjack/core/proactive_workflow.py +316 -0
- crackerjack/core/session_coordinator.py +289 -0
- crackerjack/core/workflow_orchestrator.py +640 -0
- crackerjack/dynamic_config.py +577 -0
- crackerjack/errors.py +263 -41
- crackerjack/executors/__init__.py +11 -0
- crackerjack/executors/async_hook_executor.py +431 -0
- crackerjack/executors/cached_hook_executor.py +242 -0
- crackerjack/executors/hook_executor.py +345 -0
- crackerjack/executors/individual_hook_executor.py +669 -0
- crackerjack/intelligence/__init__.py +44 -0
- crackerjack/intelligence/adaptive_learning.py +751 -0
- crackerjack/intelligence/agent_orchestrator.py +551 -0
- crackerjack/intelligence/agent_registry.py +414 -0
- crackerjack/intelligence/agent_selector.py +502 -0
- crackerjack/intelligence/integration.py +290 -0
- crackerjack/interactive.py +576 -315
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +137 -0
- crackerjack/managers/publish_manager.py +411 -0
- crackerjack/managers/test_command_builder.py +151 -0
- crackerjack/managers/test_executor.py +435 -0
- crackerjack/managers/test_manager.py +258 -0
- crackerjack/managers/test_manager_backup.py +1124 -0
- crackerjack/managers/test_progress.py +144 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +336 -0
- crackerjack/mcp/client_runner.py +104 -0
- crackerjack/mcp/context.py +615 -0
- crackerjack/mcp/dashboard.py +636 -0
- crackerjack/mcp/enhanced_progress_monitor.py +479 -0
- crackerjack/mcp/file_monitor.py +336 -0
- crackerjack/mcp/progress_components.py +569 -0
- crackerjack/mcp/progress_monitor.py +949 -0
- crackerjack/mcp/rate_limiter.py +332 -0
- crackerjack/mcp/server.py +22 -0
- crackerjack/mcp/server_core.py +244 -0
- crackerjack/mcp/service_watchdog.py +501 -0
- crackerjack/mcp/state.py +395 -0
- crackerjack/mcp/task_manager.py +257 -0
- crackerjack/mcp/tools/__init__.py +17 -0
- crackerjack/mcp/tools/core_tools.py +249 -0
- crackerjack/mcp/tools/error_analyzer.py +308 -0
- crackerjack/mcp/tools/execution_tools.py +370 -0
- crackerjack/mcp/tools/execution_tools_backup.py +1097 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +80 -0
- crackerjack/mcp/tools/intelligence_tools.py +314 -0
- crackerjack/mcp/tools/monitoring_tools.py +502 -0
- crackerjack/mcp/tools/proactive_tools.py +384 -0
- crackerjack/mcp/tools/progress_tools.py +141 -0
- crackerjack/mcp/tools/utility_tools.py +341 -0
- crackerjack/mcp/tools/workflow_executor.py +360 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +39 -0
- crackerjack/mcp/websocket/endpoints.py +559 -0
- crackerjack/mcp/websocket/jobs.py +253 -0
- crackerjack/mcp/websocket/server.py +116 -0
- crackerjack/mcp/websocket/websocket_handler.py +78 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/__init__.py +31 -0
- crackerjack/models/config.py +93 -0
- crackerjack/models/config_adapter.py +230 -0
- crackerjack/models/protocols.py +118 -0
- crackerjack/models/task.py +154 -0
- crackerjack/monitoring/ai_agent_watchdog.py +450 -0
- crackerjack/monitoring/regression_prevention.py +638 -0
- crackerjack/orchestration/__init__.py +0 -0
- crackerjack/orchestration/advanced_orchestrator.py +970 -0
- crackerjack/orchestration/execution_strategies.py +341 -0
- crackerjack/orchestration/test_progress_streamer.py +636 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +246 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +259 -0
- crackerjack/py313.py +8 -3
- crackerjack/services/__init__.py +22 -0
- crackerjack/services/cache.py +314 -0
- crackerjack/services/config.py +347 -0
- crackerjack/services/config_integrity.py +99 -0
- crackerjack/services/contextual_ai_assistant.py +516 -0
- crackerjack/services/coverage_ratchet.py +347 -0
- crackerjack/services/debug.py +736 -0
- crackerjack/services/dependency_monitor.py +617 -0
- crackerjack/services/enhanced_filesystem.py +439 -0
- crackerjack/services/file_hasher.py +151 -0
- crackerjack/services/filesystem.py +395 -0
- crackerjack/services/git.py +165 -0
- crackerjack/services/health_metrics.py +611 -0
- crackerjack/services/initialization.py +847 -0
- crackerjack/services/log_manager.py +286 -0
- crackerjack/services/logging.py +174 -0
- crackerjack/services/metrics.py +578 -0
- crackerjack/services/pattern_cache.py +362 -0
- crackerjack/services/pattern_detector.py +515 -0
- crackerjack/services/performance_benchmarks.py +653 -0
- crackerjack/services/security.py +163 -0
- crackerjack/services/server_manager.py +234 -0
- crackerjack/services/smart_scheduling.py +144 -0
- crackerjack/services/tool_version_service.py +61 -0
- crackerjack/services/unified_config.py +437 -0
- crackerjack/services/version_checker.py +248 -0
- crackerjack/slash_commands/__init__.py +14 -0
- crackerjack/slash_commands/init.md +122 -0
- crackerjack/slash_commands/run.md +163 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack-0.31.4.dist-info/METADATA +742 -0
- crackerjack-0.31.4.dist-info/RECORD +148 -0
- crackerjack-0.31.4.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -34
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config-ai.yaml +0 -149
- crackerjack/.pre-commit-config-fast.yaml +0 -69
- crackerjack/.pre-commit-config.yaml +0 -114
- crackerjack/crackerjack.py +0 -4140
- crackerjack/pyproject.toml +0 -285
- crackerjack-0.29.0.dist-info/METADATA +0 -1289
- crackerjack-0.29.0.dist-info/RECORD +0 -17
- {crackerjack-0.29.0.dist-info ā crackerjack-0.31.4.dist-info}/WHEEL +0 -0
- {crackerjack-0.29.0.dist-info ā crackerjack-0.31.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI Agent Watchdog System
|
|
3
|
+
|
|
4
|
+
Monitors AI agent execution for failures, performance issues, and regression patterns.
|
|
5
|
+
Provides real-time oversight to prevent surprise failures and ensure reliable auto-fixing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import typing as t
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from datetime import datetime, timedelta
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from crackerjack.agents.base import FixResult, Issue, IssueType, Priority
|
|
16
|
+
from crackerjack.agents.coordinator import AgentCoordinator
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.live import Live
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class AgentPerformanceMetrics:
|
|
24
|
+
"""Track agent performance over time."""
|
|
25
|
+
|
|
26
|
+
agent_name: str
|
|
27
|
+
total_issues_handled: int = 0
|
|
28
|
+
successful_fixes: int = 0
|
|
29
|
+
failed_fixes: int = 0
|
|
30
|
+
average_confidence: float = 0.0
|
|
31
|
+
average_execution_time: float = 0.0
|
|
32
|
+
issue_types_handled: dict[IssueType, int] = field(default_factory=dict)
|
|
33
|
+
recent_failures: list[str] = field(default_factory=list)
|
|
34
|
+
last_successful_fix: datetime | None = None
|
|
35
|
+
regression_patterns: list[str] = field(default_factory=list)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class WatchdogAlert:
|
|
40
|
+
"""Watchdog alert for monitoring issues."""
|
|
41
|
+
|
|
42
|
+
level: str # "warning", "error", "critical"
|
|
43
|
+
message: str
|
|
44
|
+
agent_name: str | None = None
|
|
45
|
+
issue_id: str | None = None
|
|
46
|
+
timestamp: datetime = field(default_factory=datetime.now)
|
|
47
|
+
details: dict[str, t.Any] = field(default_factory=dict)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AIAgentWatchdog:
|
|
51
|
+
"""Monitors AI agent execution and prevents regression failures."""
|
|
52
|
+
|
|
53
|
+
def __init__(self, console: Console | None = None):
|
|
54
|
+
self.console = console or Console()
|
|
55
|
+
self.performance_metrics: dict[str, AgentPerformanceMetrics] = {}
|
|
56
|
+
self.alerts: list[WatchdogAlert] = []
|
|
57
|
+
self.known_regressions: set[str] = {
|
|
58
|
+
"detect_agent_needs_complexity_22",
|
|
59
|
+
"refactoring_agent_no_changes",
|
|
60
|
+
"agent_coordination_infinite_loop",
|
|
61
|
+
}
|
|
62
|
+
self.monitoring_active = False
|
|
63
|
+
self.execution_history: list[dict[str, t.Any]] = []
|
|
64
|
+
|
|
65
|
+
# Performance thresholds
|
|
66
|
+
self.max_execution_time = 30.0 # seconds
|
|
67
|
+
self.min_success_rate = 0.6 # 60%
|
|
68
|
+
self.max_recent_failures = 3
|
|
69
|
+
|
|
70
|
+
async def start_monitoring(self, coordinator: AgentCoordinator):
|
|
71
|
+
"""Start monitoring agent coordinator."""
|
|
72
|
+
self.monitoring_active = True
|
|
73
|
+
self.console.print("š [bold green]AI Agent Watchdog Started[/bold green]")
|
|
74
|
+
|
|
75
|
+
# Initialize metrics for all agents
|
|
76
|
+
coordinator.initialize_agents()
|
|
77
|
+
for agent in coordinator.agents:
|
|
78
|
+
agent_name = agent.__class__.__name__
|
|
79
|
+
if agent_name not in self.performance_metrics:
|
|
80
|
+
self.performance_metrics[agent_name] = AgentPerformanceMetrics(
|
|
81
|
+
agent_name=agent_name
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
self.console.print(
|
|
85
|
+
f"š Monitoring {len(coordinator.agents)} agents: {[a.__class__.__name__ for a in coordinator.agents]}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def stop_monitoring(self):
|
|
89
|
+
"""Stop monitoring and generate final report."""
|
|
90
|
+
self.monitoring_active = False
|
|
91
|
+
self.console.print("š [bold yellow]AI Agent Watchdog Stopped[/bold yellow]")
|
|
92
|
+
self._generate_final_report()
|
|
93
|
+
|
|
94
|
+
async def monitor_issue_handling(
|
|
95
|
+
self, agent_name: str, issue: Issue, result: FixResult, execution_time: float
|
|
96
|
+
):
|
|
97
|
+
"""Monitor individual issue handling by agents."""
|
|
98
|
+
if not self.monitoring_active:
|
|
99
|
+
return
|
|
100
|
+
|
|
101
|
+
metrics = self.performance_metrics.get(agent_name)
|
|
102
|
+
if not metrics:
|
|
103
|
+
metrics = AgentPerformanceMetrics(agent_name=agent_name)
|
|
104
|
+
self.performance_metrics[agent_name] = metrics
|
|
105
|
+
|
|
106
|
+
# Update metrics
|
|
107
|
+
metrics.total_issues_handled += 1
|
|
108
|
+
if result.success:
|
|
109
|
+
metrics.successful_fixes += 1
|
|
110
|
+
metrics.last_successful_fix = datetime.now()
|
|
111
|
+
else:
|
|
112
|
+
metrics.failed_fixes += 1
|
|
113
|
+
failure_key = f"{issue.type.value}_{issue.message[:50]}"
|
|
114
|
+
metrics.recent_failures.append(failure_key)
|
|
115
|
+
|
|
116
|
+
# Keep only recent failures
|
|
117
|
+
if len(metrics.recent_failures) > self.max_recent_failures:
|
|
118
|
+
metrics.recent_failures.pop(0)
|
|
119
|
+
|
|
120
|
+
# Update averages
|
|
121
|
+
total_fixes = metrics.successful_fixes + metrics.failed_fixes
|
|
122
|
+
metrics.average_confidence = (
|
|
123
|
+
metrics.average_confidence * (total_fixes - 1) + result.confidence
|
|
124
|
+
) / total_fixes
|
|
125
|
+
metrics.average_execution_time = (
|
|
126
|
+
metrics.average_execution_time * (total_fixes - 1) + execution_time
|
|
127
|
+
) / total_fixes
|
|
128
|
+
|
|
129
|
+
# Track issue types
|
|
130
|
+
metrics.issue_types_handled[issue.type] = (
|
|
131
|
+
metrics.issue_types_handled.get(issue.type, 0) + 1
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Check for alerts
|
|
135
|
+
await self._check_for_alerts(agent_name, issue, result, execution_time, metrics)
|
|
136
|
+
|
|
137
|
+
# Store execution history
|
|
138
|
+
self.execution_history.append(
|
|
139
|
+
{
|
|
140
|
+
"timestamp": datetime.now().isoformat(),
|
|
141
|
+
"agent": agent_name,
|
|
142
|
+
"issue_type": issue.type.value,
|
|
143
|
+
"issue_id": issue.id,
|
|
144
|
+
"success": result.success,
|
|
145
|
+
"confidence": result.confidence,
|
|
146
|
+
"execution_time": execution_time,
|
|
147
|
+
}
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# Keep history manageable
|
|
151
|
+
if len(self.execution_history) > 1000:
|
|
152
|
+
self.execution_history = self.execution_history[-500:]
|
|
153
|
+
|
|
154
|
+
async def _check_for_alerts(
|
|
155
|
+
self,
|
|
156
|
+
agent_name: str,
|
|
157
|
+
issue: Issue,
|
|
158
|
+
result: FixResult,
|
|
159
|
+
execution_time: float,
|
|
160
|
+
metrics: AgentPerformanceMetrics,
|
|
161
|
+
):
|
|
162
|
+
"""Check for alert conditions."""
|
|
163
|
+
alerts = []
|
|
164
|
+
|
|
165
|
+
# Performance alerts
|
|
166
|
+
if execution_time > self.max_execution_time:
|
|
167
|
+
alerts.append(
|
|
168
|
+
WatchdogAlert(
|
|
169
|
+
level="warning",
|
|
170
|
+
message=f"Agent took {execution_time:.1f}s (>{self.max_execution_time}s threshold)",
|
|
171
|
+
agent_name=agent_name,
|
|
172
|
+
issue_id=issue.id,
|
|
173
|
+
details={
|
|
174
|
+
"execution_time": execution_time,
|
|
175
|
+
"threshold": self.max_execution_time,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Success rate alerts
|
|
181
|
+
if metrics.total_issues_handled >= 5: # Only after handling multiple issues
|
|
182
|
+
success_rate = metrics.successful_fixes / metrics.total_issues_handled
|
|
183
|
+
if success_rate < self.min_success_rate:
|
|
184
|
+
alerts.append(
|
|
185
|
+
WatchdogAlert(
|
|
186
|
+
level="error",
|
|
187
|
+
message=f"Agent success rate {success_rate:.1%} below {self.min_success_rate:.1%} threshold",
|
|
188
|
+
agent_name=agent_name,
|
|
189
|
+
details={
|
|
190
|
+
"success_rate": success_rate,
|
|
191
|
+
"total_handled": metrics.total_issues_handled,
|
|
192
|
+
},
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Regression pattern alerts
|
|
197
|
+
failure_signature = f"{agent_name}_{issue.type.value}_{issue.message[:30]}"
|
|
198
|
+
if failure_signature in self.known_regressions and not result.success:
|
|
199
|
+
alerts.append(
|
|
200
|
+
WatchdogAlert(
|
|
201
|
+
level="critical",
|
|
202
|
+
message="REGRESSION DETECTED: Known failure pattern returned",
|
|
203
|
+
agent_name=agent_name,
|
|
204
|
+
issue_id=issue.id,
|
|
205
|
+
details={"regression_pattern": failure_signature},
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Repeated failure alerts
|
|
210
|
+
if len(metrics.recent_failures) >= self.max_recent_failures:
|
|
211
|
+
unique_failures = set(metrics.recent_failures)
|
|
212
|
+
if len(unique_failures) == 1: # Same failure repeated
|
|
213
|
+
alerts.append(
|
|
214
|
+
WatchdogAlert(
|
|
215
|
+
level="error",
|
|
216
|
+
message=f"Agent repeating same failure {len(metrics.recent_failures)} times",
|
|
217
|
+
agent_name=agent_name,
|
|
218
|
+
details={"repeated_failure": list(unique_failures)[0]},
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Add alerts
|
|
223
|
+
for alert in alerts:
|
|
224
|
+
self.alerts.append(alert)
|
|
225
|
+
await self._handle_alert(alert)
|
|
226
|
+
|
|
227
|
+
async def _handle_alert(self, alert: WatchdogAlert):
|
|
228
|
+
"""Handle watchdog alert."""
|
|
229
|
+
colors = {"warning": "yellow", "error": "red", "critical": "bold red"}
|
|
230
|
+
color = colors.get(alert.level) or "white"
|
|
231
|
+
|
|
232
|
+
icon = {"warning": "ā ļø", "error": "šØ", "critical": "š„"}[alert.level]
|
|
233
|
+
|
|
234
|
+
self.console.print(
|
|
235
|
+
f"{icon} [bold {color}]{alert.level.upper()}[/bold {color}]: {alert.message}"
|
|
236
|
+
)
|
|
237
|
+
if alert.agent_name:
|
|
238
|
+
self.console.print(f" Agent: {alert.agent_name}")
|
|
239
|
+
if alert.issue_id:
|
|
240
|
+
self.console.print(f" Issue: {alert.issue_id}")
|
|
241
|
+
|
|
242
|
+
# For critical alerts, suggest immediate actions
|
|
243
|
+
if alert.level == "critical":
|
|
244
|
+
self.console.print(" [bold red]IMMEDIATE ACTION REQUIRED[/bold red]")
|
|
245
|
+
if "regression" in alert.message.lower():
|
|
246
|
+
self.console.print(" ā Run regression tests immediately")
|
|
247
|
+
self.console.print(" ā Check agent implementation for recent changes")
|
|
248
|
+
|
|
249
|
+
def create_monitoring_dashboard(self) -> Table:
|
|
250
|
+
"""Create real-time monitoring dashboard."""
|
|
251
|
+
table = Table(
|
|
252
|
+
title="AI Agent Watchdog Dashboard",
|
|
253
|
+
header_style="bold magenta",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
table.add_column("Agent", style="cyan", width=20)
|
|
257
|
+
table.add_column("Issues Handled", justify="center")
|
|
258
|
+
table.add_column("Success Rate", justify="center")
|
|
259
|
+
table.add_column("Avg Confidence", justify="center")
|
|
260
|
+
table.add_column("Avg Time (s)", justify="center")
|
|
261
|
+
table.add_column("Last Success", justify="center")
|
|
262
|
+
table.add_column("Status", justify="center")
|
|
263
|
+
|
|
264
|
+
for agent_name, metrics in self.performance_metrics.items():
|
|
265
|
+
if metrics.total_issues_handled == 0:
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
success_rate = metrics.successful_fixes / metrics.total_issues_handled
|
|
269
|
+
|
|
270
|
+
# Status determination
|
|
271
|
+
status_color = "green"
|
|
272
|
+
status_text = "ā
OK"
|
|
273
|
+
|
|
274
|
+
if success_rate < self.min_success_rate:
|
|
275
|
+
status_color = "red"
|
|
276
|
+
status_text = "šØ FAILING"
|
|
277
|
+
elif len(metrics.recent_failures) >= 2:
|
|
278
|
+
status_color = "yellow"
|
|
279
|
+
status_text = "ā ļø WATCH"
|
|
280
|
+
|
|
281
|
+
last_success = "Never"
|
|
282
|
+
if metrics.last_successful_fix:
|
|
283
|
+
delta = datetime.now() - metrics.last_successful_fix
|
|
284
|
+
if delta.days > 0:
|
|
285
|
+
last_success = f"{delta.days}d ago"
|
|
286
|
+
elif delta.seconds > 3600:
|
|
287
|
+
last_success = f"{delta.seconds // 3600}h ago"
|
|
288
|
+
else:
|
|
289
|
+
last_success = f"{delta.seconds // 60}m ago"
|
|
290
|
+
|
|
291
|
+
table.add_row(
|
|
292
|
+
agent_name,
|
|
293
|
+
str(metrics.total_issues_handled),
|
|
294
|
+
f"{success_rate:.1%}",
|
|
295
|
+
f"{metrics.average_confidence:.2f}",
|
|
296
|
+
f"{metrics.average_execution_time:.1f}",
|
|
297
|
+
last_success,
|
|
298
|
+
f"[{status_color}]{status_text}[/{status_color}]",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
return table
|
|
302
|
+
|
|
303
|
+
def get_recent_alerts(self, hours: int = 1) -> list[WatchdogAlert]:
|
|
304
|
+
"""Get alerts from the last N hours."""
|
|
305
|
+
cutoff = datetime.now() - timedelta(hours=hours)
|
|
306
|
+
return [alert for alert in self.alerts if alert.timestamp > cutoff]
|
|
307
|
+
|
|
308
|
+
def _generate_final_report(self):
|
|
309
|
+
"""Generate final monitoring report."""
|
|
310
|
+
self.console.print("\nš [bold]AI Agent Watchdog Final Report[/bold]")
|
|
311
|
+
|
|
312
|
+
# Summary statistics
|
|
313
|
+
total_issues = sum(
|
|
314
|
+
m.total_issues_handled for m in self.performance_metrics.values()
|
|
315
|
+
)
|
|
316
|
+
total_successes = sum(
|
|
317
|
+
m.successful_fixes for m in self.performance_metrics.values()
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
if total_issues > 0:
|
|
321
|
+
overall_success_rate = total_successes / total_issues
|
|
322
|
+
self.console.print(
|
|
323
|
+
f"Overall Success Rate: {overall_success_rate:.1%} ({total_successes}/{total_issues})"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# Alert summary
|
|
327
|
+
alert_counts = {"warning": 0, "error": 0, "critical": 0}
|
|
328
|
+
for alert in self.alerts:
|
|
329
|
+
alert_counts[alert.level] += 1
|
|
330
|
+
|
|
331
|
+
self.console.print(
|
|
332
|
+
f"Alerts: {alert_counts['critical']} Critical, {alert_counts['error']} Errors, {alert_counts['warning']} Warnings"
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Top performing agents
|
|
336
|
+
if self.performance_metrics:
|
|
337
|
+
best_agent = max(
|
|
338
|
+
(
|
|
339
|
+
m
|
|
340
|
+
for m in self.performance_metrics.values()
|
|
341
|
+
if m.total_issues_handled > 0
|
|
342
|
+
),
|
|
343
|
+
key=lambda m: m.successful_fixes / m.total_issues_handled,
|
|
344
|
+
default=None,
|
|
345
|
+
)
|
|
346
|
+
if best_agent:
|
|
347
|
+
success_rate = (
|
|
348
|
+
best_agent.successful_fixes / best_agent.total_issues_handled
|
|
349
|
+
)
|
|
350
|
+
self.console.print(
|
|
351
|
+
f"Top Performer: {best_agent.agent_name} ({success_rate:.1%} success rate)"
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
# Save detailed report
|
|
355
|
+
self._save_monitoring_report()
|
|
356
|
+
|
|
357
|
+
def _save_monitoring_report(self):
|
|
358
|
+
"""Save detailed monitoring report to file."""
|
|
359
|
+
report_data = {
|
|
360
|
+
"timestamp": datetime.now().isoformat(),
|
|
361
|
+
"metrics": {
|
|
362
|
+
name: {
|
|
363
|
+
"total_issues": m.total_issues_handled,
|
|
364
|
+
"successful_fixes": m.successful_fixes,
|
|
365
|
+
"failed_fixes": m.failed_fixes,
|
|
366
|
+
"success_rate": m.successful_fixes / m.total_issues_handled
|
|
367
|
+
if m.total_issues_handled > 0
|
|
368
|
+
else 0,
|
|
369
|
+
"average_confidence": m.average_confidence,
|
|
370
|
+
"average_execution_time": m.average_execution_time,
|
|
371
|
+
"issue_types": {
|
|
372
|
+
k.value: v for k, v in m.issue_types_handled.items()
|
|
373
|
+
},
|
|
374
|
+
"recent_failures": m.recent_failures,
|
|
375
|
+
}
|
|
376
|
+
for name, m in self.performance_metrics.items()
|
|
377
|
+
},
|
|
378
|
+
"alerts": [
|
|
379
|
+
{
|
|
380
|
+
"level": a.level,
|
|
381
|
+
"message": a.message,
|
|
382
|
+
"agent": a.agent_name,
|
|
383
|
+
"issue_id": a.issue_id,
|
|
384
|
+
"timestamp": a.timestamp.isoformat(),
|
|
385
|
+
"details": a.details,
|
|
386
|
+
}
|
|
387
|
+
for a in self.alerts
|
|
388
|
+
],
|
|
389
|
+
"execution_history": self.execution_history,
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
report_file = Path(".crackerjack") / "ai_agent_monitoring_report.json"
|
|
393
|
+
report_file.parent.mkdir(exist_ok=True)
|
|
394
|
+
|
|
395
|
+
with open(report_file, "w") as f:
|
|
396
|
+
json.dump(report_data, f, indent=2)
|
|
397
|
+
|
|
398
|
+
self.console.print(f"š Detailed report saved: {report_file}")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
async def run_agent_monitoring_demo():
|
|
402
|
+
"""Demo of the AI agent monitoring system."""
|
|
403
|
+
console = Console()
|
|
404
|
+
watchdog = AIAgentWatchdog(console)
|
|
405
|
+
|
|
406
|
+
# Simulate agent context and coordinator
|
|
407
|
+
from crackerjack.agents.base import AgentContext
|
|
408
|
+
|
|
409
|
+
context = AgentContext(project_path=Path.cwd())
|
|
410
|
+
coordinator = AgentCoordinator(context)
|
|
411
|
+
|
|
412
|
+
await watchdog.start_monitoring(coordinator)
|
|
413
|
+
|
|
414
|
+
# Simulate some agent executions with monitoring
|
|
415
|
+
with Live(
|
|
416
|
+
watchdog.create_monitoring_dashboard(), refresh_per_second=1, console=console
|
|
417
|
+
) as live:
|
|
418
|
+
for i in range(10):
|
|
419
|
+
# Simulate issue handling
|
|
420
|
+
issue = Issue(
|
|
421
|
+
id=f"demo_{i}",
|
|
422
|
+
type=IssueType.COMPLEXITY if i % 2 == 0 else IssueType.FORMATTING,
|
|
423
|
+
severity=Priority.HIGH,
|
|
424
|
+
message=f"Demo issue {i}",
|
|
425
|
+
file_path="demo.py",
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
# Simulate varying results
|
|
429
|
+
success = i % 3 != 0 # Fail every 3rd attempt
|
|
430
|
+
result = FixResult(
|
|
431
|
+
success=success,
|
|
432
|
+
confidence=0.8 if success else 0.3,
|
|
433
|
+
fixes_applied=["Demo fix"] if success else [],
|
|
434
|
+
remaining_issues=[] if success else ["Demo failure"],
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
execution_time = 2.0 + (i % 5) * 0.5 # Varying execution times
|
|
438
|
+
|
|
439
|
+
await watchdog.monitor_issue_handling(
|
|
440
|
+
"DemoAgent", issue, result, execution_time
|
|
441
|
+
)
|
|
442
|
+
live.update(watchdog.create_monitoring_dashboard())
|
|
443
|
+
|
|
444
|
+
await asyncio.sleep(0.5) # Simulate work
|
|
445
|
+
|
|
446
|
+
watchdog.stop_monitoring()
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
if __name__ == "__main__":
|
|
450
|
+
asyncio.run(run_agent_monitoring_demo())
|