Skip to main content

vox-rtc-server

Trusted Python SDK for Vox-hosted WebRTC conversations. It creates sessions over HTTP and controls them over PondSocket.

Install

pip install vox-rtc-server

Pass api_key=... or set VOX_API_KEY.

Browser WebSocket gateway

VoxRtcGateway is a dependency-free ASGI application compatible with @eleven-am/vox-rtc-client. Mount it at the same-origin path used by the browser client:

import os

from vox_rtc_server import VoxRtcGateway

app = VoxRtcGateway(
    http_base="http://vox-service.vox.svc.cluster.local:11435",
    api_key=os.environ.get("VOX_API_KEY"),
    path="/api/vox/rtc",
)

Serve app with Uvicorn, Hypercorn, Daphne, or mount it in a Starlette/FastAPI application. Each browser WebSocket creates and owns one controlled Vox session. The socket carries SDP, trickle ICE, and events for the lifetime of the call; microphone and assistant audio travel directly over WebRTC.

Generated offer and candidate generation values, including the null end-of-candidates marker, are preserved exactly. The gateway accepts legacy generation-less negotiation until a generated offer is received. It never returns the Vox API key or internal Vox address to the browser.

Use on_session_created, on_session_closed, and on_error for application ownership and cleanup. Hooks may be synchronous or asynchronous. ASGI lifespan shutdown closes active sessions and disconnects the shared control client.

PondSocket session

import asyncio
import os

from vox_rtc_server import ClientEventEnvelope, SessionConfig, VoxRtcServerClient


async def main() -> None:
    client = VoxRtcServerClient(
        http_base="http://vox-service.vox.svc.cluster.local:11435",
        api_key=os.environ.get("VOX_API_KEY"),
    )
    bootstrap, session = await client.create_controlled_session()
    session.on_transcript(
        lambda event: print("user said:", event.transcript, event.speech_context)
    )
    session.on_browser_event(lambda event: print(event.event, event.payload))
    session.configure(SessionConfig(
        stt_model="parakeet-stt:tdt-0.6b-v3",
        tts_model="kokoro-tts:v1.0",
        voice="af_heart",
        turn_profile="browser_default",
        speech_context=True,
    ))
    session.send_text_response("Hello from Python.")
    session.send_client_event(ClientEventEnvelope(event="render.ready", payload=True))
    print("session:", bootstrap.session_id)


asyncio.run(main())

Speech context is opt-in and final-only. When enabled, the final TranscriptEvent.speech_context is a typed SpeechContext; otherwise it is None.

Schema v2 exposes timestamped emotions and vocal speaker spans plus environmental sounds. Sound spans also carry a score from 0 to 1:

def handle_transcript(event: TranscriptEvent) -> None:
    context = event.speech_context
    if context is None:
        return

    for span in context.emotions or []:
        print("emotion", span.label, span.start_ms, span.end_ms)
    for span in context.vocal or []:
        print("vocal event", span.label, span.start_ms, span.end_ms)
    for sound in context.sounds or []:
        print("environment", sound.label, sound.score)

    if context.status != "complete":
        print("unavailable tracks", context.unavailable)

A partial result identifies the unavailable "speaker" or "sounds" track; a failed result identifies both. Unsupported or malformed context is decoded as None without dropping the transcript event.

Acknowledged response starts

start_response stays fire-and-forget. When you want the positive acknowledgement before pumping deltas, use start_response_and_wait, which correlates the response.created event (or the typed error) with the generation_id it sent:

from vox_rtc_server import ResponseOptions, ResponseOutputOptions

ack = await session.start_response_and_wait(
    ResponseOptions(
        output=ResponseOutputOptions(
            model="qwen3-tts:0.6b-clone",
            voice="samantha",
            language="fr",
            speed=0.9,
            params={"temperature": 0.7},
        )
    )
)
if ack.accepted:
    print("effective output:", ack.output)
    session.append_response_text("Hello.")
    session.commit_response()
else:
    print("start rejected:", ack.error.code if ack.error else None)

You can also thread your own generation id through every response command via ResponseOptions(generation_id="gen-42"); response lifecycle events (ResponseEvent, InterruptionEvent) expose the echoed generation_id. The response-scoped output is optional. Vox fills omitted fields from the session configuration and echoes the immutable effective selection on the acknowledgement and ResponseEvent.

Error handling

ErrorEvent carries code (stable slug), recoverable, and an optional generation_id scoping the failure to one response generation. Known codes are exported as ERROR_CODE_* constants (response_rejected_turn_state, response_rejected_user_speech, response_stale_generation, response_already_active, response_failed, command_invalid, session_failed).

Only recoverable is False (or the transport itself closing) should end the call. Recoverable errors are per-command failures: handle them and keep the session running. Old Vox servers omit code and recoverable; the SDK then defaults recoverable to True, so treat such errors as recoverable unless the transport closed.

on_signaling_error surfaces the rtc.signaling_error control event (WebRTC signaling failures such as a rejected local description) as a SignalingErrorEvent carrying message and a numeric generation. This event is terminal: Vox closes the session immediately after emitting it, so there is no recoverable field — treat it as the end of the call, not a per-command error like the conversation error stream.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vox_rtc_server-0.2.6.tar.gz (53.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vox_rtc_server-0.2.6-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file vox_rtc_server-0.2.6.tar.gz.

File metadata

  • Download URL: vox_rtc_server-0.2.6.tar.gz
  • Upload date:
  • Size: 53.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for vox_rtc_server-0.2.6.tar.gz
Algorithm Hash digest
SHA256 ede08b3cd68f838f44ef11ed45d19d464f33c32c6d2e026da601999afde33990
MD5 3f2ae789363b8c0d1a7206039de07871
BLAKE2b-256 ff4fa8358d0147b6cc66ea8aafac4a4f7088dc055832b917f9dbfd004adb49a7

See more details on using hashes here.

File details

Details for the file vox_rtc_server-0.2.6-py3-none-any.whl.

File metadata

  • Download URL: vox_rtc_server-0.2.6-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.0

File hashes

Hashes for vox_rtc_server-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 a06fb4aed20a84cf88cab1c20b2fb951b6d5b57e85a06b696fb134599c196ba9
MD5 5e273e6f51e0295f4c3f3ec131c531a7
BLAKE2b-256 ffb282a93dd1f50f60b299fa3c9877788ec604d190ea1c1e6543595bf4eeb33f

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.6 This release

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page