Newer
Older
cortex-hub / frontend / src / features / agents / components / drilldown / MetricsPanel.js
import React from 'react';

const MetricsPanel = ({ agent, tokenUsage, tokenError, flippedCards, setFlippedCards, handleResetMetrics, clearing }) => {
    
    const formatTimeLocal = (utcString) => {
        if (!utcString) return 'Never';
        const dateStr = utcString.endsWith('Z') || utcString.includes('+') ? utcString : utcString + 'Z';
        return new Date(dateStr).toLocaleString(undefined, { 
            month: 'short', day: 'numeric', 
            hour: '2-digit', minute: '2-digit', second: '2-digit' 
        });
    };

    return (
        <div className="p-6 flex flex-col gap-6 font-sans overflow-y-auto max-h-full scrollbar-hide">
             <div className="flex justify-between items-center mb-2 border-b border-gray-200 dark:border-gray-800 pb-2">
                <h3 className="text-xl font-bold tracking-tight text-gray-800 dark:text-gray-200">Execution Metrics</h3>
                <button 
                    onClick={handleResetMetrics}
                    disabled={clearing}
                    className="px-3 py-1.5 text-xs font-bold text-red-600 dark:text-red-400 hover:text-red-700 bg-red-50 dark:bg-red-900/20 hover:bg-red-100 dark:hover:bg-red-900/40 rounded border border-red-200 dark:border-red-800 transition-colors"
                >
                    {clearing ? "Resetting..." : "Reset Metrics"}
                </button>
            </div>

            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
                 {/* Runtime / Health Card */}
                 <div className="group bg-white dark:bg-gray-800 rounded-3xl p-6 shadow-sm border border-gray-200 dark:border-gray-700/50 hover:shadow-xl transition-all duration-300">
                    <div className="flex justify-between items-start mb-4">
                        <div className="p-3 bg-emerald-500/10 rounded-2xl">
                             <svg className="w-5 h-5 text-emerald-500" fill="none" viewBox="0 0 24 24" stroke="currentColor font-bold"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
                        </div>
                        <button 
                            onClick={() => setFlippedCards(p => ({ ...p, runtime: !p.runtime }))}
                            className="text-[10px] font-black uppercase tracking-widest text-indigo-500"
                        >
                            {flippedCards.runtime ? 'Main View' : 'Drilldown'}
                        </button>
                    </div>

                    {!flippedCards.runtime ? (
                        <>
                            <h4 className="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1">Session Status</h4>
                            <div className="text-3xl font-black text-gray-900 dark:text-white mb-2">{agent?.status || 'idle'}</div>
                            <div className="flex items-center gap-2 text-xs font-bold text-gray-500">
                                <span className={`w-2 h-2 rounded-full ${agent?.status === 'active' ? 'bg-emerald-500 animate-pulse' : 'bg-amber-500'}`}></span>
                                Last seen {new Date(agent?.last_seen_at || Date.now()).toLocaleTimeString()}
                            </div>
                        </>
                    ) : (
                        <div className="space-y-3 pt-2">
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Mesh Node</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white">{agent?.mesh_node_id || 'hub'}</span>
                             </div>
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Last Seen</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white italic">{new Date(agent?.last_seen_at || Date.now()).toLocaleString()}</span>
                             </div>
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Jail Mount</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white truncate max-w-[120px]">{agent?.current_workspace_jail || '/tmp'}</span>
                             </div>
                        </div>
                    )}
                 </div>

                 {/* Precision / Score Card */}
                 <div className="bg-white dark:bg-gray-800 rounded-3xl p-6 shadow-sm border border-gray-200 dark:border-gray-700/50">
                    <div className="flex justify-between items-start mb-4">
                        <div className="p-3 bg-amber-500/10 rounded-2xl">
                             <svg className="w-5 h-5 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor font-bold"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
                        </div>
                    </div>
                    <h4 className="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1">Quality Score</h4>
                    <div className="text-3xl font-black text-gray-900 dark:text-white mb-2">{agent?.latest_quality_score || 0}%</div>
                    <div className="h-2 w-full bg-gray-100 dark:bg-gray-900 rounded-full overflow-hidden">
                        <div className={`h-full transition-all duration-1000 ${agent?.latest_quality_score >= 80 ? 'bg-emerald-500 shadow-[0_0_10px_rgba(16,185,129,0.5)]' : 'bg-amber-500'}`} style={{ width: `${agent?.latest_quality_score || 0}%` }}></div>
                    </div>
                    <div className="mt-3 text-[10px] text-gray-500 font-bold italic">Verification results for last assistant message</div>
                 </div>

                 {/* Resource / Tokens Card */}
                 <div className="bg-white dark:bg-gray-800 rounded-3xl p-6 shadow-sm border border-gray-200 dark:border-gray-700/50 hover:shadow-xl transition-all duration-300">
                    <div className="flex justify-between items-start mb-4">
                        <div className="p-3 bg-indigo-500/10 rounded-2xl">
                             <svg className="w-5 h-5 text-indigo-500" fill="none" viewBox="0 0 24 24" stroke="currentColor font-bold"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
                        </div>
                        <button 
                            onClick={() => setFlippedCards(p => ({ ...p, tokens: !p.tokens }))}
                            className="text-[10px] font-black uppercase tracking-widest text-indigo-500"
                        >
                            {flippedCards.tokens ? 'Main View' : 'Drilldown'}
                        </button>
                    </div>

                    {tokenError ? (
                        <div className="text-[10px] text-rose-500 font-bold bg-rose-50 dark:bg-rose-500/10 p-3 rounded-xl border border-rose-500/20 italic">Context state unavailable: {tokenError}</div>
                    ) : !flippedCards.tokens ? (
                        <>
                            <h4 className="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-1">Context Pressure</h4>
                            <div className="text-3xl font-black text-gray-900 dark:text-white mb-2">{tokenUsage.percentage || 0}%</div>
                            <div className="h-2 w-full bg-gray-100 dark:bg-gray-900 rounded-full overflow-hidden">
                                <div className={`h-full transition-all duration-1000 ${tokenUsage.percentage > 85 ? 'bg-rose-500 animate-pulse' : tokenUsage.percentage > 60 ? 'bg-amber-500' : 'bg-indigo-500 shadow-[0_0_10px_rgba(99,102,241,0.5)]'}`} style={{ width: `${tokenUsage.percentage || 0}%` }}></div>
                            </div>
                            <div className="flex justify-between mt-3 text-[10px] font-bold text-gray-500 tabular-nums">
                                <span>{tokenUsage.token_count || 0} used</span>
                                <span className="text-gray-400">limit {tokenUsage.token_limit || 0}</span>
                            </div>
                        </>
                    ) : (
                        <div className="space-y-3 pt-2">
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Total Consumption</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white font-bold">{tokenUsage.token_count || 0} tokens</span>
                             </div>
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Context Window</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white">{tokenUsage.token_limit || 0} tokens</span>
                             </div>
                             <div className="flex justify-between">
                                 <span className="text-[10px] uppercase font-black text-gray-400">Provider</span>
                                 <span className="text-[10px] font-mono text-gray-900 dark:text-white italic">{agent?.session?.provider_name || 'default'}</span>
                             </div>
                        </div>
                    )}
                 </div>

                 {/* New Metrics Section (Restored from AgentDrillDown) */}
                 <div className="bg-gray-50 dark:bg-gray-800/50 p-6 rounded-3xl border border-gray-200 dark:border-gray-800">
                    <span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Total Task Executions</span>
                    <span className="text-3xl font-black text-indigo-500">{agent?.total_runs || 0}</span>
                    <div className="mt-4 flex flex-col">
                        <span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Success Rate</span>
                        <span className="text-2xl font-black text-emerald-500">
                             {agent?.total_runs ? Math.round(((agent?.successful_runs || 0) / agent.total_runs) * 100) : 0}%
                        </span>
                        <span className="text-[10px] text-gray-400 mt-1 font-mono italic">
                            {agent?.successful_runs || 0} passed / {agent?.total_runs || 0} total
                        </span>
                    </div>
                 </div>

                 <div className="bg-gray-50 dark:bg-gray-800/50 p-6 rounded-3xl border border-gray-200 dark:border-gray-800">
                    <span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Last Completion</span>
                    <span className="text-sm font-black text-blue-500 mt-2 block leading-relaxed">
                        {formatTimeLocal(agent?.last_heartbeat)}
                    </span>
                    <div className="mt-6 flex flex-col">
                        <span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Total Running Time</span>
                        <span className="text-2xl font-black text-pink-500">
                            {agent?.total_running_time_seconds ? agent.total_running_time_seconds.toLocaleString() + 's' : '0s'}
                        </span>
                    </div>
                 </div>

                 <div className="bg-gray-50 dark:bg-gray-800/50 p-6 rounded-3xl border border-gray-200 dark:border-gray-800">
                    <span className="uppercase text-[10px] tracking-widest text-gray-500 font-bold block mb-1">Cumulative Tokens</span>
                    <div className="flex flex-col gap-4 mt-2">
                        <div>
                            <span className="text-[9px] font-black text-blue-400 uppercase tracking-tight">Prompt (Input)</span>
                            <div className="text-2xl font-black text-blue-400 tabular-nums">{agent?.total_input_tokens?.toLocaleString() || 0}</div>
                        </div>
                        <div>
                            <span className="text-[9px] font-black text-emerald-500 uppercase tracking-tight">Completion (Output)</span>
                            <div className="text-2xl font-black text-emerald-500 tabular-nums">{agent?.total_output_tokens?.toLocaleString() || 0}</div>
                        </div>
                    </div>
                 </div>
            </div>

            <div className="mt-4">
                <h4 className="text-sm font-bold tracking-widest text-gray-500 uppercase mb-4 px-1">Tool Usage Intelligence</h4>
                <div className="bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 rounded-3xl overflow-hidden shadow-sm">
                    <table className="w-full text-left">
                        <thead className="bg-gray-50 dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800 text-[10px] uppercase text-gray-500 tracking-wider">
                            <tr>
                                <th className="px-6 py-4 font-black">Tool Surface</th>
                                <th className="px-6 py-4 font-black text-right">Invocations</th>
                                <th className="px-6 py-4 font-black text-center">Success</th>
                                <th className="px-6 py-4 font-black text-center">Failure Rate</th>
                            </tr>
                        </thead>
                        <tbody className="divide-y divide-gray-200 dark:divide-gray-800">
                            {agent?.tool_call_counts && Object.keys(agent.tool_call_counts).length > 0 ? (
                                Object.entries(agent.tool_call_counts).sort((a,b) => {
                                    const bVal = typeof b[1] === 'object' ? b[1].calls : b[1];
                                    const aVal = typeof a[1] === 'object' ? a[1].calls : a[1];
                                    return bVal - aVal;
                                }).map(([tool, data]) => {
                                    const calls = typeof data === 'object' ? data.calls : data;
                                    const successes = typeof data === 'object' ? data.successes : data;
                                    const failures = typeof data === 'object' ? data.failures : 0;
                                    const failRate = calls > 0 ? Math.round((failures / calls) * 100) : 0;
                                    
                                    return (
                                    <tr key={tool} className="hover:bg-gray-50 dark:hover:bg-gray-900/50 transition-colors text-sm group">
                                        <td className="px-6 py-4 text-indigo-600 dark:text-indigo-400 font-bold font-mono">{tool}</td>
                                        <td className="px-6 py-4 text-right tabular-nums font-bold text-gray-900 dark:text-white">{calls}</td>
                                        <td className="px-6 py-4 text-center">
                                            <span className="inline-flex items-center px-2 py-0.5 rounded-lg bg-emerald-500/10 text-emerald-500 font-black text-xs">
                                                {successes}
                                            </span>
                                        </td>
                                        <td className={`px-6 py-4 text-center font-bold ${failRate > 20 ? 'text-rose-500' : 'text-gray-400'}`}>
                                            {failRate > 0 ? `${failRate}%` : <span className="text-gray-200 dark:text-gray-800">0%</span>}
                                        </td>
                                    </tr>
                                    );
                                })
                            ) : (
                                <tr>
                                    <td colSpan="4" className="px-6 py-10 text-center text-gray-400 italic text-sm">
                                        No autonomous tool transactions recorded in this session environment.
                                    </td>
                                </tr>
                            )}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    );
};

export default MetricsPanel;