rich-monitor-70665
05/20/2026, 8:57 AMtall-airline-16756
05/20/2026, 8:57 AMtall-airline-16756
05/20/2026, 8:57 AMlanguage="multi") combined with dynamic TTS switching (e.g., Rime) so the agent speaks back in the detected language.
Which to choose depends on your goal:
• Do you need live translated captions for many target languages simultaneously, or translated audio responses?
• Do you need bidirectional spoken translation between two languages, or many-to-one/many-to-many translations?
• Do you need detection of mixed languages in a single stream?
Quick follow-up: which use case matches yours — live translated captions, translated audio output (TTS), bidirectional spoken translation, or language-detection + agent replies? I’ll give exact plugin config and example steps once you tell me.
Sources: Gladia STT plugin guide | LiveKit Documentation | Soniox STT plugin guide | LiveKit Documentation | Deepgram STT | LiveKit Documentation | Build a multilingual voice agent that automatically switches languages | Li..., with interactive elementsrich-monitor-70665
05/20/2026, 9:00 AMtall-airline-16756
05/20/2026, 9:01 AMlanguages (source) and translation_target_languages to get translated text streams. Good for live translated captions or producing translated text for downstream TTS.
• Azure OpenAI STT plugin — use gpt-4o-transcribe (or similar) for transcription; then use the LLM to translate or rephrase into multiple languages. Useful when you want flexible, LLM-driven translation or complex post-processing.
Key setup notes:
• Both plugins are supported in Python only.
• Install the Azure extras: uv add "livekit-agents[azure]~=1.5" (Speech) or uv add "livekit-agents[openai]~=1.5" (OpenAI).
• Set environment variables for auth:
◦ Azure Speech: AZURE_SPEECH_KEY and AZURE_SPEECH_REGION
◦ Azure OpenAI: AZURE_OPENAI_API_KEY and AZURE_OPENAI_ENDPOINT
Next step question to narrow recommendation:
• Do you need translated captions (text), translated audio output (TTS), or live bidirectional spoken translation? Also tell me which target languages you need and whether you expect mixed-language audio in one stream. I’ll give the exact plugin config and example code. Sources: Azure Speech STT plugin guide | LiveKit Documentation | Speech-to-text (STT) models overview | LiveKit Documentation | Azure OpenAI STT plugin guide | LiveKit Documentation, with interactive elementsrich-monitor-70665
05/20/2026, 9:03 AMtall-airline-16756
05/20/2026, 9:03 AMuv add "livekit-agents[azure]~=1.5"
• Set environment variables:
export AZURE_SPEECH_KEY=<your-speech-key>
export AZURE_SPEECH_REGION=<your-speech-region>
Minimal example (Python)
import os
from livekit.agents import AgentSession
from livekit.plugins.azure import STT as AzureSTT
# Dynamic inputs (could come from config/UI)
# Leave source_languages empty or set to ["auto"] if you want Azure to auto-detect.
source_languages = None # e.g., None or ["en","es","fr"]
target_languages = ["en","es","fr"] # languages to translate into
# Create Azure STT plugin configured for translation
stt_plugin = AzureSTT(
speech_key=os.environ["AZURE_SPEECH_KEY"],
speech_region=os.environ["AZURE_SPEECH_REGION"],
languages=source_languages, # source languages or None/auto
translation_target_languages=target_languages, # list of target languages
# other optional settings like profanity_filter, interim_results, etc.
)
# Create a minimal AgentSession that uses the STT plugin.
# The AgentSession wiring depends on your app; this is a conceptual minimal loop.
session = AgentSession(
stt_node=stt_plugin,
# leave other nodes default or add TTS/LLM nodes as needed
)
# Start session and handle incoming SpeechEvents
def handle_speech_event(event):
# Azure STT returns translations on the SpeechEvent
# Access raw transcription:
original_text = getattr(event, "text", None)
# Access translations per target language (field names may vary by plugin version)
translations = getattr(event, "translations", None) or getattr(event, "translation_texts", None)
print("Detected/source text:", original_text)
print("Translations:", translations)
# Example loop: receive events from session (pseudo)
for event in session.stream_events(): # replace with actual session event loop
if event.type == "speech":
handle_speech_event(event)
Notes and tips
• Use languages=None or Azure’s auto-detect option so the STT will detect the input language automatically; the plugin will surface detected language in the event.
• translation_target_languages accepts a list — include as many target languages as you need.
• If you need translated audio (TTS) output, use the translated text from the event and feed it to your TTS node (Rime or Azure TTS) per target language.
• This example is minimal/conceptual; adapt wiring to your AgentSession event loop and room/participant integration.
If you want, tell me which target languages and whether you need translated text only or translated audio output — I’ll give a concrete, runnable code sample wired to AgentSession events and TTS.
, with interactive elementsrich-monitor-70665
05/20/2026, 9:04 AMtall-airline-16756
05/20/2026, 9:04 AMlivekit-agents[azure]==1.5.*
python-dotenv
Environment (.env)
AZURE_SPEECH_KEY=your-azure-speech-key
AZURE_SPEECH_REGION=your-azure-region
LIVEKIT_API_KEY=<your-livekit-api-key-if-needed>
LIVEKIT_API_SECRET=<your-livekit-api-secret-if-needed>
agent.py
import os
import asyncio
import logging
from dotenv import load_dotenv
from livekit.agents import AgentSession, RoomContext, JobContext, cli
from livekit.plugins.azure import STT as AzureSTT
from livekit import rtc
load_dotenv()
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("azure-multilingual-agent")
# Minimal dynamic state holder for participant & targets
class DynamicTranslationAgent:
def __init__(self, session: AgentSession, initial_targets=None):
self.session = session
self.current_language = None
self.target_languages = initial_targets or ["en"]
self.participant = None
async def set_participant(self, participant: rtc.Participant):
self.participant = participant
# initialize attributes on participant
try:
await self.participant.set_attributes({
"current_language": self.current_language or "",
"target_languages": ",".join(self.target_languages),
})
except Exception as e:
logger.warning(f"Failed to set participant attrs: {e}")
async def update_target_languages(self, new_targets):
logger.info(f"Updating target languages: {new_targets}")
self.target_languages = new_targets
# Update STT plugin runtime options so translations target new languages
try:
self.session.stt.update_options(
translation_enabled=True,
translation_target_languages=new_targets
)
except Exception as e:
logger.warning(f"Failed to update STT options: {e}")
# Update participant attribute as visible metadata
if self.participant:
try:
await self.participant.set_attributes({
"target_languages": ",".join(new_targets)
})
except Exception as e:
logger.warning(f"Failed to update participant attrs: {e}")
# Hook that processes incoming speech events from STT
async def handle_speech_event(self, event):
# event structure from Azure plugin: event.alternatives[0].language (detected)
# and event.translations or similar containing list of translations per target lang.
detected = None
try:
if event.alternatives and len(event.alternatives) > 0:
detected = event.alternatives[0].language
except Exception:
detected = None
if detected and detected != self.current_language:
logger.info(f"Detected language changed: {detected}")
self.current_language = detected
# update participant attribute
if self.participant:
try:
await self.participant.set_attributes({"current_language": detected})
except Exception as e:
logger.warning(f"Failed to set participant language attribute: {e}")
# Print original transcription if present
original_text = getattr(event, "text", None)
if original_text:
logger.info(f"Original ({self.current_language}): {original_text}")
# Access translations - plugin may populate `translation` or `translations`
translations = getattr(event, "translation", None) or getattr(event, "translations", None) or getattr(event, "translation_texts", None)
# translations may be a list of objects {language, text} or dict; handle common shapes:
if translations:
# If it's dict of lang->text
if isinstance(translations, dict):
for lang, txt in translations.items():
if lang in self.target_languages:
logger.info(f"Translation [{lang}]: {txt}")
else:
# assume iterable of objects with .language and .text
for t in translations:
lang = getattr(t, "language", None) or t.get("language") if isinstance(t, dict) else None
txt = getattr(t, "text", None) or t.get("text") if isinstance(t, dict) else None
if lang and txt and lang in self.target_languages:
logger.info(f"Translation [{lang}]: {txt}")
# Entrypoint: room context to run the agent session
server = AgentSession()
@server.room_context()
async def entrypoint(ctx: JobContext):
ctx.log_context_fields = {"room": ctx.room.name}
# Configure Azure STT plugin with translation enabled.
# Use languages=None (or omit) to let Azure auto-detect.
azure_stt = AzureSTT(
speech_key=os.environ["AZURE_SPEECH_KEY"],
speech_region=os.environ["AZURE_SPEECH_REGION"],
languages=None, # let Azure auto-detect
translation_enabled=True,
translation_target_languages=["en", "es"], # initial targets
interim_results=True,
)
# Build a minimal session using the Azure STT plugin
session = AgentSession(stt=azure_stt)
# Create agent instance and attach session
agent = DynamicTranslationAgent(session=session, initial_targets=["en", "es"])
# Start the session (this registers STT/node wiring inside the agent runtime)
# The AgentSession will produce events; we listen for STT events from session.stream_events()
await session.start(agent=agent, ctx=ctx)
# Find the local participant (the participant representing this agent's connection)
try:
participant = ctx.room.local_participant
await agent.set_participant(participant)
except Exception as e:
logger.warning(f"No participant available to set attributes: {e}")
# Example: background task that listens to session events and dispatches speech events to agent
async def event_loop():
async for ev in session.stream_events():
# ev.type might be "speech" or plugin-specific; check STT event shape
# Directly pass STT speech events to agent handler when available
if getattr(ev, "type", None) in ("speech", "stt", None):
await agent.handle_speech_event(ev)
loop_task = asyncio.create_task(event_loop())
# Example: show dynamic update after 20 seconds (in real app, trigger from API/UI)
async def updater():
await asyncio.sleep(20)
await agent.update_target_languages(["fr", "de", "en"])
logger.info("Updated translation targets to fr,de,en")
updater_task = asyncio.create_task(updater())
# Wait until session ends (placeholder: session manages lifecycle)
await session.wait_closed()
# cancel background tasks if still running
loop_task.cancel()
updater_task.cancel()
if __name__ == "__main__":
cli.run_app(server)
How to run
1. Install dependencies:
◦ python -m pip install -r requirements.txt
1. Fill .env with Azure credentials (and LiveKit keys if your deployment requires them).
2. Run agent:
◦ python agent.py
Behavior summary
• Azure STT auto-detects the spoken language (languages=None) and returns detected language on STT events.
• Translations are returned per target language; the agent reads translations and logs them.
• The agent updates the participant attribute current_language whenever detection changes.
• Calling update_target_languages() updates the STT plugin options at runtime and updates participant metadata.
• Example updater demonstrates a runtime change; replace with real triggers (API/UI) in production.
Notes and caveats
• This example uses the Azure STT plugin API shapes referenced in LiveKit Agents. The STT event field names may vary slightly by plugin version; the code defensively checks common property names: alternatives[0].language, text, `translation`/`translations`/`translation_texts`.
• The code is minimal and intended to be adapted into your app’s event loop and UI triggers for updating target languages.
If you want, I can:
• adapt this example to also synthesize translated audio (TTS) per target language,
• or provide a small HTTP endpoint to accept runtime target-language updates from your UI. Which would you prefer?, with interactive elements