Spaces:
Sleeping
Sleeping
ashishMenon05 commited on
Commit ·
c90dd9f
1
Parent(s): 0815f50
feat: add unified investigation summary combining all agent conclusions
Browse files
frontend/src/components/EpisodeEndOverlay.jsx
CHANGED
|
@@ -9,18 +9,20 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 9 |
const sc = gameState.scenario || {};
|
| 10 |
const allAgents = gameState.agents || {};
|
| 11 |
const allMessages = Object.values(allAgents).flatMap(a => a?.messages || []);
|
|
|
|
| 12 |
|
| 13 |
let report = `=================================================================\n`;
|
| 14 |
-
report += `
|
| 15 |
report += `=================================================================\n\n`;
|
| 16 |
|
| 17 |
report += `[ SCENARIO METADATA ]\n`;
|
| 18 |
report += `Title: ${sc.id || 'N/A'}\n`;
|
| 19 |
report += `Domain: ${sc.domain || 'N/A'}\n`;
|
| 20 |
report += `Difficulty: ${sc.difficulty || 'N/A'}\n`;
|
| 21 |
-
report += `Final
|
| 22 |
report += `Total Steps: ${gameState?.step || metrics?.steps || 'N/A'}\n`;
|
| 23 |
-
report += `Active Agents: ${Object.keys(allAgents).length}\n
|
|
|
|
| 24 |
|
| 25 |
report += `[ AGENTS DEPLOYED ]\n`;
|
| 26 |
Object.entries(allAgents).forEach(([agentId, agentData], idx) => {
|
|
@@ -30,6 +32,22 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 30 |
report += `${idx + 1}. ${agentId}: ${msgCount} messages, ${toolCount} tool calls\n`;
|
| 31 |
});
|
| 32 |
report += `\n`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
report += `[ STEP REWARDS ]\n`;
|
| 35 |
if (gameState?.rewardHistory && gameState.rewardHistory.length > 0) {
|
|
@@ -37,7 +55,7 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 37 |
report += `Step ${i + 1}: ${r.toFixed(4)}\n`;
|
| 38 |
});
|
| 39 |
report += `Average: ${(gameState.rewardHistory.reduce((a, b) => a + b, 0) / gameState.rewardHistory.length).toFixed(4)}\n`;
|
| 40 |
-
report += `Final
|
| 41 |
} else {
|
| 42 |
report += `No step rewards recorded.\n\n`;
|
| 43 |
}
|
|
@@ -55,7 +73,7 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 55 |
|
| 56 |
report += `[ CONTEXT & ROOT CAUSE ]\n`;
|
| 57 |
report += `${sc.context || 'No context provided.'}\n`;
|
| 58 |
-
report += `
|
| 59 |
|
| 60 |
report += `=================================================================\n`;
|
| 61 |
report += `[ INVESTIGATION LOG & DETAILED TRACE ]\n`;
|
|
@@ -84,40 +102,45 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 84 |
}
|
| 85 |
report += `\n`;
|
| 86 |
|
| 87 |
-
report += `> SYSTEMS ERRORS DETECTED
|
| 88 |
if (allErrors.length > 0) {
|
| 89 |
-
// deduplicate
|
| 90 |
[...new Set(allErrors)].forEach(err => report += `${err}\n`);
|
| 91 |
} else {
|
| 92 |
-
report += `No significant system errors found
|
| 93 |
}
|
| 94 |
report += `\n`;
|
| 95 |
|
| 96 |
report += `=================================================================\n`;
|
| 97 |
-
report += `[ SOLUTION
|
| 98 |
report += `=================================================================\n\n`;
|
| 99 |
-
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
|
| 102 |
report += `=================================================================\n`;
|
| 103 |
-
report += `[
|
| 104 |
report += `=================================================================\n\n`;
|
| 105 |
-
report += `Based on the automated evaluation of this scenario, consider the following:\n`;
|
| 106 |
|
| 107 |
if (allTools.length > 15) {
|
| 108 |
-
report += `1. EFFICIENCY:
|
| 109 |
} else {
|
| 110 |
-
report += `1. EFFICIENCY: Tool
|
| 111 |
}
|
| 112 |
|
| 113 |
if (allErrors.length > 5) {
|
| 114 |
-
report += `2. ACCURACY: Multiple
|
| 115 |
}
|
| 116 |
|
| 117 |
-
report += `3. CAUSE-ANALYSIS:
|
| 118 |
-
report += `4. REMEDIATION:
|
| 119 |
|
| 120 |
-
// Trigger Download
|
| 121 |
const blob = new Blob([report], { type: 'text/plain;charset=utf-8' });
|
| 122 |
const url = URL.createObjectURL(blob);
|
| 123 |
const a = document.createElement('a');
|
|
@@ -129,6 +152,59 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 129 |
URL.revokeObjectURL(url);
|
| 130 |
};
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
return (
|
| 133 |
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-8 animate-in fade-in duration-500">
|
| 134 |
{/* Particle/Pulse Background */}
|
|
@@ -139,7 +215,7 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 139 |
</div>
|
| 140 |
|
| 141 |
{/* Summary Modal */}
|
| 142 |
-
<div className="relative w-full max-w-
|
| 143 |
{/* Modal Header */}
|
| 144 |
<div className="flex items-center justify-between p-6 bg-surface-container-highest/20 border-b border-white/5">
|
| 145 |
<div className="flex items-center gap-3">
|
|
@@ -308,11 +384,9 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 308 |
}).filter(c => c.lastMsg);
|
| 309 |
|
| 310 |
if (conclusions.length === 0) return null;
|
| 311 |
-
|
| 312 |
-
const colors = ['primary', 'secondary', 'tertiary', 'error', 'success'];
|
| 313 |
|
| 314 |
return (
|
| 315 |
-
<div className="px-8 pb-
|
| 316 |
<div className="p-6 bg-surface-container-low/40 border border-white/10 rounded-lg">
|
| 317 |
<h3 className="font-headline font-bold text-on-surface tracking-widest uppercase mb-4 flex items-center gap-2">
|
| 318 |
<span className="material-symbols-outlined">gavel</span>
|
|
@@ -336,6 +410,65 @@ const EpisodeEndOverlay = ({ isOpen, onClose, metrics, gameState }) => {
|
|
| 336 |
</div>
|
| 337 |
);
|
| 338 |
})()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 339 |
</div>
|
| 340 |
|
| 341 |
{/* Modal Footer */}
|
|
|
|
| 9 |
const sc = gameState.scenario || {};
|
| 10 |
const allAgents = gameState.agents || {};
|
| 11 |
const allMessages = Object.values(allAgents).flatMap(a => a?.messages || []);
|
| 12 |
+
const unifiedSummary = generateUnifiedSummary();
|
| 13 |
|
| 14 |
let report = `=================================================================\n`;
|
| 15 |
+
report += ` NEXUS INCIDENT INVESTIGATION REPORT \n`;
|
| 16 |
report += `=================================================================\n\n`;
|
| 17 |
|
| 18 |
report += `[ SCENARIO METADATA ]\n`;
|
| 19 |
report += `Title: ${sc.id || 'N/A'}\n`;
|
| 20 |
report += `Domain: ${sc.domain || 'N/A'}\n`;
|
| 21 |
report += `Difficulty: ${sc.difficulty || 'N/A'}\n`;
|
| 22 |
+
report += `Final Score: ${Number(gameState?.cumulativeReward || metrics?.score || 0).toFixed(4)} / 1.00\n`;
|
| 23 |
report += `Total Steps: ${gameState?.step || metrics?.steps || 'N/A'}\n`;
|
| 24 |
+
report += `Active Agents: ${Object.keys(allAgents).length}\n`;
|
| 25 |
+
report += `Status: ${unifiedSummary?.isSuccess ? 'SUCCESS' : 'INCONCLUSIVE'}\n\n`;
|
| 26 |
|
| 27 |
report += `[ AGENTS DEPLOYED ]\n`;
|
| 28 |
Object.entries(allAgents).forEach(([agentId, agentData], idx) => {
|
|
|
|
| 32 |
report += `${idx + 1}. ${agentId}: ${msgCount} messages, ${toolCount} tool calls\n`;
|
| 33 |
});
|
| 34 |
report += `\n`;
|
| 35 |
+
|
| 36 |
+
// UNIFIED SUMMARY SECTION
|
| 37 |
+
if (unifiedSummary) {
|
| 38 |
+
report += `=================================================================\n`;
|
| 39 |
+
report += `[ UNIFIED INVESTIGATION SUMMARY ]\n`;
|
| 40 |
+
report += `=================================================================\n\n`;
|
| 41 |
+
|
| 42 |
+
report += `## Combined Agent Conclusions\n`;
|
| 43 |
+
report += `${unifiedSummary.conclusionText || 'No conclusions recorded.'}\n\n`;
|
| 44 |
+
|
| 45 |
+
report += `## Key Findings & Clues\n`;
|
| 46 |
+
report += `${unifiedSummary.keyFindings || 'None recorded.'}\n\n`;
|
| 47 |
+
|
| 48 |
+
report += `## Key Tool Results\n`;
|
| 49 |
+
report += `${unifiedSummary.toolSummary}\n\n`;
|
| 50 |
+
}
|
| 51 |
|
| 52 |
report += `[ STEP REWARDS ]\n`;
|
| 53 |
if (gameState?.rewardHistory && gameState.rewardHistory.length > 0) {
|
|
|
|
| 55 |
report += `Step ${i + 1}: ${r.toFixed(4)}\n`;
|
| 56 |
});
|
| 57 |
report += `Average: ${(gameState.rewardHistory.reduce((a, b) => a + b, 0) / gameState.rewardHistory.length).toFixed(4)}\n`;
|
| 58 |
+
report += `Final Score: ${Number(gameState.cumulativeReward || 0).toFixed(4)}\n\n`;
|
| 59 |
} else {
|
| 60 |
report += `No step rewards recorded.\n\n`;
|
| 61 |
}
|
|
|
|
| 73 |
|
| 74 |
report += `[ CONTEXT & ROOT CAUSE ]\n`;
|
| 75 |
report += `${sc.context || 'No context provided.'}\n`;
|
| 76 |
+
report += `Root Cause Validation: ${metrics?.rootCause || 'N/A'}\n\n`;
|
| 77 |
|
| 78 |
report += `=================================================================\n`;
|
| 79 |
report += `[ INVESTIGATION LOG & DETAILED TRACE ]\n`;
|
|
|
|
| 102 |
}
|
| 103 |
report += `\n`;
|
| 104 |
|
| 105 |
+
report += `> SYSTEMS ERRORS DETECTED:\n`;
|
| 106 |
if (allErrors.length > 0) {
|
|
|
|
| 107 |
[...new Set(allErrors)].forEach(err => report += `${err}\n`);
|
| 108 |
} else {
|
| 109 |
+
report += `No significant system errors found.\n`;
|
| 110 |
}
|
| 111 |
report += `\n`;
|
| 112 |
|
| 113 |
report += `=================================================================\n`;
|
| 114 |
+
report += `[ SOLUTION & FIX VERIFICATION ]\n`;
|
| 115 |
report += `=================================================================\n\n`;
|
| 116 |
+
|
| 117 |
+
const resCall = gameState?.tool_calls_made?.find(c => c.tool_name === 'submit_resolution');
|
| 118 |
+
if (resCall?.params) {
|
| 119 |
+
report += `Root Cause Service: ${resCall.params.root_cause_service || 'UNKNOWN'}\n`;
|
| 120 |
+
report += `Root Cause Description: ${resCall.params.root_cause_description || 'None'}\n`;
|
| 121 |
+
report += `Fix Applied: ${resCall.params.fix_applied || 'None'}\n`;
|
| 122 |
+
} else {
|
| 123 |
+
report += `No resolution submitted.\n`;
|
| 124 |
+
}
|
| 125 |
+
report += `\n`;
|
| 126 |
|
| 127 |
report += `=================================================================\n`;
|
| 128 |
+
report += `[ RECOMMENDATIONS ]\n`;
|
| 129 |
report += `=================================================================\n\n`;
|
|
|
|
| 130 |
|
| 131 |
if (allTools.length > 15) {
|
| 132 |
+
report += `1. EFFICIENCY: ${allTools.length} tool calls made. Consider refining hypotheses.\n`;
|
| 133 |
} else {
|
| 134 |
+
report += `1. EFFICIENCY: Tool usage was concise (${allTools.length} calls).\n`;
|
| 135 |
}
|
| 136 |
|
| 137 |
if (allErrors.length > 5) {
|
| 138 |
+
report += `2. ACCURACY: Multiple errors encountered. Verify tool syntax.\n`;
|
| 139 |
}
|
| 140 |
|
| 141 |
+
report += `3. CAUSE-ANALYSIS: Check error logs before database queries.\n`;
|
| 142 |
+
report += `4. REMEDIATION: Establish better alerting for ${sc.domain || 'general'} domain.\n`;
|
| 143 |
|
|
|
|
| 144 |
const blob = new Blob([report], { type: 'text/plain;charset=utf-8' });
|
| 145 |
const url = URL.createObjectURL(blob);
|
| 146 |
const a = document.createElement('a');
|
|
|
|
| 152 |
URL.revokeObjectURL(url);
|
| 153 |
};
|
| 154 |
|
| 155 |
+
const generateUnifiedSummary = () => {
|
| 156 |
+
const allAgents = gameState?.agents || {};
|
| 157 |
+
const agentEntries = Object.entries(allAgents);
|
| 158 |
+
|
| 159 |
+
if (agentEntries.length === 0) return null;
|
| 160 |
+
|
| 161 |
+
const allConclusions = [];
|
| 162 |
+
const allToolResults = [];
|
| 163 |
+
const allClues = gameState?.clues_found || [];
|
| 164 |
+
|
| 165 |
+
agentEntries.forEach(([agentId, agentData]) => {
|
| 166 |
+
const msgs = agentData?.messages || [];
|
| 167 |
+
const textMsgs = msgs.filter(m => m.type === 'message');
|
| 168 |
+
const lastMsg = textMsgs[textMsgs.length - 1];
|
| 169 |
+
if (lastMsg) {
|
| 170 |
+
allConclusions.push({
|
| 171 |
+
agentId,
|
| 172 |
+
content: lastMsg.content || lastMsg.text || lastMsg.message || '',
|
| 173 |
+
role: agentData.role || agentId
|
| 174 |
+
});
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
const toolResults = msgs.filter(m => m.type === 'tool_result');
|
| 178 |
+
toolResults.forEach(tr => {
|
| 179 |
+
if (tr.result && !tr.result.toLowerCase().includes('error')) {
|
| 180 |
+
allToolResults.push({
|
| 181 |
+
agentId,
|
| 182 |
+
tool: tr.tool_name || tr.tool,
|
| 183 |
+
result: tr.result
|
| 184 |
+
});
|
| 185 |
+
}
|
| 186 |
+
});
|
| 187 |
+
});
|
| 188 |
+
|
| 189 |
+
const conclusionText = allConclusions.map(c => c.content).join('\n\n');
|
| 190 |
+
const keyFindings = allClues.slice(0, 5).join('\n• ');
|
| 191 |
+
const toolSummary = allToolResults.length > 0
|
| 192 |
+
? allToolResults.slice(0, 3).map(t => `• ${t.tool}: ${t.result.substring(0, 100)}...`).join('\n')
|
| 193 |
+
: 'No tool results recorded.';
|
| 194 |
+
|
| 195 |
+
const isSuccess = Number(gameState?.cumulativeReward || metrics?.score || 0) >= 0.5;
|
| 196 |
+
|
| 197 |
+
return {
|
| 198 |
+
conclusionText,
|
| 199 |
+
keyFindings: keyFindings || 'No clues recorded.',
|
| 200 |
+
toolSummary,
|
| 201 |
+
agentCount: agentEntries.length,
|
| 202 |
+
isSuccess
|
| 203 |
+
};
|
| 204 |
+
};
|
| 205 |
+
|
| 206 |
+
const unifiedSummary = generateUnifiedSummary();
|
| 207 |
+
|
| 208 |
return (
|
| 209 |
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4 md:p-8 animate-in fade-in duration-500">
|
| 210 |
{/* Particle/Pulse Background */}
|
|
|
|
| 215 |
</div>
|
| 216 |
|
| 217 |
{/* Summary Modal */}
|
| 218 |
+
<div className="relative w-full max-w-5xl max-h-[90vh] glass-panel rounded-xl overflow-hidden shadow-[0_0_80px_rgba(0,0,0,0.8)] border border-white/10 flex flex-col">
|
| 219 |
{/* Modal Header */}
|
| 220 |
<div className="flex items-center justify-between p-6 bg-surface-container-highest/20 border-b border-white/5">
|
| 221 |
<div className="flex items-center gap-3">
|
|
|
|
| 384 |
}).filter(c => c.lastMsg);
|
| 385 |
|
| 386 |
if (conclusions.length === 0) return null;
|
|
|
|
|
|
|
| 387 |
|
| 388 |
return (
|
| 389 |
+
<div className="px-8 pb-4">
|
| 390 |
<div className="p-6 bg-surface-container-low/40 border border-white/10 rounded-lg">
|
| 391 |
<h3 className="font-headline font-bold text-on-surface tracking-widest uppercase mb-4 flex items-center gap-2">
|
| 392 |
<span className="material-symbols-outlined">gavel</span>
|
|
|
|
| 410 |
</div>
|
| 411 |
);
|
| 412 |
})()}
|
| 413 |
+
|
| 414 |
+
{/* Unified Investigation Summary */}
|
| 415 |
+
{unifiedSummary && (
|
| 416 |
+
<div className="px-8 pb-8">
|
| 417 |
+
<div className="p-6 bg-gradient-to-br from-primary/5 to-secondary/5 border border-primary/20 rounded-lg">
|
| 418 |
+
<h3 className="font-headline font-bold text-primary tracking-widest uppercase mb-4 flex items-center gap-2">
|
| 419 |
+
<span className="material-symbols-outlined">psychology</span>
|
| 420 |
+
Unified Investigation Summary
|
| 421 |
+
</h3>
|
| 422 |
+
|
| 423 |
+
{/* Success/Failure Badge */}
|
| 424 |
+
<div className="mb-4">
|
| 425 |
+
<span className={`inline-flex items-center gap-2 px-3 py-1 rounded-full text-xs font-mono font-bold uppercase ${
|
| 426 |
+
unifiedSummary.isSuccess
|
| 427 |
+
? 'bg-tertiary/20 text-tertiary border border-tertiary/30'
|
| 428 |
+
: 'bg-error/20 text-error border border-error/30'
|
| 429 |
+
}`}>
|
| 430 |
+
<span className={`w-2 h-2 rounded-full ${unifiedSummary.isSuccess ? 'bg-tertiary' : 'bg-error'}`}></span>
|
| 431 |
+
{unifiedSummary.isSuccess ? 'Investigation Successful' : 'Investigation Inconclusive'}
|
| 432 |
+
</span>
|
| 433 |
+
<span className="ml-3 text-[10px] text-outline font-mono uppercase">
|
| 434 |
+
{unifiedSummary.agentCount} Agents Collaborated
|
| 435 |
+
</span>
|
| 436 |
+
</div>
|
| 437 |
+
|
| 438 |
+
{/* Combined Conclusions */}
|
| 439 |
+
<div className="mb-4">
|
| 440 |
+
<span className="font-mono text-[10px] text-primary/60 uppercase tracking-widest block mb-2">Combined Agent Conclusions</span>
|
| 441 |
+
<div className="p-4 bg-surface-container-low/50 rounded border border-white/5 max-h-48 overflow-y-auto custom-scrollbar">
|
| 442 |
+
<p className="text-sm text-on-surface/90 leading-relaxed whitespace-pre-wrap">
|
| 443 |
+
{unifiedSummary.conclusionText || 'No conclusions recorded.'}
|
| 444 |
+
</p>
|
| 445 |
+
</div>
|
| 446 |
+
</div>
|
| 447 |
+
|
| 448 |
+
{/* Key Findings */}
|
| 449 |
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
| 450 |
+
<div>
|
| 451 |
+
<span className="font-mono text-[10px] text-secondary/60 uppercase tracking-widest block mb-2">Key Findings & Clues</span>
|
| 452 |
+
<div className="p-3 bg-surface-container-low/50 rounded border border-white/5">
|
| 453 |
+
<ul className="text-xs text-on-surface/80 space-y-1 list-disc list-inside">
|
| 454 |
+
{unifiedSummary.keyFindings.split('\n• ').map((finding, i) => (
|
| 455 |
+
finding && <li key={i}>{finding}</li>
|
| 456 |
+
))}
|
| 457 |
+
</ul>
|
| 458 |
+
</div>
|
| 459 |
+
</div>
|
| 460 |
+
<div>
|
| 461 |
+
<span className="font-mono text-[10px] text-tertiary/60 uppercase tracking-widest block mb-2">Key Tool Results</span>
|
| 462 |
+
<div className="p-3 bg-surface-container-low/50 rounded border border-white/5 max-h-32 overflow-y-auto custom-scrollbar">
|
| 463 |
+
<pre className="text-[10px] text-on-surface/70 whitespace-pre-wrap font-mono">
|
| 464 |
+
{unifiedSummary.toolSummary}
|
| 465 |
+
</pre>
|
| 466 |
+
</div>
|
| 467 |
+
</div>
|
| 468 |
+
</div>
|
| 469 |
+
</div>
|
| 470 |
+
</div>
|
| 471 |
+
)}
|
| 472 |
</div>
|
| 473 |
|
| 474 |
{/* Modal Footer */}
|