Voice agents LiveKit

LiveKit

The livekit-plugins-munsit package brings both halves of an Arabic voice agent to LiveKit Agents: munsit.STT for speech recognition — optimized for Arabic, with Arabic/English code-switching through the munsit-en-ar model — and munsit.TTS for natural Arabic speech, streamed so the agent starts talking on the first audio chunk.

1

Install & authenticate

You need Python 3.10+, a Munsit account, and a LiveKit Cloud account or self-hosted LiveKit server. Generate a key at Munsit — API Keys, then install the plugin.

shell
pip install livekit-plugins-munsit

Both munsit.STT and munsit.TTS read MUNSIT_API_KEY automatically. Put it in a .env.local alongside your LiveKit and LLM credentials:

.env.local
# Munsit — speech-to-text and text-to-speech MUNSIT_API_KEY=your_MUNSIT_API_KEY_here # LiveKit Configuration LIVEKIT_URL=wss://your-livekit-server.com LIVEKIT_API_KEY=your_livekit_api_key LIVEKIT_API_SECRET=your_livekit_api_secret # LLM Configuration (for the agent's brain) OPENAI_API_KEY=your_openai_api_key
Your API key is only shown once. Save it securely. Keep it in .env.local (never committed to Git), use environment variables in production, and rotate keys periodically.
2

Quick start

Create arabic_agent.py. Munsit handles both ends of the loop, so the caller is transcribed and answered in the same Arabic register — no second vendor in the pipeline. The Silero thresholds below are tuned for real-world microphones, where post-echo-cancellation audio from the agent's own speaker can otherwise be misinterpreted as user speech.

