import React, { useState, useCallback } from 'react';
import { useApi } from '../hooks/useApi';
import {
reflect,
getAgents,
type ReflectAnalysis,
type ReflectResultData,
} from '../api/client';
type Tab = 'error_patterns' | 'tool_sequences' | 'cost_analysis' | 'performance_trends';
const TABS: { key: Tab; label: string; icon: string }[] = [
{ key: 'error_patterns', label: 'Error Patterns', icon: '🔴' },
{ key: 'tool_sequences', label: 'Tool Sequences', icon: '🔗' },
{ key: 'cost_analysis', label: 'Cost Analysis', icon: '💰' },
{ key: 'performance_trends', label: 'Performance', icon: '📈' },
];
function formatTimestamp(ts?: string): string {
if (!ts) return '—';
try {
return new Date(ts).toLocaleDateString();
} catch {
return ts;
}
}
// ─── Error Patterns ─────────────────────────────────────
function ErrorPatternsView({ data }: { data: ReflectResultData }): React.ReactElement {
const patterns = data.insights;
if (patterns.length === 0) {
return
No error patterns found.
;
}
return (
| Pattern |
Count |
First Seen |
Last Seen |
Confidence |
{patterns.map((insight, idx) => (
| {insight.summary} |
{(insight.data.count as number) ?? '—'} |
{formatTimestamp(insight.data.firstSeen as string)} |
{formatTimestamp(insight.data.lastSeen as string)} |
{(insight.confidence * 100).toFixed(0)}% |
))}
);
}
// ─── Tool Sequences ─────────────────────────────────────
function ToolSequencesView({ data }: { data: ReflectResultData }): React.ReactElement {
const sequences = data.insights;
if (sequences.length === 0) {
return No tool sequences found.
;
}
return (
| Sequence |
Frequency |
Error Rate |
Confidence |
{sequences.map((insight, idx) => (
| {insight.summary} |
{(insight.data.frequency as number) ?? '—'} |
{insight.data.errorRate != null ? `${((insight.data.errorRate as number) * 100).toFixed(1)}%` : '—'}
|
{(insight.confidence * 100).toFixed(0)}% |
))}
);
}
// ─── Cost Analysis ──────────────────────────────────────
function CostAnalysisView({ data }: { data: ReflectResultData }): React.ReactElement {
const insights = data.insights;
// Find summary insight
const summaryInsight = insights.find((i) => i.type === 'cost_summary' || i.type === 'summary');
const modelInsights = insights.filter((i) => i.type === 'cost_by_model' || i.type === 'model_breakdown');
return (
{/* Summary Cards */}
{summaryInsight && (
Total Cost
${((summaryInsight.data.totalCost as number) ?? 0).toFixed(2)}
Avg / Session
${((summaryInsight.data.avgPerSession as number) ?? 0).toFixed(4)}
Sessions
{(summaryInsight.data.totalSessions as number) ?? 0}
)}
{/* Model Breakdown or All Insights */}
{insights.length > 0 && (
| Insight |
Type |
Confidence |
{insights.map((insight, idx) => (
| {insight.summary} |
{insight.type} |
{(insight.confidence * 100).toFixed(0)}% |
))}
)}
{insights.length === 0 && (
No cost analysis data available.
)}
);
}
// ─── Performance Trends ─────────────────────────────────
function PerformanceView({ data }: { data: ReflectResultData }): React.ReactElement {
const insights = data.insights;
// Server emits these as `performance_current` (metrics) and `performance_assessment`.
const summaryInsight = insights.find((i) => i.type === 'performance_current');
const assessmentInsight = insights.find((i) => i.type === 'performance_assessment');
return (
{/* Summary Cards */}
{summaryInsight && (
Success Rate
{(((summaryInsight.data.successRate as number) ?? 0) * 100).toFixed(1)}%
Avg Duration
{((summaryInsight.data.avgDuration as number) ?? 0).toFixed(0)}ms
Avg Tool Calls
{((summaryInsight.data.avgToolCalls as number) ?? 0).toFixed(1)}
Assessment
{(assessmentInsight?.data.assessment as string) ?? '—'}
)}
{/* All Insights */}
{insights.length > 0 ? (
| Insight |
Type |
Confidence |
{insights.map((insight, idx) => (
| {insight.summary} |
{insight.type} |
{(insight.confidence * 100).toFixed(0)}% |
))}
) : (
No performance data available.
)}
);
}
// ─── Main Component ─────────────────────────────────────
export function Insights(): React.ReactElement {
const [tab, setTab] = useState('error_patterns');
const [agentFilter, setAgentFilter] = useState('');
const [from, setFrom] = useState('');
const [to, setTo] = useState('');
const agents = useApi(() => getAgents(), []);
const result = useApi(
() =>
reflect({
analysis: tab as ReflectAnalysis,
agentId: agentFilter || undefined,
from: from || undefined,
to: to || undefined,
}),
[tab, agentFilter, from, to],
);
const renderContent = useCallback(() => {
if (result.loading) {
return Analyzing...
;
}
if (result.error) {
return Error: {result.error}
;
}
if (!result.data) {
return No data
;
}
switch (tab) {
case 'error_patterns':
return ;
case 'tool_sequences':
return ;
case 'cost_analysis':
return ;
case 'performance_trends':
return ;
}
}, [tab, result]);
return (
Insights
{/* Filters */}
setFrom(e.target.value)}
/>
setTo(e.target.value)}
/>
{/* Tabs */}
{/* Metadata */}
{result.data?.metadata && (
Analyzed {result.data.metadata.sessionsAnalyzed} sessions,{' '}
{result.data.metadata.eventsAnalyzed} events
{result.data.metadata.timeRange && (
<> ({formatTimestamp(result.data.metadata.timeRange.from)} – {formatTimestamp(result.data.metadata.timeRange.to)})>
)}
)}
{/* Content */}
{renderContent()}
);
}