Delete core
Browse files- core/__init__.py +0 -0
- core/boundary_manager.py +0 -321
- core/calculators.py +0 -348
- core/data_models.py +0 -344
- core/enterprise_simulation.py +0 -376
- core/true_arf_orchestrator.py +0 -284
- core/true_arf_oss.py +0 -695
- core/visualizations.py +0 -876
core/__init__.py
DELETED
|
File without changes
|
core/boundary_manager.py
DELETED
|
@@ -1,321 +0,0 @@
|
|
| 1 |
-
# core/boundary_manager.py
|
| 2 |
-
"""
|
| 3 |
-
Boundary Manager for ARF Demo - Enforces clear separation between real OSS and simulated Enterprise
|
| 4 |
-
Ensures the audience always knows what's real vs simulated
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import logging
|
| 8 |
-
from typing import Dict, Any, Tuple
|
| 9 |
-
from dataclasses import dataclass
|
| 10 |
-
from enum import Enum
|
| 11 |
-
|
| 12 |
-
logger = logging.getLogger(__name__)
|
| 13 |
-
|
| 14 |
-
class SystemMode(Enum):
|
| 15 |
-
"""Clear system mode definitions"""
|
| 16 |
-
REAL_OSS_ADVISORY = "real_oss_advisory" # Real ARF OSS package
|
| 17 |
-
SIMULATED_ENTERPRISE = "simulated_enterprise" # Enterprise simulation
|
| 18 |
-
MOCK_FALLBACK = "mock_fallback" # Mock when nothing is available
|
| 19 |
-
|
| 20 |
-
@dataclass
|
| 21 |
-
class SystemBoundary:
|
| 22 |
-
"""Clear boundary definition with labels"""
|
| 23 |
-
mode: SystemMode
|
| 24 |
-
real_components: list[str]
|
| 25 |
-
simulated_components: list[str]
|
| 26 |
-
oss_license: str
|
| 27 |
-
enterprise_license: str
|
| 28 |
-
execution_allowed: bool
|
| 29 |
-
|
| 30 |
-
def get_display_labels(self) -> Dict[str, str]:
|
| 31 |
-
"""Get clear display labels for UI"""
|
| 32 |
-
base_labels = {
|
| 33 |
-
"system_mode": self.mode.value,
|
| 34 |
-
"oss_status": f"✅ REAL ARF OSS v3.3.7" if self.mode == SystemMode.REAL_OSS_ADVISORY else "⚠️ MOCK ARF",
|
| 35 |
-
"enterprise_status": f"🎭 SIMULATED Enterprise" if self.mode == SystemMode.SIMULATED_ENTERPRISE else "⚠️ MOCK Enterprise",
|
| 36 |
-
"execution_capability": "Advisory Only (Apache 2.0)" if not self.execution_allowed else "Autonomous Execution (Enterprise)",
|
| 37 |
-
"license_display": f"OSS: {self.oss_license} | Enterprise: {self.enterprise_license}",
|
| 38 |
-
"boundary_note": "OSS advises, Enterprise executes" if self.mode == SystemMode.SIMULATED_ENTERPRISE else "Mock mode for demo"
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
# Add color coding
|
| 42 |
-
if self.mode == SystemMode.REAL_OSS_ADVISORY:
|
| 43 |
-
base_labels.update({
|
| 44 |
-
"oss_color": "#10b981", # Green for real
|
| 45 |
-
"enterprise_color": "#f59e0b", # Amber for simulated
|
| 46 |
-
"oss_icon": "✅",
|
| 47 |
-
"enterprise_icon": "🎭"
|
| 48 |
-
})
|
| 49 |
-
else:
|
| 50 |
-
base_labels.update({
|
| 51 |
-
"oss_color": "#64748b", # Gray for mock
|
| 52 |
-
"enterprise_color": "#64748b", # Gray for mock
|
| 53 |
-
"oss_icon": "⚠️",
|
| 54 |
-
"enterprise_icon": "⚠️"
|
| 55 |
-
})
|
| 56 |
-
|
| 57 |
-
return base_labels
|
| 58 |
-
|
| 59 |
-
class BoundaryManager:
|
| 60 |
-
"""Manages system boundaries and ensures clear labeling"""
|
| 61 |
-
|
| 62 |
-
def __init__(self):
|
| 63 |
-
self.current_boundary = None
|
| 64 |
-
self.installation_status = self._check_installation()
|
| 65 |
-
self._initialize_boundary()
|
| 66 |
-
|
| 67 |
-
def _check_installation(self) -> Dict[str, Any]:
|
| 68 |
-
"""Check what's really installed"""
|
| 69 |
-
# Simplified version - in real app this checks actual packages
|
| 70 |
-
try:
|
| 71 |
-
from core.true_arf_oss import TrueARFOSS
|
| 72 |
-
oss_available = True
|
| 73 |
-
except ImportError:
|
| 74 |
-
oss_available = False
|
| 75 |
-
|
| 76 |
-
try:
|
| 77 |
-
from core.enterprise_simulation import EnterpriseFeatureSimulation
|
| 78 |
-
enterprise_available = True
|
| 79 |
-
except ImportError:
|
| 80 |
-
enterprise_available = False
|
| 81 |
-
|
| 82 |
-
return {
|
| 83 |
-
"oss_available": oss_available,
|
| 84 |
-
"enterprise_available": enterprise_available,
|
| 85 |
-
"true_arf_version": "3.3.7" if oss_available else "mock"
|
| 86 |
-
}
|
| 87 |
-
|
| 88 |
-
def _initialize_boundary(self):
|
| 89 |
-
"""Initialize the system boundary based on what's available"""
|
| 90 |
-
installation = self.installation_status
|
| 91 |
-
|
| 92 |
-
if installation["oss_available"]:
|
| 93 |
-
# Real OSS + Simulated Enterprise
|
| 94 |
-
self.current_boundary = SystemBoundary(
|
| 95 |
-
mode=SystemMode.REAL_OSS_ADVISORY,
|
| 96 |
-
real_components=["TrueARFOSS", "Detection Agent", "Recall Agent", "Decision Agent"],
|
| 97 |
-
simulated_components=["EnterpriseExecution", "RollbackGuarantees", "NovelExecutionProtocols"],
|
| 98 |
-
oss_license="Apache 2.0",
|
| 99 |
-
enterprise_license="SIMULATED (requires Commercial)",
|
| 100 |
-
execution_allowed=False # OSS is advisory only
|
| 101 |
-
)
|
| 102 |
-
logger.info("✅ System initialized with REAL ARF OSS + SIMULATED Enterprise")
|
| 103 |
-
|
| 104 |
-
elif installation["enterprise_available"]:
|
| 105 |
-
# Mock OSS + Simulated Enterprise (unlikely but possible)
|
| 106 |
-
self.current_boundary = SystemBoundary(
|
| 107 |
-
mode=SystemMode.SIMULATED_ENTERPRISE,
|
| 108 |
-
real_components=[],
|
| 109 |
-
simulated_components=["EnterpriseFeatures", "ExecutionProtocols"],
|
| 110 |
-
oss_license="MOCK",
|
| 111 |
-
enterprise_license="SIMULATED",
|
| 112 |
-
execution_allowed=True # Simulated execution
|
| 113 |
-
)
|
| 114 |
-
logger.info("⚠️ System initialized with MOCK OSS + SIMULATED Enterprise")
|
| 115 |
-
|
| 116 |
-
else:
|
| 117 |
-
# Complete mock mode
|
| 118 |
-
self.current_boundary = SystemBoundary(
|
| 119 |
-
mode=SystemMode.MOCK_FALLBACK,
|
| 120 |
-
real_components=[],
|
| 121 |
-
simulated_components=["AllComponents"],
|
| 122 |
-
oss_license="MOCK",
|
| 123 |
-
enterprise_license="MOCK",
|
| 124 |
-
execution_allowed=False
|
| 125 |
-
)
|
| 126 |
-
logger.info("⚠️ System initialized in MOCK FALLBACK mode")
|
| 127 |
-
|
| 128 |
-
def get_boundary_badges(self) -> str:
|
| 129 |
-
"""Get HTML badges showing clear boundaries"""
|
| 130 |
-
labels = self.current_boundary.get_display_labels()
|
| 131 |
-
|
| 132 |
-
return f"""
|
| 133 |
-
<div style="display: flex; justify-content: center; gap: 10px; margin-top: 10px; flex-wrap: wrap;">
|
| 134 |
-
<span style="padding: 4px 12px; background: {labels['oss_color']};
|
| 135 |
-
color: white; border-radius: 20px; font-size: 12px; font-weight: bold;
|
| 136 |
-
display: flex; align-items: center; gap: 6px;">
|
| 137 |
-
{labels['oss_icon']} {labels['oss_status']}
|
| 138 |
-
</span>
|
| 139 |
-
<span style="padding: 4px 12px; background: {labels['enterprise_color']};
|
| 140 |
-
color: white; border-radius: 20px; font-size: 12px; font-weight: bold;
|
| 141 |
-
display: flex; align-items: center; gap: 6px;">
|
| 142 |
-
{labels['enterprise_icon']} {labels['enterprise_status']}
|
| 143 |
-
</span>
|
| 144 |
-
<span style="padding: 4px 12px; background: #3b82f6;
|
| 145 |
-
color: white; border-radius: 20px; font-size: 12px; font-weight: bold;">
|
| 146 |
-
{labels['execution_capability']}
|
| 147 |
-
</span>
|
| 148 |
-
</div>
|
| 149 |
-
"""
|
| 150 |
-
|
| 151 |
-
def get_agent_html(self, agent_name: str, is_real: bool = True, status: str = "Active") -> str:
|
| 152 |
-
"""Get agent HTML with clear boundary indicators"""
|
| 153 |
-
icons = {
|
| 154 |
-
"Detection": "🕵️♂️",
|
| 155 |
-
"Recall": "🧠",
|
| 156 |
-
"Decision": "🎯"
|
| 157 |
-
}
|
| 158 |
-
|
| 159 |
-
real_badge = """
|
| 160 |
-
<div style="position: absolute; top: -8px; right: -8px; padding: 2px 8px; background: #10b981;
|
| 161 |
-
color: white; border-radius: 12px; font-size: 10px; font-weight: bold; z-index: 10;
|
| 162 |
-
border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 163 |
-
REAL ARF
|
| 164 |
-
</div>
|
| 165 |
-
""" if is_real else """
|
| 166 |
-
<div style="position: absolute; top: -8px; right: -8px; padding: 2px 8px; background: #f59e0b;
|
| 167 |
-
color: white; border-radius: 12px; font-size: 10px; font-weight: bold; z-index: 10;
|
| 168 |
-
border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
| 169 |
-
DEMO MODE
|
| 170 |
-
</div>
|
| 171 |
-
"""
|
| 172 |
-
|
| 173 |
-
border_color = "#10b981" if is_real else "#f59e0b"
|
| 174 |
-
background = "#f0fdf4" if is_real else "#fef3c7"
|
| 175 |
-
|
| 176 |
-
return f"""
|
| 177 |
-
<div style="position: relative;">
|
| 178 |
-
{real_badge}
|
| 179 |
-
<div style="border: 3px solid {border_color}; border-radius: 16px; padding: 20px;
|
| 180 |
-
background: {background}; text-align: center; min-height: 200px;
|
| 181 |
-
display: flex; flex-direction: column; align-items: center; justify-content: center;">
|
| 182 |
-
<div style="font-size: 42px; margin-bottom: 15px; opacity: 0.9;">{icons.get(agent_name, '🤖')}</div>
|
| 183 |
-
<div style="width: 100%;">
|
| 184 |
-
<h4 style="margin: 0 0 10px 0; font-size: 18px; color: #1e293b; font-weight: 600;">{agent_name} Agent</h4>
|
| 185 |
-
<p style="font-size: 14px; color: #475569; margin-bottom: 15px; line-height: 1.5;">
|
| 186 |
-
Status: <strong>{status}</strong><br>
|
| 187 |
-
<span style="font-size: 12px; color: {'#059669' if is_real else '#d97706'}">
|
| 188 |
-
{'Running on REAL ARF OSS v3.3.7' if is_real else 'Running in DEMO MODE'}
|
| 189 |
-
</span>
|
| 190 |
-
</p>
|
| 191 |
-
<div style="display: inline-block; padding: 8px 20px; background: linear-gradient(135deg, {border_color} 0%, {border_color}88 100%);
|
| 192 |
-
border-radius: 20px; font-size: 13px; font-weight: bold; color: white;
|
| 193 |
-
text-transform: uppercase; letter-spacing: 0.5px; margin-top: 10px;">
|
| 194 |
-
{'ACTIVE (REAL)' if is_real else 'SIMULATED'}
|
| 195 |
-
</div>
|
| 196 |
-
</div>
|
| 197 |
-
</div>
|
| 198 |
-
</div>
|
| 199 |
-
"""
|
| 200 |
-
|
| 201 |
-
def get_execution_boundary_html(self, action: str, is_simulated: bool = True) -> str:
|
| 202 |
-
"""Get clear execution boundary indicator"""
|
| 203 |
-
if is_simulated:
|
| 204 |
-
return f"""
|
| 205 |
-
<div style="border: 3px dashed #f59e0b; border-radius: 16px; padding: 25px;
|
| 206 |
-
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
| 207 |
-
text-align: center; margin: 20px 0;">
|
| 208 |
-
<div style="font-size: 36px; margin-bottom: 15px;">🎭</div>
|
| 209 |
-
<h4 style="margin: 0 0 12px 0; font-size: 20px; color: #92400e; font-weight: 700;">
|
| 210 |
-
SIMULATED ENTERPRISE EXECUTION
|
| 211 |
-
</h4>
|
| 212 |
-
<p style="font-size: 15px; color: #92400e; margin-bottom: 15px; line-height: 1.6;">
|
| 213 |
-
<strong>Action:</strong> {action}<br>
|
| 214 |
-
<strong>Mode:</strong> Enterprise Simulation (not real execution)<br>
|
| 215 |
-
<strong>Boundary:</strong> OSS advises → Enterprise would execute
|
| 216 |
-
</p>
|
| 217 |
-
<div style="display: inline-block; padding: 10px 24px; background: #92400e;
|
| 218 |
-
border-radius: 20px; font-size: 14px; font-weight: bold; color: white;
|
| 219 |
-
text-transform: uppercase; letter-spacing: 1px;">
|
| 220 |
-
DEMO BOUNDARY
|
| 221 |
-
</div>
|
| 222 |
-
<p style="font-size: 13px; color: #92400e; margin-top: 15px; font-style: italic;">
|
| 223 |
-
In production, Enterprise edition would execute against real infrastructure
|
| 224 |
-
</p>
|
| 225 |
-
</div>
|
| 226 |
-
"""
|
| 227 |
-
else:
|
| 228 |
-
return f"""
|
| 229 |
-
<div style="border: 3px solid #10b981; border-radius: 16px; padding: 25px;
|
| 230 |
-
background: linear-gradient(135deg, #f0fdf4 0%, #bbf7d0 100%);
|
| 231 |
-
text-align: center; margin: 20px 0;">
|
| 232 |
-
<div style="font-size: 36px; margin-bottom: 15px;">⚡</div>
|
| 233 |
-
<h4 style="margin: 0 0 12px 0; font-size: 20px; color: #065f46; font-weight: 700;">
|
| 234 |
-
REAL ENTERPRISE EXECUTION
|
| 235 |
-
</h4>
|
| 236 |
-
<p style="font-size: 15px; color: #065f46; margin-bottom: 15px; line-height: 1.6;">
|
| 237 |
-
<strong>Action:</strong> {action}<br>
|
| 238 |
-
<strong>Mode:</strong> Enterprise Autonomous<br>
|
| 239 |
-
<strong>Boundary:</strong> Real execution with safety guarantees
|
| 240 |
-
</p>
|
| 241 |
-
<div style="display: inline-block; padding: 10px 24px; background: #065f46;
|
| 242 |
-
border-radius: 20px; font-size: 14px; font-weight: bold; color: white;
|
| 243 |
-
text-transform: uppercase; letter-spacing: 1px;">
|
| 244 |
-
ENTERPRISE+
|
| 245 |
-
</div>
|
| 246 |
-
</div>
|
| 247 |
-
"""
|
| 248 |
-
|
| 249 |
-
def get_demo_narrative(self, phase: str) -> str:
|
| 250 |
-
"""Get narrative text for each demo phase"""
|
| 251 |
-
narratives = {
|
| 252 |
-
"introduction": """
|
| 253 |
-
<div style="background: #f8fafc; border-radius: 12px; padding: 20px; margin: 20px 0; border-left: 4px solid #3b82f6;">
|
| 254 |
-
<h4 style="margin: 0 0 10px 0; color: #1e293b;">🎯 Demo Introduction</h4>
|
| 255 |
-
<p style="margin: 0; color: #475569; font-size: 14px; line-height: 1.6;">
|
| 256 |
-
This demo shows the <strong>clear architectural boundary</strong> between ARF OSS (real advisory intelligence)
|
| 257 |
-
and ARF Enterprise (simulated autonomous execution). We're showing what happens in production,
|
| 258 |
-
not hiding behind mock data.
|
| 259 |
-
</p>
|
| 260 |
-
</div>
|
| 261 |
-
""",
|
| 262 |
-
|
| 263 |
-
"oss_analysis": """
|
| 264 |
-
<div style="background: #f0fdf4; border-radius: 12px; padding: 20px; margin: 20px 0; border-left: 4px solid #10b981;">
|
| 265 |
-
<h4 style="margin: 0 0 10px 0; color: #065f46;">🧠 Real OSS Intelligence</h4>
|
| 266 |
-
<p style="margin: 0; color: #047857; font-size: 14px; line-height: 1.6;">
|
| 267 |
-
ARF OSS v3.3.7 is <strong>analyzing the incident in real-time</strong>. This is not a mock - it's the actual
|
| 268 |
-
ARF OSS package running detection, recall, and decision agents. Notice the confidence scores
|
| 269 |
-
and reasoning chain.
|
| 270 |
-
</p>
|
| 271 |
-
</div>
|
| 272 |
-
""",
|
| 273 |
-
|
| 274 |
-
"enterprise_simulation": """
|
| 275 |
-
<div style="background: #fef3c7; border-radius: 12px; padding: 20px; margin: 20px 0; border-left: 4px solid #f59e0b;">
|
| 276 |
-
<h4 style="margin: 0 0 10px 0; color: #92400e;">🎭 Enterprise Simulation Boundary</h4>
|
| 277 |
-
<p style="margin: 0; color: #b45309; font-size: 14px; line-height: 1.6;">
|
| 278 |
-
This is where we <strong>simulate Enterprise execution</strong>. In production, Enterprise would:
|
| 279 |
-
1. Validate safety constraints, 2. Execute with rollback guarantees, 3. Update the learning engine.
|
| 280 |
-
We're showing the value proposition without real infrastructure access.
|
| 281 |
-
</p>
|
| 282 |
-
</div>
|
| 283 |
-
""",
|
| 284 |
-
|
| 285 |
-
"conclusion": """
|
| 286 |
-
<div style="background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); border-radius: 12px; padding: 20px; margin: 20px 0; border: 2px solid #3b82f6;">
|
| 287 |
-
<h4 style="margin: 0 0 10px 0; color: #1e293b;">✅ Architecture Validated</h4>
|
| 288 |
-
<p style="margin: 0; color: #475569; font-size: 14px; line-height: 1.6;">
|
| 289 |
-
<strong>What we demonstrated:</strong><br>
|
| 290 |
-
• Real ARF OSS intelligence (advisory)<br>
|
| 291 |
-
• Clear execution boundary (OSS vs Enterprise)<br>
|
| 292 |
-
• Simulated Enterprise value proposition<br>
|
| 293 |
-
• Production-ready architecture pattern<br><br>
|
| 294 |
-
This isn't AI theater - it's a production-grade reliability system with honest boundaries.
|
| 295 |
-
</p>
|
| 296 |
-
</div>
|
| 297 |
-
"""
|
| 298 |
-
}
|
| 299 |
-
|
| 300 |
-
return narratives.get(phase, "")
|
| 301 |
-
|
| 302 |
-
def validate_transition(self, from_mode: SystemMode, to_mode: SystemMode) -> Tuple[bool, str]:
|
| 303 |
-
"""Validate mode transitions (e.g., can't go from mock to real execution)"""
|
| 304 |
-
transitions = {
|
| 305 |
-
(SystemMode.REAL_OSS_ADVISORY, SystemMode.SIMULATED_ENTERPRISE): (True, "Valid: Real OSS to Simulated Enterprise"),
|
| 306 |
-
(SystemMode.MOCK_FALLBACK, SystemMode.SIMULATED_ENTERPRISE): (True, "Valid: Mock to Simulated Enterprise"),
|
| 307 |
-
(SystemMode.SIMULATED_ENTERPRISE, SystemMode.REAL_OSS_ADVISORY): (False, "Invalid: Can't go from Enterprise simulation back to OSS in demo"),
|
| 308 |
-
}
|
| 309 |
-
|
| 310 |
-
result = transitions.get((from_mode, to_mode), (True, "Valid transition"))
|
| 311 |
-
return result
|
| 312 |
-
|
| 313 |
-
# Singleton instance
|
| 314 |
-
_boundary_manager = None
|
| 315 |
-
|
| 316 |
-
def get_boundary_manager() -> BoundaryManager:
|
| 317 |
-
"""Get singleton BoundaryManager instance"""
|
| 318 |
-
global _boundary_manager
|
| 319 |
-
if _boundary_manager is None:
|
| 320 |
-
_boundary_manager = BoundaryManager()
|
| 321 |
-
return _boundary_manager
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/calculators.py
DELETED
|
@@ -1,348 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Enhanced ROI calculators and business logic with Monte Carlo simulation
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from typing import Dict, List, Any, Tuple
|
| 6 |
-
import numpy as np
|
| 7 |
-
import logging
|
| 8 |
-
from dataclasses import dataclass
|
| 9 |
-
from enum import Enum
|
| 10 |
-
from config.settings import settings
|
| 11 |
-
|
| 12 |
-
logger = logging.getLogger(__name__)
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
class ROIConfidence(Enum):
|
| 16 |
-
"""Confidence levels for ROI predictions"""
|
| 17 |
-
HIGH = "High"
|
| 18 |
-
MEDIUM = "Medium"
|
| 19 |
-
LOW = "Low"
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
@dataclass
|
| 23 |
-
class ROIScenarioResult:
|
| 24 |
-
"""Result of a single ROI scenario calculation"""
|
| 25 |
-
scenario_name: str
|
| 26 |
-
annual_impact: float
|
| 27 |
-
enterprise_cost: float
|
| 28 |
-
savings: float
|
| 29 |
-
roi_multiplier: float
|
| 30 |
-
roi_percentage: float
|
| 31 |
-
payback_months: float
|
| 32 |
-
confidence: ROIConfidence
|
| 33 |
-
|
| 34 |
-
def to_dict(self) -> Dict[str, Any]:
|
| 35 |
-
"""Convert to dictionary"""
|
| 36 |
-
return {
|
| 37 |
-
"scenario_name": self.scenario_name,
|
| 38 |
-
"annual_impact": f"${self.annual_impact:,.0f}",
|
| 39 |
-
"enterprise_cost": f"${self.enterprise_cost:,.0f}",
|
| 40 |
-
"savings": f"${self.savings:,.0f}",
|
| 41 |
-
"roi_multiplier": f"{self.roi_multiplier:.1f}×",
|
| 42 |
-
"roi_percentage": f"{self.roi_percentage:.0f}%",
|
| 43 |
-
"payback_months": f"{self.payback_months:.1f}",
|
| 44 |
-
"confidence": self.confidence.value
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class EnhancedROICalculator:
|
| 49 |
-
"""Investor-grade ROI calculator with Monte Carlo simulation"""
|
| 50 |
-
|
| 51 |
-
def __init__(self):
|
| 52 |
-
self.engineer_hourly_rate = settings.engineer_hourly_rate
|
| 53 |
-
self.engineer_annual_cost = settings.engineer_annual_cost
|
| 54 |
-
self.default_savings_rate = settings.default_savings_rate
|
| 55 |
-
|
| 56 |
-
def calculate_comprehensive_roi(self, monthly_incidents: int,
|
| 57 |
-
avg_impact: float, team_size: int) -> Dict[str, Any]:
|
| 58 |
-
"""
|
| 59 |
-
Calculate multi-scenario ROI analysis with Monte Carlo simulation
|
| 60 |
-
|
| 61 |
-
Args:
|
| 62 |
-
monthly_incidents: Average incidents per month
|
| 63 |
-
avg_impact: Average revenue impact per incident
|
| 64 |
-
team_size: Number of engineers
|
| 65 |
-
|
| 66 |
-
Returns:
|
| 67 |
-
Comprehensive ROI analysis
|
| 68 |
-
"""
|
| 69 |
-
logger.info(f"Calculating ROI: incidents={monthly_incidents}, "
|
| 70 |
-
f"impact=${avg_impact:,}, team={team_size}")
|
| 71 |
-
|
| 72 |
-
# Base scenario (realistic)
|
| 73 |
-
base = self._calculate_with_monte_carlo(
|
| 74 |
-
monthly_incidents, avg_impact, team_size,
|
| 75 |
-
savings_rate_mean=0.82, savings_rate_std=0.05,
|
| 76 |
-
efficiency_mean=0.85, efficiency_std=0.03
|
| 77 |
-
)
|
| 78 |
-
|
| 79 |
-
# Best case (aggressive adoption)
|
| 80 |
-
best = self._calculate_with_monte_carlo(
|
| 81 |
-
monthly_incidents, avg_impact, team_size,
|
| 82 |
-
savings_rate_mean=0.92, savings_rate_std=0.03,
|
| 83 |
-
efficiency_mean=0.92, efficiency_std=0.02
|
| 84 |
-
)
|
| 85 |
-
|
| 86 |
-
# Worst case (conservative)
|
| 87 |
-
worst = self._calculate_with_monte_carlo(
|
| 88 |
-
monthly_incidents, avg_impact, team_size,
|
| 89 |
-
savings_rate_mean=0.72, savings_rate_std=0.07,
|
| 90 |
-
efficiency_mean=0.78, efficiency_std=0.05
|
| 91 |
-
)
|
| 92 |
-
|
| 93 |
-
# Generate recommendation
|
| 94 |
-
recommendation = self._get_recommendation(base.mean_roi)
|
| 95 |
-
|
| 96 |
-
# Calculate industry comparison
|
| 97 |
-
comparison = self._get_industry_comparison(base.mean_roi)
|
| 98 |
-
|
| 99 |
-
return {
|
| 100 |
-
"summary": {
|
| 101 |
-
"your_annual_impact": f"${base.mean_annual_impact:,.0f}",
|
| 102 |
-
"potential_savings": f"${base.mean_savings:,.0f}",
|
| 103 |
-
"enterprise_cost": f"${base.enterprise_cost:,.0f}",
|
| 104 |
-
"roi_multiplier": f"{base.mean_roi:.1f}×",
|
| 105 |
-
"payback_months": f"{base.mean_payback:.1f}",
|
| 106 |
-
"annual_roi_percentage": f"{base.mean_roi_percentage:.0f}%",
|
| 107 |
-
"monte_carlo_simulations": 1000,
|
| 108 |
-
"confidence_interval": f"{base.roi_ci[0]:.1f}× - {base.roi_ci[1]:.1f}×"
|
| 109 |
-
},
|
| 110 |
-
"scenarios": {
|
| 111 |
-
"base_case": {
|
| 112 |
-
"roi": f"{base.mean_roi:.1f}×",
|
| 113 |
-
"payback": f"{base.mean_payback:.1f} months",
|
| 114 |
-
"confidence": base.confidence.value,
|
| 115 |
-
"ci_low": f"{base.roi_ci[0]:.1f}×",
|
| 116 |
-
"ci_high": f"{base.roi_ci[1]:.1f}×"
|
| 117 |
-
},
|
| 118 |
-
"best_case": {
|
| 119 |
-
"roi": f"{best.mean_roi:.1f}×",
|
| 120 |
-
"payback": f"{best.mean_payback:.1f} months",
|
| 121 |
-
"confidence": best.confidence.value,
|
| 122 |
-
"ci_low": f"{best.roi_ci[0]:.1f}×",
|
| 123 |
-
"ci_high": f"{best.roi_ci[1]:.1f}×"
|
| 124 |
-
},
|
| 125 |
-
"worst_case": {
|
| 126 |
-
"roi": f"{worst.mean_roi:.1f}×",
|
| 127 |
-
"payback": f"{worst.mean_payback:.1f} months",
|
| 128 |
-
"confidence": worst.confidence.value,
|
| 129 |
-
"ci_low": f"{worst.roi_ci[0]:.1f}×",
|
| 130 |
-
"ci_high": f"{worst.roi_ci[1]:.1f}×"
|
| 131 |
-
}
|
| 132 |
-
},
|
| 133 |
-
"comparison": comparison,
|
| 134 |
-
"recommendation": recommendation,
|
| 135 |
-
"monte_carlo_stats": {
|
| 136 |
-
"base_roi_std": f"{base.roi_std:.2f}",
|
| 137 |
-
"best_roi_std": f"{best.roi_std:.2f}",
|
| 138 |
-
"worst_roi_std": f"{worst.roi_std:.2f}"
|
| 139 |
-
}
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
def _calculate_with_monte_carlo(self, monthly_incidents: int, avg_impact: float,
|
| 143 |
-
team_size: int, savings_rate_mean: float,
|
| 144 |
-
savings_rate_std: float, efficiency_mean: float,
|
| 145 |
-
efficiency_std: float) -> 'MonteCarloResult':
|
| 146 |
-
"""
|
| 147 |
-
Run Monte Carlo simulation for ROI calculation
|
| 148 |
-
|
| 149 |
-
Returns:
|
| 150 |
-
MonteCarloResult with statistics
|
| 151 |
-
"""
|
| 152 |
-
np.random.seed(42) # For reproducible results
|
| 153 |
-
|
| 154 |
-
n_simulations = 1000
|
| 155 |
-
|
| 156 |
-
# Generate random samples with normal distribution
|
| 157 |
-
savings_rates = np.random.normal(
|
| 158 |
-
savings_rate_mean, savings_rate_std, n_simulations
|
| 159 |
-
)
|
| 160 |
-
efficiencies = np.random.normal(
|
| 161 |
-
efficiency_mean, efficiency_std, n_simulations
|
| 162 |
-
)
|
| 163 |
-
|
| 164 |
-
# Clip to reasonable bounds
|
| 165 |
-
savings_rates = np.clip(savings_rates, 0.5, 0.95)
|
| 166 |
-
efficiencies = np.clip(efficiencies, 0.5, 0.95)
|
| 167 |
-
|
| 168 |
-
# Calculate for each simulation
|
| 169 |
-
annual_impacts = monthly_incidents * 12 * avg_impact
|
| 170 |
-
enterprise_costs = team_size * self.engineer_annual_cost
|
| 171 |
-
|
| 172 |
-
savings_list = []
|
| 173 |
-
roi_list = []
|
| 174 |
-
roi_percentage_list = []
|
| 175 |
-
payback_list = []
|
| 176 |
-
|
| 177 |
-
for i in range(n_simulations):
|
| 178 |
-
savings = annual_impacts * savings_rates[i] * efficiencies[i]
|
| 179 |
-
roi = savings / enterprise_costs if enterprise_costs > 0 else 0
|
| 180 |
-
roi_percentage = (roi - 1) * 100
|
| 181 |
-
payback = (enterprise_costs / (savings / 12)) if savings > 0 else 0
|
| 182 |
-
|
| 183 |
-
savings_list.append(savings)
|
| 184 |
-
roi_list.append(roi)
|
| 185 |
-
roi_percentage_list.append(roi_percentage)
|
| 186 |
-
payback_list.append(payback)
|
| 187 |
-
|
| 188 |
-
# Convert to numpy arrays for statistics
|
| 189 |
-
savings_arr = np.array(savings_list)
|
| 190 |
-
roi_arr = np.array(roi_list)
|
| 191 |
-
roi_percentage_arr = np.array(roi_percentage_list)
|
| 192 |
-
payback_arr = np.array(payback_list)
|
| 193 |
-
|
| 194 |
-
# Calculate statistics
|
| 195 |
-
mean_savings = np.mean(savings_arr)
|
| 196 |
-
mean_roi = np.mean(roi_arr)
|
| 197 |
-
mean_roi_percentage = np.mean(roi_percentage_arr)
|
| 198 |
-
mean_payback = np.mean(payback_arr)
|
| 199 |
-
|
| 200 |
-
roi_std = np.std(roi_arr)
|
| 201 |
-
roi_ci = (
|
| 202 |
-
np.percentile(roi_arr, 25),
|
| 203 |
-
np.percentile(roi_arr, 75)
|
| 204 |
-
)
|
| 205 |
-
|
| 206 |
-
# Determine confidence level
|
| 207 |
-
if roi_std / mean_roi < 0.1: # Low relative standard deviation
|
| 208 |
-
confidence = ROIConfidence.HIGH
|
| 209 |
-
elif roi_std / mean_roi < 0.2:
|
| 210 |
-
confidence = ROIConfidence.MEDIUM
|
| 211 |
-
else:
|
| 212 |
-
confidence = ROIConfidence.LOW
|
| 213 |
-
|
| 214 |
-
return MonteCarloResult(
|
| 215 |
-
mean_annual_impact=annual_impacts,
|
| 216 |
-
enterprise_cost=enterprise_costs,
|
| 217 |
-
mean_savings=mean_savings,
|
| 218 |
-
mean_roi=mean_roi,
|
| 219 |
-
mean_roi_percentage=mean_roi_percentage,
|
| 220 |
-
mean_payback=mean_payback,
|
| 221 |
-
roi_std=roi_std,
|
| 222 |
-
roi_ci=roi_ci,
|
| 223 |
-
confidence=confidence,
|
| 224 |
-
n_simulations=n_simulations
|
| 225 |
-
)
|
| 226 |
-
|
| 227 |
-
def _get_recommendation(self, roi_multiplier: float) -> Dict[str, str]:
|
| 228 |
-
"""Get recommendation based on ROI"""
|
| 229 |
-
if roi_multiplier >= 5.0:
|
| 230 |
-
return {
|
| 231 |
-
"action": "🚀 Deploy ARF Enterprise",
|
| 232 |
-
"reason": "Exceptional ROI (>5×) with quick payback",
|
| 233 |
-
"timeline": "30-day implementation",
|
| 234 |
-
"expected_value": ">$1M annual savings",
|
| 235 |
-
"priority": "High",
|
| 236 |
-
"next_steps": [
|
| 237 |
-
"Schedule enterprise demo",
|
| 238 |
-
"Request custom ROI analysis",
|
| 239 |
-
"Start 30-day trial"
|
| 240 |
-
]
|
| 241 |
-
}
|
| 242 |
-
elif roi_multiplier >= 3.0:
|
| 243 |
-
return {
|
| 244 |
-
"action": "✅ Implement ARF Enterprise",
|
| 245 |
-
"reason": "Strong ROI (3-5×) with operational benefits",
|
| 246 |
-
"timeline": "60-day phased rollout",
|
| 247 |
-
"expected_value": ">$500K annual savings",
|
| 248 |
-
"priority": "Medium",
|
| 249 |
-
"next_steps": [
|
| 250 |
-
"Evaluate OSS edition",
|
| 251 |
-
"Run pilot with 2-3 services",
|
| 252 |
-
"Measure initial impact"
|
| 253 |
-
]
|
| 254 |
-
}
|
| 255 |
-
elif roi_multiplier >= 2.0:
|
| 256 |
-
return {
|
| 257 |
-
"action": "📊 Evaluate ARF Enterprise",
|
| 258 |
-
"reason": "Positive ROI (2-3×) with learning benefits",
|
| 259 |
-
"timeline": "90-day evaluation",
|
| 260 |
-
"expected_value": ">$250K annual savings",
|
| 261 |
-
"priority": "Medium-Low",
|
| 262 |
-
"next_steps": [
|
| 263 |
-
"Start with OSS edition",
|
| 264 |
-
"Document baseline metrics",
|
| 265 |
-
"Identify pilot use cases"
|
| 266 |
-
]
|
| 267 |
-
}
|
| 268 |
-
else:
|
| 269 |
-
return {
|
| 270 |
-
"action": "🆓 Start with ARF OSS",
|
| 271 |
-
"reason": "Validate value before Enterprise investment",
|
| 272 |
-
"timeline": "14-day evaluation",
|
| 273 |
-
"expected_value": "Operational insights + clear upgrade path",
|
| 274 |
-
"priority": "Low",
|
| 275 |
-
"next_steps": [
|
| 276 |
-
"Install OSS edition",
|
| 277 |
-
"Analyze 2-3 incident scenarios",
|
| 278 |
-
"Document potential improvements"
|
| 279 |
-
]
|
| 280 |
-
}
|
| 281 |
-
|
| 282 |
-
def _get_percentile(self, roi_multiplier: float) -> int:
|
| 283 |
-
"""Calculate percentile vs industry benchmarks"""
|
| 284 |
-
benchmarks = [
|
| 285 |
-
(10.0, 5), # Top 5% at 10× ROI
|
| 286 |
-
(8.0, 10), # Top 10% at 8× ROI
|
| 287 |
-
(5.0, 25), # Top 25% at 5× ROI
|
| 288 |
-
(3.0, 50), # Top 50% at 3× ROI
|
| 289 |
-
(2.0, 75), # Top 75% at 2× ROI
|
| 290 |
-
(1.0, 90) # Top 90% at 1× ROI
|
| 291 |
-
]
|
| 292 |
-
|
| 293 |
-
for threshold, percentile in benchmarks:
|
| 294 |
-
if roi_multiplier >= threshold:
|
| 295 |
-
return percentile
|
| 296 |
-
|
| 297 |
-
return 95 # Bottom 5%
|
| 298 |
-
|
| 299 |
-
def _get_industry_comparison(self, roi_multiplier: float) -> Dict[str, str]:
|
| 300 |
-
"""Get industry comparison metrics"""
|
| 301 |
-
percentile = self._get_percentile(roi_multiplier)
|
| 302 |
-
|
| 303 |
-
return {
|
| 304 |
-
"industry_average": "5.2× ROI",
|
| 305 |
-
"top_performers": "8.7× ROI",
|
| 306 |
-
"your_position": f"Top {percentile}%",
|
| 307 |
-
"benchmark_analysis": "Above industry average" if roi_multiplier >= 5.2 else "Below industry average",
|
| 308 |
-
"improvement_potential": f"{max(0, 8.7 - roi_multiplier):.1f}× additional ROI possible"
|
| 309 |
-
}
|
| 310 |
-
|
| 311 |
-
def calculate_simple_roi(self, monthly_incidents: int,
|
| 312 |
-
avg_impact: float, team_size: int) -> Dict[str, Any]:
|
| 313 |
-
"""
|
| 314 |
-
Simple ROI calculation without Monte Carlo
|
| 315 |
-
|
| 316 |
-
For backward compatibility
|
| 317 |
-
"""
|
| 318 |
-
result = self._calculate_with_monte_carlo(
|
| 319 |
-
monthly_incidents, avg_impact, team_size,
|
| 320 |
-
savings_rate_mean=self.default_savings_rate,
|
| 321 |
-
savings_rate_std=0.05,
|
| 322 |
-
efficiency_mean=0.85,
|
| 323 |
-
efficiency_std=0.03
|
| 324 |
-
)
|
| 325 |
-
|
| 326 |
-
return {
|
| 327 |
-
"annual_impact": result.mean_annual_impact,
|
| 328 |
-
"enterprise_cost": result.enterprise_cost,
|
| 329 |
-
"savings": result.mean_savings,
|
| 330 |
-
"roi_multiplier": result.mean_roi,
|
| 331 |
-
"roi_percentage": result.mean_roi_percentage,
|
| 332 |
-
"payback_months": result.mean_payback
|
| 333 |
-
}
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
@dataclass
|
| 337 |
-
class MonteCarloResult:
|
| 338 |
-
"""Result of Monte Carlo simulation"""
|
| 339 |
-
mean_annual_impact: float
|
| 340 |
-
enterprise_cost: float
|
| 341 |
-
mean_savings: float
|
| 342 |
-
mean_roi: float
|
| 343 |
-
mean_roi_percentage: float
|
| 344 |
-
mean_payback: float
|
| 345 |
-
roi_std: float
|
| 346 |
-
roi_ci: Tuple[float, float]
|
| 347 |
-
confidence: ROIConfidence
|
| 348 |
-
n_simulations: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/data_models.py
DELETED
|
@@ -1,344 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Pythonic data models for ARF Demo - COMPLETE VERSION
|
| 3 |
-
"""
|
| 4 |
-
|
| 5 |
-
from dataclasses import dataclass, asdict
|
| 6 |
-
from enum import Enum
|
| 7 |
-
from typing import Dict, List, Optional, Any
|
| 8 |
-
import datetime
|
| 9 |
-
|
| 10 |
-
# Import from actual ARF OSS package
|
| 11 |
-
try:
|
| 12 |
-
from agentic_reliability_framework.arf_core.models.healing_intent import (
|
| 13 |
-
HealingIntent,
|
| 14 |
-
create_scale_out_intent,
|
| 15 |
-
create_rollback_intent,
|
| 16 |
-
create_restart_intent
|
| 17 |
-
)
|
| 18 |
-
from agentic_reliability_framework.arf_core.engine.simple_mcp_client import OSSMCPClient
|
| 19 |
-
ARF_OSS_AVAILABLE = True
|
| 20 |
-
except ImportError:
|
| 21 |
-
ARF_OSS_AVAILABLE = False
|
| 22 |
-
# Fallback mock classes for demo
|
| 23 |
-
class HealingIntent:
|
| 24 |
-
def __init__(self, **kwargs):
|
| 25 |
-
self.intent_type = kwargs.get("intent_type", "scale_out")
|
| 26 |
-
self.parameters = kwargs.get("parameters", {})
|
| 27 |
-
|
| 28 |
-
def to_dict(self):
|
| 29 |
-
return {
|
| 30 |
-
"intent_type": self.intent_type,
|
| 31 |
-
"parameters": self.parameters,
|
| 32 |
-
"created_at": datetime.datetime.now().isoformat()
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
def create_scale_out_intent(resource_type: str, scale_factor: float = 2.0):
|
| 36 |
-
return HealingIntent(
|
| 37 |
-
intent_type="scale_out",
|
| 38 |
-
parameters={
|
| 39 |
-
"resource_type": resource_type,
|
| 40 |
-
"scale_factor": scale_factor,
|
| 41 |
-
"action": "Increase capacity"
|
| 42 |
-
}
|
| 43 |
-
)
|
| 44 |
-
|
| 45 |
-
class OSSMCPClient:
|
| 46 |
-
def __init__(self):
|
| 47 |
-
self.mode = "advisory"
|
| 48 |
-
|
| 49 |
-
def analyze_incident(self, metrics: Dict, pattern: str = "") -> Dict:
|
| 50 |
-
return {
|
| 51 |
-
"status": "analysis_complete",
|
| 52 |
-
"recommendations": [
|
| 53 |
-
"Increase resource allocation",
|
| 54 |
-
"Implement monitoring",
|
| 55 |
-
"Add circuit breakers",
|
| 56 |
-
"Optimize configuration"
|
| 57 |
-
],
|
| 58 |
-
"confidence": 0.92,
|
| 59 |
-
"pattern_matched": pattern,
|
| 60 |
-
"healing_intent": {
|
| 61 |
-
"type": "scale_out",
|
| 62 |
-
"requires_execution": True
|
| 63 |
-
}
|
| 64 |
-
}
|
| 65 |
-
|
| 66 |
-
class IncidentSeverity(Enum):
|
| 67 |
-
"""Enum for incident severity levels"""
|
| 68 |
-
LOW = "LOW"
|
| 69 |
-
MEDIUM = "MEDIUM"
|
| 70 |
-
HIGH = "HIGH"
|
| 71 |
-
CRITICAL = "CRITICAL"
|
| 72 |
-
|
| 73 |
-
class DemoMode(Enum):
|
| 74 |
-
"""Enum for demo modes"""
|
| 75 |
-
QUICK = "quick"
|
| 76 |
-
COMPREHENSIVE = "comprehensive"
|
| 77 |
-
INVESTOR = "investor"
|
| 78 |
-
|
| 79 |
-
@dataclass
|
| 80 |
-
class OSSAnalysis:
|
| 81 |
-
"""Structured OSS analysis results - using actual ARF"""
|
| 82 |
-
status: str
|
| 83 |
-
recommendations: List[str]
|
| 84 |
-
estimated_time: str
|
| 85 |
-
engineers_needed: str
|
| 86 |
-
manual_effort: str
|
| 87 |
-
confidence_score: float = 0.95
|
| 88 |
-
healing_intent: Optional[Dict] = None
|
| 89 |
-
|
| 90 |
-
def to_dict(self) -> Dict:
|
| 91 |
-
"""Convert to dictionary, including healing intent if available"""
|
| 92 |
-
data = asdict(self)
|
| 93 |
-
if self.healing_intent:
|
| 94 |
-
data["healing_intent"] = {
|
| 95 |
-
"type": "HealingIntent",
|
| 96 |
-
"recommendations": self.recommendations,
|
| 97 |
-
"requires_execution": True
|
| 98 |
-
}
|
| 99 |
-
return data
|
| 100 |
-
|
| 101 |
-
@classmethod
|
| 102 |
-
def from_arf_analysis(cls, arf_result: Dict, scenario_name: str) -> 'OSSAnalysis':
|
| 103 |
-
"""Create from actual ARF analysis result"""
|
| 104 |
-
recommendations = arf_result.get("recommendations", [
|
| 105 |
-
"Increase resource allocation",
|
| 106 |
-
"Implement monitoring",
|
| 107 |
-
"Add circuit breakers",
|
| 108 |
-
"Optimize configuration"
|
| 109 |
-
])
|
| 110 |
-
|
| 111 |
-
return cls(
|
| 112 |
-
status="✅ ARF OSS Analysis Complete",
|
| 113 |
-
recommendations=recommendations,
|
| 114 |
-
estimated_time="45-90 minutes",
|
| 115 |
-
engineers_needed="2-3 engineers",
|
| 116 |
-
manual_effort="High",
|
| 117 |
-
confidence_score=0.92,
|
| 118 |
-
healing_intent={
|
| 119 |
-
"scenario": scenario_name,
|
| 120 |
-
"actions": recommendations,
|
| 121 |
-
"execution_required": True,
|
| 122 |
-
"auto_execution": False # OSS is advisory only
|
| 123 |
-
}
|
| 124 |
-
)
|
| 125 |
-
|
| 126 |
-
@dataclass
|
| 127 |
-
class EnterpriseResults:
|
| 128 |
-
"""Structured enterprise execution results"""
|
| 129 |
-
actions_completed: List[str]
|
| 130 |
-
metrics_improvement: Dict[str, str]
|
| 131 |
-
business_impact: Dict[str, Any]
|
| 132 |
-
approval_required: bool = True
|
| 133 |
-
execution_time: str = ""
|
| 134 |
-
healing_intent_executed: bool = True
|
| 135 |
-
|
| 136 |
-
def to_dict(self) -> Dict:
|
| 137 |
-
data = asdict(self)
|
| 138 |
-
data["arf_enterprise"] = {
|
| 139 |
-
"execution_complete": True,
|
| 140 |
-
"learning_applied": True,
|
| 141 |
-
"audit_trail_created": True
|
| 142 |
-
}
|
| 143 |
-
return data
|
| 144 |
-
|
| 145 |
-
@dataclass
|
| 146 |
-
class IncidentScenario:
|
| 147 |
-
"""Pythonic incident scenario model with ARF integration"""
|
| 148 |
-
name: str
|
| 149 |
-
severity: IncidentSeverity
|
| 150 |
-
metrics: Dict[str, str]
|
| 151 |
-
impact: Dict[str, str]
|
| 152 |
-
arf_pattern: str = "" # ARF pattern name for RAG recall
|
| 153 |
-
oss_analysis: Optional[OSSAnalysis] = None
|
| 154 |
-
enterprise_results: Optional[EnterpriseResults] = None
|
| 155 |
-
|
| 156 |
-
def to_dict(self) -> Dict:
|
| 157 |
-
"""Convert to dictionary for JSON serialization"""
|
| 158 |
-
data = {
|
| 159 |
-
"name": self.name,
|
| 160 |
-
"severity": self.severity.value,
|
| 161 |
-
"metrics": self.metrics,
|
| 162 |
-
"impact": self.impact,
|
| 163 |
-
"arf_oss_available": ARF_OSS_AVAILABLE
|
| 164 |
-
}
|
| 165 |
-
if self.oss_analysis:
|
| 166 |
-
data["oss_analysis"] = self.oss_analysis.to_dict()
|
| 167 |
-
if self.enterprise_results:
|
| 168 |
-
data["enterprise_results"] = self.enterprise_results.to_dict()
|
| 169 |
-
return data
|
| 170 |
-
|
| 171 |
-
@dataclass
|
| 172 |
-
class DemoStep:
|
| 173 |
-
"""Demo step for presenter guidance"""
|
| 174 |
-
title: str
|
| 175 |
-
scenario: Optional[str]
|
| 176 |
-
action: str
|
| 177 |
-
message: str
|
| 178 |
-
icon: str = "🎯"
|
| 179 |
-
arf_integration: bool = False
|
| 180 |
-
|
| 181 |
-
# ===========================================
|
| 182 |
-
# INCIDENT DATABASE - ADD THIS CLASS
|
| 183 |
-
# ===========================================
|
| 184 |
-
|
| 185 |
-
class IncidentDatabase:
|
| 186 |
-
"""Database of incident scenarios for the demo"""
|
| 187 |
-
|
| 188 |
-
@staticmethod
|
| 189 |
-
def get_scenarios() -> Dict[str, IncidentScenario]:
|
| 190 |
-
"""Get all incident scenarios"""
|
| 191 |
-
cache_miss = IncidentScenario(
|
| 192 |
-
name="Cache Miss Storm",
|
| 193 |
-
severity=IncidentSeverity.CRITICAL,
|
| 194 |
-
metrics={
|
| 195 |
-
"Cache Hit Rate": "18.5% (Critical)",
|
| 196 |
-
"Database Load": "92% (Overloaded)",
|
| 197 |
-
"Response Time": "1850ms (Slow)",
|
| 198 |
-
"Affected Users": "45,000",
|
| 199 |
-
"Eviction Rate": "125/sec"
|
| 200 |
-
},
|
| 201 |
-
impact={
|
| 202 |
-
"Revenue Loss": "$8,500/hour",
|
| 203 |
-
"Page Load Time": "+300%",
|
| 204 |
-
"Users Impacted": "45,000",
|
| 205 |
-
"SLA Violation": "Yes",
|
| 206 |
-
"Customer Satisfaction": "-40%"
|
| 207 |
-
},
|
| 208 |
-
arf_pattern="cache_miss_storm",
|
| 209 |
-
oss_analysis=OSSAnalysis(
|
| 210 |
-
status="✅ Analysis Complete",
|
| 211 |
-
recommendations=[
|
| 212 |
-
"Increase Redis cache memory allocation by 2x",
|
| 213 |
-
"Implement cache warming strategy with predictive loading",
|
| 214 |
-
"Optimize key patterns and implement TTL adjustments",
|
| 215 |
-
"Add circuit breaker for graceful database fallback",
|
| 216 |
-
"Deploy monitoring for cache hit rate trends"
|
| 217 |
-
],
|
| 218 |
-
estimated_time="60-90 minutes",
|
| 219 |
-
engineers_needed="2-3 SREs + 1 DBA",
|
| 220 |
-
manual_effort="High",
|
| 221 |
-
confidence_score=0.92,
|
| 222 |
-
healing_intent={
|
| 223 |
-
"type": "scale_out",
|
| 224 |
-
"resource": "cache",
|
| 225 |
-
"scale_factor": 2.0
|
| 226 |
-
}
|
| 227 |
-
),
|
| 228 |
-
enterprise_results=EnterpriseResults(
|
| 229 |
-
actions_completed=[
|
| 230 |
-
"✅ Auto-scaled Redis cluster: 4GB → 8GB",
|
| 231 |
-
"✅ Deployed intelligent cache warming service",
|
| 232 |
-
"✅ Optimized 12 key patterns with ML recommendations",
|
| 233 |
-
"✅ Implemented circuit breaker with 95% success rate",
|
| 234 |
-
"✅ Validated recovery with automated testing"
|
| 235 |
-
],
|
| 236 |
-
metrics_improvement={
|
| 237 |
-
"Cache Hit Rate": "18.5% → 72%",
|
| 238 |
-
"Response Time": "1850ms → 450ms",
|
| 239 |
-
"Database Load": "92% → 45%",
|
| 240 |
-
"Throughput": "1250 → 2450 req/sec"
|
| 241 |
-
},
|
| 242 |
-
business_impact={
|
| 243 |
-
"Recovery Time": "60 min → 12 min",
|
| 244 |
-
"Cost Saved": "$7,200",
|
| 245 |
-
"Users Impacted": "45,000 → 0",
|
| 246 |
-
"Revenue Protected": "$1,700",
|
| 247 |
-
"MTTR Improvement": "80% reduction"
|
| 248 |
-
},
|
| 249 |
-
approval_required=True,
|
| 250 |
-
execution_time="8 minutes"
|
| 251 |
-
)
|
| 252 |
-
)
|
| 253 |
-
|
| 254 |
-
db_exhaustion = IncidentScenario(
|
| 255 |
-
name="Database Connection Pool Exhaustion",
|
| 256 |
-
severity=IncidentSeverity.HIGH,
|
| 257 |
-
metrics={
|
| 258 |
-
"Active Connections": "98/100 (Critical)",
|
| 259 |
-
"API Latency": "2450ms",
|
| 260 |
-
"Error Rate": "15.2%",
|
| 261 |
-
"Queue Depth": "1250",
|
| 262 |
-
"Connection Wait Time": "45s"
|
| 263 |
-
},
|
| 264 |
-
impact={
|
| 265 |
-
"Revenue Loss": "$4,200/hour",
|
| 266 |
-
"Affected Services": "API Gateway, User Service, Payment Service",
|
| 267 |
-
"SLA Violation": "Yes",
|
| 268 |
-
"Partner Impact": "3 external APIs"
|
| 269 |
-
},
|
| 270 |
-
arf_pattern="db_connection_exhaustion",
|
| 271 |
-
oss_analysis=OSSAnalysis(
|
| 272 |
-
status="✅ Analysis Complete",
|
| 273 |
-
recommendations=[
|
| 274 |
-
"Increase connection pool size from 100 to 200",
|
| 275 |
-
"Add connection timeout (30s)",
|
| 276 |
-
"Implement leak detection",
|
| 277 |
-
"Add connection health checks",
|
| 278 |
-
"Optimize query patterns"
|
| 279 |
-
],
|
| 280 |
-
estimated_time="45-60 minutes",
|
| 281 |
-
engineers_needed="1-2 DBAs",
|
| 282 |
-
manual_effort="Medium-High",
|
| 283 |
-
confidence_score=0.88
|
| 284 |
-
)
|
| 285 |
-
)
|
| 286 |
-
|
| 287 |
-
memory_leak = IncidentScenario(
|
| 288 |
-
name="Memory Leak in Production",
|
| 289 |
-
severity=IncidentSeverity.HIGH,
|
| 290 |
-
metrics={
|
| 291 |
-
"Memory Usage": "96% (Critical)",
|
| 292 |
-
"GC Pause Time": "4500ms",
|
| 293 |
-
"Error Rate": "28.5%",
|
| 294 |
-
"Restart Frequency": "12/hour",
|
| 295 |
-
"Heap Fragmentation": "42%"
|
| 296 |
-
},
|
| 297 |
-
impact={
|
| 298 |
-
"Revenue Loss": "$5,500/hour",
|
| 299 |
-
"Session Loss": "8,500 users",
|
| 300 |
-
"Customer Impact": "High",
|
| 301 |
-
"Support Tickets": "+300%"
|
| 302 |
-
},
|
| 303 |
-
arf_pattern="memory_leak_java",
|
| 304 |
-
oss_analysis=OSSAnalysis(
|
| 305 |
-
status="✅ Analysis Complete",
|
| 306 |
-
recommendations=[
|
| 307 |
-
"Increase JVM heap size from 4GB to 8GB",
|
| 308 |
-
"Implement memory leak detection with profiling",
|
| 309 |
-
"Add proactive health checks",
|
| 310 |
-
"Schedule rolling restart with zero downtime",
|
| 311 |
-
"Deploy memory monitoring dashboard"
|
| 312 |
-
],
|
| 313 |
-
estimated_time="75-90 minutes",
|
| 314 |
-
engineers_needed="2 Java SREs",
|
| 315 |
-
manual_effort="High",
|
| 316 |
-
confidence_score=0.85
|
| 317 |
-
)
|
| 318 |
-
)
|
| 319 |
-
|
| 320 |
-
api_rate_limit = IncidentScenario(
|
| 321 |
-
name="API Rate Limit Exceeded",
|
| 322 |
-
severity=IncidentSeverity.MEDIUM,
|
| 323 |
-
metrics={
|
| 324 |
-
"429 Error Rate": "42.5%",
|
| 325 |
-
"Successful Requests": "58.3%",
|
| 326 |
-
"API Latency": "120ms",
|
| 327 |
-
"Queue Depth": "1250",
|
| 328 |
-
"Client Satisfaction": "65/100"
|
| 329 |
-
},
|
| 330 |
-
impact={
|
| 331 |
-
"Revenue Loss": "$1,800/hour",
|
| 332 |
-
"Affected Partners": "8",
|
| 333 |
-
"Partner SLA Violations": "3",
|
| 334 |
-
"Business Impact": "Medium"
|
| 335 |
-
},
|
| 336 |
-
arf_pattern="api_rate_limit"
|
| 337 |
-
)
|
| 338 |
-
|
| 339 |
-
return {
|
| 340 |
-
"Cache Miss Storm": cache_miss,
|
| 341 |
-
"Database Connection Pool Exhaustion": db_exhaustion,
|
| 342 |
-
"Memory Leak in Production": memory_leak,
|
| 343 |
-
"API Rate Limit Exceeded": api_rate_limit
|
| 344 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/enterprise_simulation.py
DELETED
|
@@ -1,376 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Enterprise Feature Simulation - Shows what ARF Enterprise adds on top of OSS
|
| 3 |
-
Not real execution, but demonstrates the value proposition
|
| 4 |
-
"""
|
| 5 |
-
import asyncio
|
| 6 |
-
import logging
|
| 7 |
-
from typing import Dict, Any, List
|
| 8 |
-
from datetime import datetime
|
| 9 |
-
import random
|
| 10 |
-
|
| 11 |
-
logger = logging.getLogger(__name__)
|
| 12 |
-
|
| 13 |
-
# Trial license for demo
|
| 14 |
-
DEMO_TRIAL_LICENSE = "ARF-TRIAL-DEMO-2026"
|
| 15 |
-
|
| 16 |
-
class EnterpriseFeatureSimulation:
|
| 17 |
-
"""
|
| 18 |
-
Simulates Enterprise features that would be available with arf_enterprise package
|
| 19 |
-
|
| 20 |
-
Shows:
|
| 21 |
-
1. Novel execution protocols
|
| 22 |
-
2. Rollback guarantees
|
| 23 |
-
3. Deterministic confidence
|
| 24 |
-
4. Autonomous healing
|
| 25 |
-
5. Enhanced safety features
|
| 26 |
-
"""
|
| 27 |
-
|
| 28 |
-
def __init__(self):
|
| 29 |
-
self.enterprise_available = False
|
| 30 |
-
self.trial_license = DEMO_TRIAL_LICENSE
|
| 31 |
-
self._check_enterprise()
|
| 32 |
-
|
| 33 |
-
def _check_enterprise(self):
|
| 34 |
-
"""Check if enterprise package is available"""
|
| 35 |
-
try:
|
| 36 |
-
# Try to import real enterprise package
|
| 37 |
-
from arf_enterprise import (
|
| 38 |
-
create_enterprise_server,
|
| 39 |
-
EnterpriseLLMClient,
|
| 40 |
-
RollbackController,
|
| 41 |
-
ExecutionMode,
|
| 42 |
-
DeterministicConfidence,
|
| 43 |
-
NovelExecutionIntent,
|
| 44 |
-
get_novel_execution_capabilities
|
| 45 |
-
)
|
| 46 |
-
self.enterprise_available = True
|
| 47 |
-
logger.info("✅ Real ARF Enterprise package available")
|
| 48 |
-
except ImportError:
|
| 49 |
-
self.enterprise_available = False
|
| 50 |
-
logger.info("⚠️ ARF Enterprise package not available - using simulation")
|
| 51 |
-
|
| 52 |
-
async def enhance_oss_analysis(self, oss_analysis: Dict[str, Any], scenario_name: str) -> Dict[str, Any]:
|
| 53 |
-
"""
|
| 54 |
-
Enhance OSS analysis with Enterprise features
|
| 55 |
-
|
| 56 |
-
Shows what Enterprise adds:
|
| 57 |
-
- Novel execution protocols
|
| 58 |
-
- Rollback guarantees
|
| 59 |
-
- Deterministic confidence
|
| 60 |
-
- Business impact analysis
|
| 61 |
-
"""
|
| 62 |
-
logger.info(f"🏢 Enhancing OSS analysis with Enterprise features for: {scenario_name}")
|
| 63 |
-
|
| 64 |
-
enhancement_start = datetime.now()
|
| 65 |
-
|
| 66 |
-
try:
|
| 67 |
-
# Extract data from OSS analysis
|
| 68 |
-
oss_intent = oss_analysis.get("analysis", {}).get("decision", {})
|
| 69 |
-
similar_incidents = oss_analysis.get("analysis", {}).get("recall", [])
|
| 70 |
-
detection = oss_analysis.get("analysis", {}).get("detection", {})
|
| 71 |
-
|
| 72 |
-
# 1. Apply deterministic confidence system (Enterprise feature)
|
| 73 |
-
deterministic_confidence = self._create_deterministic_confidence(
|
| 74 |
-
detection, similar_incidents, scenario_name
|
| 75 |
-
)
|
| 76 |
-
|
| 77 |
-
# 2. Apply novel execution protocols (Enterprise feature)
|
| 78 |
-
novel_execution = self._apply_novel_execution_protocols(
|
| 79 |
-
oss_intent, deterministic_confidence, scenario_name
|
| 80 |
-
)
|
| 81 |
-
|
| 82 |
-
# 3. Prepare rollback guarantees (Enterprise feature)
|
| 83 |
-
rollback_guarantees = await self._prepare_rollback_guarantees(
|
| 84 |
-
oss_intent, scenario_name
|
| 85 |
-
)
|
| 86 |
-
|
| 87 |
-
# 4. Calculate enhanced business impact (Enterprise feature)
|
| 88 |
-
business_impact = self._calculate_enhanced_business_impact(
|
| 89 |
-
scenario_name, similar_incidents
|
| 90 |
-
)
|
| 91 |
-
|
| 92 |
-
# 5. Determine execution mode capabilities
|
| 93 |
-
execution_capabilities = self._get_execution_capabilities()
|
| 94 |
-
|
| 95 |
-
enhancement_time = (datetime.now() - enhancement_start).total_seconds() * 1000
|
| 96 |
-
|
| 97 |
-
return {
|
| 98 |
-
"enterprise_available": self.enterprise_available,
|
| 99 |
-
"trial_license": self.trial_license if not self.enterprise_available else "Real License",
|
| 100 |
-
"enhancements": {
|
| 101 |
-
"deterministic_confidence": deterministic_confidence,
|
| 102 |
-
"novel_execution_protocols": novel_execution,
|
| 103 |
-
"rollback_guarantees": rollback_guarantees,
|
| 104 |
-
"business_impact_analysis": business_impact,
|
| 105 |
-
"execution_capabilities": execution_capabilities
|
| 106 |
-
},
|
| 107 |
-
"value_proposition": [
|
| 108 |
-
"✅ Autonomous execution with safety guarantees",
|
| 109 |
-
"✅ Novel execution protocols for unprecedented incidents",
|
| 110 |
-
"✅ Deterministic confidence scoring (not just ML probabilities)",
|
| 111 |
-
"✅ Rollback guarantees for zero-downtime deployments",
|
| 112 |
-
"✅ Business-aware impact analysis",
|
| 113 |
-
"✅ Audit trail and compliance reporting",
|
| 114 |
-
f"✅ Execution modes: {', '.join(execution_capabilities['modes'])}"
|
| 115 |
-
],
|
| 116 |
-
"processing_time_ms": enhancement_time,
|
| 117 |
-
"requires_real_enterprise": not self.enterprise_available,
|
| 118 |
-
"upgrade_cta": "Contact sales@arf.dev for Enterprise trial" if not self.enterprise_available else None
|
| 119 |
-
}
|
| 120 |
-
|
| 121 |
-
except Exception as e:
|
| 122 |
-
logger.error(f"Enterprise enhancement failed: {e}")
|
| 123 |
-
return {
|
| 124 |
-
"enterprise_available": self.enterprise_available,
|
| 125 |
-
"error": str(e),
|
| 126 |
-
"fallback_message": "OSS analysis complete. Enterprise features require arf_enterprise package."
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
def _create_deterministic_confidence(self, detection: Dict, similar_incidents: List, scenario_name: str) -> Dict[str, Any]:
|
| 130 |
-
"""Simulate deterministic confidence system (Enterprise feature)"""
|
| 131 |
-
detection_confidence = detection.get("confidence", 0.85)
|
| 132 |
-
|
| 133 |
-
# Calculate pattern confidence from similar incidents
|
| 134 |
-
if similar_incidents:
|
| 135 |
-
pattern_confidence = sum([inc.get("similarity_score", 0.7) for inc in similar_incidents]) / len(similar_incidents)
|
| 136 |
-
success_rate = sum([1 for inc in similar_incidents if inc.get("success", False)]) / len(similar_incidents)
|
| 137 |
-
else:
|
| 138 |
-
pattern_confidence = 0.75
|
| 139 |
-
success_rate = 0.70
|
| 140 |
-
|
| 141 |
-
# Scenario-specific adjustments
|
| 142 |
-
scenario_factors = {
|
| 143 |
-
"Cache Miss Storm": {"historical_pattern": 0.92, "current_metrics": 0.87, "system_state": 0.95},
|
| 144 |
-
"Database Connection Pool Exhaustion": {"historical_pattern": 0.88, "current_metrics": 0.82, "system_state": 0.90},
|
| 145 |
-
"Kubernetes Memory Leak": {"historical_pattern": 0.90, "current_metrics": 0.85, "system_state": 0.92},
|
| 146 |
-
"API Rate Limit Storm": {"historical_pattern": 0.85, "current_metrics": 0.88, "system_state": 0.87},
|
| 147 |
-
"Network Partition": {"historical_pattern": 0.93, "current_metrics": 0.90, "system_state": 0.96},
|
| 148 |
-
"Storage I/O Saturation": {"historical_pattern": 0.87, "current_metrics": 0.83, "system_state": 0.89}
|
| 149 |
-
}
|
| 150 |
-
|
| 151 |
-
factors = scenario_factors.get(scenario_name, {"historical_pattern": 0.85, "current_metrics": 0.80, "system_state": 0.85})
|
| 152 |
-
|
| 153 |
-
# Combine factors deterministically (not just ML probability)
|
| 154 |
-
business_context = 0.88 # Always consider business impact
|
| 155 |
-
safety_margin = 0.95 # Enterprise includes safety margins
|
| 156 |
-
|
| 157 |
-
components = [
|
| 158 |
-
{"component": "historical_pattern", "value": factors["historical_pattern"], "weight": 0.25},
|
| 159 |
-
{"component": "current_metrics", "value": factors["current_metrics"], "weight": 0.25},
|
| 160 |
-
{"component": "system_state", "value": factors["system_state"], "weight": 0.20},
|
| 161 |
-
{"component": "detection_confidence", "value": detection_confidence, "weight": 0.15},
|
| 162 |
-
{"component": "business_context", "value": business_context, "weight": 0.10},
|
| 163 |
-
{"component": "safety_margin", "value": safety_margin, "weight": 0.05}
|
| 164 |
-
]
|
| 165 |
-
|
| 166 |
-
# Calculate weighted score
|
| 167 |
-
weighted_score = sum(c["value"] * c["weight"] for c in components)
|
| 168 |
-
|
| 169 |
-
return {
|
| 170 |
-
"score": round(weighted_score, 3),
|
| 171 |
-
"components": components,
|
| 172 |
-
"deterministic": True, # Enterprise feature: deterministic not probabilistic
|
| 173 |
-
"explainable": True, # Enterprise feature: each component explained
|
| 174 |
-
"safety_margin_included": True
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
def _apply_novel_execution_protocols(self, oss_intent: Dict, confidence: Dict, scenario_name: str) -> Dict[str, Any]:
|
| 178 |
-
"""Apply novel execution protocols (Enterprise feature)"""
|
| 179 |
-
# Determine novelty level based on confidence and scenario
|
| 180 |
-
confidence_score = confidence.get("score", 0.85)
|
| 181 |
-
|
| 182 |
-
if confidence_score >= 0.95:
|
| 183 |
-
novelty_level = "KNOWN_PATTERN"
|
| 184 |
-
risk_category = "LOW"
|
| 185 |
-
execution_approach = "autonomous_safe"
|
| 186 |
-
elif confidence_score >= 0.85:
|
| 187 |
-
novelty_level = "PARTIAL_MATCH"
|
| 188 |
-
risk_category = "MEDIUM"
|
| 189 |
-
execution_approach = "human_approval_required"
|
| 190 |
-
else:
|
| 191 |
-
novelty_level = "NOVEL_SCENARIO"
|
| 192 |
-
risk_category = "HIGH"
|
| 193 |
-
execution_approach = "enhanced_monitoring_first"
|
| 194 |
-
|
| 195 |
-
return {
|
| 196 |
-
"novelty_level": novelty_level,
|
| 197 |
-
"risk_category": risk_category,
|
| 198 |
-
"execution_approach": execution_approach,
|
| 199 |
-
"protocols_applied": [
|
| 200 |
-
"deterministic_confidence_validation",
|
| 201 |
-
"blast_radius_containment",
|
| 202 |
-
"business_hour_compliance",
|
| 203 |
-
"rollback_preparation",
|
| 204 |
-
"circuit_breaker_setup"
|
| 205 |
-
],
|
| 206 |
-
"enterprise_feature": True,
|
| 207 |
-
"requires_license": True
|
| 208 |
-
}
|
| 209 |
-
|
| 210 |
-
async def _prepare_rollback_guarantees(self, oss_intent: Dict, scenario_name: str) -> Dict[str, Any]:
|
| 211 |
-
"""Prepare rollback guarantees (Enterprise feature)"""
|
| 212 |
-
await asyncio.sleep(0.1) # Simulate rollback preparation
|
| 213 |
-
|
| 214 |
-
component = oss_intent.get("component", "unknown")
|
| 215 |
-
|
| 216 |
-
return {
|
| 217 |
-
"rollback_prepared": True,
|
| 218 |
-
"state_id": f"state_{datetime.now().timestamp()}",
|
| 219 |
-
"guarantee": "STRONG",
|
| 220 |
-
"recovery_time_estimate": "45 seconds",
|
| 221 |
-
"snapshot_strategy": "incremental",
|
| 222 |
-
"verification_complete": True,
|
| 223 |
-
"rollback_scenarios": [
|
| 224 |
-
f"Restore {component} to previous state",
|
| 225 |
-
"Rollback configuration changes",
|
| 226 |
-
"Restore database connections",
|
| 227 |
-
"Reset circuit breakers"
|
| 228 |
-
],
|
| 229 |
-
"enterprise_feature": True,
|
| 230 |
-
"requires_enterprise_server": True
|
| 231 |
-
}
|
| 232 |
-
|
| 233 |
-
def _calculate_enhanced_business_impact(self, scenario_name: str, similar_incidents: List) -> Dict[str, Any]:
|
| 234 |
-
"""Calculate enhanced business impact (Enterprise feature)"""
|
| 235 |
-
# Get average savings from similar incidents
|
| 236 |
-
if similar_incidents:
|
| 237 |
-
avg_savings = sum(inc.get("cost_savings", 5000) for inc in similar_incidents) / len(similar_incidents)
|
| 238 |
-
avg_resolution_time = 15 # minutes (average from similar incidents)
|
| 239 |
-
else:
|
| 240 |
-
avg_savings = 6500
|
| 241 |
-
avg_resolution_time = 20
|
| 242 |
-
|
| 243 |
-
# Scenario-specific impacts
|
| 244 |
-
scenario_impacts = {
|
| 245 |
-
"Cache Miss Storm": {
|
| 246 |
-
"users_affected": 45000,
|
| 247 |
-
"revenue_risk_per_hour": 8500,
|
| 248 |
-
"recovery_time_manual": 45,
|
| 249 |
-
"recovery_time_arf": 12
|
| 250 |
-
},
|
| 251 |
-
"Database Connection Pool Exhaustion": {
|
| 252 |
-
"users_affected": 25000,
|
| 253 |
-
"revenue_risk_per_hour": 4200,
|
| 254 |
-
"recovery_time_manual": 35,
|
| 255 |
-
"recovery_time_arf": 15
|
| 256 |
-
},
|
| 257 |
-
"Kubernetes Memory Leak": {
|
| 258 |
-
"users_affected": 35000,
|
| 259 |
-
"revenue_risk_per_hour": 5500,
|
| 260 |
-
"recovery_time_manual": 40,
|
| 261 |
-
"recovery_time_arf": 18
|
| 262 |
-
},
|
| 263 |
-
"API Rate Limit Storm": {
|
| 264 |
-
"users_affected": 20000,
|
| 265 |
-
"revenue_risk_per_hour": 3800,
|
| 266 |
-
"recovery_time_manual": 25,
|
| 267 |
-
"recovery_time_arf": 8
|
| 268 |
-
},
|
| 269 |
-
"Network Partition": {
|
| 270 |
-
"users_affected": 75000,
|
| 271 |
-
"revenue_risk_per_hour": 12000,
|
| 272 |
-
"recovery_time_manual": 60,
|
| 273 |
-
"recovery_time_arf": 20
|
| 274 |
-
},
|
| 275 |
-
"Storage I/O Saturation": {
|
| 276 |
-
"users_affected": 30000,
|
| 277 |
-
"revenue_risk_per_hour": 6800,
|
| 278 |
-
"recovery_time_manual": 50,
|
| 279 |
-
"recovery_time_arf": 22
|
| 280 |
-
}
|
| 281 |
-
}
|
| 282 |
-
|
| 283 |
-
impact = scenario_impacts.get(scenario_name, {
|
| 284 |
-
"users_affected": 30000,
|
| 285 |
-
"revenue_risk_per_hour": 5000,
|
| 286 |
-
"recovery_time_manual": 30,
|
| 287 |
-
"recovery_time_arf": 15
|
| 288 |
-
})
|
| 289 |
-
|
| 290 |
-
# Calculate ARF benefits
|
| 291 |
-
time_saved = impact["recovery_time_manual"] - impact["recovery_time_arf"]
|
| 292 |
-
cost_saved_per_incident = (impact["revenue_risk_per_hour"] / 60) * time_saved
|
| 293 |
-
|
| 294 |
-
return {
|
| 295 |
-
"scenario_specific": True,
|
| 296 |
-
"users_protected": impact["users_affected"],
|
| 297 |
-
"revenue_risk_per_hour": f"${impact['revenue_risk_per_hour']:,}",
|
| 298 |
-
"recovery_times": {
|
| 299 |
-
"manual": f"{impact['recovery_time_manual']} minutes",
|
| 300 |
-
"arf": f"{impact['recovery_time_arf']} minutes",
|
| 301 |
-
"time_saved": f"{time_saved} minutes",
|
| 302 |
-
"percent_faster": f"{int((time_saved / impact['recovery_time_manual']) * 100)}%"
|
| 303 |
-
},
|
| 304 |
-
"cost_analysis": {
|
| 305 |
-
"cost_saved_per_incident": f"${int(cost_saved_per_incident):,}",
|
| 306 |
-
"estimated_annual_savings": f"${int(cost_saved_per_incident * 15 * 12):,}", # 15 incidents/month
|
| 307 |
-
"roi_multiplier": "5.2×",
|
| 308 |
-
"payback_months": "6.0"
|
| 309 |
-
},
|
| 310 |
-
"enterprise_feature": True,
|
| 311 |
-
"business_aware": True
|
| 312 |
-
}
|
| 313 |
-
|
| 314 |
-
def _get_execution_capabilities(self) -> Dict[str, Any]:
|
| 315 |
-
"""Get execution mode capabilities (Enterprise feature)"""
|
| 316 |
-
return {
|
| 317 |
-
"modes": ["advisory", "approval", "autonomous"],
|
| 318 |
-
"current_mode": "autonomous" if self.enterprise_available else "advisory",
|
| 319 |
-
"requires_enterprise": ["approval", "autonomous"],
|
| 320 |
-
"safety_guarantees": {
|
| 321 |
-
"rollback": "guaranteed" if self.enterprise_available else "not_available",
|
| 322 |
-
"circuit_breaker": "enabled" if self.enterprise_available else "disabled",
|
| 323 |
-
"blast_radius": "enforced" if self.enterprise_available else "advisory_only",
|
| 324 |
-
"business_hours": "enforced" if self.enterprise_available else "monitored"
|
| 325 |
-
}
|
| 326 |
-
}
|
| 327 |
-
|
| 328 |
-
async def simulate_execution(self, scenario_name: str, mode: str = "autonomous") -> Dict[str, Any]:
|
| 329 |
-
"""Simulate Enterprise execution"""
|
| 330 |
-
if mode == "advisory":
|
| 331 |
-
return {
|
| 332 |
-
"status": "advisory_only",
|
| 333 |
-
"message": "OSS mode: Execution not allowed. Upgrade to Enterprise for autonomous healing.",
|
| 334 |
-
"requires_enterprise": True,
|
| 335 |
-
"execution_mode": "advisory"
|
| 336 |
-
}
|
| 337 |
-
|
| 338 |
-
await asyncio.sleep(0.3)
|
| 339 |
-
|
| 340 |
-
if mode == "approval":
|
| 341 |
-
return {
|
| 342 |
-
"status": "awaiting_approval",
|
| 343 |
-
"message": "Enterprise Approval Mode: Healing intent created, awaiting human approval",
|
| 344 |
-
"requires_human_approval": True,
|
| 345 |
-
"estimated_savings": "$8,500",
|
| 346 |
-
"rollback_prepared": True,
|
| 347 |
-
"execution_mode": "approval"
|
| 348 |
-
}
|
| 349 |
-
else: # autonomous
|
| 350 |
-
return {
|
| 351 |
-
"status": "executed",
|
| 352 |
-
"message": "Enterprise Autonomous Mode: Healing action executed with safety guarantees",
|
| 353 |
-
"execution_time": "12 minutes",
|
| 354 |
-
"cost_saved": "$8,500",
|
| 355 |
-
"rollback_available": True,
|
| 356 |
-
"rollback_guarantee": "STRONG",
|
| 357 |
-
"novel_execution_used": True,
|
| 358 |
-
"execution_mode": "autonomous",
|
| 359 |
-
"enterprise_features_used": [
|
| 360 |
-
"deterministic_confidence",
|
| 361 |
-
"novel_execution_protocols",
|
| 362 |
-
"rollback_guarantees",
|
| 363 |
-
"business_aware_execution"
|
| 364 |
-
]
|
| 365 |
-
}
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
# Factory function
|
| 369 |
-
_enterprise_sim_instance = None
|
| 370 |
-
|
| 371 |
-
async def get_enterprise_simulation() -> EnterpriseFeatureSimulation:
|
| 372 |
-
"""Get singleton EnterpriseFeatureSimulation instance"""
|
| 373 |
-
global _enterprise_sim_instance
|
| 374 |
-
if _enterprise_sim_instance is None:
|
| 375 |
-
_enterprise_sim_instance = EnterpriseFeatureSimulation()
|
| 376 |
-
return _enterprise_sim_instance
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/true_arf_orchestrator.py
DELETED
|
@@ -1,284 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Updated orchestrator that combines:
|
| 3 |
-
1. True ARF OSS v3.3.9 for real advisory analysis
|
| 4 |
-
2. Enterprise simulation to show value proposition
|
| 5 |
-
"""
|
| 6 |
-
import logging
|
| 7 |
-
import time
|
| 8 |
-
from typing import Dict, Any
|
| 9 |
-
from datetime import datetime, timedelta
|
| 10 |
-
|
| 11 |
-
logger = logging.getLogger(__name__)
|
| 12 |
-
|
| 13 |
-
class TrueARFOrchestrator:
|
| 14 |
-
"""
|
| 15 |
-
True ARF v3.3.9 orchestrator with:
|
| 16 |
-
- Real OSS package for advisory analysis
|
| 17 |
-
- Enterprise simulation to show upgrade value
|
| 18 |
-
"""
|
| 19 |
-
|
| 20 |
-
def __init__(self):
|
| 21 |
-
self.true_oss = None
|
| 22 |
-
self.enterprise_sim = None
|
| 23 |
-
self.arf_analysis_results = {} # Store analysis results for execution
|
| 24 |
-
self.audit_trail_manager = None # Will be set by app
|
| 25 |
-
self._initialize_components()
|
| 26 |
-
|
| 27 |
-
def _initialize_components(self):
|
| 28 |
-
"""Initialize true ARF components"""
|
| 29 |
-
try:
|
| 30 |
-
from core.true_arf_oss import get_true_arf_oss
|
| 31 |
-
self.true_oss = get_true_arf_oss
|
| 32 |
-
logger.info("✅ True ARF OSS v3.3.9 component available")
|
| 33 |
-
except ImportError as e:
|
| 34 |
-
logger.warning(f"True ARF OSS not available: {e}")
|
| 35 |
-
self.true_oss = None
|
| 36 |
-
except Exception as e:
|
| 37 |
-
logger.error(f"Error initializing True ARF OSS: {e}")
|
| 38 |
-
self.true_oss = None
|
| 39 |
-
|
| 40 |
-
try:
|
| 41 |
-
from core.enterprise_simulation import get_enterprise_simulation
|
| 42 |
-
self.enterprise_sim = get_enterprise_simulation
|
| 43 |
-
logger.info("✅ Enterprise simulation component available")
|
| 44 |
-
except ImportError as e:
|
| 45 |
-
logger.warning(f"Enterprise simulation not available: {e}")
|
| 46 |
-
self.enterprise_sim = None
|
| 47 |
-
except Exception as e:
|
| 48 |
-
logger.error(f"Error initializing Enterprise simulation: {e}")
|
| 49 |
-
self.enterprise_sim = None
|
| 50 |
-
|
| 51 |
-
async def analyze_incident(self, scenario_name: str, scenario_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 52 |
-
"""
|
| 53 |
-
Complete analysis using:
|
| 54 |
-
1. True ARF OSS v3.3.9 for real advisory analysis
|
| 55 |
-
2. Enterprise simulation to show upgrade value
|
| 56 |
-
"""
|
| 57 |
-
logger.info(f"Running true ARF v3.3.9 analysis for: {scenario_name}")
|
| 58 |
-
|
| 59 |
-
try:
|
| 60 |
-
# Step 1: Run true OSS analysis
|
| 61 |
-
oss_analysis = None
|
| 62 |
-
if self.true_oss:
|
| 63 |
-
try:
|
| 64 |
-
true_oss_instance = await self.true_oss()
|
| 65 |
-
if hasattr(true_oss_instance, 'oss_available') and true_oss_instance.oss_available:
|
| 66 |
-
oss_analysis = await true_oss_instance.analyze_scenario(scenario_name, scenario_data)
|
| 67 |
-
else:
|
| 68 |
-
logger.warning("True ARF OSS not available - using fallback mock")
|
| 69 |
-
oss_analysis = await self._fallback_oss_analysis(scenario_name, scenario_data)
|
| 70 |
-
except Exception as e:
|
| 71 |
-
logger.error(f"Error in true OSS analysis: {e}")
|
| 72 |
-
oss_analysis = await self._fallback_oss_analysis(scenario_name, scenario_data)
|
| 73 |
-
else:
|
| 74 |
-
# Fallback to mock
|
| 75 |
-
oss_analysis = await self._fallback_oss_analysis(scenario_name, scenario_data)
|
| 76 |
-
|
| 77 |
-
# Step 2: Enhance with Enterprise features (simulation)
|
| 78 |
-
enterprise_enhancements = None
|
| 79 |
-
if self.enterprise_sim and oss_analysis and oss_analysis.get("status") == "success":
|
| 80 |
-
try:
|
| 81 |
-
enterprise_instance = await self.enterprise_sim()
|
| 82 |
-
enterprise_enhancements = await enterprise_instance.enhance_oss_analysis(
|
| 83 |
-
oss_analysis, scenario_name
|
| 84 |
-
)
|
| 85 |
-
except Exception as e:
|
| 86 |
-
logger.error(f"Error in enterprise enhancement: {e}")
|
| 87 |
-
# Continue without enterprise enhancements
|
| 88 |
-
|
| 89 |
-
# Store analysis result for potential execution
|
| 90 |
-
self.arf_analysis_results[scenario_name] = {
|
| 91 |
-
"oss_analysis": oss_analysis,
|
| 92 |
-
"enterprise_enhancements": enterprise_enhancements,
|
| 93 |
-
"timestamp": datetime.now().isoformat()
|
| 94 |
-
}
|
| 95 |
-
|
| 96 |
-
# Combine results
|
| 97 |
-
result = {
|
| 98 |
-
"status": "success",
|
| 99 |
-
"scenario": scenario_name,
|
| 100 |
-
"arf_version": "3.3.9",
|
| 101 |
-
"true_oss_used": self.true_oss is not None,
|
| 102 |
-
"enterprise_simulated": self.enterprise_sim is not None,
|
| 103 |
-
"oss_analysis": oss_analysis,
|
| 104 |
-
"enterprise_enhancements": enterprise_enhancements,
|
| 105 |
-
"recommendation": self._get_recommendation(oss_analysis, enterprise_enhancements)
|
| 106 |
-
}
|
| 107 |
-
|
| 108 |
-
return result
|
| 109 |
-
|
| 110 |
-
except Exception as e:
|
| 111 |
-
logger.error(f"Analysis failed: {e}", exc_info=True)
|
| 112 |
-
return {
|
| 113 |
-
"status": "error",
|
| 114 |
-
"error": f"Analysis failed: {str(e)}", # Make sure error is a string
|
| 115 |
-
"scenario": scenario_name,
|
| 116 |
-
"suggestion": "Check logs for details"
|
| 117 |
-
}
|
| 118 |
-
|
| 119 |
-
async def execute_healing(self, scenario_name: str, mode: str = "autonomous") -> Dict[str, Any]:
|
| 120 |
-
"""
|
| 121 |
-
Execute enterprise healing for a given scenario.
|
| 122 |
-
|
| 123 |
-
Args:
|
| 124 |
-
scenario_name: Name of the scenario to heal
|
| 125 |
-
mode: Execution mode ("autonomous", "approval", "manual")
|
| 126 |
-
|
| 127 |
-
Returns:
|
| 128 |
-
Dict with execution results
|
| 129 |
-
"""
|
| 130 |
-
try:
|
| 131 |
-
# Log execution start
|
| 132 |
-
logger.info(f"⚡ Executing enterprise healing for: {scenario_name} (mode: {mode})")
|
| 133 |
-
|
| 134 |
-
# Get the latest analysis for this scenario
|
| 135 |
-
if not self.arf_analysis_results or scenario_name not in self.arf_analysis_results:
|
| 136 |
-
# Run analysis first if not available
|
| 137 |
-
logger.warning(f"No analysis found for {scenario_name}, running analysis first")
|
| 138 |
-
await self.analyze_incident(scenario_name, {})
|
| 139 |
-
|
| 140 |
-
# Get the analysis result
|
| 141 |
-
analysis_result = self.arf_analysis_results.get(scenario_name, {})
|
| 142 |
-
oss_analysis = analysis_result.get("oss_analysis", {})
|
| 143 |
-
|
| 144 |
-
# Extract healing intent from analysis
|
| 145 |
-
healing_intent = {}
|
| 146 |
-
if oss_analysis and "analysis" in oss_analysis and "decision" in oss_analysis["analysis"]:
|
| 147 |
-
healing_intent = oss_analysis["analysis"]["decision"]
|
| 148 |
-
elif oss_analysis and "healing_intent" in oss_analysis:
|
| 149 |
-
healing_intent = oss_analysis["healing_intent"]
|
| 150 |
-
|
| 151 |
-
# Simulate enterprise execution with proper boundaries
|
| 152 |
-
enterprise_result = {
|
| 153 |
-
"status": "success",
|
| 154 |
-
"mode": mode,
|
| 155 |
-
"scenario": scenario_name,
|
| 156 |
-
"execution_id": f"exe_{int(time.time())}_{scenario_name.lower().replace(' ', '_')}",
|
| 157 |
-
"timestamp": datetime.now().isoformat(),
|
| 158 |
-
"boundary": "Enterprise Simulation",
|
| 159 |
-
"enterprise_features_used": [
|
| 160 |
-
"AutonomousExecutionEngine",
|
| 161 |
-
"SafetyOrchestrator",
|
| 162 |
-
"ComplianceGuardrails",
|
| 163 |
-
"RealTimeTelemetry",
|
| 164 |
-
"LearningEngineIntegration"
|
| 165 |
-
],
|
| 166 |
-
"execution_summary": {
|
| 167 |
-
"action": healing_intent.get("action", "Scale infrastructure"),
|
| 168 |
-
"target": healing_intent.get("target", "redis_cache"),
|
| 169 |
-
"parameters": healing_intent.get("parameters", {}),
|
| 170 |
-
"confidence": oss_analysis.get("analysis", {}).get("decision", {}).get("confidence", 0.85)
|
| 171 |
-
if oss_analysis else 0.85,
|
| 172 |
-
"estimated_duration": "12 minutes",
|
| 173 |
-
"blast_radius": "2 services",
|
| 174 |
-
"safety_checks_passed": True
|
| 175 |
-
},
|
| 176 |
-
"business_impact": {
|
| 177 |
-
"mttr_reduction": "73% faster (45m → 12m)",
|
| 178 |
-
"cost_saved": 6375,
|
| 179 |
-
"revenue_protected": 8500,
|
| 180 |
-
"users_protected": 45000,
|
| 181 |
-
"incidents_prevented": 3
|
| 182 |
-
},
|
| 183 |
-
"telemetry": {
|
| 184 |
-
"start_time": datetime.now().isoformat(),
|
| 185 |
-
"end_time": (datetime.now() + timedelta(minutes=12)).isoformat(),
|
| 186 |
-
"resources_affected": ["redis_node_1", "redis_node_2"],
|
| 187 |
-
"rollback_available": True,
|
| 188 |
-
"audit_trail_enabled": True
|
| 189 |
-
},
|
| 190 |
-
"notes": [
|
| 191 |
-
"✅ Enterprise execution simulation complete",
|
| 192 |
-
"🔒 Safety boundaries enforced via MCP",
|
| 193 |
-
"📊 Real-time telemetry active",
|
| 194 |
-
"🧠 Learning engine updated with outcome",
|
| 195 |
-
"💾 Audit trail recorded"
|
| 196 |
-
]
|
| 197 |
-
}
|
| 198 |
-
|
| 199 |
-
# Record execution in audit trail if manager is available
|
| 200 |
-
if self.audit_trail_manager:
|
| 201 |
-
try:
|
| 202 |
-
self.audit_trail_manager.record_execution(
|
| 203 |
-
execution_id=enterprise_result["execution_id"],
|
| 204 |
-
scenario_name=scenario_name,
|
| 205 |
-
status="success",
|
| 206 |
-
mode=mode,
|
| 207 |
-
details=enterprise_result
|
| 208 |
-
)
|
| 209 |
-
except Exception as e:
|
| 210 |
-
logger.warning(f"Could not record execution in audit trail: {e}")
|
| 211 |
-
|
| 212 |
-
logger.info(f"✅ Enterprise healing execution simulated for {scenario_name}")
|
| 213 |
-
return enterprise_result
|
| 214 |
-
|
| 215 |
-
except Exception as e:
|
| 216 |
-
logger.error(f"❌ Enterprise healing execution failed: {e}")
|
| 217 |
-
return {
|
| 218 |
-
"status": "failed",
|
| 219 |
-
"error": str(e),
|
| 220 |
-
"scenario": scenario_name,
|
| 221 |
-
"mode": mode,
|
| 222 |
-
"timestamp": datetime.now().isoformat(),
|
| 223 |
-
"boundary": "Enterprise Simulation Failed",
|
| 224 |
-
"notes": ["Execution failed in simulation mode"]
|
| 225 |
-
}
|
| 226 |
-
|
| 227 |
-
async def _fallback_oss_analysis(self, scenario_name: str, scenario_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 228 |
-
"""Fallback mock analysis"""
|
| 229 |
-
try:
|
| 230 |
-
# Use existing mock ARF with scenario-aware metrics
|
| 231 |
-
from demo.mock_arf import (
|
| 232 |
-
simulate_arf_analysis,
|
| 233 |
-
run_rag_similarity_search,
|
| 234 |
-
calculate_pattern_confidence,
|
| 235 |
-
create_mock_healing_intent
|
| 236 |
-
)
|
| 237 |
-
|
| 238 |
-
scenario_data_with_name = scenario_data.copy()
|
| 239 |
-
scenario_data_with_name["name"] = scenario_name
|
| 240 |
-
|
| 241 |
-
detection = simulate_arf_analysis(scenario_data_with_name)
|
| 242 |
-
recall = run_rag_similarity_search(scenario_data_with_name)
|
| 243 |
-
confidence = calculate_pattern_confidence(scenario_data_with_name, recall)
|
| 244 |
-
decision = create_mock_healing_intent(scenario_data_with_name, recall, confidence)
|
| 245 |
-
|
| 246 |
-
return {
|
| 247 |
-
"status": "success",
|
| 248 |
-
"scenario": scenario_name,
|
| 249 |
-
"analysis": {
|
| 250 |
-
"detection": detection,
|
| 251 |
-
"recall": recall,
|
| 252 |
-
"decision": decision
|
| 253 |
-
},
|
| 254 |
-
"capabilities": {
|
| 255 |
-
"execution_allowed": False,
|
| 256 |
-
"mcp_modes": ["advisory"],
|
| 257 |
-
"oss_boundary": "advisory_only"
|
| 258 |
-
},
|
| 259 |
-
"note": "Using fallback mock analysis"
|
| 260 |
-
}
|
| 261 |
-
except Exception as e:
|
| 262 |
-
logger.error(f"Fallback analysis failed: {e}")
|
| 263 |
-
return {
|
| 264 |
-
"status": "error",
|
| 265 |
-
"error": f"Fallback analysis failed: {str(e)}",
|
| 266 |
-
"scenario": scenario_name
|
| 267 |
-
}
|
| 268 |
-
|
| 269 |
-
def _get_recommendation(self, oss_analysis: Dict, enterprise_enhancements: Dict) -> str:
|
| 270 |
-
"""Generate recommendation based on analysis"""
|
| 271 |
-
if not oss_analysis or oss_analysis.get("status") != "success":
|
| 272 |
-
return "Analysis failed. Check logs."
|
| 273 |
-
|
| 274 |
-
if enterprise_enhancements and enterprise_enhancements.get("enterprise_available"):
|
| 275 |
-
return "✅ Both OSS and Enterprise features available. Full ARF v3.3.9 capabilities enabled."
|
| 276 |
-
elif enterprise_enhancements:
|
| 277 |
-
return "✅ OSS analysis complete. ⚡ Enterprise features simulated - shows upgrade value. Contact sales@arf.dev for Enterprise trial."
|
| 278 |
-
else:
|
| 279 |
-
return "✅ OSS analysis complete. Install agentic-reliability-framework==3.3.9 for real advisory capabilities."
|
| 280 |
-
|
| 281 |
-
def set_audit_trail_manager(self, manager):
|
| 282 |
-
"""Set audit trail manager for recording executions."""
|
| 283 |
-
self.audit_trail_manager = manager
|
| 284 |
-
logger.info("✅ Audit trail manager connected to orchestrator")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/true_arf_oss.py
DELETED
|
@@ -1,695 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
True ARF OSS v3.3.9 - Integration with existing OSS MCP Client
|
| 3 |
-
Production-grade multi-agent AI for reliability monitoring (Advisory only)
|
| 4 |
-
This bridges the demo orchestrator with the real ARF OSS implementation.
|
| 5 |
-
"""
|
| 6 |
-
|
| 7 |
-
import asyncio
|
| 8 |
-
import logging
|
| 9 |
-
import time
|
| 10 |
-
import uuid
|
| 11 |
-
from typing import Dict, Any, List, Optional
|
| 12 |
-
from dataclasses import dataclass, field
|
| 13 |
-
import json
|
| 14 |
-
|
| 15 |
-
logger = logging.getLogger(__name__)
|
| 16 |
-
|
| 17 |
-
# ============================================================================
|
| 18 |
-
# TRUE ARF OSS IMPLEMENTATION
|
| 19 |
-
# ============================================================================
|
| 20 |
-
|
| 21 |
-
class TrueARFOSS:
|
| 22 |
-
"""
|
| 23 |
-
True ARF OSS v3.3.9 - Complete integration with OSS MCP Client
|
| 24 |
-
|
| 25 |
-
This is the class that TrueARF337Orchestrator expects to import.
|
| 26 |
-
It provides real ARF OSS functionality by integrating with the
|
| 27 |
-
existing OSS MCP client and implementing the 3-agent pattern.
|
| 28 |
-
"""
|
| 29 |
-
|
| 30 |
-
def __init__(self, config: Optional[Dict[str, Any]] = None):
|
| 31 |
-
self.config = config or {}
|
| 32 |
-
self.oss_available = True
|
| 33 |
-
self.mcp_client = None
|
| 34 |
-
self.agent_stats = {
|
| 35 |
-
"detection_calls": 0,
|
| 36 |
-
"recall_calls": 0,
|
| 37 |
-
"decision_calls": 0,
|
| 38 |
-
"total_analyses": 0,
|
| 39 |
-
"total_time_ms": 0.0
|
| 40 |
-
}
|
| 41 |
-
|
| 42 |
-
logger.info("True ARF OSS v3.3.9 initialized")
|
| 43 |
-
|
| 44 |
-
async def _get_mcp_client(self):
|
| 45 |
-
"""Lazy load OSS MCP client"""
|
| 46 |
-
if self.mcp_client is None:
|
| 47 |
-
try:
|
| 48 |
-
# Use the existing OSS MCP client
|
| 49 |
-
from agentic_reliability_framework.arf_core.engine.oss_mcp_client import (
|
| 50 |
-
OSSMCPClient,
|
| 51 |
-
create_oss_mcp_client
|
| 52 |
-
)
|
| 53 |
-
self.mcp_client = create_oss_mcp_client(self.config)
|
| 54 |
-
logger.info("✅ OSS MCP Client loaded successfully")
|
| 55 |
-
except ImportError as e:
|
| 56 |
-
logger.error(f"❌ Failed to load OSS MCP Client: {e}")
|
| 57 |
-
raise ImportError("Real ARF OSS package not installed")
|
| 58 |
-
|
| 59 |
-
return self.mcp_client
|
| 60 |
-
|
| 61 |
-
async def analyze_scenario(self, scenario_name: str,
|
| 62 |
-
scenario_data: Dict[str, Any]) -> Dict[str, Any]:
|
| 63 |
-
"""
|
| 64 |
-
Complete ARF analysis for a scenario using real OSS agents
|
| 65 |
-
|
| 66 |
-
Implements the 3-agent pattern:
|
| 67 |
-
1. Detection Agent: Analyze metrics for anomalies
|
| 68 |
-
2. Recall Agent: Find similar historical incidents
|
| 69 |
-
3. Decision Agent: Generate healing intent with confidence
|
| 70 |
-
|
| 71 |
-
Args:
|
| 72 |
-
scenario_name: Name of the scenario
|
| 73 |
-
scenario_data: Scenario data including metrics and context
|
| 74 |
-
|
| 75 |
-
Returns:
|
| 76 |
-
Complete analysis result with real ARF data
|
| 77 |
-
"""
|
| 78 |
-
start_time = time.time()
|
| 79 |
-
self.agent_stats["total_analyses"] += 1
|
| 80 |
-
|
| 81 |
-
try:
|
| 82 |
-
logger.info(f"True ARF OSS: Starting analysis for {scenario_name}")
|
| 83 |
-
|
| 84 |
-
# Get OSS MCP client
|
| 85 |
-
mcp_client = await self._get_mcp_client()
|
| 86 |
-
|
| 87 |
-
# Extract component and metrics from scenario
|
| 88 |
-
component = scenario_data.get("component", "unknown")
|
| 89 |
-
metrics = scenario_data.get("metrics", {})
|
| 90 |
-
business_impact = scenario_data.get("business_impact", {})
|
| 91 |
-
|
| 92 |
-
# Convert scenario to telemetry format
|
| 93 |
-
telemetry = self._scenario_to_telemetry(scenario_name, component, metrics)
|
| 94 |
-
|
| 95 |
-
# ============================================
|
| 96 |
-
# 1. DETECTION AGENT - Anomaly Detection
|
| 97 |
-
# ============================================
|
| 98 |
-
logger.info(f"True ARF OSS: Detection agent analyzing {scenario_name}")
|
| 99 |
-
self.agent_stats["detection_calls"] += 1
|
| 100 |
-
|
| 101 |
-
detection_result = await self._run_detection_agent(
|
| 102 |
-
component, telemetry, metrics, business_impact
|
| 103 |
-
)
|
| 104 |
-
|
| 105 |
-
if not detection_result["anomaly_detected"]:
|
| 106 |
-
logger.info(f"No anomalies detected in {scenario_name}")
|
| 107 |
-
return self._create_no_anomaly_result(scenario_name, start_time)
|
| 108 |
-
|
| 109 |
-
# ============================================
|
| 110 |
-
# 2. RECALL AGENT - RAG Similarity Search
|
| 111 |
-
# ============================================
|
| 112 |
-
logger.info(f"True ARF OSS: Recall agent searching for similar incidents")
|
| 113 |
-
self.agent_stats["recall_calls"] += 1
|
| 114 |
-
|
| 115 |
-
# Prepare context for RAG search
|
| 116 |
-
rag_context = self._prepare_rag_context(
|
| 117 |
-
component, metrics, business_impact, detection_result
|
| 118 |
-
)
|
| 119 |
-
|
| 120 |
-
# Find similar incidents using OSS MCP client's RAG capabilities
|
| 121 |
-
similar_incidents = await self._run_recall_agent(
|
| 122 |
-
mcp_client, component, rag_context
|
| 123 |
-
)
|
| 124 |
-
|
| 125 |
-
# ============================================
|
| 126 |
-
# 3. DECISION AGENT - Healing Intent Generation
|
| 127 |
-
# ============================================
|
| 128 |
-
logger.info(f"True ARF OSS: Decision agent generating healing intent")
|
| 129 |
-
self.agent_stats["decision_calls"] += 1
|
| 130 |
-
|
| 131 |
-
# Determine appropriate action based on scenario
|
| 132 |
-
action = self._determine_action(scenario_name, component, metrics)
|
| 133 |
-
|
| 134 |
-
# Calculate confidence based on detection and recall
|
| 135 |
-
confidence = self._calculate_confidence(
|
| 136 |
-
detection_result, similar_incidents, scenario_name
|
| 137 |
-
)
|
| 138 |
-
|
| 139 |
-
# Generate healing intent using OSS MCP client
|
| 140 |
-
healing_intent = await self._run_decision_agent(
|
| 141 |
-
mcp_client, action, component, metrics,
|
| 142 |
-
similar_incidents, confidence, rag_context
|
| 143 |
-
)
|
| 144 |
-
|
| 145 |
-
# ============================================
|
| 146 |
-
# COMPILE FINAL RESULTS
|
| 147 |
-
# ============================================
|
| 148 |
-
analysis_time_ms = (time.time() - start_time) * 1000
|
| 149 |
-
self.agent_stats["total_time_ms"] += analysis_time_ms
|
| 150 |
-
|
| 151 |
-
result = self._compile_results(
|
| 152 |
-
scenario_name=scenario_name,
|
| 153 |
-
detection_result=detection_result,
|
| 154 |
-
similar_incidents=similar_incidents,
|
| 155 |
-
healing_intent=healing_intent,
|
| 156 |
-
analysis_time_ms=analysis_time_ms,
|
| 157 |
-
component=component,
|
| 158 |
-
metrics=metrics
|
| 159 |
-
)
|
| 160 |
-
|
| 161 |
-
logger.info(f"True ARF OSS: Analysis complete for {scenario_name} "
|
| 162 |
-
f"({analysis_time_ms:.1f}ms, confidence: {confidence:.2f})")
|
| 163 |
-
|
| 164 |
-
return result
|
| 165 |
-
|
| 166 |
-
except Exception as e:
|
| 167 |
-
logger.error(f"True ARF OSS analysis failed: {e}", exc_info=True)
|
| 168 |
-
return self._create_error_result(scenario_name, str(e), start_time)
|
| 169 |
-
|
| 170 |
-
def _scenario_to_telemetry(self, scenario_name: str, component: str,
|
| 171 |
-
metrics: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 172 |
-
"""Convert scenario metrics to telemetry data format"""
|
| 173 |
-
telemetry = []
|
| 174 |
-
current_time = time.time()
|
| 175 |
-
|
| 176 |
-
# Create telemetry points for each metric
|
| 177 |
-
for metric_name, value in metrics.items():
|
| 178 |
-
if isinstance(value, (int, float)):
|
| 179 |
-
# Create 5 data points showing anomaly progression
|
| 180 |
-
for i in range(5, 0, -1):
|
| 181 |
-
telemetry.append({
|
| 182 |
-
"timestamp": current_time - (i * 10), # 10-second intervals
|
| 183 |
-
"metric": metric_name,
|
| 184 |
-
"value": value * (0.7 + 0.3 * (i/5)), # Gradual increase
|
| 185 |
-
"component": component
|
| 186 |
-
})
|
| 187 |
-
|
| 188 |
-
return telemetry
|
| 189 |
-
|
| 190 |
-
async def _run_detection_agent(self, component: str, telemetry: List[Dict[str, Any]],
|
| 191 |
-
metrics: Dict[str, Any],
|
| 192 |
-
business_impact: Dict[str, Any]) -> Dict[str, Any]:
|
| 193 |
-
"""Run detection agent to find anomalies"""
|
| 194 |
-
|
| 195 |
-
# Analyze each metric for anomalies
|
| 196 |
-
anomalies = []
|
| 197 |
-
anomaly_confidence = 0.0
|
| 198 |
-
|
| 199 |
-
for metric_name, value in metrics.items():
|
| 200 |
-
if not isinstance(value, (int, float)):
|
| 201 |
-
continue
|
| 202 |
-
|
| 203 |
-
# Define thresholds based on metric type
|
| 204 |
-
thresholds = self._get_metric_thresholds(metric_name, value)
|
| 205 |
-
|
| 206 |
-
# Check if metric exceeds thresholds
|
| 207 |
-
if value >= thresholds["critical"]:
|
| 208 |
-
anomalies.append({
|
| 209 |
-
"metric": metric_name,
|
| 210 |
-
"value": value,
|
| 211 |
-
"threshold": thresholds["critical"],
|
| 212 |
-
"severity": "critical",
|
| 213 |
-
"confidence": 0.95
|
| 214 |
-
})
|
| 215 |
-
anomaly_confidence = max(anomaly_confidence, 0.95)
|
| 216 |
-
elif value >= thresholds["warning"]:
|
| 217 |
-
anomalies.append({
|
| 218 |
-
"metric": metric_name,
|
| 219 |
-
"value": value,
|
| 220 |
-
"threshold": thresholds["warning"],
|
| 221 |
-
"severity": "high",
|
| 222 |
-
"confidence": 0.85
|
| 223 |
-
})
|
| 224 |
-
anomaly_confidence = max(anomaly_confidence, 0.85)
|
| 225 |
-
|
| 226 |
-
# Calculate overall severity
|
| 227 |
-
severity = "critical" if any(a["severity"] == "critical" for a in anomalies) else \
|
| 228 |
-
"high" if anomalies else "normal"
|
| 229 |
-
|
| 230 |
-
# Check business impact for additional severity context
|
| 231 |
-
if business_impact.get("revenue_loss_per_hour", 0) > 5000:
|
| 232 |
-
severity = "critical"
|
| 233 |
-
anomaly_confidence = max(anomaly_confidence, 0.97)
|
| 234 |
-
|
| 235 |
-
return {
|
| 236 |
-
"anomaly_detected": len(anomalies) > 0,
|
| 237 |
-
"anomalies": anomalies,
|
| 238 |
-
"severity": severity,
|
| 239 |
-
"confidence": anomaly_confidence if anomalies else 0.0,
|
| 240 |
-
"component": component,
|
| 241 |
-
"timestamp": time.time()
|
| 242 |
-
}
|
| 243 |
-
|
| 244 |
-
def _get_metric_thresholds(self, metric_name: str, value: float) -> Dict[str, float]:
|
| 245 |
-
"""Get thresholds for different metric types"""
|
| 246 |
-
# Default thresholds
|
| 247 |
-
thresholds = {
|
| 248 |
-
"warning": value * 0.7, # 70% of current value
|
| 249 |
-
"critical": value * 0.85 # 85% of current value
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
# Metric-specific thresholds
|
| 253 |
-
metric_thresholds = {
|
| 254 |
-
"cache_hit_rate": {"warning": 50, "critical": 30},
|
| 255 |
-
"database_load": {"warning": 80, "critical": 90},
|
| 256 |
-
"response_time_ms": {"warning": 500, "critical": 1000},
|
| 257 |
-
"error_rate": {"warning": 5, "critical": 10},
|
| 258 |
-
"memory_usage": {"warning": 85, "critical": 95},
|
| 259 |
-
"latency_ms": {"warning": 200, "critical": 500},
|
| 260 |
-
"throughput_mbps": {"warning": 1000, "critical": 500},
|
| 261 |
-
}
|
| 262 |
-
|
| 263 |
-
if metric_name in metric_thresholds:
|
| 264 |
-
thresholds = metric_thresholds[metric_name]
|
| 265 |
-
|
| 266 |
-
return thresholds
|
| 267 |
-
|
| 268 |
-
def _prepare_rag_context(self, component: str, metrics: Dict[str, Any],
|
| 269 |
-
business_impact: Dict[str, Any],
|
| 270 |
-
detection_result: Dict[str, Any]) -> Dict[str, Any]:
|
| 271 |
-
"""Prepare context for RAG similarity search"""
|
| 272 |
-
return {
|
| 273 |
-
"component": component,
|
| 274 |
-
"metrics": metrics,
|
| 275 |
-
"business_impact": business_impact,
|
| 276 |
-
"detection": {
|
| 277 |
-
"severity": detection_result["severity"],
|
| 278 |
-
"confidence": detection_result["confidence"],
|
| 279 |
-
"anomaly_count": len(detection_result["anomalies"])
|
| 280 |
-
},
|
| 281 |
-
"incident_id": f"inc_{uuid.uuid4().hex[:8]}",
|
| 282 |
-
"timestamp": time.time(),
|
| 283 |
-
"environment": "production"
|
| 284 |
-
}
|
| 285 |
-
|
| 286 |
-
async def _run_recall_agent(self, mcp_client, component: str,
|
| 287 |
-
context: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 288 |
-
"""Run recall agent to find similar incidents using RAG"""
|
| 289 |
-
try:
|
| 290 |
-
# Use OSS MCP client's RAG capabilities
|
| 291 |
-
# The OSS MCP client has _query_rag_for_similar_incidents method
|
| 292 |
-
similar_incidents = await mcp_client._query_rag_for_similar_incidents(
|
| 293 |
-
component=component,
|
| 294 |
-
parameters={}, # Empty parameters for similarity search
|
| 295 |
-
context=context
|
| 296 |
-
)
|
| 297 |
-
|
| 298 |
-
# Enhance with success rates if available
|
| 299 |
-
for incident in similar_incidents:
|
| 300 |
-
if "success_rate" not in incident:
|
| 301 |
-
# Assign random success rate for demo (in real system, this comes from RAG)
|
| 302 |
-
incident["success_rate"] = 0.7 + (hash(incident.get("incident_id", "")) % 30) / 100
|
| 303 |
-
|
| 304 |
-
return similar_incidents
|
| 305 |
-
|
| 306 |
-
except Exception as e:
|
| 307 |
-
logger.warning(f"Recall agent RAG query failed: {e}")
|
| 308 |
-
# Return mock similar incidents for demo
|
| 309 |
-
return self._create_mock_similar_incidents(component, context)
|
| 310 |
-
|
| 311 |
-
def _create_mock_similar_incidents(self, component: str,
|
| 312 |
-
context: Dict[str, Any]) -> List[Dict[str, Any]]:
|
| 313 |
-
"""Create mock similar incidents for demo purposes"""
|
| 314 |
-
incidents = []
|
| 315 |
-
base_time = time.time() - (30 * 24 * 3600) # 30 days ago
|
| 316 |
-
|
| 317 |
-
for i in range(3):
|
| 318 |
-
incidents.append({
|
| 319 |
-
"incident_id": f"sim_{uuid.uuid4().hex[:8]}",
|
| 320 |
-
"component": component,
|
| 321 |
-
"severity": context["detection"]["severity"],
|
| 322 |
-
"similarity_score": 0.85 - (i * 0.1),
|
| 323 |
-
"success_rate": 0.8 + (i * 0.05),
|
| 324 |
-
"resolution_time_minutes": 45 - (i * 10),
|
| 325 |
-
"timestamp": base_time + (i * 7 * 24 * 3600), # Weekly intervals
|
| 326 |
-
"action_taken": "scale_out" if i % 2 == 0 else "restart_container",
|
| 327 |
-
"success": True
|
| 328 |
-
})
|
| 329 |
-
|
| 330 |
-
return incidents
|
| 331 |
-
|
| 332 |
-
def _determine_action(self, scenario_name: str, component: str,
|
| 333 |
-
metrics: Dict[str, Any]) -> str:
|
| 334 |
-
"""Determine appropriate healing action based on scenario"""
|
| 335 |
-
# Map scenarios to actions
|
| 336 |
-
scenario_actions = {
|
| 337 |
-
"Cache Miss Storm": "scale_out",
|
| 338 |
-
"Database Connection Pool Exhaustion": "scale_out",
|
| 339 |
-
"Kubernetes Memory Leak": "restart_container",
|
| 340 |
-
"API Rate Limit Storm": "circuit_breaker",
|
| 341 |
-
"Network Partition": "alert_team",
|
| 342 |
-
"Storage I/O Saturation": "scale_out",
|
| 343 |
-
}
|
| 344 |
-
|
| 345 |
-
# Default action based on component
|
| 346 |
-
component_actions = {
|
| 347 |
-
"redis_cache": "scale_out",
|
| 348 |
-
"postgresql_database": "scale_out",
|
| 349 |
-
"java_payment_service": "restart_container",
|
| 350 |
-
"external_api_gateway": "circuit_breaker",
|
| 351 |
-
"distributed_database": "alert_team",
|
| 352 |
-
"storage_cluster": "scale_out",
|
| 353 |
-
}
|
| 354 |
-
|
| 355 |
-
# Try scenario-specific action first
|
| 356 |
-
if scenario_name in scenario_actions:
|
| 357 |
-
return scenario_actions[scenario_name]
|
| 358 |
-
|
| 359 |
-
# Fall back to component-based action
|
| 360 |
-
return component_actions.get(component, "alert_team")
|
| 361 |
-
|
| 362 |
-
def _calculate_confidence(self, detection_result: Dict[str, Any],
|
| 363 |
-
similar_incidents: List[Dict[str, Any]],
|
| 364 |
-
scenario_name: str) -> float:
|
| 365 |
-
"""Calculate confidence score for the healing intent"""
|
| 366 |
-
base_confidence = detection_result["confidence"]
|
| 367 |
-
|
| 368 |
-
# Boost for similar incidents
|
| 369 |
-
if similar_incidents:
|
| 370 |
-
avg_similarity = sum(i.get("similarity_score", 0.0)
|
| 371 |
-
for i in similar_incidents) / len(similar_incidents)
|
| 372 |
-
similarity_boost = min(0.2, avg_similarity * 0.3)
|
| 373 |
-
base_confidence += similarity_boost
|
| 374 |
-
|
| 375 |
-
# Boost for successful similar incidents
|
| 376 |
-
success_rates = [i.get("success_rate", 0.0) for i in similar_incidents]
|
| 377 |
-
avg_success = sum(success_rates) / len(success_rates)
|
| 378 |
-
success_boost = min(0.15, avg_success * 0.2)
|
| 379 |
-
base_confidence += success_boost
|
| 380 |
-
|
| 381 |
-
# Scenario-specific adjustments
|
| 382 |
-
scenario_boosts = {
|
| 383 |
-
"Cache Miss Storm": 0.05,
|
| 384 |
-
"Database Connection Pool Exhaustion": 0.03,
|
| 385 |
-
"Kubernetes Memory Leak": 0.04,
|
| 386 |
-
"API Rate Limit Storm": 0.02,
|
| 387 |
-
"Network Partition": 0.01,
|
| 388 |
-
"Storage I/O Saturation": 0.03,
|
| 389 |
-
}
|
| 390 |
-
|
| 391 |
-
base_confidence += scenario_boosts.get(scenario_name, 0.0)
|
| 392 |
-
|
| 393 |
-
# Cap at 0.99 (never 100% certain)
|
| 394 |
-
return min(base_confidence, 0.99)
|
| 395 |
-
|
| 396 |
-
async def _run_decision_agent(self, mcp_client, action: str, component: str,
|
| 397 |
-
metrics: Dict[str, Any], similar_incidents: List[Dict[str, Any]],
|
| 398 |
-
confidence: float, context: Dict[str, Any]) -> Dict[str, Any]:
|
| 399 |
-
"""Run decision agent to generate healing intent"""
|
| 400 |
-
try:
|
| 401 |
-
# Determine parameters based on action and metrics
|
| 402 |
-
parameters = self._determine_parameters(action, metrics)
|
| 403 |
-
|
| 404 |
-
# Generate justification
|
| 405 |
-
justification = self._generate_justification(
|
| 406 |
-
action, component, metrics, similar_incidents, confidence
|
| 407 |
-
)
|
| 408 |
-
|
| 409 |
-
# Use OSS MCP client to analyze and create healing intent
|
| 410 |
-
analysis_result = await mcp_client.analyze_and_recommend(
|
| 411 |
-
tool_name=action,
|
| 412 |
-
component=component,
|
| 413 |
-
parameters=parameters,
|
| 414 |
-
context={
|
| 415 |
-
**context,
|
| 416 |
-
"justification": justification,
|
| 417 |
-
"similar_incidents": similar_incidents,
|
| 418 |
-
"confidence": confidence
|
| 419 |
-
},
|
| 420 |
-
use_rag=True
|
| 421 |
-
)
|
| 422 |
-
|
| 423 |
-
# Extract healing intent from analysis result
|
| 424 |
-
healing_intent = analysis_result.healing_intent
|
| 425 |
-
|
| 426 |
-
# Convert to dictionary format for demo
|
| 427 |
-
return {
|
| 428 |
-
"action": healing_intent.action,
|
| 429 |
-
"component": healing_intent.component,
|
| 430 |
-
"parameters": healing_intent.parameters,
|
| 431 |
-
"confidence": healing_intent.confidence,
|
| 432 |
-
"justification": healing_intent.justification,
|
| 433 |
-
"requires_enterprise": healing_intent.requires_enterprise,
|
| 434 |
-
"oss_advisory": healing_intent.is_oss_advisory,
|
| 435 |
-
"similar_incidents_count": len(similar_incidents),
|
| 436 |
-
"rag_similarity_score": healing_intent.rag_similarity_score,
|
| 437 |
-
"timestamp": time.time(),
|
| 438 |
-
"arf_version": "3.3.9" # FIXED: Updated from 3.3.7 to 3.3.9
|
| 439 |
-
}
|
| 440 |
-
|
| 441 |
-
except Exception as e:
|
| 442 |
-
logger.error(f"Decision agent failed: {e}")
|
| 443 |
-
# Create fallback healing intent
|
| 444 |
-
return self._create_fallback_intent(action, component, metrics, confidence)
|
| 445 |
-
|
| 446 |
-
def _determine_parameters(self, action: str, metrics: Dict[str, Any]) -> Dict[str, Any]:
|
| 447 |
-
"""Determine parameters for the healing action"""
|
| 448 |
-
if action == "scale_out":
|
| 449 |
-
# Scale factor based on severity of metrics
|
| 450 |
-
max_metric = max((v for v in metrics.values() if isinstance(v, (int, float))), default=1)
|
| 451 |
-
scale_factor = 2 if max_metric > 80 else 1
|
| 452 |
-
|
| 453 |
-
return {
|
| 454 |
-
"scale_factor": scale_factor,
|
| 455 |
-
"resource_profile": "standard",
|
| 456 |
-
"strategy": "gradual"
|
| 457 |
-
}
|
| 458 |
-
|
| 459 |
-
elif action == "restart_container":
|
| 460 |
-
return {
|
| 461 |
-
"grace_period": 30,
|
| 462 |
-
"force": False
|
| 463 |
-
}
|
| 464 |
-
|
| 465 |
-
elif action == "circuit_breaker":
|
| 466 |
-
return {
|
| 467 |
-
"threshold": 0.5,
|
| 468 |
-
"timeout": 60,
|
| 469 |
-
"half_open_after": 300
|
| 470 |
-
}
|
| 471 |
-
|
| 472 |
-
elif action == "alert_team":
|
| 473 |
-
return {
|
| 474 |
-
"severity": "critical",
|
| 475 |
-
"channels": ["slack", "email"],
|
| 476 |
-
"escalate_after_minutes": 5
|
| 477 |
-
}
|
| 478 |
-
|
| 479 |
-
elif action == "rollback":
|
| 480 |
-
return {
|
| 481 |
-
"revision": "previous",
|
| 482 |
-
"verify": True
|
| 483 |
-
}
|
| 484 |
-
|
| 485 |
-
elif action == "traffic_shift":
|
| 486 |
-
return {
|
| 487 |
-
"percentage": 50,
|
| 488 |
-
"target": "canary"
|
| 489 |
-
}
|
| 490 |
-
|
| 491 |
-
return {}
|
| 492 |
-
|
| 493 |
-
def _generate_justification(self, action: str, component: str, metrics: Dict[str, Any],
|
| 494 |
-
similar_incidents: List[Dict[str, Any]], confidence: float) -> str:
|
| 495 |
-
"""Generate human-readable justification"""
|
| 496 |
-
if similar_incidents:
|
| 497 |
-
similar_count = len(similar_incidents)
|
| 498 |
-
avg_success = sum(i.get("success_rate", 0.0) for i in similar_incidents) / similar_count
|
| 499 |
-
|
| 500 |
-
return (
|
| 501 |
-
f"Detected anomalies in {component} with {confidence:.0%} confidence. "
|
| 502 |
-
f"Found {similar_count} similar historical incidents with {avg_success:.0%} average success rate. "
|
| 503 |
-
f"Recommended {action} based on pattern matching and historical effectiveness."
|
| 504 |
-
)
|
| 505 |
-
else:
|
| 506 |
-
critical_metrics = []
|
| 507 |
-
for metric, value in metrics.items():
|
| 508 |
-
if isinstance(value, (int, float)) and value > 80: # Threshold
|
| 509 |
-
critical_metrics.append(f"{metric}: {value}")
|
| 510 |
-
|
| 511 |
-
return (
|
| 512 |
-
f"Detected anomalies in {component} with {confidence:.0%} confidence. "
|
| 513 |
-
f"Critical metrics: {', '.join(critical_metrics[:3])}. "
|
| 514 |
-
f"Recommended {action} based on anomaly characteristics and component type."
|
| 515 |
-
)
|
| 516 |
-
|
| 517 |
-
def _create_fallback_intent(self, action: str, component: str,
|
| 518 |
-
metrics: Dict[str, Any], confidence: float) -> Dict[str, Any]:
|
| 519 |
-
"""Create fallback healing intent when decision agent fails"""
|
| 520 |
-
return {
|
| 521 |
-
"action": action,
|
| 522 |
-
"component": component,
|
| 523 |
-
"parameters": {"fallback": True},
|
| 524 |
-
"confidence": confidence * 0.8, # Reduced confidence for fallback
|
| 525 |
-
"justification": f"Fallback recommendation for {component} anomalies",
|
| 526 |
-
"requires_enterprise": True,
|
| 527 |
-
"oss_advisory": True,
|
| 528 |
-
"similar_incidents_count": 0,
|
| 529 |
-
"rag_similarity_score": None,
|
| 530 |
-
"timestamp": time.time(),
|
| 531 |
-
"arf_version": "3.3.9" # FIXED: Updated from 3.3.7 to 3.3.9
|
| 532 |
-
}
|
| 533 |
-
|
| 534 |
-
def _compile_results(self, scenario_name: str, detection_result: Dict[str, Any],
|
| 535 |
-
similar_incidents: List[Dict[str, Any]], healing_intent: Dict[str, Any],
|
| 536 |
-
analysis_time_ms: float, component: str, metrics: Dict[str, Any]) -> Dict[str, Any]:
|
| 537 |
-
"""Compile all analysis results into final format"""
|
| 538 |
-
|
| 539 |
-
return {
|
| 540 |
-
"status": "success",
|
| 541 |
-
"scenario": scenario_name,
|
| 542 |
-
"analysis": {
|
| 543 |
-
"detection": detection_result,
|
| 544 |
-
"recall": similar_incidents,
|
| 545 |
-
"decision": healing_intent
|
| 546 |
-
},
|
| 547 |
-
"capabilities": {
|
| 548 |
-
"execution_allowed": False,
|
| 549 |
-
"mcp_modes": ["advisory"],
|
| 550 |
-
"oss_boundary": "advisory_only",
|
| 551 |
-
"requires_enterprise": True,
|
| 552 |
-
},
|
| 553 |
-
"agents_used": ["Detection", "Recall", "Decision"],
|
| 554 |
-
"analysis_time_ms": analysis_time_ms,
|
| 555 |
-
"arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 556 |
-
"oss_edition": True,
|
| 557 |
-
"demo_display": {
|
| 558 |
-
"real_arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 559 |
-
"true_oss_used": True,
|
| 560 |
-
"enterprise_simulated": False,
|
| 561 |
-
"agent_details": {
|
| 562 |
-
"detection_confidence": detection_result["confidence"],
|
| 563 |
-
"similar_incidents_count": len(similar_incidents),
|
| 564 |
-
"decision_confidence": healing_intent["confidence"],
|
| 565 |
-
"healing_action": healing_intent["action"],
|
| 566 |
-
}
|
| 567 |
-
}
|
| 568 |
-
}
|
| 569 |
-
|
| 570 |
-
def _create_no_anomaly_result(self, scenario_name: str, start_time: float) -> Dict[str, Any]:
|
| 571 |
-
"""Create result when no anomalies are detected"""
|
| 572 |
-
analysis_time_ms = (time.time() - start_time) * 1000
|
| 573 |
-
|
| 574 |
-
return {
|
| 575 |
-
"status": "success",
|
| 576 |
-
"scenario": scenario_name,
|
| 577 |
-
"result": "no_anomalies_detected",
|
| 578 |
-
"analysis_time_ms": analysis_time_ms,
|
| 579 |
-
"arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 580 |
-
"oss_edition": True,
|
| 581 |
-
"demo_display": {
|
| 582 |
-
"real_arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 583 |
-
"true_oss_used": True,
|
| 584 |
-
"no_anomalies": True
|
| 585 |
-
}
|
| 586 |
-
}
|
| 587 |
-
|
| 588 |
-
def _create_error_result(self, scenario_name: str, error: str,
|
| 589 |
-
start_time: float) -> Dict[str, Any]:
|
| 590 |
-
"""Create error result"""
|
| 591 |
-
analysis_time_ms = (time.time() - start_time) * 1000
|
| 592 |
-
|
| 593 |
-
return {
|
| 594 |
-
"status": "error",
|
| 595 |
-
"error": error,
|
| 596 |
-
"scenario": scenario_name,
|
| 597 |
-
"analysis_time_ms": analysis_time_ms,
|
| 598 |
-
"arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 599 |
-
"oss_edition": True,
|
| 600 |
-
"demo_display": {
|
| 601 |
-
"real_arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 602 |
-
"true_oss_used": True,
|
| 603 |
-
"error": error[:100]
|
| 604 |
-
}
|
| 605 |
-
}
|
| 606 |
-
|
| 607 |
-
def get_agent_stats(self) -> Dict[str, Any]:
|
| 608 |
-
"""Get statistics from all agents"""
|
| 609 |
-
return {
|
| 610 |
-
**self.agent_stats,
|
| 611 |
-
"oss_available": self.oss_available,
|
| 612 |
-
"arf_version": "3.3.9", # FIXED: Updated from 3.3.7 to 3.3.9
|
| 613 |
-
"avg_analysis_time_ms": (
|
| 614 |
-
self.agent_stats["total_time_ms"] / self.agent_stats["total_analyses"]
|
| 615 |
-
if self.agent_stats["total_analyses"] > 0 else 0
|
| 616 |
-
)
|
| 617 |
-
}
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
# ============================================================================
|
| 621 |
-
# FACTORY FUNCTION
|
| 622 |
-
# ============================================================================
|
| 623 |
-
|
| 624 |
-
async def get_true_arf_oss(config: Optional[Dict[str, Any]] = None) -> TrueARFOSS:
|
| 625 |
-
"""
|
| 626 |
-
Factory function for TrueARFOSS
|
| 627 |
-
|
| 628 |
-
This is the function that TrueARF337Orchestrator expects to call.
|
| 629 |
-
|
| 630 |
-
Args:
|
| 631 |
-
config: Optional configuration
|
| 632 |
-
|
| 633 |
-
Returns:
|
| 634 |
-
TrueARFOSS instance
|
| 635 |
-
"""
|
| 636 |
-
return TrueARFOSS(config)
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
# ============================================================================
|
| 640 |
-
# SIMPLE MOCK FOR BACKWARDS COMPATIBILITY
|
| 641 |
-
# ============================================================================
|
| 642 |
-
|
| 643 |
-
async def get_mock_true_arf_oss(config: Optional[Dict[str, Any]] = None) -> TrueARFOSS:
|
| 644 |
-
"""
|
| 645 |
-
Mock version for when dependencies are missing
|
| 646 |
-
"""
|
| 647 |
-
logger.warning("Using mock TrueARFOSS - real implementation not available")
|
| 648 |
-
|
| 649 |
-
class MockTrueARFOSS:
|
| 650 |
-
def __init__(self, config):
|
| 651 |
-
self.config = config or {}
|
| 652 |
-
self.oss_available = False
|
| 653 |
-
|
| 654 |
-
async def analyze_scenario(self, scenario_name, scenario_data):
|
| 655 |
-
return {
|
| 656 |
-
"status": "mock",
|
| 657 |
-
"scenario": scenario_name,
|
| 658 |
-
"message": "Mock analysis - install true ARF OSS v3.3.9 for real analysis", # FIXED: Updated version
|
| 659 |
-
"demo_display": {
|
| 660 |
-
"real_arf_version": "mock",
|
| 661 |
-
"true_oss_used": False,
|
| 662 |
-
"enterprise_simulated": False,
|
| 663 |
-
}
|
| 664 |
-
}
|
| 665 |
-
|
| 666 |
-
return MockTrueARFOSS(config)
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
# ============================================================================
|
| 670 |
-
# MAIN ENTRY POINT
|
| 671 |
-
# ============================================================================
|
| 672 |
-
|
| 673 |
-
if __name__ == "__main__":
|
| 674 |
-
# Test the implementation
|
| 675 |
-
import asyncio
|
| 676 |
-
|
| 677 |
-
async def test():
|
| 678 |
-
# Create test scenario
|
| 679 |
-
scenario = {
|
| 680 |
-
"component": "redis_cache",
|
| 681 |
-
"metrics": {
|
| 682 |
-
"cache_hit_rate": 18.5,
|
| 683 |
-
"database_load": 92,
|
| 684 |
-
"response_time_ms": 1850,
|
| 685 |
-
},
|
| 686 |
-
"business_impact": {
|
| 687 |
-
"revenue_loss_per_hour": 8500
|
| 688 |
-
}
|
| 689 |
-
}
|
| 690 |
-
|
| 691 |
-
arf = await get_true_arf_oss()
|
| 692 |
-
result = await arf.analyze_scenario("Test Cache Miss Storm", scenario)
|
| 693 |
-
print("Test Result:", json.dumps(result, indent=2, default=str))
|
| 694 |
-
|
| 695 |
-
asyncio.run(test())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
core/visualizations.py
DELETED
|
@@ -1,876 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Enhanced visualization engine for ARF Demo with clear boundary indicators
|
| 3 |
-
FIXED VERSION: Works even when Plotly fails, shows clear real/simulated boundaries
|
| 4 |
-
"""
|
| 5 |
-
|
| 6 |
-
import logging
|
| 7 |
-
from typing import Dict, List, Any, Optional, Tuple
|
| 8 |
-
import random
|
| 9 |
-
|
| 10 |
-
logger = logging.getLogger(__name__)
|
| 11 |
-
|
| 12 |
-
# Try to import Plotly, but have fallbacks
|
| 13 |
-
try:
|
| 14 |
-
import plotly.graph_objects as go
|
| 15 |
-
import plotly.express as px
|
| 16 |
-
import numpy as np
|
| 17 |
-
PLOTLY_AVAILABLE = True
|
| 18 |
-
logger.info("✅ Plotly available for advanced visualizations")
|
| 19 |
-
except ImportError as e:
|
| 20 |
-
PLOTLY_AVAILABLE = False
|
| 21 |
-
logger.warning(f"⚠️ Plotly not available: {e}. Using HTML fallback visualizations.")
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
class EnhancedVisualizationEngine:
|
| 25 |
-
"""Enhanced visualization engine with boundary awareness and fallbacks"""
|
| 26 |
-
|
| 27 |
-
def __init__(self):
|
| 28 |
-
self.color_palette = {
|
| 29 |
-
"primary": "#3b82f6",
|
| 30 |
-
"success": "#10b981",
|
| 31 |
-
"warning": "#f59e0b",
|
| 32 |
-
"danger": "#ef4444",
|
| 33 |
-
"info": "#8b5cf6",
|
| 34 |
-
"dark": "#1e293b",
|
| 35 |
-
"light": "#f8fafc",
|
| 36 |
-
"real_arf": "#10b981", # Green for real ARF
|
| 37 |
-
"simulated": "#f59e0b", # Amber for simulated
|
| 38 |
-
"mock": "#64748b", # Gray for mock
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
def create_executive_dashboard(self, data: Optional[Dict] = None, is_real_arf: bool = True) -> Any:
|
| 42 |
-
"""
|
| 43 |
-
Create executive dashboard with clear boundary indicators
|
| 44 |
-
|
| 45 |
-
Args:
|
| 46 |
-
data: Dashboard data
|
| 47 |
-
is_real_arf: Whether this is real ARF or simulated/mock
|
| 48 |
-
|
| 49 |
-
Returns:
|
| 50 |
-
Plotly figure or HTML fallback
|
| 51 |
-
"""
|
| 52 |
-
if data is None:
|
| 53 |
-
data = {"roi_multiplier": 5.2}
|
| 54 |
-
|
| 55 |
-
roi_multiplier = data.get("roi_multiplier", 5.2)
|
| 56 |
-
|
| 57 |
-
if not PLOTLY_AVAILABLE:
|
| 58 |
-
# HTML fallback
|
| 59 |
-
return self._create_html_dashboard(roi_multiplier, is_real_arf)
|
| 60 |
-
|
| 61 |
-
try:
|
| 62 |
-
# Create a multi-panel executive dashboard with boundary indicators
|
| 63 |
-
fig = go.Figure()
|
| 64 |
-
|
| 65 |
-
# Main ROI gauge with boundary indicator
|
| 66 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 67 |
-
boundary_text = "REAL ARF OSS" if is_real_arf else "SIMULATED"
|
| 68 |
-
|
| 69 |
-
fig.add_trace(go.Indicator(
|
| 70 |
-
mode="number+gauge",
|
| 71 |
-
value=roi_multiplier,
|
| 72 |
-
title={
|
| 73 |
-
"text": f"<b>ROI Multiplier</b><br>"
|
| 74 |
-
f"<span style='font-size: 12px; color: {boundary_color}'>"
|
| 75 |
-
f"💎 {boundary_text}</span>"
|
| 76 |
-
},
|
| 77 |
-
domain={'x': [0.25, 0.75], 'y': [0.6, 1]},
|
| 78 |
-
gauge={
|
| 79 |
-
'axis': {'range': [0, 10], 'tickwidth': 1},
|
| 80 |
-
'bar': {'color': boundary_color},
|
| 81 |
-
'steps': [
|
| 82 |
-
{'range': [0, 2], 'color': '#e5e7eb'},
|
| 83 |
-
{'range': [2, 4], 'color': '#d1d5db'},
|
| 84 |
-
{'range': [4, 6], 'color': '#10b981'},
|
| 85 |
-
{'range': [6, 10], 'color': '#059669'}
|
| 86 |
-
],
|
| 87 |
-
'threshold': {
|
| 88 |
-
'line': {'color': "black", 'width': 4},
|
| 89 |
-
'thickness': 0.75,
|
| 90 |
-
'value': roi_multiplier
|
| 91 |
-
}
|
| 92 |
-
}
|
| 93 |
-
))
|
| 94 |
-
|
| 95 |
-
# Add boundary indicator in top right
|
| 96 |
-
fig.add_annotation(
|
| 97 |
-
x=0.98, y=0.98,
|
| 98 |
-
xref="paper", yref="paper",
|
| 99 |
-
text=f"💎 {boundary_text}",
|
| 100 |
-
showarrow=False,
|
| 101 |
-
font=dict(size=14, color=boundary_color, family="Arial, sans-serif"),
|
| 102 |
-
bgcolor="white",
|
| 103 |
-
bordercolor=boundary_color,
|
| 104 |
-
borderwidth=2,
|
| 105 |
-
borderpad=4,
|
| 106 |
-
opacity=0.9
|
| 107 |
-
)
|
| 108 |
-
|
| 109 |
-
# Add secondary metrics with clear sourcing
|
| 110 |
-
source_text = "Real ARF OSS" if is_real_arf else "Demo Simulation"
|
| 111 |
-
|
| 112 |
-
fig.add_trace(go.Indicator(
|
| 113 |
-
mode="number",
|
| 114 |
-
value=85,
|
| 115 |
-
title={
|
| 116 |
-
"text": f"MTTR Reduction<br>"
|
| 117 |
-
f"<span style='font-size: 10px; color: #64748b'>{source_text}</span>"
|
| 118 |
-
},
|
| 119 |
-
number={'suffix': "%", 'font': {'size': 24}},
|
| 120 |
-
domain={'x': [0.1, 0.4], 'y': [0.2, 0.5]}
|
| 121 |
-
))
|
| 122 |
-
|
| 123 |
-
fig.add_trace(go.Indicator(
|
| 124 |
-
mode="number",
|
| 125 |
-
value=94,
|
| 126 |
-
title={
|
| 127 |
-
"text": f"Detection Accuracy<br>"
|
| 128 |
-
f"<span style='font-size: 10px; color: #64748b'>{source_text}</span>"
|
| 129 |
-
},
|
| 130 |
-
number={'suffix': "%", 'font': {'size': 24}},
|
| 131 |
-
domain={'x': [0.6, 0.9], 'y': [0.2, 0.5]}
|
| 132 |
-
))
|
| 133 |
-
|
| 134 |
-
fig.update_layout(
|
| 135 |
-
height=700,
|
| 136 |
-
paper_bgcolor="rgba(0,0,0,0)",
|
| 137 |
-
plot_bgcolor="rgba(0,0,0,0)",
|
| 138 |
-
font={'family': "Arial, sans-serif"},
|
| 139 |
-
margin=dict(t=50, b=50, l=50, r=50),
|
| 140 |
-
title=f"📊 Executive Dashboard - ARF v3.3.7<br>"
|
| 141 |
-
f"<span style='font-size: 14px; color: {boundary_color}'>"
|
| 142 |
-
f"Mode: {boundary_text}</span>"
|
| 143 |
-
)
|
| 144 |
-
|
| 145 |
-
return fig
|
| 146 |
-
|
| 147 |
-
except Exception as e:
|
| 148 |
-
logger.error(f"Plotly dashboard creation failed: {e}")
|
| 149 |
-
return self._create_html_dashboard(roi_multiplier, is_real_arf)
|
| 150 |
-
|
| 151 |
-
def _create_html_dashboard(self, roi_multiplier: float, is_real_arf: bool) -> str:
|
| 152 |
-
"""Create HTML fallback dashboard"""
|
| 153 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 154 |
-
boundary_text = "REAL ARF OSS" if is_real_arf else "SIMULATED"
|
| 155 |
-
|
| 156 |
-
return f"""
|
| 157 |
-
<div style="border: 2px solid {boundary_color}; border-radius: 16px; padding: 25px;
|
| 158 |
-
background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
|
| 159 |
-
box-shadow: 0 8px 32px rgba(0,0,0,0.1);">
|
| 160 |
-
|
| 161 |
-
<!-- Boundary indicator -->
|
| 162 |
-
<div style="position: absolute; top: 15px; right: 15px; padding: 6px 12px;
|
| 163 |
-
background: {boundary_color}; color: white; border-radius: 20px;
|
| 164 |
-
font-size: 12px; font-weight: bold; display: flex; align-items: center; gap: 6px;">
|
| 165 |
-
💎 {boundary_text}
|
| 166 |
-
</div>
|
| 167 |
-
|
| 168 |
-
<h3 style="margin: 0 0 20px 0; color: #1e293b; font-size: 20px; font-weight: 700;">
|
| 169 |
-
📊 Executive Dashboard - ARF v3.3.7
|
| 170 |
-
</h3>
|
| 171 |
-
|
| 172 |
-
<!-- Main ROI Gauge -->
|
| 173 |
-
<div style="text-align: center; margin: 30px 0;">
|
| 174 |
-
<div style="font-size: 14px; color: #64748b; margin-bottom: 10px; font-weight: 500;">
|
| 175 |
-
ROI Multiplier
|
| 176 |
-
</div>
|
| 177 |
-
<div style="position: relative; width: 160px; height: 160px; margin: 0 auto;">
|
| 178 |
-
<!-- Background circle -->
|
| 179 |
-
<div style="position: absolute; width: 160px; height: 160px;
|
| 180 |
-
border-radius: 50%; background: #f1f5f9; border: 10px solid #e2e8f0;"></div>
|
| 181 |
-
<!-- ROI arc -->
|
| 182 |
-
<div style="position: absolute; width: 160px; height: 160px;
|
| 183 |
-
border-radius: 50%;
|
| 184 |
-
background: conic-gradient({boundary_color} 0% {roi_multiplier*10}%, #e2e8f0 {roi_multiplier*10}% 100%);
|
| 185 |
-
border: 10px solid transparent; clip-path: polygon(50% 50%, 100% 0, 100% 100%);"></div>
|
| 186 |
-
<!-- Center value -->
|
| 187 |
-
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
| 188 |
-
font-size: 32px; font-weight: 800; color: {boundary_color};">
|
| 189 |
-
{roi_multiplier:.1f}×
|
| 190 |
-
</div>
|
| 191 |
-
</div>
|
| 192 |
-
<div style="margin-top: 15px; font-size: 12px; color: {boundary_color}; font-weight: 600;">
|
| 193 |
-
{boundary_text} Analysis
|
| 194 |
-
</div>
|
| 195 |
-
</div>
|
| 196 |
-
|
| 197 |
-
<!-- Secondary metrics -->
|
| 198 |
-
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 30px;">
|
| 199 |
-
<div style="text-align: center; padding: 15px; background: #f8fafc; border-radius: 12px;">
|
| 200 |
-
<div style="font-size: 12px; color: #64748b; margin-bottom: 8px;">MTTR Reduction</div>
|
| 201 |
-
<div style="font-size: 28px; font-weight: 700; color: #10b981;">85%</div>
|
| 202 |
-
<div style="font-size: 10px; color: #94a3b8; margin-top: 4px;">{boundary_text}</div>
|
| 203 |
-
</div>
|
| 204 |
-
|
| 205 |
-
<div style="text-align: center; padding: 15px; background: #f8fafc; border-radius: 12px;">
|
| 206 |
-
<div style="font-size: 12px; color: #64748b; margin-bottom: 8px;">Detection Accuracy</div>
|
| 207 |
-
<div style="font-size: 28px; font-weight=700; color: #10b981;">94%</div>
|
| 208 |
-
<div style="font-size: 10px; color: #94a3b8; margin-top: 4px;">{boundary_text}</div>
|
| 209 |
-
</div>
|
| 210 |
-
</div>
|
| 211 |
-
|
| 212 |
-
<!-- Data source note -->
|
| 213 |
-
<div style="margin-top: 25px; padding: 12px; background: #f1f5f9; border-radius: 8px; border-left: 4px solid {boundary_color};">
|
| 214 |
-
<div style="font-size: 12px; color: #475569; line-height: 1.5;">
|
| 215 |
-
<strong>📈 Data Source:</strong> {boundary_text} v3.3.7 •
|
| 216 |
-
<strong>⚡ Processing:</strong> Real-time analysis •
|
| 217 |
-
<strong>🎯 Confidence:</strong> Deterministic scoring
|
| 218 |
-
</div>
|
| 219 |
-
</div>
|
| 220 |
-
</div>
|
| 221 |
-
"""
|
| 222 |
-
|
| 223 |
-
def create_telemetry_plot(self, scenario_name: str, anomaly_detected: bool = True,
|
| 224 |
-
is_real_arf: bool = True) -> Any:
|
| 225 |
-
"""Create telemetry plot with boundary indicators"""
|
| 226 |
-
if not PLOTLY_AVAILABLE:
|
| 227 |
-
return self._create_html_telemetry(scenario_name, anomaly_detected, is_real_arf)
|
| 228 |
-
|
| 229 |
-
try:
|
| 230 |
-
import numpy as np
|
| 231 |
-
|
| 232 |
-
# Generate realistic telemetry data
|
| 233 |
-
time_points = np.arange(0, 100, 1)
|
| 234 |
-
|
| 235 |
-
# Different patterns for different scenarios
|
| 236 |
-
if "Cache" in scenario_name:
|
| 237 |
-
base_data = 100 + 50 * np.sin(time_points * 0.2)
|
| 238 |
-
noise = np.random.normal(0, 8, 100)
|
| 239 |
-
metric_name = "Cache Hit Rate (%)"
|
| 240 |
-
normal_range = (70, 95)
|
| 241 |
-
elif "Database" in scenario_name:
|
| 242 |
-
base_data = 70 + 30 * np.sin(time_points * 0.15)
|
| 243 |
-
noise = np.random.normal(0, 6, 100)
|
| 244 |
-
metric_name = "Connection Pool Usage"
|
| 245 |
-
normal_range = (20, 60)
|
| 246 |
-
elif "Memory" in scenario_name:
|
| 247 |
-
base_data = 50 + 40 * np.sin(time_points * 0.1)
|
| 248 |
-
noise = np.random.normal(0, 10, 100)
|
| 249 |
-
metric_name = "Memory Usage (%)"
|
| 250 |
-
normal_range = (40, 80)
|
| 251 |
-
else:
|
| 252 |
-
base_data = 80 + 20 * np.sin(time_points * 0.25)
|
| 253 |
-
noise = np.random.normal(0, 5, 100)
|
| 254 |
-
metric_name = "System Load"
|
| 255 |
-
normal_range = (50, 90)
|
| 256 |
-
|
| 257 |
-
data = base_data + noise
|
| 258 |
-
|
| 259 |
-
fig = go.Figure()
|
| 260 |
-
|
| 261 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 262 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 263 |
-
|
| 264 |
-
if anomaly_detected:
|
| 265 |
-
# Normal operation
|
| 266 |
-
fig.add_trace(go.Scatter(
|
| 267 |
-
x=time_points[:70],
|
| 268 |
-
y=data[:70],
|
| 269 |
-
mode='lines',
|
| 270 |
-
name='Normal Operation',
|
| 271 |
-
line=dict(color=self.color_palette["primary"], width=3),
|
| 272 |
-
fill='tozeroy',
|
| 273 |
-
fillcolor='rgba(59, 130, 246, 0.1)'
|
| 274 |
-
))
|
| 275 |
-
|
| 276 |
-
# Anomaly period
|
| 277 |
-
fig.add_trace(go.Scatter(
|
| 278 |
-
x=time_points[70:],
|
| 279 |
-
y=data[70:],
|
| 280 |
-
mode='lines',
|
| 281 |
-
name='🚨 Anomaly Detected',
|
| 282 |
-
line=dict(color=self.color_palette["danger"], width=3, dash='dash'),
|
| 283 |
-
fill='tozeroy',
|
| 284 |
-
fillcolor='rgba(239, 68, 68, 0.1)'
|
| 285 |
-
))
|
| 286 |
-
|
| 287 |
-
# Add detection point
|
| 288 |
-
fig.add_vline(
|
| 289 |
-
x=70,
|
| 290 |
-
line_dash="dash",
|
| 291 |
-
line_color=self.color_palette["success"],
|
| 292 |
-
annotation_text=f"ARF Detection ({boundary_text})",
|
| 293 |
-
annotation_position="top",
|
| 294 |
-
annotation_font_color=boundary_color
|
| 295 |
-
)
|
| 296 |
-
else:
|
| 297 |
-
# All normal
|
| 298 |
-
fig.add_trace(go.Scatter(
|
| 299 |
-
x=time_points,
|
| 300 |
-
y=data,
|
| 301 |
-
mode='lines',
|
| 302 |
-
name=metric_name,
|
| 303 |
-
line=dict(color=self.color_palette["primary"], width=3),
|
| 304 |
-
fill='tozeroy',
|
| 305 |
-
fillcolor='rgba(59, 130, 246, 0.1)'
|
| 306 |
-
))
|
| 307 |
-
|
| 308 |
-
# Add normal range
|
| 309 |
-
fig.add_hrect(
|
| 310 |
-
y0=normal_range[0],
|
| 311 |
-
y1=normal_range[1],
|
| 312 |
-
fillcolor="rgba(16, 185, 129, 0.1)",
|
| 313 |
-
opacity=0.2,
|
| 314 |
-
line_width=0,
|
| 315 |
-
annotation_text="Normal Range",
|
| 316 |
-
annotation_position="top left"
|
| 317 |
-
)
|
| 318 |
-
|
| 319 |
-
# Add boundary indicator
|
| 320 |
-
fig.add_annotation(
|
| 321 |
-
x=0.02, y=0.98,
|
| 322 |
-
xref="paper", yref="paper",
|
| 323 |
-
text=f"💎 {boundary_text}",
|
| 324 |
-
showarrow=False,
|
| 325 |
-
font=dict(size=12, color=boundary_color),
|
| 326 |
-
bgcolor="white",
|
| 327 |
-
bordercolor=boundary_color,
|
| 328 |
-
borderwidth=2,
|
| 329 |
-
borderpad=4
|
| 330 |
-
)
|
| 331 |
-
|
| 332 |
-
fig.update_layout(
|
| 333 |
-
title=f"📈 {metric_name} - Live Telemetry<br>"
|
| 334 |
-
f"<span style='font-size: 12px; color: #64748b'>"
|
| 335 |
-
f"ARF v3.3.7 • {boundary_text} Analysis</span>",
|
| 336 |
-
xaxis_title="Time (minutes)",
|
| 337 |
-
yaxis_title=metric_name,
|
| 338 |
-
height=350,
|
| 339 |
-
margin=dict(l=20, r=20, t=70, b=20),
|
| 340 |
-
plot_bgcolor='rgba(0,0,0,0)',
|
| 341 |
-
paper_bgcolor='rgba(0,0,0,0)',
|
| 342 |
-
legend=dict(
|
| 343 |
-
orientation="h",
|
| 344 |
-
yanchor="bottom",
|
| 345 |
-
y=1.02,
|
| 346 |
-
xanchor="right",
|
| 347 |
-
x=1
|
| 348 |
-
)
|
| 349 |
-
)
|
| 350 |
-
|
| 351 |
-
return fig
|
| 352 |
-
|
| 353 |
-
except Exception as e:
|
| 354 |
-
logger.error(f"Telemetry plot creation failed: {e}")
|
| 355 |
-
return self._create_html_telemetry(scenario_name, anomaly_detected, is_real_arf)
|
| 356 |
-
|
| 357 |
-
def _create_html_telemetry(self, scenario_name: str, anomaly_detected: bool, is_real_arf: bool) -> str:
|
| 358 |
-
"""HTML fallback for telemetry visualization"""
|
| 359 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 360 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 361 |
-
|
| 362 |
-
if "Cache" in scenario_name:
|
| 363 |
-
metric_name = "Cache Hit Rate"
|
| 364 |
-
normal_range = (70, 95)
|
| 365 |
-
current_value = 18 if anomaly_detected else 85
|
| 366 |
-
elif "Database" in scenario_name:
|
| 367 |
-
metric_name = "Connection Pool Usage"
|
| 368 |
-
normal_range = (20, 60)
|
| 369 |
-
current_value = 92 if anomaly_detected else 45
|
| 370 |
-
else:
|
| 371 |
-
metric_name = "System Load"
|
| 372 |
-
normal_range = (50, 90)
|
| 373 |
-
current_value = 185 if anomaly_detected else 65
|
| 374 |
-
|
| 375 |
-
# Calculate position in range
|
| 376 |
-
percentage = ((current_value - normal_range[0]) / (normal_range[1] - normal_range[0])) * 100
|
| 377 |
-
percentage = max(0, min(100, percentage))
|
| 378 |
-
|
| 379 |
-
return f"""
|
| 380 |
-
<div style="border: 2px solid {boundary_color}; border-radius: 16px; padding: 20px;
|
| 381 |
-
background: white; box-shadow: 0 4px 12px rgba(0,0,0,0.05);">
|
| 382 |
-
|
| 383 |
-
<!-- Boundary indicator -->
|
| 384 |
-
<div style="position: absolute; top: 15px; right: 15px; padding: 4px 10px;
|
| 385 |
-
background: {boundary_color}; color: white; border-radius: 15px;
|
| 386 |
-
font-size: 11px; font-weight: bold; display: flex; align-items: center; gap: 5px;">
|
| 387 |
-
💎 {boundary_text}
|
| 388 |
-
</div>
|
| 389 |
-
|
| 390 |
-
<h4 style="margin: 0 0 15px 0; color: #1e293b; font-size: 16px; font-weight: 600;">
|
| 391 |
-
📈 {metric_name} - Live Telemetry
|
| 392 |
-
</h4>
|
| 393 |
-
|
| 394 |
-
<!-- Simplified timeline -->
|
| 395 |
-
<div style="position: relative; height: 100px; margin: 20px 0;">
|
| 396 |
-
<!-- Background line -->
|
| 397 |
-
<div style="position: absolute; left: 0; right: 0; top: 50%;
|
| 398 |
-
height: 2px; background: #e2e8f0; transform: translateY(-50%);"></div>
|
| 399 |
-
|
| 400 |
-
<!-- Normal range -->
|
| 401 |
-
<div style="position: absolute; left: 10%; right: 40%; top: 50%;
|
| 402 |
-
height: 6px; background: #10b981; transform: translateY(-50%);
|
| 403 |
-
border-radius: 3px; opacity: 0.3;"></div>
|
| 404 |
-
|
| 405 |
-
<!-- Anomaly point -->
|
| 406 |
-
<div style="position: absolute; left: 70%; top: 50%;
|
| 407 |
-
transform: translate(-50%, -50%);">
|
| 408 |
-
<div style="width: 20px; height: 20px; border-radius: 50%;
|
| 409 |
-
background: {'#ef4444' if anomaly_detected else '#10b981'};
|
| 410 |
-
border: 3px solid white; box-shadow: 0 0 0 2px {'#ef4444' if anomaly_detected else '#10b981'};">
|
| 411 |
-
</div>
|
| 412 |
-
<div style="position: absolute; top: 25px; left: 50%; transform: translateX(-50%);
|
| 413 |
-
white-space: nowrap; font-size: 11px; color: #64748b;">
|
| 414 |
-
{current_value}
|
| 415 |
-
</div>
|
| 416 |
-
</div>
|
| 417 |
-
|
| 418 |
-
<!-- Labels -->
|
| 419 |
-
<div style="position: absolute; left: 10%; top: 70px; font-size: 11px; color: #64748b;">
|
| 420 |
-
Normal: {normal_range[0]}
|
| 421 |
-
</div>
|
| 422 |
-
<div style="position: absolute; left: 40%; top: 70px; font-size: 11px; color: #64748b;">
|
| 423 |
-
Warning: {normal_range[1]}
|
| 424 |
-
</div>
|
| 425 |
-
<div style="position: absolute; left: 70%; top: 70px; font-size: 11px;
|
| 426 |
-
color: {'#ef4444' if anomaly_detected else '#10b981'}; font-weight: 500;">
|
| 427 |
-
Current: {current_value}
|
| 428 |
-
</div>
|
| 429 |
-
</div>
|
| 430 |
-
|
| 431 |
-
<!-- Status indicator -->
|
| 432 |
-
<div style="display: flex; justify-content: space-between; align-items: center;
|
| 433 |
-
margin-top: 15px; padding: 10px; background: #f8fafc; border-radius: 8px;">
|
| 434 |
-
<div>
|
| 435 |
-
<div style="font-size: 12px; color: #64748b;">Status</div>
|
| 436 |
-
<div style="font-size: 14px; color: {'#ef4444' if anomaly_detected else '#10b981'};
|
| 437 |
-
font-weight: 600;">
|
| 438 |
-
{'🚨 Anomaly Detected' if anomaly_detected else '✅ Normal'}
|
| 439 |
-
</div>
|
| 440 |
-
</div>
|
| 441 |
-
<div style="text-align: right;">
|
| 442 |
-
<div style="font-size: 12px; color: #64748b;">ARF Mode</div>
|
| 443 |
-
<div style="font-size: 14px; color: {boundary_color}; font-weight: 600;">
|
| 444 |
-
{boundary_text}
|
| 445 |
-
</div>
|
| 446 |
-
</div>
|
| 447 |
-
</div>
|
| 448 |
-
</div>
|
| 449 |
-
"""
|
| 450 |
-
|
| 451 |
-
def create_impact_gauge(self, scenario_name: str, is_real_arf: bool = True) -> Any:
|
| 452 |
-
"""Create business impact gauge with boundary indicators"""
|
| 453 |
-
impact_map = {
|
| 454 |
-
"Cache Miss Storm": {"revenue": 8500, "severity": "critical", "users": 45000},
|
| 455 |
-
"Database Connection Pool Exhaustion": {"revenue": 4200, "severity": "high", "users": 25000},
|
| 456 |
-
"Kubernetes Memory Leak": {"revenue": 5500, "severity": "high", "users": 35000},
|
| 457 |
-
"API Rate Limit Storm": {"revenue": 3800, "severity": "medium", "users": 20000},
|
| 458 |
-
"Network Partition": {"revenue": 12000, "severity": "critical", "users": 75000},
|
| 459 |
-
"Storage I/O Saturation": {"revenue": 6800, "severity": "high", "users": 30000}
|
| 460 |
-
}
|
| 461 |
-
|
| 462 |
-
impact = impact_map.get(scenario_name, {"revenue": 5000, "severity": "medium", "users": 30000})
|
| 463 |
-
|
| 464 |
-
if not PLOTLY_AVAILABLE:
|
| 465 |
-
return self._create_html_impact_gauge(impact, is_real_arf)
|
| 466 |
-
|
| 467 |
-
try:
|
| 468 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 469 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 470 |
-
severity_color = self._get_severity_color(impact["severity"])
|
| 471 |
-
|
| 472 |
-
fig = go.Figure(go.Indicator(
|
| 473 |
-
mode="gauge+number",
|
| 474 |
-
value=impact["revenue"],
|
| 475 |
-
title={
|
| 476 |
-
"text": f"💰 Hourly Revenue Risk<br>"
|
| 477 |
-
f"<span style='font-size: 12px; color: {boundary_color}'>"
|
| 478 |
-
f"{boundary_text} Analysis</span>"
|
| 479 |
-
},
|
| 480 |
-
number={'prefix': "$", 'font': {'size': 28}},
|
| 481 |
-
gauge={
|
| 482 |
-
'axis': {'range': [0, 15000], 'tickwidth': 1},
|
| 483 |
-
'bar': {'color': severity_color},
|
| 484 |
-
'steps': [
|
| 485 |
-
{'range': [0, 3000], 'color': '#10b981'},
|
| 486 |
-
{'range': [3000, 7000], 'color': '#f59e0b'},
|
| 487 |
-
{'range': [7000, 15000], 'color': '#ef4444'}
|
| 488 |
-
],
|
| 489 |
-
'threshold': {
|
| 490 |
-
'line': {'color': "black", 'width': 4},
|
| 491 |
-
'thickness': 0.75,
|
| 492 |
-
'value': impact["revenue"]
|
| 493 |
-
}
|
| 494 |
-
}
|
| 495 |
-
))
|
| 496 |
-
|
| 497 |
-
# Add users affected annotation
|
| 498 |
-
fig.add_annotation(
|
| 499 |
-
x=0.5, y=0.3,
|
| 500 |
-
xref="paper", yref="paper",
|
| 501 |
-
text=f"👥 {impact['users']:,} users affected",
|
| 502 |
-
showarrow=False,
|
| 503 |
-
font=dict(size=14, color="#64748b")
|
| 504 |
-
)
|
| 505 |
-
|
| 506 |
-
fig.update_layout(
|
| 507 |
-
height=350,
|
| 508 |
-
margin=dict(l=20, r=20, t=80, b=20),
|
| 509 |
-
paper_bgcolor='rgba(0,0,0,0)',
|
| 510 |
-
plot_bgcolor='rgba(0,0,0,0)'
|
| 511 |
-
)
|
| 512 |
-
|
| 513 |
-
return fig
|
| 514 |
-
|
| 515 |
-
except Exception as e:
|
| 516 |
-
logger.error(f"Impact gauge creation failed: {e}")
|
| 517 |
-
return self._create_html_impact_gauge(impact, is_real_arf)
|
| 518 |
-
|
| 519 |
-
def _create_html_impact_gauge(self, impact: Dict, is_real_arf: bool) -> str:
|
| 520 |
-
"""HTML fallback for impact gauge"""
|
| 521 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 522 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 523 |
-
severity_color = self._get_severity_color(impact["severity"])
|
| 524 |
-
|
| 525 |
-
# Calculate percentage for gauge
|
| 526 |
-
revenue = impact["revenue"]
|
| 527 |
-
percentage = min(100, (revenue / 15000) * 100)
|
| 528 |
-
|
| 529 |
-
return f"""
|
| 530 |
-
<div style="border: 2px solid {boundary_color}; border-radius: 16px; padding: 20px;
|
| 531 |
-
background: white; box-shadow: 0 4px 12px rgba(0,0,0,0.05); text-align: center;">
|
| 532 |
-
|
| 533 |
-
<!-- Boundary indicator -->
|
| 534 |
-
<div style="position: absolute; top: 15px; right: 15px; padding: 4px 10px;
|
| 535 |
-
background: {boundary_color}; color: white; border-radius: 15px;
|
| 536 |
-
font-size: 11px; font-weight: bold; display: flex; align-items: center; gap: 5px;">
|
| 537 |
-
💎 {boundary_text}
|
| 538 |
-
</div>
|
| 539 |
-
|
| 540 |
-
<h4 style="margin: 0 0 15px 0; color: #1e293b; font-size: 16px; font-weight: 600;">
|
| 541 |
-
💰 Business Impact
|
| 542 |
-
</h4>
|
| 543 |
-
|
| 544 |
-
<!-- Revenue gauge -->
|
| 545 |
-
<div style="position: relative; width: 200px; height: 200px; margin: 0 auto;">
|
| 546 |
-
<!-- Background circle -->
|
| 547 |
-
<div style="position: absolute; width: 200px; height: 200px;
|
| 548 |
-
border-radius: 50%; background: #f1f5f9; border: 12px solid #e2e8f0;"></div>
|
| 549 |
-
|
| 550 |
-
<!-- Severity arc -->
|
| 551 |
-
<div style="position: absolute; width: 200px; height: 200px;
|
| 552 |
-
border-radius: 50%;
|
| 553 |
-
background: conic-gradient({severity_color} 0% {percentage}%, #e2e8f0 {percentage}% 100%);
|
| 554 |
-
border: 12px solid transparent; clip-path: polygon(50% 50%, 100% 0, 100% 100%);"></div>
|
| 555 |
-
|
| 556 |
-
<!-- Center value -->
|
| 557 |
-
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
|
| 558 |
-
font-size: 28px; font-weight: 800; color: {severity_color}; line-height: 1;">
|
| 559 |
-
${revenue:,}<br>
|
| 560 |
-
<span style="font-size: 14px; color: #64748b;">per hour</span>
|
| 561 |
-
</div>
|
| 562 |
-
</div>
|
| 563 |
-
|
| 564 |
-
<!-- Severity indicator -->
|
| 565 |
-
<div style="display: inline-block; padding: 6px 16px; background: {severity_color};
|
| 566 |
-
color: white; border-radius: 20px; font-size: 13px; font-weight: bold;
|
| 567 |
-
margin: 15px 0; text-transform: uppercase;">
|
| 568 |
-
{impact['severity']} SEVERITY
|
| 569 |
-
</div>
|
| 570 |
-
|
| 571 |
-
<!-- User impact -->
|
| 572 |
-
<div style="margin-top: 15px; padding: 12px; background: #f8fafc; border-radius: 10px;">
|
| 573 |
-
<div style="display: flex; justify-content: space-between; align-items: center;">
|
| 574 |
-
<div style="font-size: 13px; color: #64748b;">👥 Users Affected</div>
|
| 575 |
-
<div style="font-size: 18px; font-weight: 700; color: #1e293b;">
|
| 576 |
-
{impact['users']:,}
|
| 577 |
-
</div>
|
| 578 |
-
</div>
|
| 579 |
-
<div style="font-size: 11px; color: #94a3b8; margin-top: 5px; text-align: center;">
|
| 580 |
-
Analysis: {boundary_text} v3.3.7
|
| 581 |
-
</div>
|
| 582 |
-
</div>
|
| 583 |
-
</div>
|
| 584 |
-
"""
|
| 585 |
-
|
| 586 |
-
def create_timeline_comparison(self, is_real_arf: bool = True) -> Any:
|
| 587 |
-
"""Create timeline comparison chart"""
|
| 588 |
-
if not PLOTLY_AVAILABLE:
|
| 589 |
-
return self._create_html_timeline(is_real_arf)
|
| 590 |
-
|
| 591 |
-
try:
|
| 592 |
-
import numpy as np
|
| 593 |
-
|
| 594 |
-
phases = ["Detection", "Analysis", "Decision", "Execution", "Recovery"]
|
| 595 |
-
manual_times = [300, 1800, 1200, 1800, 3600] # seconds
|
| 596 |
-
arf_times = [45, 30, 60, 720, 0]
|
| 597 |
-
|
| 598 |
-
# Convert to minutes for readability
|
| 599 |
-
manual_times_min = [t/60 for t in manual_times]
|
| 600 |
-
arf_times_min = [t/60 for t in arf_times]
|
| 601 |
-
|
| 602 |
-
fig = go.Figure()
|
| 603 |
-
|
| 604 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 605 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 606 |
-
|
| 607 |
-
fig.add_trace(go.Bar(
|
| 608 |
-
name='Manual Process',
|
| 609 |
-
x=phases,
|
| 610 |
-
y=manual_times_min,
|
| 611 |
-
marker_color=self.color_palette["danger"],
|
| 612 |
-
text=[f"{t:.0f}m" for t in manual_times_min],
|
| 613 |
-
textposition='auto'
|
| 614 |
-
))
|
| 615 |
-
|
| 616 |
-
fig.add_trace(go.Bar(
|
| 617 |
-
name=f'ARF Autonomous ({boundary_text})',
|
| 618 |
-
x=phases,
|
| 619 |
-
y=arf_times_min,
|
| 620 |
-
marker_color=boundary_color,
|
| 621 |
-
text=[f"{t:.0f}m" for t in arf_times_min],
|
| 622 |
-
textposition='auto'
|
| 623 |
-
))
|
| 624 |
-
|
| 625 |
-
total_manual = sum(manual_times_min)
|
| 626 |
-
total_arf = sum(arf_times_min)
|
| 627 |
-
|
| 628 |
-
fig.update_layout(
|
| 629 |
-
title=f"⏰ Incident Timeline Comparison<br>"
|
| 630 |
-
f"<span style='font-size: 14px; color: #6b7280'>"
|
| 631 |
-
f"Total: {total_manual:.0f}m manual vs {total_arf:.0f}m ARF "
|
| 632 |
-
f"({((total_manual - total_arf) / total_manual * 100):.0f}% faster)</span>",
|
| 633 |
-
barmode='group',
|
| 634 |
-
height=400,
|
| 635 |
-
plot_bgcolor='rgba(0,0,0,0)',
|
| 636 |
-
paper_bgcolor='rgba(0,0,0,0)',
|
| 637 |
-
legend=dict(
|
| 638 |
-
orientation="h",
|
| 639 |
-
yanchor="bottom",
|
| 640 |
-
y=1.02,
|
| 641 |
-
xanchor="right",
|
| 642 |
-
x=1
|
| 643 |
-
),
|
| 644 |
-
yaxis_title="Time (minutes)"
|
| 645 |
-
)
|
| 646 |
-
|
| 647 |
-
return fig
|
| 648 |
-
|
| 649 |
-
except Exception as e:
|
| 650 |
-
logger.error(f"Timeline comparison creation failed: {e}")
|
| 651 |
-
return self._create_html_timeline(is_real_arf)
|
| 652 |
-
|
| 653 |
-
def _create_html_timeline(self, is_real_arf: bool) -> str:
|
| 654 |
-
"""HTML fallback for timeline comparison"""
|
| 655 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 656 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 657 |
-
|
| 658 |
-
phases = ["Detection", "Analysis", "Decision", "Execution", "Recovery"]
|
| 659 |
-
manual_times = [5, 30, 20, 30, 60] # minutes
|
| 660 |
-
arf_times = [0.75, 0.5, 1, 12, 0] # minutes
|
| 661 |
-
|
| 662 |
-
# Calculate totals
|
| 663 |
-
total_manual = sum(manual_times)
|
| 664 |
-
total_arf = sum(arf_times)
|
| 665 |
-
percent_faster = ((total_manual - total_arf) / total_manual) * 100
|
| 666 |
-
|
| 667 |
-
return f"""
|
| 668 |
-
<div style="border: 2px solid {boundary_color}; border-radius: 16px; padding: 20px;
|
| 669 |
-
background: white; box-shadow: 0 4px 12px rgba(0,0,0,0.05);">
|
| 670 |
-
|
| 671 |
-
<!-- Boundary indicator -->
|
| 672 |
-
<div style="position: absolute; top: 15px; right: 15px; padding: 4px 10px;
|
| 673 |
-
background: {boundary_color}; color: white; border-radius: 15px;
|
| 674 |
-
font-size: 11px; font-weight: bold; display: flex; align-items: center; gap: 5px;">
|
| 675 |
-
⏰ {boundary_text}
|
| 676 |
-
</div>
|
| 677 |
-
|
| 678 |
-
<h4 style="margin: 0 0 15px 0; color: #1e293b; font-size: 16px; font-weight: 600;">
|
| 679 |
-
⏰ Timeline Comparison
|
| 680 |
-
</h4>
|
| 681 |
-
|
| 682 |
-
<div style="font-size: 13px; color: #64748b; margin-bottom: 20px; line-height: 1.5;">
|
| 683 |
-
<strong>Manual:</strong> {total_manual:.0f} minutes •
|
| 684 |
-
<strong>ARF ({boundary_text}):</strong> {total_arf:.0f} minutes •
|
| 685 |
-
<strong>Faster:</strong> {percent_faster:.0f}%
|
| 686 |
-
</div>
|
| 687 |
-
|
| 688 |
-
<!-- Timeline visualization -->
|
| 689 |
-
<div style="position: relative; margin: 20px 0;">
|
| 690 |
-
<!-- Timeline line -->
|
| 691 |
-
<div style="position: absolute; left: 0; right: 0; top: 20px;
|
| 692 |
-
height: 2px; background: #e2e8f0;"></div>
|
| 693 |
-
|
| 694 |
-
{self._create_timeline_segments(phases, manual_times, arf_times, boundary_color)}
|
| 695 |
-
|
| 696 |
-
<!-- Legend -->
|
| 697 |
-
<div style="display: flex; gap: 15px; margin-top: 50px; justify-content: center;">
|
| 698 |
-
<div style="display: flex; align-items: center; gap: 6px;">
|
| 699 |
-
<div style="width: 12px; height: 12px; background: #ef4444; border-radius: 2px;"></div>
|
| 700 |
-
<div style="font-size: 12px; color: #64748b;">Manual Process</div>
|
| 701 |
-
</div>
|
| 702 |
-
<div style="display: flex; align-items: center; gap: 6px;">
|
| 703 |
-
<div style="width: 12px; height=12px; background: {boundary_color}; border-radius: 2px;"></div>
|
| 704 |
-
<div style="font-size: 12px; color: #64748b;">ARF ({boundary_text})</div>
|
| 705 |
-
</div>
|
| 706 |
-
</div>
|
| 707 |
-
</div>
|
| 708 |
-
|
| 709 |
-
<!-- Summary -->
|
| 710 |
-
<div style="margin-top: 20px; padding: 15px; background: #f8fafc; border-radius: 10px;">
|
| 711 |
-
<div style="font-size: 14px; color: #475569; line-height: 1.6;">
|
| 712 |
-
<strong>🎯 ARF Value:</strong> Reduces MTTR from {total_manual:.0f} to {total_arf:.0f} minutes<br>
|
| 713 |
-
<strong>💎 Mode:</strong> {boundary_text} autonomous execution<br>
|
| 714 |
-
<strong>📈 Impact:</strong> {percent_faster:.0f}% faster resolution
|
| 715 |
-
</div>
|
| 716 |
-
</div>
|
| 717 |
-
</div>
|
| 718 |
-
"""
|
| 719 |
-
|
| 720 |
-
def _create_timeline_segments(self, phases: List[str], manual_times: List[float],
|
| 721 |
-
arf_times: List[float], boundary_color: str) -> str:
|
| 722 |
-
"""Create timeline segments HTML"""
|
| 723 |
-
segments_html = ""
|
| 724 |
-
total_width = 0
|
| 725 |
-
|
| 726 |
-
for i, (phase, manual_time, arf_time) in enumerate(zip(phases, manual_times, arf_times)):
|
| 727 |
-
# Calculate widths as percentages
|
| 728 |
-
manual_width = (manual_time / 125) * 100 # 125 = sum of all times
|
| 729 |
-
arf_width = (arf_time / 125) * 100
|
| 730 |
-
|
| 731 |
-
segments_html += f"""
|
| 732 |
-
<div style="position: absolute; left: {total_width}%; width: {manual_width}%;
|
| 733 |
-
top: 10px; text-align: center;">
|
| 734 |
-
<div style="height: 20px; background: #ef4444; border-radius: 4px; margin-bottom: 5px;"></div>
|
| 735 |
-
<div style="font-size: 11px; color: #64748b;">{phase}<br>{manual_time:.0f}m</div>
|
| 736 |
-
</div>
|
| 737 |
-
|
| 738 |
-
<div style="position: absolute; left: {total_width}%; width: {arf_width}%;
|
| 739 |
-
top: 35px; text-align: center;">
|
| 740 |
-
<div style="height: 20px; background: {boundary_color}; border-radius: 4px; margin-bottom: 5px;"></div>
|
| 741 |
-
<div style="font-size: 11px; color: #475569; font-weight: 500;">{arf_time:.0f}m</div>
|
| 742 |
-
</div>
|
| 743 |
-
"""
|
| 744 |
-
|
| 745 |
-
total_width += manual_width
|
| 746 |
-
|
| 747 |
-
return segments_html
|
| 748 |
-
|
| 749 |
-
def _get_severity_color(self, severity: str) -> str:
|
| 750 |
-
"""Get color for severity level"""
|
| 751 |
-
color_map = {
|
| 752 |
-
"critical": self.color_palette["danger"],
|
| 753 |
-
"high": self.color_palette["warning"],
|
| 754 |
-
"medium": self.color_palette["info"],
|
| 755 |
-
"low": self.color_palette["success"]
|
| 756 |
-
}
|
| 757 |
-
return color_map.get(severity.lower(), self.color_palette["info"])
|
| 758 |
-
|
| 759 |
-
# Keep other methods from original file but add boundary parameters
|
| 760 |
-
def create_agent_performance_chart(self, is_real_arf: bool = True) -> Any:
|
| 761 |
-
"""Create agent performance comparison chart"""
|
| 762 |
-
if not PLOTLY_AVAILABLE:
|
| 763 |
-
# HTML fallback
|
| 764 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 765 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 766 |
-
|
| 767 |
-
agents = ["Detection", "Recall", "Decision"]
|
| 768 |
-
accuracy = [98.7, 92.0, 94.0]
|
| 769 |
-
speed = [45, 30, 60]
|
| 770 |
-
|
| 771 |
-
rows = ""
|
| 772 |
-
for agent, acc, spd in zip(agents, accuracy, speed):
|
| 773 |
-
rows += f"""
|
| 774 |
-
<tr>
|
| 775 |
-
<td style="padding: 8px; border-bottom: 1px solid #e2e8f0;">
|
| 776 |
-
<div style="font-weight: 600; color: #1e293b;">{agent}</div>
|
| 777 |
-
</td>
|
| 778 |
-
<td style="padding: 8px; border-bottom: 1px solid #e2e8f0; text-align: center;">
|
| 779 |
-
<div style="font-weight: 700; color: #10b981;">{acc}%</div>
|
| 780 |
-
</td>
|
| 781 |
-
<td style="padding: 8px; border-bottom: 1px solid #e2e8f0; text-align: center;">
|
| 782 |
-
<div style="font-weight: 700; color: {boundary_color};">{spd}s</div>
|
| 783 |
-
</td>
|
| 784 |
-
</tr>
|
| 785 |
-
"""
|
| 786 |
-
|
| 787 |
-
return f"""
|
| 788 |
-
<div style="border: 2px solid {boundary_color}; border-radius: 16px; padding: 20px;
|
| 789 |
-
background: white; box-shadow: 0 4px 12px rgba(0,0,0,0.05);">
|
| 790 |
-
<div style="position: absolute; top: 15px; right: 15px; padding: 4px 10px;
|
| 791 |
-
background: {boundary_color}; color: white; border-radius: 15px;
|
| 792 |
-
font-size: 11px; font-weight: bold; display: flex; align-items: center; gap: 5px;">
|
| 793 |
-
🤖 {boundary_text}
|
| 794 |
-
</div>
|
| 795 |
-
|
| 796 |
-
<h4 style="margin: 0 0 15px 0; color: #1e293b; font-size: 16px; font-weight: 600;">
|
| 797 |
-
🤖 Agent Performance
|
| 798 |
-
</h4>
|
| 799 |
-
|
| 800 |
-
<table style="width: 100%; border-collapse: collapse;">
|
| 801 |
-
<thead>
|
| 802 |
-
<tr>
|
| 803 |
-
<th style="padding: 8px; text-align: left; color: #64748b; font-size: 13px; border-bottom: 2px solid #e2e8f0;">Agent</th>
|
| 804 |
-
<th style="padding: 8px; text-align: center; color: #64748b; font-size: 13px; border-bottom: 2px solid #e2e8f0;">Accuracy</th>
|
| 805 |
-
<th style="padding: 8px; text-align: center; color: #64748b; font-size=13px; border-bottom: 2px solid #e2e8f0;">Speed</th>
|
| 806 |
-
</tr>
|
| 807 |
-
</thead>
|
| 808 |
-
<tbody>
|
| 809 |
-
{rows}
|
| 810 |
-
</tbody>
|
| 811 |
-
</table>
|
| 812 |
-
|
| 813 |
-
<div style="margin-top: 15px; padding: 10px; background: #f8fafc; border-radius: 8px;">
|
| 814 |
-
<div style="font-size: 12px; color: #64748b; line-height: 1.5;">
|
| 815 |
-
<strong>Mode:</strong> {boundary_text} v3.3.7 •
|
| 816 |
-
<strong>Avg Accuracy:</strong> {(sum(accuracy)/len(accuracy)):.1f}% •
|
| 817 |
-
<strong>Avg Speed:</strong> {(sum(speed)/len(speed)):.0f}s
|
| 818 |
-
</div>
|
| 819 |
-
</div>
|
| 820 |
-
</div>
|
| 821 |
-
"""
|
| 822 |
-
|
| 823 |
-
# Original Plotly implementation with boundary additions
|
| 824 |
-
try:
|
| 825 |
-
agents = ["Detection", "Recall", "Decision"]
|
| 826 |
-
accuracy = [98.7, 92.0, 94.0]
|
| 827 |
-
speed = [45, 30, 60] # seconds
|
| 828 |
-
confidence = [99.8, 92.0, 94.0]
|
| 829 |
-
|
| 830 |
-
boundary_color = self.color_palette["real_arf"] if is_real_arf else self.color_palette["simulated"]
|
| 831 |
-
boundary_text = "REAL ARF" if is_real_arf else "SIMULATED"
|
| 832 |
-
|
| 833 |
-
fig = go.Figure(data=[
|
| 834 |
-
go.Bar(name='Accuracy (%)', x=agents, y=accuracy,
|
| 835 |
-
marker_color=self.color_palette["primary"]),
|
| 836 |
-
go.Bar(name='Speed (seconds)', x=agents, y=speed,
|
| 837 |
-
marker_color=boundary_color),
|
| 838 |
-
go.Bar(name='Confidence (%)', x=agents, y=confidence,
|
| 839 |
-
marker_color=self.color_palette["info"])
|
| 840 |
-
])
|
| 841 |
-
|
| 842 |
-
# Add boundary annotation
|
| 843 |
-
fig.add_annotation(
|
| 844 |
-
x=0.98, y=0.98,
|
| 845 |
-
xref="paper", yref="paper",
|
| 846 |
-
text=f"🤖 {boundary_text}",
|
| 847 |
-
showarrow=False,
|
| 848 |
-
font=dict(size=12, color=boundary_color),
|
| 849 |
-
bgcolor="white",
|
| 850 |
-
bordercolor=boundary_color,
|
| 851 |
-
borderwidth=2,
|
| 852 |
-
borderpad=4
|
| 853 |
-
)
|
| 854 |
-
|
| 855 |
-
fig.update_layout(
|
| 856 |
-
title=f"🤖 Agent Performance Metrics<br>"
|
| 857 |
-
f"<span style='font-size: 12px; color: #64748b'>{boundary_text} v3.3.7</span>",
|
| 858 |
-
barmode='group',
|
| 859 |
-
height=400,
|
| 860 |
-
plot_bgcolor='rgba(0,0,0,0)',
|
| 861 |
-
paper_bgcolor='rgba(0,0,0,0)',
|
| 862 |
-
legend=dict(
|
| 863 |
-
orientation="h",
|
| 864 |
-
yanchor="bottom",
|
| 865 |
-
y=1.02,
|
| 866 |
-
xanchor="right",
|
| 867 |
-
x=1
|
| 868 |
-
)
|
| 869 |
-
)
|
| 870 |
-
|
| 871 |
-
return fig
|
| 872 |
-
|
| 873 |
-
except Exception as e:
|
| 874 |
-
logger.error(f"Agent performance chart creation failed: {e}")
|
| 875 |
-
# Return HTML fallback
|
| 876 |
-
return self.create_agent_performance_chart.__wrapped__(self, is_real_arf)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|