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

# LiveKit Integration

> Use 60db STT, TTS, and LLM inside a LiveKit Agents voice pipeline.

# LiveKit Integration

Build real-time voice agents with [LiveKit Agents](https://github.com/livekit/agents) powered by 60db's speech and language services — STT, TTS, and LLM all in one plugin.

<CardGroup cols={3}>
  <Card title="Speech-to-Text" icon="microphone" color="#4A3228">
    Real-time streaming transcription via WebSocket with interim results
  </Card>

  <Card title="Text-to-Speech" icon="volume-high" color="#4A3228">
    Low-latency streaming synthesis with chunked audio delivery
  </Card>

  <Card title="LLM Chat" icon="brain" color="#4A3228">
    OpenAI-compatible chat completions with tool-call support
  </Card>
</CardGroup>

***

## Installation

<Steps>
  <Step title="Install the plugin">
    Requires Python **3.10+**.

    ```bash theme={null}
    pip install livekit-plugins-60db
    ```
  </Step>

  <Step title="Set your API key">
    Choose one of the following methods:

    **Option A — Environment variable (recommended):**

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

    **Option B — `.env.local` file:**

    ```
    SIXTY_DB_API_KEY=your-api-key
    ```

    **Option C — Pass it directly in code:**

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

    client = _60dbClient("your-api-key")  # sets global default
    ```
  </Step>
</Steps>

***

## Quick Start

Wire all three services together in a `VoicePipelineAgent`:

```python theme={null}
import asyncio
from livekit.agents import VoicePipelineAgent, cli, WorkerType
from livekit.plugins import silero
from livekit.plugins._60db import _60dbClient, STT, TTS, LLM

client = _60dbClient("your-api-key")  # sets global default


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

    agent = VoicePipelineAgent(
        vad=silero.VAD.load(),   # voice activity detection
        stt=STT(),               # 60db speech-to-text
        llm=LLM(),               # 60db language model
        tts=TTS(),               # 60db text-to-speech
    )

    agent.start(ctx.room)


if __name__ == "__main__":
    cli.run_app(worker_type=WorkerType.ROOM, entrypoint_fnc=entrypoint)
```

***

## Configuration

### Environment Variables

All services read from the same environment variables by default. You only need to override them if you use a custom deployment.

| Variable           | Default                                   | Description                  |
| ------------------ | ----------------------------------------- | ---------------------------- |
| `SIXTY_DB_API_KEY` | —                                         | Your 60db API key (required) |
| `SIXTY_DB_STT_URL` | `wss://api.60db.ai/ws/stt`                | STT WebSocket endpoint       |
| `SIXTY_DB_TTS_URL` | `wss://api.60db.ai/ws/tts`                | TTS WebSocket endpoint       |
| `SIXTY_DB_LLM_URL` | `https://api.60db.ai/v1/chat/completions` | LLM HTTP endpoint            |

Each service also accepts a direct `ws_url` (or `api_url`) constructor argument which takes precedence over environment variables.

***

## Services

<Tabs>
  <Tab title="STT">
    ### Speech-to-Text

    The STT service streams audio to 60db over WebSocket and returns transcriptions in real time — including interim (partial) results as the speaker is still talking.

    #### Parameters

    | Name              | Type                | Default                 | Description                                      |
    | ----------------- | ------------------- | ----------------------- | ------------------------------------------------ |
    | `api_key`         | `str \| None`       | global client / env var | Your 60db API key                                |
    | `ws_url`          | `str \| None`       | env var                 | WebSocket endpoint URL                           |
    | `languages`       | `list[str] \| None` | `["en"]`                | Language codes for recognition                   |
    | `encoding`        | `str`               | `"mulaw"`               | Audio encoding format                            |
    | `sample_rate`     | `int`               | `8000`                  | Audio sample rate in Hz                          |
    | `continuous_mode` | `bool`              | `True`                  | Keep the session open for continuous recognition |

    #### Example

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

    stt = STT()  # picks up api_key from global client or env var

    async with stt.stream() as stream:
        stream.push_frame(audio_frame)  # push raw audio frames

        async for event in stream:
            if event.type == stt.SpeechEventType.FINAL_TRANSCRIPT:
                print(event.alternatives[0].text)
    ```

    #### Audio Formats

    The plugin automatically handles audio conversion for you — no manual preprocessing needed.

    | Step          | What happens                                                          |
    | ------------- | --------------------------------------------------------------------- |
    | Stereo → Mono | Downmix via `audioop.tomono`                                          |
    | Resampling    | Any input rate → target rate via `audioop.ratecv`                     |
    | Encoding      | LINEAR16 PCM → mulaw via `audioop.lin2ulaw` (when `encoding="mulaw"`) |

    | Parameter   | Default   | Supported                 |
    | ----------- | --------- | ------------------------- |
    | Encoding    | `mulaw`   | `mulaw`, `LINEAR16`       |
    | Sample rate | `8000 Hz` | Any rate (auto-resampled) |
  </Tab>

  <Tab title="TTS">
    ### Text-to-Speech

    The TTS service converts text to speech over WebSocket. It supports both **one-shot** synthesis and **streaming** mode for low-latency incremental output.

    #### Parameters

    | Name          | Type          | Default                                  | Description              |
    | ------------- | ------------- | ---------------------------------------- | ------------------------ |
    | `api_key`     | `str \| None` | global client / env var                  | Your 60db API key        |
    | `ws_url`      | `str \| None` | env var                                  | WebSocket endpoint URL   |
    | `voice_id`    | `str`         | `"fbb75ed2-975a-40c7-9e06-38e30524a9a1"` | Voice identifier         |
    | `encoding`    | `str`         | `"LINEAR16"`                             | Output audio encoding    |
    | `sample_rate` | `int`         | `16000`                                  | Output sample rate in Hz |

    #### Examples

    **One-shot synthesis** — best for short, complete responses:

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

    tts = TTS()

    async for chunk in tts.synthesize("Hello, how can I help you?"):
        # chunk.data contains raw PCM audio bytes
        print(f"Received {len(chunk.data)} bytes")
    ```

    **Streaming synthesis** — best for incremental LLM output:

    ```python theme={null}
    async with tts.stream() as stream:
        stream.push_text("Hello, ")
        stream.push_text("how can I help you?")
        stream.end_input()

        async for chunk in stream:
            print(f"Received {len(chunk.data)} bytes")
    ```

    #### Audio Format

    | Parameter   | Default    | Notes                          |
    | ----------- | ---------- | ------------------------------ |
    | Encoding    | `LINEAR16` | Raw PCM                        |
    | Sample rate | `16000 Hz` | Configurable via `sample_rate` |

    Audio is returned as base64-encoded PCM chunks by the server, decoded automatically by the plugin.
  </Tab>

  <Tab title="LLM">
    ### Language Model

    The LLM service provides OpenAI-compatible chat completions with SSE streaming and tool-call support.

    #### Parameters

    | Name                   | Type            | Default                 | Description                         |
    | ---------------------- | --------------- | ----------------------- | ----------------------------------- |
    | `api_key`              | `str \| None`   | global client / env var | Your 60db API key                   |
    | `api_url`              | `str \| None`   | env var                 | Chat completions endpoint URL       |
    | `model`                | `str`           | `"qcall/slm-3b-int4"`   | Model identifier                    |
    | `temperature`          | `float \| None` | `None`                  | Sampling temperature (0–2)          |
    | `top_p`                | `float \| None` | `None`                  | Nucleus sampling probability        |
    | `top_k`                | `int \| None`   | `None`                  | Top-K sampling parameter            |
    | `max_tokens`           | `int \| None`   | `None`                  | Maximum tokens in the response      |
    | `chat_template_kwargs` | `dict \| None`  | `None`                  | Extra template kwargs for the model |

    #### Example

    ```python theme={null}
    from livekit.plugins._60db import LLM
    from livekit.agents import llm

    model = LLM(temperature=0.7)

    chat_ctx = llm.ChatContext()
    chat_ctx.append(role="system", text="You are a helpful voice assistant.")
    chat_ctx.append(role="user", text="What is the capital of France?")

    async for chunk in model.chat(chat_ctx=chat_ctx):
        print(chunk.choices[0].delta.content, end="")
    ```

    #### Tool Calls

    The LLM supports OpenAI-style function tool calls. When the model calls a tool, the plugin accumulates the streamed argument fragments and emits a complete `FunctionToolCall` once the stream finishes.
  </Tab>
</Tabs>

***

## Timeouts

Control timeouts per-request by passing `APIConnectOptions`:

```python theme={null}
from livekit.agents import APIConnectOptions

opts = APIConnectOptions(timeout=30.0)

# STT
async with stt.stream(conn_options=opts) as s:
    ...

# TTS
async for chunk in tts.synthesize("Hello", conn_options=opts):
    ...

# LLM
async for chunk in model.chat(chat_ctx=ctx, conn_options=opts):
    ...
```

***

## Error Handling

All three services raise standard LiveKit Agents exceptions:

| Exception            | When it's raised                                                        |
| -------------------- | ----------------------------------------------------------------------- |
| `APIConnectionError` | WebSocket handshake failure, HTTP error, or unexpected protocol message |
| `APITimeoutError`    | Request exceeds the configured timeout                                  |
| `ValueError`         | Missing API key or service URL at construction time                     |

### Common HTTP Error Codes (LLM)

| Status | Meaning                    |
| ------ | -------------------------- |
| `401`  | Invalid or missing API key |
| `429`  | Rate limit exceeded        |
| `500`  | Server error               |
| `503`  | Service unavailable        |

### Retry Tips

* **STT**: If you receive an `error` message during the handshake, reconnect with a short delay. A `connecting` status before `connection_established` is normal.
* **TTS**: Retry connection failures with exponential backoff.
* **LLM**: `httpx.TimeoutException` → `APITimeoutError`; `httpx.HTTPStatusError` → `APIConnectionError`.

***

## WebSocket Protocol Reference

<Accordion title="STT WebSocket Protocol">
  **Connect:**

  ```
  wss://api.60db.ai/ws/stt?apiKey={API_KEY}
  ```

  **Handshake:**

  Server → `{"connection_established": true}`

  Client → start command:

  ```json theme={null}
  {
    "type": "start",
    "languages": ["en"],
    "config": {
      "encoding": "mulaw",
      "sample_rate": 8000,
      "continuous_mode": true
    }
  }
  ```

  Server → `{"type": "connected"}`

  **Audio:** Send raw audio as **binary WebSocket frames**.

  **Transcription response:**

  ```json theme={null}
  {
    "type": "transcription",
    "text": "Hello, world",
    "is_final": false,
    "language": "en"
  }
  ```

  **Stop:**

  Client → `{"type": "stop"}`

  Server →

  ```json theme={null}
  {
    "type": "session_stopped",
    "billing_summary": { "total_cost": "..." }
  }
  ```
</Accordion>

<Accordion title="TTS WebSocket Protocol">
  **Connect:**

  ```
  wss://api.60db.ai/ws/tts?apiKey={API_KEY}
  ```

  **Create context:**

  ```json theme={null}
  {
    "create_context": {
      "context_id": "unique-id",
      "voice_id": "fbb75ed2-975a-40c7-9e06-38e30524a9a1",
      "audio_config": {
        "audio_encoding": "LINEAR16",
        "sample_rate_hertz": 16000
      }
    }
  }
  ```

  **Send text:**

  ```json theme={null}
  { "send_text": { "context_id": "unique-id", "text": "Hello!" } }
  ```

  **Flush** (trigger audio generation):

  ```json theme={null}
  { "flush_context": { "context_id": "unique-id" } }
  ```

  **Audio response:**

  ```json theme={null}
  { "audio_chunk": { "audioContent": "<base64 PCM>" } }
  ```

  Followed by `{"flush_completed": true}` when all audio has been delivered.

  **Close context:**

  ```json theme={null}
  { "close_context": { "context_id": "unique-id" } }
  ```
</Accordion>
