> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slng.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# LiveKit plugin for SLNG

> Use the livekit-plugins-slng Python package to connect LiveKit Agents to STT and TTS models on the SLNG platform, with failover, warm standby connections, and low-latency turn finalization.

`livekit-plugins-slng` adds STT and TTS adapters for [LiveKit Agents](https://docs.livekit.io/agents/). It connects through SLNG's Unmute Bridge, so models on the SLNG platform work with the same code: pass a model identifier and the plugin builds the endpoint itself.

The plugin is realtime WebSocket only. Models that expose only HTTP endpoints (for example batch STT models such as `sarvam/saaras:v3` or `slng/speechmatics/batch`) are not available through it; check the [Models](/models/index) catalog for streaming support.

<Note>
  This page documents plugin version **1.6.7 and later**, a major rewrite
  distributed through the official LiveKit Agents repository. If you are
  upgrading from 1.6.6 or earlier, read [Migrating from earlier
  versions](#migrating-from-earlier-versions) first: several parameters
  changed or were removed.
</Note>

## Prerequisites

* Python 3.10+
* `livekit-agents>=1.6.7`
* A [LiveKit Agents](https://docs.livekit.io/agents/) project
* An SLNG key (get one at [app.slng.ai](https://app.slng.ai/api-keys))

## Installation

```bash theme={null}
uv add livekit-plugins-slng
# or
pip install livekit-plugins-slng
```

## Credentials

The plugin reads your SLNG key from the `SLNG_API_KEY` environment variable automatically:

```bash theme={null}
export SLNG_API_KEY="your-slng-api-key"
```

You can also pass it explicitly via `api_key`:

```python theme={null}
stt = slng.STT(api_key="your-slng-api-key", model="deepgram/nova:3")
```

<Note>
  `slng.STT` also accepts a legacy `api_token=` alias, but it is deprecated.
  Use `api_key` in new code.
</Note>

## Quickstart

Create an STT and TTS instance, then pass them to your LiveKit agent session:

```python theme={null}
from livekit.plugins import slng

stt = slng.STT(
    model="deepgram/nova:3",
    language="en",
)

tts = slng.TTS(
    model="deepgram/aura:2",
    voice="aura-2-thalia-en",  # provider voice ID, required
    language="en",
)
```

A model identifier is all that is needed: the plugin builds the Unmute Bridge endpoint (`wss://api.slng.ai/v1/bridges/unmute/{stt|tts}/<model>`) by itself.

## Lower STT turn latency

For the lowest end-of-turn latency, let the plugin know when the user stops speaking. The plugin then sends a finalize signal so the provider returns the final transcript immediately instead of waiting for its own endpointing:

```python theme={null}
session = AgentSession(stt=stt, tts=tts, vad=vad)
stt.attach_to_session(session)
```

Or wire it manually:

```python theme={null}
@session.on("user_state_changed")
def _on_user_state_changed(ev):
    stt.notify_user_state(ev.new_state)
```

Without this hook the plugin still works, but end-of-turn detection relies entirely on the provider's endpointing, which typically adds a few hundred milliseconds per turn.

## Full voice agent example

This example wires up STT, TTS, and VAD into a complete LiveKit agent that greets the user on join:

```python theme={null}
from livekit.agents import Agent, AgentSession, JobContext, WorkerOptions, cli
from livekit.plugins import silero, slng


class MyAgent(Agent):
    async def on_enter(self):
        await self.session.say("Hello! How can I help?")


async def entrypoint(ctx: JobContext):
    await ctx.connect()

    stt = slng.STT(
        model="deepgram/nova:3",
        language="en",
        sample_rate=16000,
    )

    tts = slng.TTS(
        model="deepgram/aura:2",
        voice="aura-2-thalia-en",
        language="en",
        sample_rate=24000,
    )

    session = AgentSession(
        stt=stt,
        tts=tts,
        vad=silero.VAD.load(),
    )

    stt.attach_to_session(session)  # low-latency end-of-turn finalization

    await session.start(agent=MyAgent(), room=ctx.room)


if __name__ == "__main__":
    cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
```

## Model identifiers

Models follow the format `provider/model:variant`. Prefix with `slng/` to target an SLNG-hosted instance:

```
provider/model:variant          # third-party passthrough
slng/provider/model:variant     # SLNG-hosted
```

Examples:

```python theme={null}
model="deepgram/nova:3"          # Deepgram Nova 3 (passthrough)
model="slng/deepgram/nova:3-en"  # SLNG-hosted Deepgram Nova 3, English
model="cartesia/sonic:3"         # Cartesia Sonic 3 (passthrough)
```

See the [Models](/models/index) page for the full list of available models.

## STT reference

`slng.STT` streams speech-to-text over WebSocket through the Unmute Bridge.

### Constructor

```python theme={null}
stt = slng.STT(
    api_key=None,                        # SLNG API key. Falls back to SLNG_API_KEY env var.
    model=None,                          # Model identifier. Required unless `connections` is set.
    connections=None,                    # Ordered failover candidates (see Failover below).
    provider_api_key=None,               # BYOK: your own provider credential (see BYOK below).
    language="en",                       # Language code, sent verbatim to the model.
    sample_rate=16000,                   # Audio sample rate in Hz.
    encoding="pcm_s16le",                # Only 16-bit PCM input is supported.
    enable_partial_transcripts=True,     # Enable interim results.
    enable_diarization=False,            # Speaker identification (model support required).
    min_speakers=None,                   # Minimum speakers for diarization.
    max_speakers=None,                   # Maximum speakers for diarization.
    vad_threshold=0.5,                   # VAD threshold (model support required).
    vad_min_silence_duration_ms=300,     # Minimum silence for VAD (ms).
    vad_speech_pad_ms=30,                # Speech padding for VAD (ms).
    final_timeout_s=None,                # Watchdog for stalled finals. Disabled unless set.
    fallback_recovery_cooldown_s=60.0,   # Seconds before retrying a failed candidate.
    region_override=None,                # Region routing (see Region override below).
    world_part_override=None,            # Broad geographic zone routing.
    external_agent_id=None,              # Optional tracking ID attached to usage events.
    external_session_id=None,            # Optional tracking ID attached to usage events.
    slng_base_url="api.slng.ai",         # Gateway host override.
    http_session=None,                   # Optional reused aiohttp.ClientSession.
    # **model_options                    # Model-specific options (see below).
)
```

Either `model` or `connections` must be provided. Any additional keyword argument is forwarded to the bridge init payload; the bridge applies the options the selected model's catalog declares and ignores the rest, so option names must match that model's contract (for example Deepgram consumes `endpointing` and `smart_format`). The generic VAD and diarization options apply only to models that declare those fields.

<Warning>
  Only 16-bit PCM (`pcm_s16le`) input audio is supported, and batch
  `recognize()` is not available: the bridge is WebSocket-only, so use
  `stream()`.
</Warning>

## TTS reference

`slng.TTS` streams text-to-speech over WebSocket through the Unmute Bridge.

### Constructor

```python theme={null}
tts = slng.TTS(
    api_key=None,                        # SLNG API key. Falls back to SLNG_API_KEY env var.
    model=None,                          # Model identifier. Required unless `connections` is set.
    connections=None,                    # Ordered failover candidates (see Failover below).
    provider_api_key=None,               # BYOK: your own provider credential (see BYOK below).
    voice="...",                         # Required. Provider voice ID, passed verbatim.
    language="en",                       # Language code, sent verbatim to the model.
    sample_rate=24000,                   # Audio sample rate in Hz.
    speed=1.0,                           # Speech speed multiplier.
    text_chunking="auto",                # "auto", "word", or "phrase" (see below).
    phrase_max_chars=60,                 # Max characters per phrase batch.
    warm_standby_enabled=False,          # Pre-open the next connection (see below).
    first_audio_timeout_s=None,          # Fail over if no audio arrives in time. Disabled unless set.
    fallback_recovery_cooldown_s=60.0,   # Seconds before retrying a failed candidate.
    region_override=None,                # Region routing (see Region override below).
    world_part_override=None,            # Broad geographic zone routing.
    external_agent_id=None,              # Optional tracking ID attached to usage events.
    external_session_id=None,            # Optional tracking ID attached to usage events.
    slng_base_url="api.slng.ai",         # Gateway host override.
    word_tokenizer=None,                 # Optional custom tokenize.WordTokenizer.
    http_session=None,                   # Optional reused aiohttp.ClientSession.
    # **model_options                    # Model-specific options (see below).
)
```

`voice` is required and passed verbatim as the provider's voice identifier (use the provider's voice ID, not a display name). Additional keyword arguments are forwarded to the bridge init payload according to the selected model's contract, for example Rime Arcana `speakingStyle`, Sarvam Bulbul `pace`, or Cartesia Sonic `emotion`. You can also pass `pronunciation={"mode": "rewrite", "name": "my-dictionary"}` to apply a [pronunciation dictionary](/pronunciation-dictionaries).

You can change `voice`, `language`, and `speed` at runtime with `tts.update_options(...)`; the change propagates to failover candidates and safely replaces any pre-opened standby connection.

### Text chunking

`text_chunking` controls how streamed text is sent to the provider. The default `"auto"` batches words into phrases (flushing at punctuation or `phrase_max_chars`), which avoids the choppy audio and slow completions that word-by-word streaming causes on some providers. Set `"word"` only if you specifically need per-word forwarding.

### Warm standby connections

By default, each spoken turn opens a fresh connection, which adds connection setup time to the first audio of every turn. With `warm_standby_enabled=True`, the plugin pre-opens the next connection in the background while the current turn is playing, so the next turn starts on a connection that is already open all the way to the provider. Time-to-first-audio then drops to roughly the provider's generation time:

```python theme={null}
tts = slng.TTS(
    model="cartesia/sonic:3",
    voice="f786b574-daa5-4673-aa0c-cbe3e8534c02",
    warm_standby_enabled=True,
)
```

Notes:

* The standby is a single connection per TTS instance (it never accumulates), and it counts toward your concurrency limit while it waits.
* If the prepared connection expires during a long user silence, the plugin automatically reconnects for that turn at regular latency. No error surfaces.
* Some providers close idle connections after a short window, which limits how long a prepared connection survives silence. Where the provider exposes an inactivity option in its model contract, pass it as a model option to keep the standby alive through natural pauses in conversation.

### Streaming vs batch

* `tts.stream()` streams text and returns audio chunks in real time. Use this for voice agents.
* `tts.synthesize(text)` does one-shot synthesis over the same bridge connection. Works well for previews and static prompts.

### Voice selection

Pick a voice that matches your chosen model. See the [Voices](/voices/deepgram-aura) pages for what's available per provider.

## Failover

Both `STT` and `TTS` accept `connections=[...]`, an ordered list of candidates. Candidates may be model identifiers, Unmute Bridge endpoint URLs, or typed connection configurations. A `model` is not needed when `connections` supplies the complete list:

```python theme={null}
stt = slng.STT(
    connections=[
        "deepgram/nova:3",
        "soniox/speech-ai:rt-v4",
    ],
)

tts = slng.TTS(
    voice="aura-2-thalia-en",
    connections=[
        "deepgram/aura:2",
        slng.TTSConnectionConfig(
            endpoint="wss://api.slng.ai/v1/bridges/unmute/tts/cartesia/sonic:3",
            voice="f786b574-daa5-4673-aa0c-cbe3e8534c02",
        ),
    ],
)
```

How it behaves:

* Each candidate gets `APIConnectOptions.max_retry` attempts before the next candidate is selected.
* STT fails over at safe stream boundaries and replays buffered audio (including a pending finalize) onto the new connection, so no speech is lost mid-utterance.
* TTS switches only before its first audio. All TTS candidates must use the same sample rate and channel count.
* HTTP 413 (payload too large) is terminal: every candidate would reject the same oversized request, so the error surfaces without walking the chain.
* After `fallback_recovery_cooldown_s` (60 seconds by default), the primary is tried again on the next request or utterance.
* With a single candidate, a transient mid-utterance connection drop reconnects the same endpoint and replays the buffered audio instead of ending the stream.

`STTConnectionConfig` and `TTSConnectionConfig` keep endpoint-specific headers, init payloads, and voices together:

```python theme={null}
stt = slng.STT(
    connections=[
        slng.STTConnectionConfig(
            endpoint="wss://api.slng.ai/v1/bridges/unmute/stt/deepgram/nova:3",
            headers={"X-Region-Override": "eu-west-1"},
            init={"type": "init", "config": {"language": "en"}},
        )
    ]
)
```

Global settings are inherited by simple fallback candidates.

## Bring your own key (BYOK)

Pass `provider_api_key` to use your own provider credential. The plugin sends it as the `X-Slng-Provider-Key` header, and the gateway forwards it to the upstream provider. External (third-party) models only; see [BYOK](/execution-layer/byok) for details:

```python theme={null}
tts = slng.TTS(
    model="cartesia/sonic:3",
    voice="your-voice-id",
    provider_api_key="your-cartesia-key",
)
```

## Region override

Both `STT` and `TTS` accept `region_override`, which maps to the gateway's `X-Region-Override` header. Pass a single region or a list of preferred regions in priority order:

```python theme={null}
stt = slng.STT(
    model="deepgram/nova:3",
    region_override="eu-west-1",
)

tts = slng.TTS(
    model="deepgram/aura:2",
    voice="aura-2-thalia-en",
    region_override=["eu-west-1", "us-east-1"],
)
```

To constrain routing to a broad geographic zone instead of a specific region, use `world_part_override` (for example `"eu"`), which maps to the gateway's `X-World-Part-Override` header. `region_override` takes precedence when both are set. See [Region override](/region-override) for accepted values.

## Tracking IDs

`external_agent_id` and `external_session_id` attach your own identifiers to SLNG usage events (as the `X-Slng-Agent-Id` and `X-Slng-Session-Id` headers), so you can correlate gateway usage with your own analytics. Both are optional, max 128 characters.

## Plugin events

Subscribe to `slng_event` for typed events covering gateway session identifiers and failover activity:

```python theme={null}
@tts.on("slng_event")
def on_slng_event(event: slng.PluginEvent) -> None:
    print(event.name, event.component, event.data)
```

Events include `gateway.session` (the gateway request and session IDs for each connection) and `fallback.attempt_failed`, `fallback.switch_succeeded`, `fallback.primary_recovered`, and `fallback.exhausted` for failover monitoring.

## Migrating from earlier versions

Version 1.6.7 is a breaking rewrite of the plugin:

* All traffic goes through the Unmute Bridge. `model_endpoint` and `model_endpoints` were removed and now raise an error; pass a model identifier (`model="deepgram/nova:3"`) or `connections=[...]` instead.
* STT no longer defaults to `model="deepgram/nova:3"`; pass a model (or `connections`) explicitly.
* TTS `voice` is required and passed verbatim as the provider's voice identifier (use the provider's voice ID, not a display name).
* Language codes are no longer normalized client-side; send the value the model expects (for example BCP-47 `hi-IN` for Sarvam Bulbul, not `hi`).
* STT `recognize()` (HTTP batch) is no longer supported; use `stream()`. Only `pcm_s16le` input audio is supported.
* `api_token` still works on STT but is deprecated; use `api_key`.
* Provider-specific defaults (voice normalization, implicit fallbacks) were removed; configure candidates explicitly via `connections`.

<Note>
  The plugin registers itself with LiveKit on import and outputs `linear16`
  PCM audio. Both `STT` and `TTS` authenticate with `api_key`.
</Note>

## Next steps

* Browse available [Models](/models/index) for STT and TTS
* Check the [Voices](/voices/deepgram-aura) pages for voice options per provider
* See [Voice Agents](/voice-agents) for the SLNG-managed agents API
