// Performance monitoring for translation latency export class PerformanceMonitor { private static instance: PerformanceMonitor; private metrics: Map = new Map(); private timers: Map = new Map(); private constructor() {} static getInstance(): PerformanceMonitor { if (!PerformanceMonitor.instance) { PerformanceMonitor.instance = new PerformanceMonitor(); } return PerformanceMonitor.instance; } // Start timing an operation startTimer(operation: string): void { this.timers.set(operation, performance.now()); } // End timing and record the duration endTimer(operation: string): number { const startTime = this.timers.get(operation); if (!startTime) { console.warn(`No start time found for operation: ${operation}`); return 0; } const duration = performance.now() - startTime; this.recordMetric(operation, duration); this.timers.delete(operation); return duration; } // Record a metric value recordMetric(name: string, value: number): void { if (!this.metrics.has(name)) { this.metrics.set(name, []); } const values = this.metrics.get(name)!; values.push(value); // Keep only last 100 values if (values.length > 100) { values.shift(); } } // Get average metric value getAverageMetric(name: string): number { const values = this.metrics.get(name); if (!values || values.length === 0) { return 0; } const sum = values.reduce((a, b) => a + b, 0); return sum / values.length; } // Get time to first byte (TTFB) for streaming measureTTFB(operation: string, firstByteTime: number): number { const startTime = this.timers.get(operation); if (!startTime) { return 0; } const ttfb = firstByteTime - startTime; this.recordMetric(`${operation}_ttfb`, ttfb); return ttfb; } // Get performance summary getPerformanceSummary(): { streaming: { avgTotalTime: number; avgTTFB: number; count: number; }; regular: { avgTotalTime: number; count: number; }; improvement: { ttfbReduction: number; perceivedLatencyReduction: number; }; } { const streamingTotal = this.getAverageMetric('streaming_translation'); const streamingTTFB = this.getAverageMetric('streaming_translation_ttfb'); const streamingCount = this.metrics.get('streaming_translation')?.length || 0; const regularTotal = this.getAverageMetric('regular_translation'); const regularCount = this.metrics.get('regular_translation')?.length || 0; // Calculate improvements const ttfbReduction = regularTotal > 0 && streamingTTFB > 0 ? ((regularTotal - streamingTTFB) / regularTotal) * 100 : 0; // Perceived latency is based on TTFB for streaming vs total time for regular const perceivedLatencyReduction = ttfbReduction; return { streaming: { avgTotalTime: streamingTotal, avgTTFB: streamingTTFB, count: streamingCount }, regular: { avgTotalTime: regularTotal, count: regularCount }, improvement: { ttfbReduction: Math.round(ttfbReduction), perceivedLatencyReduction: Math.round(perceivedLatencyReduction) } }; } // Log performance stats to console logPerformanceStats(): void { const summary = this.getPerformanceSummary(); console.group('Translation Performance Stats'); console.log('Streaming Translation:'); console.log(` Average Total Time: ${summary.streaming.avgTotalTime.toFixed(2)}ms`); console.log(` Average TTFB: ${summary.streaming.avgTTFB.toFixed(2)}ms`); console.log(` Sample Count: ${summary.streaming.count}`); console.log('Regular Translation:'); console.log(` Average Total Time: ${summary.regular.avgTotalTime.toFixed(2)}ms`); console.log(` Sample Count: ${summary.regular.count}`); console.log('Improvements:'); console.log(` TTFB Reduction: ${summary.improvement.ttfbReduction}%`); console.log(` Perceived Latency Reduction: ${summary.improvement.perceivedLatencyReduction}%`); console.groupEnd(); } // Clear all metrics clearMetrics(): void { this.metrics.clear(); this.timers.clear(); } }