arabic_agent.py
""" Arabic voice assistant — Munsit STT + Munsit TTS """ from dotenv import load_dotenv from livekit import agents from livekit.agents import Agent, AgentServer, AgentSession from livekit.plugins import munsit, openai, silero load_dotenv(".env.local") class ArabicAssistant(Agent): """Arabic-speaking voice assistant""" def __init__(self) -> None: super().__init__( instructions="""أنت مساعد صوتي ذكي يتحدث العربية بطلاقة. مهمتك مساعدة المستخدمين بالإجابة على أسئلتهم بطريقة واضحة ومفيدة. كن ودوداً ومحترماً في تعاملك.""" ) server = AgentServer() @server.rtc_session() async def my_agent(ctx: agents.JobContext): session = AgentSession( stt=munsit.STT(model="munsit-en-ar"), # Arabic + English code-switch llm=openai.LLM(model="gpt-4o", temperature=0.7), tts=munsit.TTS( voice_id="ar-uae-male-1", # Copy IDs from the Voice Library model="faseeh-v1-preview", stability=0.75, speed=1.0, ), vad=silero.VAD.load(activation_threshold=0.6, min_speech_duration=0.3), ) await session.start(room=ctx.room, agent=ArabicAssistant()) await session.generate_reply( instructions="رحب بالمستخدم باللغة العربية وقدم نفسك كمساعد ذكي جاهز للمساعدة." ) if __name__ == "__main__": agents.cli.run_app(server)

Run it in dev mode to start a local LiveKit server, launch the agent, and get a test URL to open in your browser — allow microphone access, speak Arabic, and the agent answers in a Munsit voice. Use start for deployment against your LiveKit Cloud or self-hosted server.

development
python arabic_agent.py dev
Tip: use console mode while developing — it runs locally with your microphone and prints transcripts to stdout, no LiveKit server required. Fastest debugging loop there is.
3

Speech-to-text

munsit.STT() uses streaming mode by default: it keeps a live WebSocket to Munsit, emits interim transcripts while the caller speaks, and finalizes each turn with word-level timestamps the moment the server's turn detection fires — the right choice for voice agents. Pass mode="batch" to instead buffer each utterance and POST it as one request.

ModeWhen to useEndpointLatencyWord timestampsInterim events
streaming (default)Voice agents and live captions: first partials in ~0.7 s (median), server-side turn detection.WS /api/v1/listen~0.7 s median to first partial; final ~1.2 s after speech ends at the default endpointingYes, on final transcripts, plus an utterance-level confidence scoreYes, through INTERIM_TRANSCRIPT events
batchTranscribing recorded audio; pipelines where one request per utterance is preferred.POST /api/v1/audio/transcribeVAD detection + upload + server processing (~1–2 s for short utterances)Yes, populated on SpeechData.wordsNo
python
from livekit.plugins import munsit streaming_stt = munsit.STT() batch_stt = munsit.STT(mode="batch")
STT.recognize(audio_buffer) always uses the batch HTTP endpoint, even when mode="streaming" is configured.

Pick the model by input language — and pin it explicitly (model="munsit" or model="munsit-en-ar") so a future plugin default change never affects your agent.

ModelUse case
munsit-en-arMixed Arabic-English speech with code-switching. This is the plugin default.
munsitPure Arabic speech recognition (fastest, and the only model that supports custom vocabulary).

Every constructor parameter on munsit.STT():

ParameterDefaultDescription
modestreamingstreaming for the live WebSocket or batch for HTTP transcription.
modelmunsit-en-armunsit-en-ar (Arabic-English code-switching) or munsit (pure Arabic). Applies to both modes — streaming routes the session to the matching live engine.
api_keyenv MUNSIT_API_KEYMunsit API key.
base_urlMunsit production WebSocket URLOverride the streaming WebSocket URL.
batch_base_urlMunsit production HTTPS URLOverride the batch HTTP URL.
auth_methodheaderAuthentication style: header, bearer, or query.
sample_rate16000Input rate hint. Streaming negotiates 8000/16000 with the server and resamples anything else automatically (LiveKit tracks are typically 48000).
num_channels1Number of audio channels.
interim_resultsTrueEmits interim transcripts in streaming mode.
endpointing_ms800Server-side silence window that ends a turn (100–5000). Retunable mid-session via ListenStream.configure_endpointing().
smart_turnTrueGate end-of-turn on the server's semantic turn-completion model in addition to silence.
hotwordsNoneCustom vocabulary: rare terms or phrases (≤200 entries, ≤40 chars each). Works in both modes on munsit; ignored, with a warning, on munsit-en-ar.
correlation_idNoneSession identifier echoed on every streaming event (≤128 chars).
return_confidenceFalseBatch only: include per-word confidence in timestamps.
on_enrichmentNoneCallback receiving raw per-turn Sentiment / Gender event dicts from streaming.
languageNoneLabel attached to SpeechData.language; defaults to ar.
http_sessionNoneCustom aiohttp.ClientSession.
extra_query_paramsNoneExtra query params for the streaming WebSocket endpoint.

Munsit accepts the API key in three places — all three work on both the batch HTTP endpoint and the streaming WebSocket. Pick whichever fits your deployment:

python
# Default: sends the key as the x-api-key header. munsit.STT(auth_method="header") # Authorization: Bearer <key> munsit.STT(auth_method="bearer") # Query parameter (?token=<key>): useful when an upstream proxy strips headers. munsit.STT(auth_method="query")
The 0.3.x streaming parameters are deprecated. endpointing="server_diff"/"client_vad", finalize_after_silence_ms, energy_filter and vad_silence_ms are superseded by server-side turn detection on /api/v1/listen. Each is still accepted with a DeprecationWarning, and finalize_after_silence_ms maps onto endpointing_ms.
4

Text-to-speech

Everything tunable on munsit.TTS(). Streaming is always on — the agent starts speaking as soon as the first audio chunk is ready. Pick a voice from the Voice Library: listen, then click "Copy Voice ID" next to the one you want.

ParameterValuesWhat it does
voice_ide.g. ar-uae-male-1The Arabic voice to synthesize with. Copy IDs from the Voice Library.
modelfaseeh-v1-previewHigh-quality Arabic voice synthesis.
stability0.01.0Voice consistency. 0.0–0.4 more expressive, creative, but can hallucinate · 0.5–0.7 balanced (recommended) · 0.8–1.0 very consistent, less variation.
speed0.71.2Speech rate. 0.7–0.9 slower (clearer for complex content) · 1.0 normal (default) · 1.1–1.2 faster (quick responses).
sample_rate800048000Output PCM rate requested from the API. Defaults to 48000 — the engine's native rate, so audio skips downsampling and needs no resampling for WebRTC. Lower it only for a narrowband transport such as telephony.
dialecte.g. emiratiOptional pronunciation hint for the selected voice.

Rules of thumb for stability: chatbots 0.6–0.8, professional applications 0.8–1.0, creative content 0.3–0.5. You can also change voice settings at runtime with update_options:

python
# Start with one voice tts = munsit.TTS(voice_id="ar-uae-male-1", speed=1.0) # Switch to a different voice based on context tts.update_options( voice_id="ar-hijazi-female-2", stability=0.8, speed=1.0 )
5

Endpointing & recognize()

In streaming mode the server decides when a turn ends: it waits for endpointing_ms of silence, and with smart_turn enabled a semantic turn-completion model must also agree the speaker is finished — so a caller who pauses mid-thought isn't cut off. A turn always ends after 2× the silence window regardless. Both are tunable at construction, and the silence window can be retuned mid-session.

streaming endpointing
# Snappier finals: shorter silence window, semantic gating still on. stt = munsit.STT( mode="streaming", endpointing_ms=300, smart_turn=True, ) # Retune mid-session on the live stream (100–5000 ms). stream = stt.stream() await stream.configure_endpointing(800)

Use recognize directly when you have a recorded audio buffer — a voicemail or uploaded file — and don't need a live stream. It always hits the batch HTTP endpoint regardless of mode and returns a transcript with word-level timestamps.

6

Tracking turn metrics

Each conversation turn carries timing data on its ChatMessage. Subscribe to conversation_item_added to read transcription delay, end-of-turn delay, and downstream LLM/TTS metrics. All values are reported in seconds.

python
from livekit.agents import ChatMessage @session.on("conversation_item_added") def on_item(event): msg = event.item if not isinstance(msg, ChatMessage): return metrics = msg.metrics or {} if msg.role == "user": transcription_delay = metrics.get("transcription_delay") end_of_turn_delay = metrics.get("end_of_turn_delay") if transcription_delay is not None: print(f"STT delay: {transcription_delay * 1000:.0f} ms") if end_of_turn_delay is not None: print(f"EOU delay: {end_of_turn_delay * 1000:.0f} ms") elif msg.role == "assistant": llm_ttft = metrics.get("llm_node_ttft") tts_ttfb = metrics.get("tts_node_ttfb") if llm_ttft: print(f"LLM TTFT: {llm_ttft * 1000:.0f} ms") if tts_ttfb: print(f"TTS TTFB: {tts_ttfb * 1000:.0f} ms")
The previous metrics_collected event is deprecated. New integrations should use conversation_item_added and read metrics from ChatMessage.metrics.
7

Troubleshooting

The failure modes we see most, and the fix for each.

SymptomFix
"Invalid API Key"Verify MUNSIT_API_KEY is available to the process running your agent: echo $MUNSIT_API_KEY.
"Payment Required"Your Munsit account balance is low. Top up at app.munsit.com.
"Rate Limit Exceeded"Too many requests. Implement rate limiting or contact Munsit support to raise your limits.
No final transcriptBatch mode finalizes after LiveKit signals end-of-speech — make sure your AgentSession includes VAD: silero.VAD.load(activation_threshold=0.6, min_speech_duration=0.3).
Microphone feedback loopThe agent transcribes its own TTS playback: residual audio leaks through the mic after echo cancellation, and munsit-en-ar is more sensitive to low-energy input than munsit. Tighten the VAD as above; if it persists, switch temporarily to model="munsit" to confirm it's model-specific, or run with headphones.
Need live captionsUse munsit.STT(mode="streaming", interim_results=True) when your UI needs transcript updates before the speaker finishes.
No audio outputCheck: API key is valid, network is stable, microphone permissions granted, browser supports WebRTC, firewall allows WebSocket connections.
Poor audio qualityRaise stability to 0.8+, and check your network and audio codec support.
8

Where next

Go under the hood, or wire Munsit into a different agent framework.

Working with an AI assistant? Every page is available as Markdown: add .md to the URL, or send an Accept: text/markdown header. For the whole documentation in one request, point it at llms-full.txt; the page index is llms.txt. Or use Copy Page, top right.