Iron Law
NO VOICE ENGINE WITHOUT INTERRUPT HANDLING AND ASYNC WORKER ISOLATION — EVERY COMPONENT RUNS IN ITS OWN QUEUE
A voice engine without interrupts is a demo, not a product. Every worker (transcriber, agent, synthesizer) MUST run in an isolated asyncio.Queue-based loop. Interrupt handling MUST be wired before any other feature is added. No exceptions.
Voice AI Engine Development — Gemini Live API + Python 3.14 + FastAPI
Quick Scaffold
uv init voice-engine && cd voice-engine
uv add "google-genai>=1.0.0" "fastapi>=0.135.2" "uvicorn[standard]" \
"websockets>=13.0" pydantic pydantic-settings structlog \
"langgraph>=1.0.7" "langchain-core>=1.2.8" \
"google-adk>=1.28.0" pydub numpy aiolimiter
uv add --dev pytest pytest-asyncio httpx ruff mypy
Process
- Scaffold —
uv init + install google-genai (NOT google-generativeai) and dependencies
- Configure —
core/config.py with pydantic-settings, .env, structured logging (no print())
- Implement BaseWorker — asyncio.Queue input/output,
start(), _run_loop(), terminate()
- Implement GeminiTranscriberWorker — WebSocket to Gemini Live API, mute/unmute for echo prevention
- Implement Agent Worker — either
LangGraphAgentWorker (StateGraph) or ADKAgentWorker (SequentialAgent)
- Implement GeminiSynthesizerWorker —
gemini-2.5-flash-tts-preview or gemini-2.5-pro-tts-preview
- Wire Pipeline — transcriber → agent → synthesizer via asyncio.Queue;
StreamingConversation orchestrator
- Add Interrupt System —
InterruptibleEvent, broadcast_interrupt(), rate-limited chunk playback, mute logic
- Expose WebSocket — FastAPI
/conversation endpoint, asynccontextmanager lifespan (NEVER @app.on_event)
- Test — unit test each worker in isolation, integration test full pipeline, test interrupt path
- Error Handling — every except block MUST log + rethrow; no silent returns; no mock data
Key Patterns
| Pattern |
Implementation |
Reference |
| Worker Pipeline |
BaseWorker → asyncio.Queue → isolated _run_loop |
reference/worker-pipeline.md |
| Gemini Live STT |
GeminiTranscriberWorker — WebSocket to gemini-live-2.5-flash-native-audio |
reference/worker-pipeline.md |
| Gemini TTS |
GeminiSynthesizerWorker — gemini-2.5-flash-tts-preview / gemini-2.5-pro-tts-preview |
reference/worker-pipeline.md |
| Interrupt System |
InterruptibleEvent + broadcast_interrupt() + rate-limited chunks + transcriber mute |
reference/interrupt-handling.md |
| Provider Factory |
VoiceComponentFactory — config-driven worker creation |
reference/worker-pipeline.md |
| WebSocket Integration |
FastAPI /conversation + asynccontextmanager lifespan + WebsocketOutputDevice |
reference/worker-pipeline.md |
| Error Recovery |
Worker loop catches + logs + reraises; session reconnect for Gemini Live timeouts |
reference/interrupt-handling.md |
Documentation Sources
Before generating code, consult these sources:
| Source |
URL / Tool |
Purpose |
| Gemini Live API |
https://ai.google.dev/gemini-api/docs/llms.txt |
Live API WebSocket protocol, audio format, barge-in |
| google-genai SDK |
Context7 MCP → resolve google-genai |
Current SDK API signatures |
| LangGraph |
https://langchain-ai.github.io/langgraph/llms-full.txt |
StateGraph, nodes, edges |
| Google ADK |
Context7 MCP → resolve google-adk |
SequentialAgent, session patterns |
| FastAPI |
Context7 MCP → resolve fastapi |
WebSocket, lifespan patterns |
Reference Files
| File |
Content |
When to Use |
reference/gemini-provider-setup.md |
google-genai install, Live API WebSocket setup, model names, audio format, auth, rate limits |
First — before writing any Gemini code |
reference/worker-pipeline.md |
BaseWorker/BaseTranscriber/BaseAgent/BaseSynthesizer + GeminiTranscriberWorker + GeminiSynthesizerWorker + LangGraphAgentWorker + ADKAgentWorker + FastAPI wiring |
Core pipeline implementation |
reference/interrupt-handling.md |
InterruptibleEvent, broadcast_interrupt, rate-limited playback, transcriber mute, state machine, graceful shutdown |
Interrupt system implementation |
reference/provider-comparison.md |
Gemini vs Deepgram vs ElevenLabs vs Azure — latency, cost, features, when to use alternatives |
Provider selection |
reference/common-pitfalls.md |
Audio jumping, echo feedback, interrupts not working, Gemini-specific traps, session timeouts |
Debugging and prevention |
Common Commands
uvicorn src.main:app --reload # Run dev server with hot reload
pytest -q # Run all tests
pytest -q tests/test_workers.py # Test worker isolation
pytest -q tests/test_interrupt.py # Test interrupt path
ruff check --fix . # Lint and auto-fix
ruff format . # Format code
mypy src/ # Type check
Error Handling
Iron rule: Every except block in a worker loop MUST:
- Log with
structlog at error level (include full exception context)
- Either rethrow OR return a proper error state (never return empty/None/mock)
# Required pattern in every worker loop
async def _run_loop(self) -> None:
while self.active:
try:
item = await self.input_queue.get()
await self.process(item)
except asyncio.CancelledError:
raise # Never swallow CancelledError
except Exception as exc:
self._log.error("worker_error", worker=self.__class__.__name__, error=str(exc), exc_info=True)
# Continue loop — worker recovers from transient errors
# For fatal errors: self.terminate(); raise
Gemini Live session timeouts: Sessions expire after ~10 minutes. Implement reconnect with exponential backoff. See reference/interrupt-handling.md § Error Recovery.
No print() anywhere. Use structlog.get_logger().
Post-Code Review
After writing voice engine code, dispatch:
security-reviewer — WebSocket input validation, API key handling
agentic-ai-reviewer (if LangGraph agent) — graph correctness, iteration limits
code-reviewer — async patterns, resource cleanup, error handling
1---2name: voice-ai-engine-development3description: Build production-ready real-time conversational voice AI engines using async worker pipelines, Gemini Live API for streaming STT, Gemini TTS for synthesis, LangGraph or Google ADK agents, interrupt handling, and FastAPI WebSocket integration. Use when building voice assistants, real-time voice bots, or conversational AI pipelines with full interrupt support.4---56## Iron Law78**NO VOICE ENGINE WITHOUT INTERRUPT HANDLING AND ASYNC WORKER ISOLATION — EVERY COMPONENT RUNS IN ITS OWN QUEUE**910A voice engine without interrupts is a demo, not a product. Every worker (transcriber, agent, synthesizer) MUST run in an isolated `asyncio.Queue`-based loop. Interrupt handling MUST be wired before any other feature is added. No exceptions.1112# Voice AI Engine Development — Gemini Live API + Python 3.14 + FastAPI1314## Quick Scaffold1516```bash17uv init voice-engine && cd voice-engine18uv add "google-genai>=1.0.0" "fastapi>=0.135.2" "uvicorn[standard]" \19 "websockets>=13.0" pydantic pydantic-settings structlog \20 "langgraph>=1.0.7" "langchain-core>=1.2.8" \21 "google-adk>=1.28.0" pydub numpy aiolimiter22uv add --dev pytest pytest-asyncio httpx ruff mypy23```2425## Process26271. **Scaffold** — `uv init` + install `google-genai` (NOT google-generativeai) and dependencies282. **Configure** — `core/config.py` with pydantic-settings, `.env`, structured logging (no `print()`)293. **Implement BaseWorker** — asyncio.Queue input/output, `start()`, `_run_loop()`, `terminate()`304. **Implement GeminiTranscriberWorker** — WebSocket to Gemini Live API, mute/unmute for echo prevention315. **Implement Agent Worker** — either `LangGraphAgentWorker` (StateGraph) or `ADKAgentWorker` (SequentialAgent)326. **Implement GeminiSynthesizerWorker** — `gemini-2.5-flash-tts-preview` or `gemini-2.5-pro-tts-preview`337. **Wire Pipeline** — transcriber → agent → synthesizer via asyncio.Queue; `StreamingConversation` orchestrator348. **Add Interrupt System** — `InterruptibleEvent`, `broadcast_interrupt()`, rate-limited chunk playback, mute logic359. **Expose WebSocket** — FastAPI `/conversation` endpoint, `asynccontextmanager` lifespan (NEVER `@app.on_event`)3610. **Test** — unit test each worker in isolation, integration test full pipeline, test interrupt path3711. **Error Handling** — every except block MUST log + rethrow; no silent returns; no mock data3839## Key Patterns4041| Pattern | Implementation | Reference |42|---------|---------------|-----------|43| Worker Pipeline | `BaseWorker` → `asyncio.Queue` → isolated `_run_loop` | `reference/worker-pipeline.md` |44| Gemini Live STT | `GeminiTranscriberWorker` — WebSocket to `gemini-live-2.5-flash-native-audio` | `reference/worker-pipeline.md` |45| Gemini TTS | `GeminiSynthesizerWorker` — `gemini-2.5-flash-tts-preview` / `gemini-2.5-pro-tts-preview` | `reference/worker-pipeline.md` |46| Interrupt System | `InterruptibleEvent` + `broadcast_interrupt()` + rate-limited chunks + transcriber mute | `reference/interrupt-handling.md` |47| Provider Factory | `VoiceComponentFactory` — config-driven worker creation | `reference/worker-pipeline.md` |48| WebSocket Integration | FastAPI `/conversation` + `asynccontextmanager` lifespan + `WebsocketOutputDevice` | `reference/worker-pipeline.md` |49| Error Recovery | Worker loop catches + logs + reraises; session reconnect for Gemini Live timeouts | `reference/interrupt-handling.md` |5051## Documentation Sources5253Before generating code, consult these sources:5455| Source | URL / Tool | Purpose |56|--------|-----------|---------|57| Gemini Live API | `https://ai.google.dev/gemini-api/docs/llms.txt` | Live API WebSocket protocol, audio format, barge-in |58| google-genai SDK | Context7 MCP → resolve `google-genai` | Current SDK API signatures |59| LangGraph | `https://langchain-ai.github.io/langgraph/llms-full.txt` | StateGraph, nodes, edges |60| Google ADK | Context7 MCP → resolve `google-adk` | SequentialAgent, session patterns |61| FastAPI | Context7 MCP → resolve `fastapi` | WebSocket, lifespan patterns |6263## Reference Files6465| File | Content | When to Use |66|------|---------|-------------|67| `reference/gemini-provider-setup.md` | google-genai install, Live API WebSocket setup, model names, audio format, auth, rate limits | First — before writing any Gemini code |68| `reference/worker-pipeline.md` | BaseWorker/BaseTranscriber/BaseAgent/BaseSynthesizer + GeminiTranscriberWorker + GeminiSynthesizerWorker + LangGraphAgentWorker + ADKAgentWorker + FastAPI wiring | Core pipeline implementation |69| `reference/interrupt-handling.md` | InterruptibleEvent, broadcast_interrupt, rate-limited playback, transcriber mute, state machine, graceful shutdown | Interrupt system implementation |70| `reference/provider-comparison.md` | Gemini vs Deepgram vs ElevenLabs vs Azure — latency, cost, features, when to use alternatives | Provider selection |71| `reference/common-pitfalls.md` | Audio jumping, echo feedback, interrupts not working, Gemini-specific traps, session timeouts | Debugging and prevention |7273## Common Commands7475```bash76uvicorn src.main:app --reload # Run dev server with hot reload77pytest -q # Run all tests78pytest -q tests/test_workers.py # Test worker isolation79pytest -q tests/test_interrupt.py # Test interrupt path80ruff check --fix . # Lint and auto-fix81ruff format . # Format code82mypy src/ # Type check83```8485## Error Handling8687**Iron rule:** Every `except` block in a worker loop MUST:881. Log with `structlog` at `error` level (include full exception context)892. Either rethrow OR return a proper error state (never return empty/None/mock)9091```python92# Required pattern in every worker loop93async def _run_loop(self) -> None:94 while self.active:95 try:96 item = await self.input_queue.get()97 await self.process(item)98 except asyncio.CancelledError:99 raise # Never swallow CancelledError100 except Exception as exc:101 self._log.error("worker_error", worker=self.__class__.__name__, error=str(exc), exc_info=True)102 # Continue loop — worker recovers from transient errors103 # For fatal errors: self.terminate(); raise104```105106**Gemini Live session timeouts:** Sessions expire after ~10 minutes. Implement reconnect with exponential backoff. See `reference/interrupt-handling.md` § Error Recovery.107108**No `print()` anywhere.** Use `structlog.get_logger()`.109110## Post-Code Review111112After writing voice engine code, dispatch:113- `security-reviewer` — WebSocket input validation, API key handling114- `agentic-ai-reviewer` (if LangGraph agent) — graph correctness, iteration limits115- `code-reviewer` — async patterns, resource cleanup, error handling