Python 88.3%
TypeScript 7.6%
Shell 4.1%
1#!/usr/bin/env python32"""Streaming TTS (gemini-3.1-flash-tts-preview only) via streamGenerateContent?alt=sse — raw HTTP through scripts/live.py.34STATUS: LIVE_VERIFIED 2026-09-18: 26 SSE events in 1.0 s for "Say: OK"; each `data:` event carries one inlineData chunk5 (first 1,920 bytes = 40 ms), last event has finishReason STOP + usageMetadata (50 audio tokens). Free tier.6Run: .venv/bin/python examples/gemini/speech/tts_stream.py "Say: OK"7"""8from __future__ import annotations910import base6411import json12import sys13import wave14from pathlib import Path1516ROOT = Path(__file__).resolve().parents[3]17sys.path.insert(0, str(ROOT))18from scripts import live # noqa: E4021920text = sys.argv[1] if len(sys.argv) > 1 else "Say: OK"21body = {"contents": [{"parts": [{"text": text}]}],22 "generationConfig": {"responseModalities": ["AUDIO"], "speechConfig": {"voiceConfig": {"prebuiltVoiceConfig": {"voiceName": "Kore"}}}}}23st, lines, _ = live.gemini_request("POST", "/v1beta/models/gemini-3.1-flash-tts-preview:streamGenerateContent?alt=sse", body,24 stream=True, est_cost_usd=0.001, note="example tts_stream.py")25print("HTTP", st)26pcm = bytearray(); n = 0; last = None27for line in lines:28 if not line.startswith("data:"):29 continue30 ev = json.loads(line[5:]); n += 1; last = ev31 for p in ev.get("candidates", [{}])[0].get("content", {}).get("parts", []):32 if "inlineData" in p:33 pcm += base64.b64decode(p["inlineData"]["data"])34out = ROOT / "tmp-live" / "gemini-tts-stream.wav"35with wave.open(str(out), "wb") as w:36 w.setnchannels(1); w.setsampwidth(2); w.setframerate(24000); w.writeframes(bytes(pcm))37print(f"{n} events, {len(pcm)} PCM bytes ({len(pcm) / 48000:.2f} s) -> {out}; last finishReason={last['candidates'][0].get('finishReason') if last else None}; usage={last.get('usageMetadata') if last else None}")38