| import React, { useState } from 'react'; |
| import { BarChart, Bar, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, AreaChart, Area, ComposedChart } from 'recharts'; |
| import { TrendingDown, TrendingUp, Activity, FileText, ArrowDownRight, Zap } from 'lucide-react'; |
|
|
| const TokenReport = () => { |
| const [activeTab, setActiveTab] = useState('overview'); |
|
|
| |
|
|
| |
| const timeSeriesData = [ |
| { turn: 'Turn 1', Baseline1: 45, Baseline2: 52, Enforce1: 48, Enforce2: 46, Enforce3: 55 }, |
| { turn: 'Turn 2', Baseline1: 49, Baseline2: 68, Enforce1: 37, Enforce2: 33, Enforce3: 53 }, |
| { turn: 'Turn 3', Baseline1: 57, Baseline2: 62, Enforce1: 35, Enforce2: 19, Enforce3: 32 }, |
| { turn: 'Turn 4', Baseline1: 42, Baseline2: 63, Enforce1: 19, Enforce2: 13, Enforce3: 10 }, |
| { turn: 'Turn 5', Baseline1: 37, Baseline2: 69, Enforce1: 17, Enforce2: 12, Enforce3: 5 }, |
| ]; |
|
|
| const totalComparisonData = [ |
| { name: 'Baseline 1', total: 230, avg: 46, type: 'Standard' }, |
| { name: 'Baseline 2', total: 316, avg: 63, type: 'Standard' }, |
| { name: 'Enforcement 1', total: 156, avg: 31.2, type: 'Optimized' }, |
| { name: 'Enforcement 2', total: 123, avg: 24.6, type: 'Optimized' }, |
| { name: 'Enforcement 3', total: 155, avg: 31, type: 'Optimized' }, |
| ]; |
|
|
| |
| const baseline1SubtotalInput = 54; |
| const baseline1SubtotalOutput = 97; |
| const baseline1IncreaseRaw = baseline1SubtotalOutput - baseline1SubtotalInput; |
| const baseline1IncreasePercent = ((baseline1IncreaseRaw / baseline1SubtotalInput) * 100).toFixed(1); |
|
|
| |
| const bestReduction = ((316 - 123) / 316 * 100).toFixed(1); |
|
|
| return ( |
| <div className="min-h-screen bg-slate-50 text-slate-900 font-sans p-4 md:p-8"> |
| |
| {/* HEADER */} |
| <div className="max-w-6xl mx-auto mb-8"> |
| <h1 className="text-3xl font-bold text-slate-800 mb-2">LLM Token Economy Report</h1> |
| <p className="text-slate-600">Analyzing the "Mirroring Effect" vs. "Enforcement Protocols" in conversational agents.</p> |
| </div> |
| |
| {/* KPI CARDS */} |
| <div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-4 mb-8"> |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-slate-200"> |
| <div className="flex items-center justify-between mb-4"> |
| <h3 className="text-sm font-semibold text-slate-500 uppercase">Baseline Bloat</h3> |
| <TrendingUp className="text-red-500" size={20} /> |
| </div> |
| <div className="text-4xl font-bold text-slate-800">+{baseline1IncreasePercent}%</div> |
| <p className="text-sm text-slate-500 mt-2">Token increase in Baseline Test 1 (Input vs Output)</p> |
| </div> |
| |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-slate-200"> |
| <div className="flex items-center justify-between mb-4"> |
| <h3 className="text-sm font-semibold text-slate-500 uppercase">Max Efficiency</h3> |
| <Zap className="text-green-500" size={20} /> |
| </div> |
| <div className="text-4xl font-bold text-slate-800">{bestReduction}%</div> |
| <p className="text-sm text-slate-500 mt-2">Reduction in total volume (Baseline 2 vs. Enforcement 2)</p> |
| </div> |
| |
| <div className="bg-white p-6 rounded-xl shadow-sm border border-slate-200"> |
| <div className="flex items-center justify-between mb-4"> |
| <h3 className="text-sm font-semibold text-slate-500 uppercase">Avg Decay Rate</h3> |
| <ArrowDownRight className="text-blue-500" size={20} /> |
| </div> |
| <div className="text-4xl font-bold text-slate-800">-71%</div> |
| <p className="text-sm text-slate-500 mt-2">Token drop from Turn 1 to Turn 5 in Enforcement models</p> |
| </div> |
| </div> |
| |
| {/* TABS & CONTENT */} |
| <div className="max-w-6xl mx-auto bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden"> |
| <div className="flex border-b border-slate-200"> |
| <button |
| onClick={() => setActiveTab('overview')} |
| className={`px-6 py-4 font-medium text-sm focus:outline-none ${activeTab === 'overview' ? 'bg-slate-50 text-blue-600 border-b-2 border-blue-600' : 'text-slate-500 hover:text-slate-800'}`} |
| > |
| Total Comparison |
| </button> |
| <button |
| onClick={() => setActiveTab('velocity')} |
| className={`px-6 py-4 font-medium text-sm focus:outline-none ${activeTab === 'velocity' ? 'bg-slate-50 text-blue-600 border-b-2 border-blue-600' : 'text-slate-500 hover:text-slate-800'}`} |
| > |
| Turn Velocity |
| </button> |
| <button |
| onClick={() => setActiveTab('detailed')} |
| className={`px-6 py-4 font-medium text-sm focus:outline-none ${activeTab === 'detailed' ? 'bg-slate-50 text-blue-600 border-b-2 border-blue-600' : 'text-slate-500 hover:text-slate-800'}`} |
| > |
| Detailed Analysis |
| </button> |
| </div> |
| |
| <div className="p-6 md:p-8"> |
| |
| {/* TAB 1: TOTAL COMPARISON */} |
| {activeTab === 'overview' && ( |
| <div className="animate-fade-in"> |
| <h2 className="text-xl font-bold text-slate-800 mb-6">Total Token Load (5 Turns)</h2> |
| <div className="h-80 w-full mb-8"> |
| <ResponsiveContainer width="100%" height="100%"> |
| <BarChart data={totalComparisonData} layout="vertical" margin={{ top: 5, right: 30, left: 40, bottom: 5 }}> |
| <CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={true} /> |
| <XAxis type="number" /> |
| <YAxis dataKey="name" type="category" width={100} tick={{fontSize: 12}} /> |
| <Tooltip |
| contentStyle={{ backgroundColor: '#fff', borderRadius: '8px', border: '1px solid #e2e8f0' }} |
| cursor={{fill: 'transparent'}} |
| /> |
| <Legend /> |
| <Bar dataKey="total" name="Total Tokens" fill="#3b82f6" radius={[0, 4, 4, 0]}> |
| { |
| totalComparisonData.map((entry, index) => ( |
| <cell key={`cell-${index}`} fill={entry.type === 'Standard' ? '#ef4444' : '#22c55e'} /> |
| )) |
| } |
| </Bar> |
| </BarChart> |
| </ResponsiveContainer> |
| </div> |
| <div className="bg-slate-50 p-4 rounded-lg border border-slate-200"> |
| <h4 className="font-semibold text-slate-700 mb-2">Key Finding:</h4> |
| <p className="text-slate-600 text-sm"> |
| Standard LLM behavior ("Baseline") resulted in a <strong>mirroring effect</strong>, where the model repeats and expands upon information, causing token count to grow or remain stagnant high. The "Enforcement" protocols reduced total token load by up to <strong>61%</strong> compared to the worst-case baseline. |
| </p> |
| </div> |
| </div> |
| )} |
| |
| {/* TAB 2: VELOCITY */} |
| {activeTab === 'velocity' && ( |
| <div className="animate-fade-in"> |
| <h2 className="text-xl font-bold text-slate-800 mb-6">Token Decay vs. Stagnation</h2> |
| <p className="text-sm text-slate-500 mb-4">Tracking total tokens (Input + Output) per turn.</p> |
| <div className="h-80 w-full mb-8"> |
| <ResponsiveContainer width="100%" height="100%"> |
| <LineChart data={timeSeriesData} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}> |
| <CartesianGrid strokeDasharray="3 3" vertical={false} /> |
| <XAxis dataKey="turn" /> |
| <YAxis /> |
| <Tooltip contentStyle={{ backgroundColor: '#fff', borderRadius: '8px', border: '1px solid #e2e8f0' }} /> |
| <Legend /> |
| |
| {/* Baselines (Red/Orange) */} |
| <Line type="monotone" dataKey="Baseline1" stroke="#ef4444" strokeWidth={3} dot={{r: 4}} /> |
| <Line type="monotone" dataKey="Baseline2" stroke="#f97316" strokeWidth={3} dot={{r: 4}} /> |
| |
| {/* Enforcement (Greens/Blues) */} |
| <Line type="monotone" dataKey="Enforce1" stroke="#3b82f6" strokeWidth={2} strokeDasharray="5 5" /> |
| <Line type="monotone" dataKey="Enforce2" stroke="#22c55e" strokeWidth={2} strokeDasharray="5 5" /> |
| <Line type="monotone" dataKey="Enforce3" stroke="#0ea5e9" strokeWidth={2} strokeDasharray="5 5" /> |
| </LineChart> |
| </ResponsiveContainer> |
| </div> |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> |
| <div className="bg-red-50 p-4 rounded-lg border border-red-100"> |
| <h4 className="font-semibold text-red-700 mb-1">Baseline Trend</h4> |
| <p className="text-red-600 text-xs"> |
| Remains consistently high (40-70 tokens/turn). The model feels "obligated" to reply with conversational filler ("Got it", "Just to confirm"), maintaining high volume. |
| </p> |
| </div> |
| <div className="bg-green-50 p-4 rounded-lg border border-green-100"> |
| <h4 className="font-semibold text-green-700 mb-1">Enforcement Trend</h4> |
| <p className="text-green-600 text-xs"> |
| Aggressive decay. By Turn 4 and 5, the model enters a "maintenance mode," utilizing only 5-15 tokens per turn to sustain the context. |
| </p> |
| </div> |
| </div> |
| </div> |
| )} |
| |
| {/* TAB 3: DETAILED ANALYSIS */} |
| {activeTab === 'detailed' && ( |
| <div className="animate-fade-in space-y-8"> |
| |
| {/* Question 1 Answer */} |
| <div className="bg-indigo-50 border border-indigo-100 rounded-lg p-6"> |
| <h3 className="text-lg font-bold text-indigo-900 mb-2">Answer to your Request: Baseline 1 Inflation</h3> |
| <p className="text-indigo-800 text-sm mb-4"> |
| "What is the % increase on this?" (Based on the first 3 turns of Baseline 1) |
| </p> |
| <div className="flex items-end gap-2 mb-2"> |
| <span className="text-3xl font-bold text-indigo-700">79.6%</span> |
| <span className="text-sm text-indigo-600 mb-2">Increase</span> |
| </div> |
| <div className="w-full bg-indigo-200 rounded-full h-2.5 mb-2"> |
| <div className="bg-indigo-600 h-2.5 rounded-full" style={{ width: '79.6%' }}></div> |
| </div> |
| <ul className="list-disc list-inside text-xs text-indigo-800 space-y-1"> |
| <li><strong>Input Subtotal:</strong> 54 tokens</li> |
| <li><strong>Output Subtotal:</strong> 97 tokens</li> |
| <li><strong>Net Increase:</strong> +43 tokens</li> |
| </ul> |
| </div> |
| |
| {/* Data Table */} |
| <div> |
| <h3 className="text-lg font-bold text-slate-800 mb-4">Turn-by-Turn Data Breakdown</h3> |
| <div className="overflow-x-auto"> |
| <table className="min-w-full divide-y divide-slate-200 border border-slate-200 rounded-lg"> |
| <thead className="bg-slate-50"> |
| <tr> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Test Name</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Turn 1</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Turn 2</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Turn 3</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Turn 4</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-500 uppercase tracking-wider">Turn 5</th> |
| <th className="px-6 py-3 text-left text-xs font-medium text-slate-900 uppercase tracking-wider">Avg</th> |
| </tr> |
| </thead> |
| <tbody className="bg-white divide-y divide-slate-200"> |
| {[ |
| { name: 'Baseline 1', data: [45, 49, 57, 42, 37], avg: 46 }, |
| { name: 'Baseline 2', data: [52, 68, 62, 63, 69], avg: 63 }, |
| { name: 'Enforce 1', data: [48, 37, 35, 19, 17], avg: 31 }, |
| { name: 'Enforce 2', data: [46, 33, 19, 13, 12], avg: 24 }, |
| { name: 'Enforce 3', data: [55, 53, 32, 10, 5], avg: 30 }, |
| ].map((row, idx) => ( |
| <tr key={idx} className={idx < 2 ? 'bg-red-50/30' : 'bg-green-50/30'}> |
| <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-slate-900">{row.name}</td> |
| {row.data.map((val, i) => ( |
| <td key={i} className="px-6 py-4 whitespace-nowrap text-sm text-slate-500">{val}</td> |
| ))} |
| <td className="px-6 py-4 whitespace-nowrap text-sm font-bold text-slate-800">{row.avg}</td> |
| </tr> |
| ))} |
| </tbody> |
| </table> |
| </div> |
| </div> |
| </div> |
| )} |
| |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
|
|
| export default TokenReport; |