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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Bootstrap poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1
Expand All @@ -26,7 +26,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Bootstrap poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1
Expand All @@ -48,7 +48,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Bootstrap poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Bootstrap poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1
Expand All @@ -26,7 +26,7 @@ jobs:
- name: Set up python
uses: actions/setup-python@v4
with:
python-version: 3.8
python-version: 3.9
- name: Bootstrap poetry
run: |
curl -sSL https://install.python-poetry.org | python - -y --version 1.5.1
Expand Down
100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,106 @@ client_tools.register("calculate_sum", calculate_sum, is_async=False)
client_tools.register("fetch_data", fetch_data, is_async=True)
```

### WebRTC Support

ElevenLabs Python SDK supports WebRTC connections for real-time, low-latency conversations using LiveKit infrastructure. WebRTC provides better audio quality, lower latency, and improved connectivity compared to traditional WebSocket connections.

#### Key Benefits
- **Lower Latency**: Direct peer-to-peer audio streaming
- **Better Audio Quality**: Optimized for real-time audio
- **Improved Connectivity**: NAT traversal and firewall handling
- **Adaptive Bitrate**: Automatic quality adjustment based on network conditions

#### Basic WebRTC Usage

```python
import asyncio
from elevenlabs import ElevenLabs
from elevenlabs.conversational_ai.conversation_factory import create_webrtc_conversation
from elevenlabs.conversational_ai.conversation import AsyncAudioInterface

class SimpleAsyncAudioInterface(AsyncAudioInterface):
async def start(self, input_callback):
print("Audio interface started")
self.input_callback = input_callback

async def stop(self):
print("Audio interface stopped")

async def output(self, audio: bytes):
print(f"Received audio: {len(audio)} bytes")

async def interrupt(self):
print("Audio interrupted")

async def main():
client = ElevenLabs(api_key="YOUR_API_KEY")
audio_interface = SimpleAsyncAudioInterface()

# WebRTC conversation with automatic token fetching
conversation = create_webrtc_conversation(
client=client,
agent_id="your-agent-id",
audio_interface=audio_interface,
)

await conversation.start_session()
await conversation.send_user_message("Hello!")
await asyncio.sleep(10)
await conversation.end_session()

asyncio.run(main())
```

#### Connection Type Comparison

```python
from elevenlabs.conversational_ai.conversation_factory import create_conversation
from elevenlabs.conversational_ai.base_connection import ConnectionType

# WebSocket (existing)
ws_conversation = create_conversation(
client=client,
agent_id="your-agent-id",
connection_type=ConnectionType.WEBSOCKET,
# Uses sync AudioInterface
)

# WebRTC (new)
webrtc_conversation = create_conversation(
client=client,
agent_id="your-agent-id",
connection_type=ConnectionType.WEBRTC,
audio_interface=AsyncAudioInterface(), # Async interface required
)
```

#### Authentication Methods

WebRTC conversations support multiple authentication approaches:

1. **Automatic Token Fetching**: Provide only `agent_id` and the SDK fetches the conversation token automatically
2. **Explicit Token**: Provide both `agent_id` and `conversation_token` for manual token management

```python
# Automatic token fetching (recommended)
conversation = create_webrtc_conversation(
client=client,
agent_id="your-agent-id",
audio_interface=audio_interface
)

# Explicit token
conversation = create_webrtc_conversation(
client=client,
agent_id="your-agent-id",
conversation_token="your-conversation-token",
audio_interface=audio_interface
)
```

**Requirements**: WebRTC conversations require the `livekit` dependency (`pip install livekit`), which is automatically installed with the SDK. All WebRTC conversations must use `AsyncAudioInterface` implementations.

## Languages Supported

Explore [all models & languages](https://elevenlabs.io/docs/models).
Expand Down
117 changes: 116 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pydantic-core = ">=2.18.2"
requests = ">=2.20"
typing_extensions = ">= 4.0.0"
websockets = ">=11.0"
livekit = { version = "^1.0.13", python = ">=3.9" }

[tool.poetry.group.dev.dependencies]
mypy = "==1.13.0"
Expand Down
75 changes: 75 additions & 0 deletions src/elevenlabs/conversational_ai/base_connection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
from abc import ABC, abstractmethod
import asyncio
import json
from typing import Callable, Optional, Awaitable, Union, Any, Literal, Dict
from enum import Enum


class ConnectionType(str, Enum):
"""Connection types available for conversations."""
WEBSOCKET = "websocket"
WEBRTC = "webrtc"


class BaseConnection(ABC):
"""Base class for conversation connections."""

def __init__(self) -> None:
self.conversation_id: Optional[str] = None
self._message_queue: list[dict] = []
self._on_message_callback: Optional[Callable[[dict], Union[None, Awaitable[None]]]] = None

@abstractmethod
async def connect(self) -> None:
"""Establish the connection."""
pass

@abstractmethod
async def close(self) -> None:
"""Close the connection."""
pass

@abstractmethod
async def send_message(self, message: dict) -> None:
"""Send a message through the connection."""
pass

@abstractmethod
async def send_audio(self, audio_data: bytes) -> None:
"""Send audio data through the connection."""
pass

def send_message_sync(self, message: dict) -> None:
"""Send a message synchronously (for compatibility with sync code)."""
import asyncio
try:
# Try to get the current event loop
loop = asyncio.get_event_loop()
if loop.is_running():
# If loop is running, create a task
asyncio.create_task(self.send_message(message))
else:
# If loop is not running, run the coroutine
loop.run_until_complete(self.send_message(message))
except RuntimeError:
# No event loop, create new one
asyncio.run(self.send_message(message))

def on_message(self, callback: Callable[[dict], Union[None, Awaitable[None]]]) -> None:
"""Set the message callback."""
self._on_message_callback = callback
# Process any queued messages
if self._message_queue:
for message in self._message_queue:
self._handle_message(message)
self._message_queue.clear()

def _handle_message(self, message: dict) -> None:
"""Handle incoming messages."""
if self._on_message_callback:
if asyncio.iscoroutinefunction(self._on_message_callback):
asyncio.create_task(self._on_message_callback(message))
else:
self._on_message_callback(message)
else:
self._message_queue.append(message)
Loading