← Back to Plugins
Voice

Openclaw_livekit_agent

vstan1986 By vstan1986 👁 104 views ▲ 0 votes

SIP-backed voice AI assistant: LiveKit Agent + Sber STT + Silero TTS + any OpenAI-compatible LLM. Inbound/outbound calls over SIP with real-time speech recognition and synthesis.

GitHub

README

<p align="center">
  <img src="https://img.shields.io/badge/python-3.11-blue?logo=python" alt="Python">
  <img src="https://img.shields.io/badge/Silero%20TTS-v1.0-green" alt="Silero TTS">
  <img src="https://img.shields.io/badge/LiveKit%20Agent-v1.6-purple?logo=livekit" alt="LiveKit">
  <img src="https://img.shields.io/badge/Sber%20STT-SaluteSpeech-blueviolet" alt="Sber STT">
  <img src="https://img.shields.io/badge/OpenClaw-plugin-orange" alt="OpenClaw Plugin">
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License"></a>
</p>

# LiveKit Voice Agent β€” Voice Channel for OpenClaw AI

**A voice AI assistant that answers calls over SIP and speaks back using Sber Voice (STT/TTS) and any OpenAI-compatible LLM. Works perfectly as a real-time voice channel for [OpenClaw](https://github.com/vstan1986/openclaw) AI agents via OpenClaw's OpenAI-compatible API.**

Callers speak to the assistant over a regular phone line (SIP). The assistant transcribes their speech with Sber STT, generates a response with any OpenAI-compatible LLM (including OpenClaw's built-in API), and speaks it back using Silero TTS β€” all in real time.

```
Caller ◄──SIP──► LiveKit SIP Trunk ◄──WebRTC──► Agent (LiveKit SDK)
                                                    β”‚
                                           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”
                                           β–Ό        β–Ό        β–Ό
                                        Sber STT  LLM   Silero TTS
                                                    β”‚
                                           OpenClaw β”‚ OpenAI-compatible
                                           AI Agent β”‚ (Ollama, GPT, ...)
```

## ✨ Features

- **πŸ€– OpenClaw AI voice channel** β€” use any [OpenClaw](https://github.com/vstan1986/openclaw) agent as the LLM brain via its OpenAI-compatible API
- **πŸ“ž SIP telephony** β€” inbound + outbound calls via LiveKit SIP Trunk
- **πŸŽ™οΈ Sber SaluteSpeech STT** β€” gRPC streaming speech recognition (Russian language)
- **πŸ—£οΈ Silero TTS** β€” self-hosted neural text-to-speech (HTTP microservice)
- **🧠 Any LLM** β€” OpenAI-compatible API (OpenClaw, Ollama, GPT, Claude, etc.)
- **πŸ”‡ No VAD needed** β€” server-side endpointing via Sber's EOU detection
- **πŸ›‘οΈ Confirmation phrases** β€” instant "one moment" playback while LLM thinks (no silence gaps)
- **πŸ”Œ Modular services** β€” STT, TTS, auth all run as separate microservices
- **🐳 Docker Compose** β€” single `up -d` to start everything
- **⚠️ Resilience** β€” configurable retry, apology playback on LLM errors, silence timeout β†’ hangup

## πŸ“¦ Quick Start

```bash
# 1. Copy and fill in environment
cp .env.example .env

# 2. Start all services
docker compose up -d
```

### Required Environment Variables

| Variable | Description |
|----------|-------------|
| `LIVEKIT_URL` | `ws://<host>:7880` |
| `LIVEKIT_API_KEY` | From `livekit.yaml` |
| `LIVEKIT_API_SECRET` | From `livekit.yaml` |
| `EXTERNAL_IP` | Server public IP |
| `LLM_BASE_URL` | OpenAI-compatible LLM endpoint (e.g. `http://openclaw:8080/v1` for OpenClaw) |
| `LLM_API_KEY` | LLM API key (use `ollama` for Ollama, `openclaw` for OpenClaw) |
| `SBER_CLIENT_ID` | Sber RCE key Client ID |
| `SBER_CLIENT_SECRET` | Sber RCE key secret (base64) |
| `SIP_OUTBOUND_TRUNK_ID` | LiveKit outbound trunk ID (for outbound calls) |

See [`.env.example`](.env.example) for the full list.

## 🧠 How It Works

### Agent Architecture

The LiveKit agent runs in **two modes**, determined automatically from dispatch metadata:

|      Mode    |                  Trigger               |               Behaviour              |
|--------------|----------------------------------------|--------------------------------------|
| **inbound**  | metadata has no `phone_number`         | Agent waits for an incoming SIP call |
| **outbound** | metadata has `"phone_number": "+7..."` | Agent dials out and starts speaking  |

### Call Flow

```
 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
 β”‚  Caller  β”‚ SIP β”‚ LiveKit SIP  β”‚ WS  β”‚  Agent    β”‚ gRPC β”‚ Sber STT β”‚     β”‚   LLM     β”‚
 β”‚          │────►│   Trunk      │────►│(LiveKit   │─────►│(Salute-  β”‚     β”‚(OpenClaw /β”‚
 β”‚          β”‚     β”‚              β”‚     β”‚  SDK)     β”‚      β”‚ Speech)  β”‚     β”‚ Ollama /  β”‚
 β”‚          │◄────│              │◄────│           │◄─────│          β”‚     β”‚  GPT)     β”‚
 β”‚          β”‚ SIP β”‚              β”‚ WS  β”‚           β”‚ HTTP β”‚          β”‚     β”‚           β”‚
 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚           β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚           │◄────── HTTP ─────────┐
                                       β”‚           β”‚                      β”‚
                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                                              β”‚     Silero TTS        β”‚
                                                              β”‚    (tts-service)      β”‚
                                                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

1. **Call arrives** β€” SIP provider rings LiveKit SIP Trunk
2. **Agent joins** β€” LiveKit dispatches the call to the agent
3. **Listening** β€” agent opens a gRPC stream to Sber STT and listens for speech
4. **User speaks** β€” audio is streamed to Sber, which detects end-of-utterance (EOU)
5. **Confirmation** β€” agent instantly plays a short "one moment" phrase via Silero TTS
6. **LLM turn** β€” transcript is sent to the LLM (OpenClaw, Ollama, GPT, etc.); the response is streamed back
7. **Response spoken** β€” LLM text is synthesised by Silero TTS and played to the caller
8. **Loop** β€” agent returns to listening state for the next turn

### Turn-taking (Strict FSM)

Turn handling is strict β€” no overlap between user and agent speech:

- `allow_interruptions=False` β€” Sber transcripts received during agent TTS are ignored
- `discard_audio_if_uninterruptible=False` β€” no audio filtering, Sber decides
- Server-side endpointing via Sber's EOU signal, no client-side VAD

## πŸ—ΊοΈ Service Map

| Service | Container | Role |
|---------|-----------|------|
| **livekit** | `livekit` | WebRTC SFU (signalling + media), v1.12 |
| **redis** | `redis` | LiveKit coordination |
| **lk-tts** | `tts-service` | Silero TTS HTTP microservice |
| **lk-auth** | `auth-service` | Sber OAuth 2.0 token management |
| **lk-inbound** | `agent` | LiveKit Agent β€” SIP ↔ LLM orchestration |

## πŸ”§ LiveKit SIP Setup

### 1. Inbound trunk β€” receive calls from SIP provider

```bash
lk sip inbound create inbound-trunk.json
lk sip inbound list   # save trunk_id
```

### 2. Outbound trunk β€” outbound calls

```bash
MANGO_PASSWORD=$MANGO_PASSWORD lk sip outbound create outbound-trunk.json
lk sip outbound list   # save trunk_id β†’ SIP_OUTBOUND_TRUNK_ID
```

### 3. Dispatch rule β€” route inbound call to agent

```bash
lk sip dispatch create dispatch-rule.json
```

### 4. Outbound call (via agent)

```bash
lk dispatch create \
  --new-room \
  --agent-name sber-voice-assistant \
  --metadata '{"phone_number": "+71234567890"}'
```

## πŸ“Š Architecture

```
livekit-agent/
β”œβ”€β”€ my_agent/                # LiveKit agent package
β”‚   β”œβ”€β”€ session.py           # CallSession β€” turn orchestration
β”‚   β”œβ”€β”€ plugin_stt.py        # WebSocket STT plugin (β†’ Sber)
β”‚   β”œβ”€β”€ plugin_tts.py        # HTTP TTS plugin (β†’ Silero)
β”‚   β”œβ”€β”€ plugin_tts_transforms.py  # Text transforms (digits β†’ words)
β”‚   β”œβ”€β”€ sentence_splitter.py # Aggressive sentence tokenizer for TTS
β”‚   β”œβ”€β”€ http_api.py          # FastAPI (/call, /hangup)
β”‚   └── config.py            # Centralised configuration
β”œβ”€β”€ stt_service/             # STT microservice
β”‚   β”œβ”€β”€ server.py            # HTTP/WebSocket entrypoint
β”‚   β”œβ”€β”€ sber_stt.py          # gRPC streaming client (Sber v2)
β”‚   └── token_manager.py     # Sber OAuth 2.0 token management
β”œβ”€β”€ tts_service/             # TTS microservice
β”‚   β”œβ”€β”€ server.py            # HTTP entrypoint
β”‚   β”œβ”€β”€ tts_engine.py        # Silero TTS wrapper
β”‚   └── translit.py          # Latin β†’ Cyrillic transliteration
└── tests/                   # Pytest suite (52 tests)
```

## 🀝 Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md).

## πŸ”’ Security

See [SECURITY.md](SECURITY.md) for our security policy and vulnerability reporting process.

## πŸ“„ License

This project is licensed under the **MIT License** β€” see the [LICENSE](LICENSE) file for details.

### Third-Party Licenses

This project uses several open-source components with different licenses.
See [NOTICE.md](NOTICE.md) for full attribution and license information,
including:

- **Apache 2.0** β€” livekit-agents, livekit-plugins-openai, requests, grpcio, protobuf
- **LGPL** β€” num2words
- **CC BY-NC-SA 4.0** β€” Silero TTS model weights (non-commercial use only)
voice

Comments

Sign in to leave a comment

Loading comments...