Other clients

Smallest AI API drop-in

Smallest AI's own text-to-speech API, REST, SSE and WebSocket, through the cache: keep Smallest AI's SDK, change the base URLs and key.

View as Markdown

Smallest AI's Python SDK#

Smallest AI’s text-to-speech API, mirrored under https://api.ttscacherouter.dev/smallestai. Point Smallest AI’s SDK at the router and use your router API key as the API key.

import osfrom smallestai.waves import TTSConfig, WavesStreamingTTStts = WavesStreamingTTS(    TTSConfig(        voice_id="meher",        api_key=os.environ["TTS_CACHE_ROUTER_API_KEY"],        model="lightning_v3.1_pro",        language="en",        sample_rate=24000,    ))tts.ws_url = "wss://api.ttscacherouter.dev/smallestai/waves/v1/tts/live"audio = b"".join(tts.synthesize("Your order has shipped. It arrives on Friday."))with open("speech.pcm", "wb") as f:    f.write(audio)

The SDK’s environment sets the text-to-speech base URLs (waves and waves_ws); WavesStreamingTTS takes its URL in ws_url. Requests go to Smallest AI with the Smallest AI key stored under Provider keys. For a LiveKit agent, see Smallest AI with LiveKit.

WebSocket#

wss://api.ttscacherouter.dev/smallestai/waves/v1/tts/live speaks Smallest AI’s WebSocket protocol: messages with text and the synthesis settings, answered with chunk messages of base64 PCM and complete.

Send your router API key in Authorization: Bearer. Both of Smallest AI’s ways of streaming text work: continue and flush (one complete after a request’s audio), and continuations with a context_id, ended by continue: false or context_close: true and stopped by cancel_request: true. {"type": "ping"} gets {"type": "pong"}, and a connection with no message for 60 seconds is closed (the timeout query parameter sets up to 180), as at Smallest AI.

The router splits the text into sentences and serves each sentence from the cache or synthesizes it at Smallest AI, so the messages are Smallest AI’s with these differences:

  • Audio for a sentence starts once the sentence is complete.
  • Each sentence is a take of its own: it doesn’t continue from the audio of the sentence before, as text in one Smallest AI continuation does.
  • With continuations, a complete follows each sentence.
  • session_id and request_id are the router’s own; yours come back as external_session_id and external_request_id.
  • A sentence that fails gets an error message for its request, and the rest of that request is dropped.
  • word_timestamps is accepted, and no word timings are sent. max_buffer_flush_ms and max_buffer_delay_ms change nothing.

As at Smallest AI, the audio is always 16-bit PCM at sample_rate, and text with no letter or digit (... alone) gets no audio.

REST and SSE#

  • POST /smallestai/waves/v1/tts: the audio in the body’s output_format, with Smallest AI’s own Content-Type.
  • POST /smallestai/waves/v1/tts/live: Smallest AI’s server-sent events, base64 audio then done.
  • GET /smallestai/waves/v1/lightning-v3.1/get_voices (and lightning-v3.1-pro) and GET /smallestai/waves/v1/voice-cloning (your cloned voices): Smallest AI’s voice lists, never cached. With the SDK, client.waves.get_voices(...) and client.waves.list_voice_clones(). GET /v1/voices lists both in the router’s own shape.

Any other path under /smallestai answers 404.

curl "https://api.ttscacherouter.dev/smallestai/waves/v1/tts" \  -H "Authorization: Bearer $TTS_CACHE_ROUTER_API_KEY" \  -H "Content-Type: application/json" \  -d '{"text": "Your order has shipped.", "voice_id": "meher", "model": "lightning_v3.1_pro", "language": "en", "sample_rate": 24000, "output_format": "wav"}' \  --output speech.wav

What is cached#

  • REST and SSE: each request as a whole. The same body again replays the same audio.
  • WebSocket: each sentence, so a sentence you have sent before with the same settings comes from the cache whatever came around it. Sentences share the cache with the router’s own /v1/stream when the settings are the same.
  • A request with pronunciation_dicts is cached by the dictionaries’ ids: if you edit a dictionary in place, entries cached before keep the old pronunciation until your cache retention expires them.

Errors#

A missing or wrong router key gets Smallest AI’s own refusal: HTTP 401 unauthorized, at the WebSocket handshake too. An org with no Smallest AI key stored gets the same, with a message that links to the dashboard. Errors from Smallest AI, such as a voice of another model or your Smallest AI concurrency limit, reach you as Smallest AI sent them; on the WebSocket, as an error message carrying Smallest AI’s message, and the connection stays open.

HTTP/1.1 401 Unauthorized{"error": "unauthorized"}