Voice Synthesis REST API
A standalone Flask microservice that exposes text-to-speech as a single, reusable REST endpoint.
A single-endpoint Flask service that exists because a capability trapped inside a bigger pipeline is only useful to that pipeline. Extracting text-to-speech into a standalone REST API made it callable from anything, not just the one script it originally lived in.
Requirements
- Expose XTTS-v2 synthesis as a plain REST endpoint any caller can hit, not something coupled to the media pipeline's internals
- Keep request latency low enough to be usable as a real API — solving model load time, not just wiring up an endpoint
- No client SDK — the contract itself had to be simple enough that a new caller doesn't need one
Problem
- The XTTS-v2 text-to-speech engine was originally embedded inside the media pipeline — useful there, but unusable by anything else without duplicating the model-loading code.
Architecture
- A Flask microservice with a single REST endpoint: accepts text input, runs it through the XTTS-v2 model, returns synthesized speech.
- Model loading and inference are isolated from any caller-specific logic, keeping the service generic.
API Design
- A single POST /synthesize endpoint — deliberately minimal. Text in, audio out, no auth layer yet since the only current caller is trusted internal code.
Database
- None. The service is stateless — every request is independent and nothing is persisted between calls.
Authentication
None yet, by design for now — the only current caller is trusted internal code (the media pipeline). Documented plainly in the roadmap as the first thing to add before any external caller is given access.
Infrastructure
- Runs as a long-lived Flask process so the XTTS-v2 model can be loaded once and kept warm, rather than a serverless function that would cold-load the model on every invocation.
Deployment
- A single Python process (app.py) — no containerization or orchestration layer yet, appropriate for its current single-caller usage.
Monitoring
No monitoring or alerting set up — the service is small enough that failures surface directly to the caller as a failed request. That's a real gap the moment there's more than one consumer or the service runs unattended.
Engineering Decisions
- Decoupled the TTS engine from the main content pipeline into an independently callable service, so any future project can call the same endpoint instead of re-implementing voice synthesis.
Challenges & Trade-offs
- Balancing model load time against request latency in a synchronous Flask service — informed how the service is intended to be run (warm process, not cold-started per request).
Performance
- Request latency is dominated by model inference time rather than network or serialization overhead, because the model stays warm in memory across requests instead of reloading per call.
Scaling
- Vertical only today — one warm model per process.
- The natural next step is horizontal: multiple warm-model worker processes behind a load balancer, since the model itself, not I/O, is the bottleneck.
- Request queuing would matter before worker count does, since a single slow request currently blocks the worker handling it.
Lessons Learned
- Extracting a single well-defined capability into its own service made it directly reusable — the same pattern is a natural next step for the transcription stage too.
Future Roadmap
- Add authentication for external callers, and move to multiple warm-model workers behind a load balancer if concurrent traffic increases.
Process timeline
The real execution order of this system, stage by stage — not a development calendar, the actual request/data flow, with what dominates time at each step (also explorable interactively above via "Trace a request").
- 1ClientDetermined entirely by the caller's own request pattern — nothing the service controls.
- 2Flask endpointSingle-worker synchronous handling means one slow request blocks the next — the main throughput ceiling at current traffic.
- 3XTTS-v2 (warm model)Removing model load from the request path is the single biggest latency win in this service — the whole reason it's warm-loaded.
- 4Audio responseSynchronous return keeps the response path simple but ties up the connection for the full synthesis duration.
Code examples
Where each piece actually lives in the repository — pointers to the real source, not reconstructed snippets. Browse the full repo →
app.pyFlask endpointmodel/xtts_loader.pyXTTS-v2 (warm model)app.pyAudio response