/** * TTFB Benchmark — Compares Streaming vs Non-Streaming Latency * Usage: node benchmark_stream.js */ const OpenAI = require("openai"); const fs = require("fs"); if (!fs.existsSync("test_voices")) fs.mkdirSync("test_voices"); const BASE_URL = process.env.BASE_URL || "http://localhost:3100/v1"; const API_KEY = process.env.API_KEY || "sk-key"; // Use a long text to emphasize the difference between waiting for the whole file vs streaming chunks const TEXT = `Asalamualaykum. We are drawn to these scenes not merely as observers but as participants in an ancient relationship. Biologists call it biophilia, our innate love of living things, but it feels deeper than scientific terminology suggests. When we stand at the edge of the Grand Canyon or watch a hawk spiral on thermals, something in our chest loosens. Our shoulders drop. Our breathing slows to match the rhythm of waves or wind. In nature, we find the antidote to the fragmentation of modern life; here, everything is connected, everything belongs. The Japanese practice of shinrin-yoku, forest bathing, recognizes that trees don't just provide scenery; they exhale compounds that lower our cortisol levels. But the healing goes beyond chemistry. Nature teaches us patience — the oak spends decades preparing to drop its acorns — resilience — the wildflower cracks concrete to reach light — and impermanence — the sunset lasts only minutes, making it precious precisely because it cannot be possessed.`; async function runBenchmark(voiceName) { console.log(`\n======================================================`); console.log(`🏁 BENCHMARKING VOICE: ${voiceName.toUpperCase()}`); console.log(`======================================================\n`); // ─── 1. Non-Streaming (Wait for entire file) ─────────────────────────── console.log(`⏳ 1. NON-STREAMING (Legacy Behavior)`); console.log(` Waiting for Deepgram to generate the entire audio file...`); const client = new OpenAI({ baseURL: BASE_URL, apiKey: API_KEY }); let t0 = Date.now(); const nonStreamRes = await client.audio.speech.create({ model: "gpt-4o-mini-tts", input: TEXT, voice: voiceName, }); const nonStreamBuf = Buffer.from(await nonStreamRes.arrayBuffer()); const nonStreamElapsed = Date.now() - t0; fs.writeFileSync(`test_voices/benchmark_nonstream_${voiceName}.wav`, nonStreamBuf); console.log(` ✅ Received ${nonStreamBuf.length} bytes.`); console.log(` ⏱️ Total Time (TTFB): ${nonStreamElapsed}ms\n`); // ─── 2. True Real-Time Streaming (New Behavior) ──────────────────────── console.log(`⚡ 2. CHUNKED STREAMING (New Behavior)`); console.log(` Connecting to streaming endpoint...`); t0 = Date.now(); let firstChunkTime = null; let totalBytes = 0; let chunks = 0; const streamRes = await fetch(`${BASE_URL}/audio/speech`, { method: "POST", headers: { "Authorization": `Bearer ${API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "gpt-4o-mini-tts", input: TEXT, voice: voiceName, }), }); if (!streamRes.ok) throw new Error(`HTTP ${streamRes.status}: ${await streamRes.text()}`); const writer = fs.createWriteStream(`test_voices/benchmark_stream_${voiceName}.wav`); const reader = streamRes.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; if (!firstChunkTime) { firstChunkTime = Date.now() - t0; console.log(` 🔥 FIRST BYTE ARRIVED (TTFB): ${firstChunkTime}ms!`); } chunks++; totalBytes += value.length; writer.write(Buffer.from(value)); } writer.end(); const streamElapsed = Date.now() - t0; console.log(` ✅ Received ${totalBytes} bytes in ${chunks} chunks.`); console.log(` ⏱️ Total Download Time: ${streamElapsed}ms\n`); // ─── 3. Comparison ───────────────────────────────────────────────────── console.log(`📊 COMPARISON`); const speedup = (nonStreamElapsed / firstChunkTime).toFixed(1); console.log(` - Legacy Wait Time : ${nonStreamElapsed}ms`); console.log(` - New TTFB Time : ${firstChunkTime}ms`); console.log(` 🚀 The new streaming integration starts playing audio ${speedup}x faster!\n`); } async function main() { try { await runBenchmark("jessica"); await runBenchmark("piper"); } catch (err) { console.error("❌ Benchmark failed:", err); } } main();