parakeet-v3-streaming / source /src /components /PerformanceMetrics.jsx
andito's picture
andito HF Staff
Make UI more minimal and compact
b76aacc
/**
* Performance Metrics Component
*
* Developer-focused dashboard showing:
* - Real-time inference speed (tokens/sec, RTF)
* - Progressive update latency
* - Window state (growing vs sliding)
* - Memory usage
*/
import { useState, useEffect } from 'react';
export default function PerformanceMetrics({
latency,
rtf,
audioDuration,
windowState,
device,
updateInterval,
isProcessingFile,
fileDuration,
transcribedDuration,
}) {
const [memoryUsage, setMemoryUsage] = useState(null);
const [wasProcessingFile, setWasProcessingFile] = useState(false);
// Track if we were processing a file (to keep labels after completion)
useEffect(() => {
if (isProcessingFile) {
setWasProcessingFile(true);
}
}, [isProcessingFile]);
useEffect(() => {
// Monitor memory usage if available
if (performance.memory) {
const interval = setInterval(() => {
const memory = performance.memory;
setMemoryUsage({
used: (memory.usedJSHeapSize / 1024 / 1024).toFixed(1),
total: (memory.totalJSHeapSize / 1024 / 1024).toFixed(1),
limit: (memory.jsHeapSizeLimit / 1024 / 1024).toFixed(1),
});
}, 1000);
return () => clearInterval(interval);
}
}, []);
const formatDuration = (seconds) => {
if (seconds === null || seconds === undefined) return null;
if (seconds < 60) {
return seconds.toFixed(1);
}
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}m ${secs}s`;
};
const MetricCard = ({ label, value, unit, color = 'gray', formatTime = false }) => {
const displayValue = formatTime ? formatDuration(value) : (value !== null && value !== undefined ? value.toFixed(2) : null);
const displayUnit = formatTime ? '' : unit;
return (
<div className="bg-gray-800 rounded-lg p-4 border border-gray-700">
<div className="text-xs text-gray-400 uppercase tracking-wider mb-1">
{label}
</div>
<div className={`text-2xl font-bold text-${color}-400 font-mono`}>
{displayValue !== null ? displayValue : 'β€”'}
{displayUnit && <span className="text-sm ml-1 text-gray-500">{displayUnit}</span>}
</div>
</div>
);
};
const getRTFColor = (rtf) => {
if (rtf === null || rtf === undefined) return 'gray';
// Higher RTF is better (means faster than real-time)
if (rtf > 5) return 'green'; // Excellent (5x+ real-time)
if (rtf > 1) return 'yellow'; // Good (faster than real-time)
return 'red'; // Slow (slower than real-time)
};
const getLatencyColor = (latency) => {
if (latency === null || latency === undefined) return 'cyan';
// Lower latency is better
if (latency < 0.5) return 'green'; // Excellent (<500ms)
if (latency < 1.0) return 'yellow'; // Good (<1s)
return 'red'; // Slow (>1s)
};
const getWindowStateIcon = (state) => {
if (state === 'growing') return 'πŸ“ˆ';
if (state === 'sliding') return '↔️';
return '⏸️';
};
return (
<div className="w-full max-w-4xl mx-auto mt-6">
<div className="bg-gray-900 rounded-lg border border-gray-700 p-6 shadow-xl">
<h2 className="text-xl font-semibold text-gray-100 mb-4 flex items-center gap-2">
<span>πŸ“Š</span> Performance Metrics
</h2>
{/* Metrics Grid */}
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-4">
<MetricCard
label={(isProcessingFile || wasProcessingFile) ? "Processing Time" : "Latency"}
value={latency}
unit="s"
color={(isProcessingFile || wasProcessingFile) ? "blue" : getLatencyColor(latency)}
formatTime={true}
/>
<MetricCard
label="Real-time Factor"
value={rtf}
unit="x"
color={getRTFColor(rtf)}
/>
<MetricCard
label={(isProcessingFile || wasProcessingFile) ? "Transcribed" : "Window Size"}
value={(isProcessingFile || wasProcessingFile) ? transcribedDuration : audioDuration}
unit="s"
color="blue"
formatTime={true}
/>
</div>
{/* Additional Info */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* Window State */}
<div className="bg-gray-800 rounded-lg p-4 border border-gray-700">
<div className="text-xs text-gray-400 uppercase tracking-wider mb-1">
Window State
</div>
<div className="text-lg font-semibold text-gray-200">
{getWindowStateIcon(windowState)} {windowState || 'idle'}
</div>
<div className="text-xs text-gray-500 mt-1">
{windowState === 'growing' && 'Building context (0-15s)'}
{windowState === 'sliding' && 'Sliding window (>15s)'}
{!windowState && 'Not recording'}
</div>
</div>
{/* Device */}
<div className="bg-gray-800 rounded-lg p-4 border border-gray-700">
<div className="text-xs text-gray-400 uppercase tracking-wider mb-1">
Acceleration
</div>
<div className="text-lg font-semibold text-gray-200">
{device === 'webgpu-hybrid' && 'πŸš€ WebGPU Hybrid'}
{device === 'webgpu' && 'πŸš€ WebGPU'}
{device === 'wasm' && 'βš™οΈ WebAssembly'}
{device === 'cpu' && 'πŸ–₯️ CPU'}
{!device && 'β€”'}
</div>
<div className="text-xs text-gray-500 mt-1">
{device === 'webgpu-hybrid' && 'GPU encoder + WASM decoder'}
{device === 'webgpu' && 'Hardware accelerated'}
{device === 'wasm' && 'Software optimized'}
{device === 'cpu' && 'Fallback mode'}
</div>
</div>
{/* Memory (if available) */}
{memoryUsage && (
<div className="bg-gray-800 rounded-lg p-4 border border-gray-700">
<div className="text-xs text-gray-400 uppercase tracking-wider mb-1">
Memory Usage
</div>
<div className="text-lg font-semibold text-gray-200">
{memoryUsage.used} MB
</div>
<div className="text-xs text-gray-500 mt-1">
of {memoryUsage.total} MB allocated
</div>
</div>
)}
</div>
{/* RTF Explanation */}
{rtf !== null && rtf !== undefined && (
<div className="mt-4 p-3 bg-gray-800 border border-gray-700 rounded text-xs text-gray-400">
<strong>Real-time Factor (RTF):</strong> How many times faster than real-time.
{rtf > 1 && ` βœ“ ${rtf.toFixed(1)}x faster than real-time`}
{rtf <= 1 && ' ⚠️ Slower than real-time'}
{' (Higher is better)'}
</div>
)}
</div>
{/* Technical Info */}
<div className="mt-4 text-xs text-gray-500 text-center space-y-1">
<p>Model: Parakeet TDT 0.6B v3 (ONNX) | Sample Rate: 16kHz</p>
<p>Progressive updates every 500ms | Smart window management (15s max)</p>
</div>
</div>
);
}