Skip to main content
nika:tts_generate is the image pipeline’s sibling for audio: one file lands under a permit-gated output_dir:, sha256-named, manifest beside it, and audio bytes never ride workflow outputs — tasks pass a path and a hash, not megabytes of base64.

The four providers

Keys and the local URL are engine config, never workflow args: OPENAI_API_KEY / ELEVENLABS_API_KEY (or NIKA_-prefixed), NIKA_TTS_LOCAL_URL (+ optional NIKA_TTS_LOCAL_API_KEY). nika doctor prints a tts line naming what’s wired.

Quickstart

Honesty rules

  • The extension follows the bytes. WAV RIFF…WAVE and MP3 headers are sniffed; a mislabel is a format_mismatch: warning, a non-audio payload is a hard error — never a corrupt file on disk.
  • Duration is exact or absent. WAV duration_ms is pure header math; MP3 duration is honestly null rather than a frame-walking guess.
  • text: caps at 4096 chars (the strictest wire’s limit, held portably) — fan longer scripts out with for_each over paragraphs.
  • Content credentials are detected, not verified: audio C2PA carriage (RIFF C2PA · MP3 GEOB) is surfaced as content_credentials in output and manifest, the same contract as images.
  • Watermarking is declared, never byte-verified. The manifest’s watermark_declared is a catalog fact: elevenlabs"synthid (provider-declared · not byte-verified)", every other provider → null — no such declaration, honestly absent.

Cookbook (proven against live APIs)

An LLM writes the script, another model speaks it — one run:
The manifest records the LLM’s exact sentence as the resolved request — the provenance chain covers who wrote the words, not just who spoke them. Retry a local server that’s still loading its model:
Local CPU synthesis gets a 300s default timeout (timeout_ms: up to 600000); clouds default to 120s. Sub-second values are refused — under 1s is a typo’d unit.