Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,203 changes: 2,203 additions & 0 deletions 0001-silma-tts-plugin.patch

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions livekit-agents/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ runway = ["livekit-plugins-runway>=1.8.0"]
rtzr = ["livekit-plugins-rtzr>=1.8.0"]
sarvam = ["livekit-plugins-sarvam>=1.8.0"]
silero = ["livekit-plugins-silero>=1.8.0"]
silma = ["livekit-plugins-silma>=1.8.0"]
simli = ["livekit-plugins-simli>=1.8.0"]
smallestai = ["livekit-plugins-smallestai>=1.8.0"]
simplismart = ["livekit-plugins-simplismart>=1.8.0"]
Expand Down
105 changes: 105 additions & 0 deletions livekit-plugins/livekit-plugins-silma/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# SILMA AI plugin for LiveKit Agents

Support for Arabic and English speech synthesis with [SILMA AI](https://silma.ai/)
TTS v2 — Modern Standard Arabic, the Saudi (Najdi) dialect, and English.

More information is available in the docs for the
[TTS integration](https://docs.livekit.io/agents/models/tts/plugins/silma/).

## Installation

```bash
pip install livekit-plugins-silma
```

Or with the LiveKit Agents extra:

```bash
uv add "livekit-agents[silma]"
```

## Pre-requisites

You'll need an API key from [SILMA](https://app.silma.ai/api-keys). Set it as an
environment variable:

```bash
export SILMA_API_KEY="..."
```

## Usage

```python
from livekit.agents import AgentSession
from livekit.plugins import silma

session = AgentSession(
tts=silma.TTS(
model="silma-tts-v2-msa",
voice="sarah",
),
# ... stt, llm, vad
)
```

### Models and voices

| Model | Language | Voices |
| --- | --- | --- |
| `silma-tts-v2-english` | English | `james`, `emma` |
| `silma-tts-v2-msa` | Modern Standard Arabic | `sarah`, `salma`, `salwa`, `saja`, `sultan`, `salman`, `sulaiman`, `salim` |
| `silma-tts-v2-ksa` | Arabic, Saudi (Najdi) dialect | same as MSA |

### Cloned voices

Upload a voice under **Custom Voices** at https://app.silma.ai/voices and pass
its id along with your user id:

```python
silma.TTS(
model="silma-tts-v2-ksa",
voice="sarah",
user_id="...",
custom_audio_id="voice_1769817467123",
)
```

### Pronunciation hints

SILMA reads phone numbers, emails and links correctly when they are tagged in
the text:

```python
await session.say(
"You can reach us on <STAG_PN>92005455</STAG_PN> or at <STAG_EMAIL>hi@silma.ai</STAG_EMAIL>."
)
```

The plugin keeps these tags intact when it splits text, so a tag is never cut in
half across two requests.

Account-level pronunciation overrides configured at https://app.silma.ai/control
are applied when you pass `user_id` and
`enable_server_pronunciation_overrides=True`.

## How it works

`stream()` uses the realtime WebSocket API (`wss://api.silma.ai/tts/v2/ws/stream`)
and sentence-tokenizes incoming LLM text so each request is a complete
utterance. `synthesize()` uses the binary streaming endpoint
(`POST /stream`).

SILMA caps `text` at 250 characters per request, so longer input is split on
word boundaries and sent as sequential requests concatenated into one audio
segment.

SILMA returns a 24 kHz mono float32 waveform; the plugin converts it to 16-bit
PCM for the agent pipeline.

Requests carry a `User-Agent` of
`LiveKit-Agents-SILMA/<plugin version> livekit-agents/<framework version>` on
both transports, so SILMA can tell plugin traffic apart from direct API use.

WebSocket connections are pooled and reused across turns. If a pooled
connection has gone stale, the plugin reconnects once and replays the utterance,
which is safe because no audio has reached the caller at that point.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2026 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""SILMA AI plugin for LiveKit Agents

Arabic and English text-to-speech with SILMA TTS v2.

See https://silma.ai/ for more information.
"""

from .models import (
ARABIC_VOICES,
DEFAULT_MODEL,
DEFAULT_VOICE,
ENGLISH_VOICES,
TTSArabicVoices,
TTSEnglishVoices,
TTSModels,
TTSVoices,
)
from .tts import TTS, ChunkedStream, SynthesizeStream
from .version import __version__

__all__ = [
"TTS",
"ChunkedStream",
"SynthesizeStream",
"TTSModels",
"TTSVoices",
"TTSArabicVoices",
"TTSEnglishVoices",
"ARABIC_VOICES",
"ENGLISH_VOICES",
"DEFAULT_MODEL",
"DEFAULT_VOICE",
"__version__",
]

from livekit.agents import Plugin

from .log import logger


class SilmaPlugin(Plugin):
def __init__(self) -> None:
super().__init__(__name__, __version__, __package__, logger)


Plugin.register_plugin(SilmaPlugin())

# Cleanup docs of unexported modules
_module = dir()
NOT_IN_ALL = [m for m in _module if m not in __all__]

__pdoc__ = {}

for n in NOT_IN_ALL:
__pdoc__[n] = False
Loading