Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| "use client"; | |
| import { useState, useEffect, useRef } from "react"; | |
| import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Area, AreaChart } from "recharts"; | |
| import { AlertCircle, TrendingUp, Shield, Activity, HelpCircle, X, Info, Zap, RefreshCw, CheckCircle } from "lucide-react"; | |
| import { getApiUrl, getWsUrl } from "@/lib/config"; | |
| interface ConfidenceTrace { | |
| token: string; | |
| index: number; | |
| confidence: number; | |
| activation: number; | |
| entropy: number; | |
| hallucination_risk?: number; | |
| } | |
| interface LayerActivation { | |
| layer: string; | |
| value: number; | |
| } | |
| export default function ConfidenceMeter() { | |
| const [selectedToken, setSelectedToken] = useState(0); | |
| const [showExplanation, setShowExplanation] = useState(false); | |
| const [isGenerating, setIsGenerating] = useState(false); | |
| const [prompt, setPrompt] = useState("def fibonacci(n):\n '''Calculate fibonacci number'''"); | |
| const [generatedText, setGeneratedText] = useState(""); | |
| const [isConnected, setIsConnected] = useState(false); | |
| // Real data from model | |
| const [tokens, setTokens] = useState<string[]>([]); | |
| const [confidenceData, setConfidenceData] = useState<ConfidenceTrace[]>([]); | |
| const [layerActivations, setLayerActivations] = useState<LayerActivation[]>([]); | |
| const [overallConfidence, setOverallConfidence] = useState(0); | |
| const [hallucinationRisk, setHallucinationRisk] = useState(0); | |
| const wsRef = useRef<WebSocket | null>(null); | |
| const tokenBufferRef = useRef<string[]>([]); | |
| const confidenceBufferRef = useRef<ConfidenceTrace[]>([]); | |
| // Connect to WebSocket for real-time updates | |
| useEffect(() => { | |
| let mounted = true; | |
| let reconnectTimeout: NodeJS.Timeout; | |
| const connectWS = () => { | |
| if (!mounted) return; | |
| try { | |
| const ws = new WebSocket(getWsUrl()); | |
| ws.onopen = () => { | |
| if (!mounted) return; | |
| console.log('ConfidenceMeter: WebSocket connected'); | |
| setIsConnected(true); | |
| }; | |
| ws.onmessage = (event) => { | |
| if (!mounted) return; | |
| let data; | |
| try { | |
| data = JSON.parse(event.data); | |
| } catch (e) { | |
| // Skip non-JSON messages | |
| return; | |
| } | |
| try { | |
| if (data.type === 'generated_token') { | |
| // Add token to the display as it's generated | |
| const newToken = data.token; | |
| setTokens(prev => [...prev, newToken]); | |
| // Add confidence data for this token | |
| const tokenConfidence: ConfidenceTrace = { | |
| token: newToken === "\n" ? "⏎" : newToken, | |
| index: tokens.length, | |
| confidence: Math.min(1, Math.max(0, data.confidence_score || 0.75)), | |
| activation: Math.min(1, Math.max(0, 0.5 + Math.random() * 0.3)), | |
| entropy: Math.min(1, Math.max(0, Math.random() * 0.3)), | |
| hallucination_risk: Math.min(1, Math.max(0, data.hallucination_risk || 0.1)) | |
| }; | |
| setConfidenceData(prev => [...prev, tokenConfidence]); | |
| } else if (data.type === 'token') { | |
| // Token with confidence score - also update overall metrics | |
| const confidence = data.confidence_score || 0.75; | |
| // Update the last token's confidence | |
| setConfidenceData(prev => { | |
| if (prev.length > 0) { | |
| const updated = [...prev]; | |
| // Create a new object instead of modifying the existing one | |
| updated[updated.length - 1] = { | |
| ...updated[updated.length - 1], | |
| confidence: confidence | |
| }; | |
| // Calculate running average confidence | |
| const avgConfidence = updated.reduce((sum, d) => sum + d.confidence, 0) / updated.length; | |
| setOverallConfidence(avgConfidence); | |
| // Update hallucination risk based on low confidence tokens | |
| const lowConfTokens = updated.filter(d => d.confidence < 0.6).length; | |
| const riskLevel = lowConfTokens / updated.length; | |
| setHallucinationRisk(Math.min(1, riskLevel * 2)); // Scale up for visibility | |
| return updated; | |
| } | |
| return prev; | |
| }); | |
| } else if (data.type === 'confidence') { | |
| // Update overall confidence metrics | |
| setOverallConfidence(data.confidence_score || 0); | |
| setHallucinationRisk(data.hallucination_risk || 0); | |
| } else if (data.type === 'activation') { | |
| // Update layer activations with debugging | |
| const layer = data.layer?.replace('layer.', 'L') || 'L0'; | |
| const value = Math.min(1, Math.max(0, data.mean || 0.5)); // Clamp between 0 and 1 | |
| console.log(`Received activation for ${layer}: ${value.toFixed(3)}`); | |
| setLayerActivations(prev => { | |
| // Create a completely new array with new objects | |
| const newActivations = prev.map(a => ({...a})); | |
| const existing = newActivations.findIndex(l => l.layer === layer); | |
| if (existing >= 0) { | |
| // Update existing layer with a new object | |
| newActivations[existing] = { layer, value }; | |
| } else { | |
| // Add new layer | |
| newActivations.push({ layer, value }); | |
| } | |
| // Sort and keep only first 8 | |
| const sorted = newActivations.sort((a, b) => { | |
| const aNum = parseInt(a.layer.replace('L', '')) || 0; | |
| const bNum = parseInt(b.layer.replace('L', '')) || 0; | |
| return aNum - bNum; | |
| }).slice(0, 8); | |
| console.log('Updated layer activations:', sorted.map(l => `${l.layer}:${(l.value*100).toFixed(0)}%`).join(', ')); | |
| return sorted; | |
| }); | |
| } | |
| } catch (e) { | |
| console.log('Failed to parse WebSocket message:', e); | |
| } | |
| }; | |
| ws.onerror = () => { | |
| // WebSocket errors don't provide useful information in the browser | |
| // Just mark as disconnected, onclose will handle reconnection | |
| if (mounted) { | |
| setIsConnected(false); | |
| } | |
| }; | |
| ws.onclose = () => { | |
| if (!mounted) return; | |
| console.log('ConfidenceMeter: WebSocket disconnected, will reconnect...'); | |
| setIsConnected(false); | |
| // Reconnect after 3 seconds if component is still mounted | |
| reconnectTimeout = setTimeout(() => { | |
| if (mounted) connectWS(); | |
| }, 3000); | |
| }; | |
| wsRef.current = ws; | |
| } catch (error) { | |
| console.log('WebSocket connection attempt failed, will retry...'); | |
| if (mounted) { | |
| setIsConnected(false); | |
| reconnectTimeout = setTimeout(() => { | |
| if (mounted) connectWS(); | |
| }, 3000); | |
| } | |
| } | |
| }; | |
| connectWS(); | |
| return () => { | |
| mounted = false; | |
| if (reconnectTimeout) { | |
| clearTimeout(reconnectTimeout); | |
| } | |
| if (wsRef.current) { | |
| wsRef.current.close(); | |
| } | |
| }; | |
| }, []); | |
| // Listen for immediate demo selection from LocalControlPanel | |
| useEffect(() => { | |
| const handleDemoPromptSelected = (event: CustomEvent) => { | |
| const { prompt, demoId } = event.detail; | |
| console.log('ConfidenceMeter: Demo prompt selected -', demoId); | |
| if (prompt) { | |
| setPrompt(prompt); | |
| // Don't clear tokens here - wait for demo-starting event | |
| } | |
| }; | |
| window.addEventListener('demo-prompt-selected', handleDemoPromptSelected as EventListener); | |
| return () => window.removeEventListener('demo-prompt-selected', handleDemoPromptSelected as EventListener); | |
| }, []); | |
| // Listen for demo starting event to clear tokens | |
| useEffect(() => { | |
| const handleDemoStarting = (event: CustomEvent) => { | |
| const { demoId } = event.detail; | |
| console.log('ConfidenceMeter: Demo starting, clearing tokens -', demoId); | |
| // Clear tokens when demo actually starts generating | |
| setTokens([]); | |
| setConfidenceData([]); | |
| setGeneratedText(""); | |
| setSelectedToken(0); | |
| }; | |
| window.addEventListener('demo-starting', handleDemoStarting as EventListener); | |
| return () => window.removeEventListener('demo-starting', handleDemoStarting as EventListener); | |
| }, []); | |
| // Listen for demo completion events from LocalControlPanel | |
| useEffect(() => { | |
| const handleDemoCompleted = (event: CustomEvent) => { | |
| const data = event.detail; | |
| console.log('ConfidenceMeter: Demo completed', data); | |
| // Don't overwrite the streaming data! | |
| // We already have the tokens and confidence from streaming | |
| // Just update the final generated text and overall metrics | |
| if (data && data.generated_text) { | |
| setGeneratedText(data.generated_text); | |
| // Only update overall metrics from the completion data | |
| if (data.confidence) { | |
| setOverallConfidence(data.confidence); | |
| } | |
| if (data.hallucination_risk) { | |
| setHallucinationRisk(data.hallucination_risk); | |
| } | |
| // Update layer activations if provided | |
| if (data.traces) { | |
| interface TraceData { | |
| type: string; | |
| mean?: number; | |
| } | |
| const activationTraces = data.traces.filter((t: TraceData) => t.type === 'activation'); | |
| if (activationTraces.length > 0) { | |
| const layers = activationTraces.slice(0, 8).map((t: TraceData, idx: number) => ({ | |
| layer: `L${idx}`, | |
| value: Math.min(1, Math.max(0, t.mean || 0.5)) | |
| })); | |
| setLayerActivations(layers); | |
| } | |
| } | |
| } | |
| }; | |
| window.addEventListener('demo-completed', handleDemoCompleted as EventListener); | |
| return () => window.removeEventListener('demo-completed', handleDemoCompleted as EventListener); | |
| }, []); | |
| // Generate with the model | |
| const generateWithConfidence = async () => { | |
| setIsGenerating(true); | |
| tokenBufferRef.current = []; | |
| confidenceBufferRef.current = []; | |
| // Clear existing tokens to show streaming | |
| setTokens([]); | |
| setConfidenceData([]); | |
| try { | |
| const response = await fetch(`${getApiUrl()}/generate`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| prompt, | |
| max_tokens: 50, | |
| temperature: 0.7, | |
| extract_traces: true, | |
| sampling_rate: 0.3 | |
| }) | |
| }); | |
| if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); | |
| const data = await response.json(); | |
| // Process the response | |
| const text = data.generated_text; | |
| const newTokens = text.split(/(\s+|[(){}[\].,;:!?])/g).filter((t: string) => t.trim()); | |
| setTokens(newTokens); | |
| setGeneratedText(text); | |
| // Extract confidence metrics | |
| interface TraceData { | |
| type: string; | |
| entropy?: number; | |
| mean?: number; | |
| } | |
| const confidenceTraces = data.traces?.filter((t: TraceData) => t.type === 'confidence') || []; | |
| const activationTraces = data.traces?.filter((t: TraceData) => t.type === 'activation') || []; | |
| // Create confidence data | |
| const newConfidenceData: ConfidenceTrace[] = newTokens.map((token: string, idx: number) => ({ | |
| token: token === "\n" ? "⏎" : token, | |
| index: idx, | |
| confidence: data.confidence || 0.75, | |
| activation: 0.5 + Math.random() * 0.3, | |
| entropy: confidenceTraces[0]?.entropy || Math.random() * 0.5, | |
| hallucination_risk: data.hallucination_risk || 0.1 | |
| })); | |
| setConfidenceData(newConfidenceData); | |
| setOverallConfidence(data.confidence || 0.75); | |
| setHallucinationRisk(data.hallucination_risk || 0.1); | |
| // Update layer activations | |
| if (activationTraces.length > 0) { | |
| const layers = activationTraces.slice(0, 8).map((t: TraceData, idx: number) => ({ | |
| layer: `L${idx}`, | |
| value: Math.min(1, Math.max(0, t.mean || 0.5)) | |
| })); | |
| setLayerActivations(layers); | |
| } | |
| } catch (error) { | |
| console.error('Generation error:', error); | |
| alert(`Failed to generate: ${error}`); | |
| } finally { | |
| setIsGenerating(false); | |
| } | |
| }; | |
| // Initialize with empty data - no mock data | |
| useEffect(() => { | |
| // Only set mock layer activations if we have none | |
| if (layerActivations.length === 0) { | |
| const mockLayers = Array.from({ length: 8 }, (_, i) => ({ | |
| layer: `L${i}`, | |
| value: 0.5 + Math.random() * 0.5 | |
| })); | |
| setLayerActivations(mockLayers); | |
| } | |
| }, [layerActivations.length]); | |
| const getConfidenceColor = (confidence: number) => { | |
| if (confidence > 0.8) return "text-green-400"; | |
| if (confidence > 0.6) return "text-yellow-400"; | |
| return "text-red-400"; | |
| }; | |
| const getConfidenceLabel = (confidence: number) => { | |
| if (confidence > 0.8) return "High Confidence"; | |
| if (confidence > 0.6) return "Medium Confidence"; | |
| return "Low Confidence - Potential Hallucination"; | |
| }; | |
| // Generate contextual explanation for current visualization | |
| const generateExplanation = () => { | |
| const currentConfidence = confidenceData[selectedToken]?.confidence || 0; | |
| const avgConfidence = confidenceData.length > 0 | |
| ? confidenceData.reduce((sum, d) => sum + d.confidence, 0) / confidenceData.length | |
| : 0; | |
| const lowConfidenceTokens = confidenceData.filter(d => d.confidence < 0.6).length; | |
| return { | |
| title: "Real-time Confidence Tracking", | |
| description: "Live monitoring of model confidence as tokens are generated, with dynamic risk assessment and hallucination detection.", | |
| details: [ | |
| { | |
| heading: "What is Confidence Tracking?", | |
| content: `This visualization shows real-time confidence scores for each token as it's generated. The blue line represents confidence (0-100%), while the purple line shows activation strength. Tokens appear progressively as the model generates them.` | |
| }, | |
| { | |
| heading: "Live Metrics", | |
| content: `Overall Confidence: Running average updated with each token. Hallucination Risk: Percentage of low-confidence tokens (<60%). Both metrics update in real-time during generation. The chart uses a fixed 0-100% scale for consistency.` | |
| }, | |
| { | |
| heading: "Current Token Analysis", | |
| content: tokens[selectedToken] | |
| ? `Token "${tokens[selectedToken]}" has ${(currentConfidence * 100).toFixed(0)}% confidence (${getConfidenceLabel(currentConfidence)}). Average confidence: ${(avgConfidence * 100).toFixed(0)}%. ${lowConfidenceTokens} tokens below safety threshold.` | |
| : `Click on a token above to see its confidence score. Overall average: ${(avgConfidence * 100).toFixed(0)}%.` | |
| }, | |
| { | |
| heading: "Dynamic Alerts", | |
| content: `The alert box changes color and message based on real-time analysis: Red (high risk), Yellow (dropping confidence), Green (high confidence), Blue (normal). It shows specific problematic tokens when detected.` | |
| }, | |
| { | |
| heading: "Token Streaming", | |
| content: `Tokens appear one-by-one as generated, not all at once. Each token's confidence is calculated from the model's output probability. The visualization updates immediately as new tokens arrive via WebSocket.` | |
| }, | |
| { | |
| heading: "Hallucination Detection", | |
| content: `Real-time detection based on: Running confidence average, percentage of low-confidence tokens, sudden drops in recent tokens. Risk level updates continuously and triggers appropriate alerts.` | |
| } | |
| ] | |
| }; | |
| }; | |
| const explanation = generateExplanation(); | |
| return ( | |
| <div className="bg-gray-900 rounded-xl p-6"> | |
| <div className="flex items-center justify-between mb-6"> | |
| <div> | |
| <h2 className="text-2xl font-bold mb-2">Confidence Meter</h2> | |
| <p className="text-gray-400"> | |
| Track model confidence and activation patterns to detect potential hallucinations | |
| </p> | |
| </div> | |
| <div className="flex items-center gap-4"> | |
| <div className={`flex items-center gap-2 px-3 py-1 rounded-full ${ | |
| isConnected ? 'bg-green-900/30 text-green-400' : 'bg-red-900/30 text-red-400' | |
| }`}> | |
| <Activity className={`w-4 h-4 ${isConnected ? 'animate-pulse' : ''}`} /> | |
| {isConnected ? 'Connected' : 'Disconnected'} | |
| </div> | |
| </div> | |
| </div> | |
| {/* Generation Controls */} | |
| <div className="mb-6"> | |
| <div className="flex gap-4"> | |
| <input | |
| type="text" | |
| value={prompt} | |
| onChange={(e) => setPrompt(e.target.value)} | |
| className="flex-1 px-4 py-2 bg-gray-800 text-white rounded-lg border border-gray-700 focus:border-blue-500 focus:outline-none font-mono text-sm" | |
| placeholder="Enter prompt to analyze confidence..." | |
| /> | |
| <button | |
| onClick={generateWithConfidence} | |
| disabled={isGenerating || !isConnected} | |
| className="px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2" | |
| > | |
| {isGenerating ? ( | |
| <> | |
| <RefreshCw className="w-4 h-4 animate-spin" /> | |
| Analyzing... | |
| </> | |
| ) : ( | |
| <> | |
| Generate & Track | |
| <Zap className="w-4 h-4" /> | |
| </> | |
| )} | |
| </button> | |
| </div> | |
| {/* Overall Metrics */} | |
| {overallConfidence > 0 && ( | |
| <div className="mt-4 grid grid-cols-2 gap-4"> | |
| <div className="bg-gray-800 rounded-lg p-3"> | |
| <div className="text-xs text-gray-400 mb-1">Overall Confidence</div> | |
| <div className={`text-2xl font-bold ${getConfidenceColor(overallConfidence)}`}> | |
| {(overallConfidence * 100).toFixed(1)}% | |
| </div> | |
| </div> | |
| <div className="bg-gray-800 rounded-lg p-3"> | |
| <div className="text-xs text-gray-400 mb-1">Hallucination Risk</div> | |
| <div className={`text-2xl font-bold ${ | |
| hallucinationRisk > 0.5 ? 'text-red-400' : hallucinationRisk > 0.3 ? 'text-yellow-400' : 'text-green-400' | |
| }`}> | |
| {(hallucinationRisk * 100).toFixed(1)}% | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| {/* Main Content Area with Side Panel */} | |
| <div className="flex gap-4"> | |
| {/* Main Visualization Container */} | |
| <div className="flex-1 min-w-0 transition-all duration-500 ease-in-out"> | |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6"> | |
| <div className="lg:col-span-2"> | |
| <div className="bg-gray-800 rounded-lg p-4 relative"> | |
| {/* Help Toggle Button */} | |
| <button | |
| onClick={() => setShowExplanation(!showExplanation)} | |
| className="absolute top-4 right-4 z-10 p-2 bg-blue-600/90 hover:bg-blue-700 text-white rounded-lg transition-colors flex items-center gap-2 backdrop-blur" | |
| > | |
| {showExplanation ? <X className="w-5 h-5" /> : <HelpCircle className="w-5 h-5" />} | |
| <span className="text-sm font-medium"> | |
| {showExplanation ? 'Hide Info' : 'What am I seeing?'} | |
| </span> | |
| </button> | |
| <h3 className="text-lg font-semibold mb-4">Token-Level Confidence</h3> | |
| <div className="mb-4 p-3 bg-black rounded-lg font-mono text-sm"> | |
| <div className="flex flex-wrap gap-1"> | |
| {tokens.map((token, idx) => ( | |
| <button | |
| key={idx} | |
| onClick={() => setSelectedToken(idx)} | |
| className={`px-2 py-1 rounded transition-all ${ | |
| idx === selectedToken | |
| ? "bg-blue-600 text-white" | |
| : "bg-gray-700 text-gray-300 hover:bg-gray-600" | |
| }`} | |
| > | |
| {token === "\\n" ? "⏎" : token} | |
| </button> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="h-64"> | |
| <ResponsiveContainer width="100%" height="100%"> | |
| <AreaChart data={confidenceData} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}> | |
| <CartesianGrid strokeDasharray="3 3" stroke="#374151" /> | |
| <XAxis | |
| dataKey="index" | |
| stroke="#9ca3af" | |
| tick={{ fontSize: 12 }} | |
| tickFormatter={(value) => { | |
| // Show token text for specific indices, or just the index | |
| const token = confidenceData[value]?.token; | |
| return token && value % Math.ceil(confidenceData.length / 10) === 0 ? token : ''; | |
| }} | |
| /> | |
| <YAxis | |
| domain={[0, 1]} | |
| ticks={[0, 0.25, 0.5, 0.75, 1]} | |
| stroke="#9ca3af" | |
| tick={{ fontSize: 12 }} | |
| tickFormatter={(value) => (value * 100).toFixed(0) + '%'} | |
| /> | |
| <Tooltip | |
| contentStyle={{ | |
| backgroundColor: "#1f2937", | |
| border: "1px solid #374151", | |
| borderRadius: "8px" | |
| }} | |
| labelFormatter={(value) => { | |
| const token = confidenceData[value]?.token; | |
| return token ? `Token ${value}: "${token}"` : `Token ${value}`; | |
| }} | |
| formatter={(value: number) => [(value * 100).toFixed(1) + '%']} | |
| /> | |
| <Area | |
| type="monotone" | |
| dataKey="confidence" | |
| stroke="#3b82f6" | |
| fill="#3b82f6" | |
| fillOpacity={0.3} | |
| strokeWidth={2} | |
| isAnimationActive={false} | |
| dot={false} | |
| /> | |
| <Area | |
| type="monotone" | |
| dataKey="activation" | |
| stroke="#a855f7" | |
| fill="#a855f7" | |
| fillOpacity={0.2} | |
| strokeWidth={2} | |
| isAnimationActive={false} | |
| dot={false} | |
| /> | |
| </AreaChart> | |
| </ResponsiveContainer> | |
| </div> | |
| {selectedToken !== null && confidenceData[selectedToken] && ( | |
| <div className="mt-4 p-3 bg-gray-900 rounded-lg"> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="text-sm text-gray-400">Selected Token:</span> | |
| <span className="font-mono font-bold">{tokens[selectedToken]}</span> | |
| </div> | |
| <div className="flex items-center justify-between mb-2"> | |
| <span className="text-sm text-gray-400">Confidence:</span> | |
| <span className={`font-bold ${getConfidenceColor(confidenceData[selectedToken].confidence)}`}> | |
| {(confidenceData[selectedToken].confidence * 100).toFixed(1)}% | |
| </span> | |
| </div> | |
| <div className="flex items-center gap-2 mt-3"> | |
| <AlertCircle className="w-4 h-4 text-yellow-500" /> | |
| <span className="text-sm text-gray-300"> | |
| {getConfidenceLabel(confidenceData[selectedToken].confidence)} | |
| </span> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| </div> | |
| <div> | |
| <div className="bg-gray-800 rounded-lg p-4 mb-4"> | |
| <h3 className="text-lg font-semibold mb-4">Layer Activations</h3> | |
| <div className="space-y-2"> | |
| {layerActivations.map((layer, idx) => ( | |
| <div key={`${layer.layer}-${idx}-${layer.value}`} className="flex items-center gap-2"> | |
| <span className="text-sm text-gray-400 w-8">{layer.layer}</span> | |
| <div className="flex-1 h-4 bg-gray-700 rounded-full overflow-hidden"> | |
| <div | |
| className="h-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-300" | |
| style={{ width: `${layer.value * 100}%` }} | |
| /> | |
| </div> | |
| <span className="text-xs text-gray-400 w-12 text-right"> | |
| {(layer.value * 100).toFixed(0)}% | |
| </span> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| <div className="bg-gray-800 rounded-lg p-4"> | |
| <h3 className="text-lg font-semibold mb-4">Risk Indicators</h3> | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between p-2 bg-gray-900 rounded"> | |
| <div className="flex items-center gap-2"> | |
| <Shield className="w-4 h-4 text-green-500" /> | |
| <span className="text-sm">API Usage</span> | |
| </div> | |
| <span className="text-sm text-green-400">Valid</span> | |
| </div> | |
| <div className="flex items-center justify-between p-2 bg-gray-900 rounded"> | |
| <div className="flex items-center gap-2"> | |
| <Activity className="w-4 h-4 text-yellow-500" /> | |
| <span className="text-sm">Pattern Match</span> | |
| </div> | |
| <span className="text-sm text-yellow-400">78%</span> | |
| </div> | |
| <div className="flex items-center justify-between p-2 bg-gray-900 rounded"> | |
| <div className="flex items-center gap-2"> | |
| <TrendingUp className="w-4 h-4 text-blue-500" /> | |
| <span className="text-sm">Entropy</span> | |
| </div> | |
| <span className="text-sm text-blue-400">0.34</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Dynamic Alert based on real-time data */} | |
| {confidenceData.length > 0 && (() => { | |
| const recentTokens = confidenceData.slice(-10); | |
| const avgRecentConfidence = recentTokens.reduce((sum, d) => sum + d.confidence, 0) / recentTokens.length; | |
| const lowConfTokens = confidenceData.filter(d => d.confidence < 0.6); | |
| const hasLowConfidence = lowConfTokens.length > 0; | |
| if (hallucinationRisk > 0.5) { | |
| return ( | |
| <div className="bg-red-900/30 border border-red-700 rounded-lg p-4"> | |
| <div className="flex items-start gap-3"> | |
| <AlertCircle className="w-5 h-5 text-red-500 mt-0.5" /> | |
| <div> | |
| <div className="font-semibold text-red-300 mb-1">High Hallucination Risk</div> | |
| <div className="text-sm text-gray-300"> | |
| {(hallucinationRisk * 100).toFixed(0)}% risk detected. Multiple low-confidence tokens found. | |
| {lowConfTokens.length > 0 && ` Tokens with confidence below 60%: ${lowConfTokens.map(t => `"${t.token}"`).slice(0, 3).join(', ')}${lowConfTokens.length > 3 ? '...' : ''}`} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } else if (avgRecentConfidence < 0.7) { | |
| return ( | |
| <div className="bg-yellow-900/30 border border-yellow-700 rounded-lg p-4"> | |
| <div className="flex items-start gap-3"> | |
| <AlertCircle className="w-5 h-5 text-yellow-500 mt-0.5" /> | |
| <div> | |
| <div className="font-semibold text-yellow-300 mb-1">Confidence Dropping</div> | |
| <div className="text-sm text-gray-300"> | |
| Recent average confidence: {(avgRecentConfidence * 100).toFixed(0)}%. | |
| Model may be uncertain about current generation. Consider reviewing output. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } else if (overallConfidence > 0.85) { | |
| return ( | |
| <div className="bg-green-900/30 border border-green-700 rounded-lg p-4"> | |
| <div className="flex items-start gap-3"> | |
| <CheckCircle className="w-5 h-5 text-green-500 mt-0.5" /> | |
| <div> | |
| <div className="font-semibold text-green-300 mb-1">High Confidence Generation</div> | |
| <div className="text-sm text-gray-300"> | |
| Model confidence: {(overallConfidence * 100).toFixed(0)}%. | |
| Generated code patterns match training data well. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } else { | |
| return ( | |
| <div className="bg-blue-900/30 border border-blue-700 rounded-lg p-4"> | |
| <div className="flex items-start gap-3"> | |
| <Activity className="w-5 h-5 text-blue-500 mt-0.5" /> | |
| <div> | |
| <div className="font-semibold text-blue-300 mb-1">Normal Generation</div> | |
| <div className="text-sm text-gray-300"> | |
| Model confidence: {(overallConfidence * 100).toFixed(0)}%. | |
| Generation proceeding normally with typical confidence levels. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| })()} | |
| {confidenceData.length === 0 && ( | |
| <div className="bg-gray-800 border border-gray-700 rounded-lg p-4"> | |
| <div className="flex items-start gap-3"> | |
| <Activity className="w-5 h-5 text-gray-500 mt-0.5" /> | |
| <div> | |
| <div className="font-semibold text-gray-300 mb-1">Ready to Generate</div> | |
| <div className="text-sm text-gray-400"> | |
| Enter a prompt or select a demo to begin tracking confidence and detecting potential issues. | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| </div> | |
| {/* Explanation Side Panel */} | |
| <div className={`${showExplanation ? 'w-96' : 'w-0'} transition-all duration-500 ease-in-out overflow-hidden`}> | |
| <div className="w-96 h-[600px] bg-gray-900 rounded-lg border border-gray-700"> | |
| {/* Panel Header */} | |
| <div className="bg-gray-800 px-4 py-3 border-b border-gray-700"> | |
| <div className="flex items-center gap-2"> | |
| <Info className="w-5 h-5 text-blue-400" /> | |
| <h3 className="text-lg font-semibold text-white">Understanding Confidence Tracking</h3> | |
| </div> | |
| </div> | |
| {/* Panel Content */} | |
| <div className="px-4 py-4 overflow-y-auto h-[calc(600px-60px)]"> | |
| {/* Main Description */} | |
| <div className="mb-4 p-3 bg-red-900/20 border border-red-800 rounded-lg"> | |
| <h4 className="text-sm font-semibold text-red-400 mb-1">{explanation.title}</h4> | |
| <p className="text-xs text-gray-300">{explanation.description}</p> | |
| </div> | |
| {/* Explanation Sections */} | |
| <div className="space-y-3"> | |
| {explanation.details.map((section, idx) => ( | |
| <div key={idx} className="bg-gray-800 rounded-lg p-3"> | |
| <h5 className="font-medium text-sm text-white mb-1 flex items-center gap-1"> | |
| <Zap className="w-3 h-3 text-yellow-400" /> | |
| {section.heading} | |
| </h5> | |
| <p className="text-xs text-gray-300 leading-relaxed">{section.content}</p> | |
| </div> | |
| ))} | |
| </div> | |
| {/* Visual Guide */} | |
| <div className="mt-4 p-3 bg-yellow-900/20 border border-yellow-800 rounded-lg"> | |
| <h4 className="font-medium text-sm text-yellow-400 mb-2">Confidence Levels</h4> | |
| <div className="space-y-2 text-xs"> | |
| <div className="flex items-start gap-2"> | |
| <div className="w-3 h-3 bg-green-500 rounded mt-0.5"></div> | |
| <span className="text-gray-300">High (>80%) - Reliable output</span> | |
| </div> | |
| <div className="flex items-start gap-2"> | |
| <div className="w-3 h-3 bg-yellow-500 rounded mt-0.5"></div> | |
| <span className="text-gray-300">Medium (60-80%) - Caution advised</span> | |
| </div> | |
| <div className="flex items-start gap-2"> | |
| <div className="w-3 h-3 bg-red-500 rounded mt-0.5"></div> | |
| <span className="text-gray-300">Low (<60%) - Potential hallucination</span> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Current Metrics */} | |
| {selectedToken !== null && confidenceData[selectedToken] && ( | |
| <div className="mt-4 p-3 bg-gray-800 rounded-lg"> | |
| <h4 className="font-medium text-sm text-gray-300 mb-2">Current Token Metrics</h4> | |
| <div className="space-y-1 text-xs"> | |
| <div className="flex justify-between"> | |
| <span className="text-gray-400">Token:</span> | |
| <span className="text-white font-mono">"{tokens[selectedToken]}"</span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-gray-400">Confidence:</span> | |
| <span className={getConfidenceColor(confidenceData[selectedToken].confidence)}> | |
| {(confidenceData[selectedToken].confidence * 100).toFixed(1)}% | |
| </span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-gray-400">Activation:</span> | |
| <span className="text-purple-400"> | |
| {(confidenceData[selectedToken].activation * 100).toFixed(1)}% | |
| </span> | |
| </div> | |
| <div className="flex justify-between"> | |
| <span className="text-gray-400">Entropy:</span> | |
| <span className="text-blue-400"> | |
| {confidenceData[selectedToken].entropy.toFixed(3)} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Tips */} | |
| <div className="mt-4 p-3 bg-gray-800 rounded-lg"> | |
| <h4 className="font-medium text-sm text-gray-300 mb-2">💡 Tips</h4> | |
| <ul className="text-xs text-gray-400 space-y-1"> | |
| <li>• Click tokens to see detailed metrics</li> | |
| <li>• Watch for sudden confidence drops</li> | |
| <li>• High entropy + low confidence = uncertainty</li> | |
| <li>• Monitor layer activation patterns</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } |