glamorous-telephone-35069
03/04/2026, 4:20 PMtall-airline-16756
03/04/2026, 4:21 PMtall-airline-16756
03/04/2026, 4:21 PMRealtimeModel plugins so you can stream audio straight to a model that does audio understanding and response generation without a separate STT step.
Before I give code: could you tell me which provider you want to use (OpenAI Realtime, Azure OpenAI Realtime, Google Gemini Live, or a self-hosted audio-LLM)? If you don’t have a preference, I’ll show a concise example using OpenAI’s Realtime plugin for LiveKit.
Below is a minimal Python example using LiveKit’s agent APIs and OpenAI’s Realtime model to receive audio directly, have the model process it, and speak back. This example assumes you already have LiveKit agent libraries installed and credentials configured.
1) Minimal async worker that attaches a realtime audio-capable model and streams audio in/out
import asyncio
from livekit.agents import AgentSession, JobContext, WorkerOptions, cli
from livekit.plugins.openai import realtime
# Configure the realtime model to accept audio and produce audio/text
realtime_model = realtime.RealtimeModel(
voice="alloy", # model TTS voice (audio out)
temperature=0.2,
instructions="You are a helpful assistant. Answer concisely.",
# modalities default to audio+text for audio-LLMs that support it
)
async def entrypoint(ctx: JobContext):
# connect to the LiveKit room and subscribe only to audio
await ctx.connect(auto_subscribe=ctx.AutoSubscribe.AUDIO_ONLY)
# create an AgentSession which will route incoming audio to the realtime model
session = AgentSession(llm=realtime_model)
# start handling the room (this wires audio in → model and model audio/text → room)
session.start(ctx.room)
# Optional: send an initial spoken prompt from the model (TTS)
await session.generate_reply("Hello — I'm listening. Ask me anything.")
# Keep the worker running while the room is active
await ctx.wait_for_room_end()
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
2) How this works — short summary
• Incoming participant audio is forwarded to the realtime audio-LLM plugin. No separate STT step is required.
• The realtime model understands audio directly (so it can use prosody, intent, etc.) and can return text and/or synthesized audio.
• You can force the model to return text-only (e.g., if you want to use your own TTS) by configuring the model to output text modalities only.
3) If you want text-only output (use your own TTS)
realtime_model = realtime.RealtimeModel(
modalities=["text"], # force text responses only
instructions="Answer in short text form."
)
# Then run TTS (your chosen engine) on the returned text and play into the room.
Next steps / quick questions so I can tailor the example:
• Which provider/plugin do you want (OpenAI Realtime, Azure, Gemini Live, or self-hosted)?
• Do you want the model to return audio responses directly, or do you prefer the model return text and then use a separate TTS engine?
• Are you integrating with an existing LiveKit room or building a new agent worker from scratch?
Tell me those details and I’ll provide a focused example (including any required config snippets) for your exact provider and desired flow.
Sources: Realtime models overview | LiveKit Documentation | OpenAI Realtime API plugin guide | LiveKit Documentation | Azure OpenAI Realtime API and LiveKit | LiveKit Documentation | Gemini Realtime Agent with Live Vision | LiveKit Documentation | xAI LLM plugin guide | LiveKit Documentationglamorous-telephone-35069
03/04/2026, 4:22 PMtall-airline-16756
03/04/2026, 4:23 PMstt_node et tts_node dans un Agent pour appeler Qwen3 Omni en streaming et QwenTTS pour synthèse. Cela s’intègre proprement dans le cycle audio de LiveKit.
Exemple schématique (structure — à compléter avec vos appels Qwen) :
from livekit.agents import Agent, ModelSettings
from typing import AsyncIterable, Optional
import asyncio
class QwenAgent(Agent):
async def stt_node(self, audio_frames: AsyncIterable, model_settings: ModelSettings) -> Optional[AsyncIterable]:
# 1) Convertir audio_frames en chunks/support attendu par Qwen3 Omni
# 2) Stream/POST les chunks à Qwen3 Omni SDK/endpoint
# 3) Yield/retourner des événements de parole/text à la pipeline LiveKit
pass
async def tts_node(self, text_stream: AsyncIterable[str], model_settings: ModelSettings) -> AsyncIterable:
# 1) Appeler QwenTTS pour chaque texte reçu
# 2) Récupérer l'audio généré (PCM/opus etc.)
# 3) Convertir en AudioFrames et yield vers LiveKit pour playback
pass
Points à implémenter :
• Conversion audio <-> format attendu (échantillonnage, PCM, encodage).
• Gestion du streaming (si Qwen propose websocket/streaming).
• Latence/VAD pour détection de fin de parole si vous voulez réponses rapides.
Option B — Pipeline externe + injection audio dans LiveKit
• Idée : Recevoir l’audio depuis la room, bufferer/segmenter, appeler Qwen3 Omni (stream ou batch), obtenir texte, envoyer au LLM ou logique métier, générer TTS via QwenTTS, puis jouer l’audio résultant dans la room (publier un track ou streamer dans la session).
• Utile si vous préférez garder Qwen hors du runtime Agent ou si vous n’avez pas besoin d’intégration profonde côté nœuds.
Schéma minimal pour playback :
1. Subscriber audio → sauvegarde/chunks
2. Appel Qwen3 Omni → texte
3. Traitement / génération réponse
4. Appel QwenTTS → audio (wav/pcm)
5. Publisher audio dans la room (nouveau track) pour que tous entendent
Questions pratiques pour fournir code concret
• Pouvez-vous partager le type d’API Qwen (HTTP REST, websocket, SDK Python) et exemples d’authentification ?
• Souhaitez-vous un exemple complet pour le streaming low-latency (websocket/stream) ou un exemple plus simple en mode “segment/poll” (upload de petits fichiers) ?
• Avez-vous besoin d’un exemple montrant la conversion audio (PCM ↔️ LiveKit AudioFrame) ou la manière de publier un track TTS dans la room ?
Si vous confirmez que vous avez un SDK HTTP/REST pour Qwen3 Omni et QwenTTS, je peux :
• fournir un exemple concret de stt_node et tts_node en Python en utilisant requests/asyncio (upload de chunks),
• ou fournir un exemple de streaming WebSocket si Qwen le propose (basé sur votre info).
Dites-moi quelle méthode vous préférez (streaming vs batch) et si vous avez les endpoints/SDK — je vous écris l’exemple adapté.
Sources: Speech-to-text (STT) models overview | LiveKit Documentation | Text-to-speech (TTS) models overview | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentationglamorous-telephone-35069
03/04/2026, 4:31 PMtall-airline-16756
03/04/2026, 4:32 PMimport asyncio
from livekit.agents import AgentSession, JobContext, WorkerOptions, cli
from livekit.plugins import openai, deepgram # exemples de plugins existants
import aiohttp
# Configuration endpoints
VLLM_BASE = "<http://localhost:8000/v1>" # vLLM OpenAI-compatible
QWENTTS_BASE = "<http://localhost:9000/tts_stream>" # endpoint streaming TTS (wrapper si besoin)
# LLM config pointant vers vLLM (OpenAI-compatible)
qwen_llm = openai.LLM(
model="qwen3-omni",
base_url=VLLM_BASE,
api_key=None, # si vous avez token, mettez-le
stream=True # indique qu'on utilisera streaming
)
# STT low-latency (ex: Deepgram flux). Utilisez votre plugin/config STT réel.
stt = deepgram.STT(model="flux")
async def tts_stream_to_livekit(session, text_stream_async_iterable):
"""
Exemple de wrapper simple pour envoyer texte en streaming à QwenTTS
et recevoir des chunks audio (PCM/opus) renvoyés vers LiveKit.
Implémentez votre conversion vers AudioFrames selon vos besoins.
"""
async with aiohttp.ClientSession() as http:
# Exemple POST streaming (adapté à votre API)
async with <http://http.post|http.post>(QWENTTS_BASE, data=text_stream_generator(text_stream_async_iterable)) as resp:
async for chunk in resp.content.iter_chunked(4096):
# Convertir chunk -> AudioFrame et publier dans la room
# session.publish_audio_chunk(chunk) # pseudo-fonction à implémenter
pass
def text_stream_generator(text_iterable):
"""Convertit un AsyncIterable[str] en bytes pour l'endpoint TTS streaming."""
async def _gen():
async for t in text_iterable:
yield t.encode("utf-8")
return _gen()
async def entrypoint(ctx: JobContext):
await ctx.connect(auto_subscribe=ctx.AutoSubscribe.AUDIO_ONLY)
# Crée la session agent avec STT + LLM. On laisse TTS en externe pour contrôle.
agent_session = AgentSession(stt=stt, llm=qwen_llm, preemptive_generation=True)
agent_session.start(ctx.room)
# Exemple: si vLLM renvoie stream text partials, vous devez les récupérer
# et forwarder vers tts_stream_to_livekit pour synthèse immédiate.
# L'API AgentSession/LLM expose callbacks/events pour partials -> abonnez-vous.
# Pseudo-code d'abonnement :
async def on_llm_partial(token: str):
# buffer tokens / or forward token-to-tts immediately
await send_token_to_tts(token) # implémentez streaming vers QwenTTS
# Assurez-vous d'attacher le callback sur l'objet LLM/session (pseudo)
qwen_llm.on_partial = on_llm_partial
await ctx.wait_for_room_end()
if __name__ == "__main__":
cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))
Points de mise en œuvre importants pour la latence minimale
• Preemptive generation : commencer à générer réponse pendant que l’utilisateur parle.
• Token streaming : utiliser le stream token‑by‑token du vLLM et envoyer ces tokens immédiatement au TTS chunker.
• TTS streaming : TTS doit renvoyer audio chunké (opus/pcm) afin de publier sans attendre fichier complet.
• VAD et small buffers : réduire la taille de buffer audio envoyé au STT (ex : 100–200 ms) et utiliser VAD pour couper les silences.
• Co‑localisation : déployer vLLM, TTS et l’agent dans la même région/host pour réduire RTT.
• Format audio cohérent : utilisez le même sample rate et encodage attendu par les composants (ex : 16k/16bit PCM ou opus).
Si QwenTTS n’a pas d’API streaming
• Créez un petit wrapper qui expose une route websocket/SSE acceptant des fragments textuels et qui renvoie des chunks audio générés progressivement.
• Ce wrapper appelle QwenTTS synchronously/async en petits lots et stream les résultats au client LiveKit.
Questions rapides pour finaliser un exemple complet
1. QwenTTS propose-t‑il déjà un endpoint streaming (websocket / SSE) ?
2. Souhaitez-vous un exemple complet montrant la conversion chunk→AudioFrame et la publication d’un track LiveKit (je peux fournir le code de conversion PCM→AudioFrame) ?
3. Voulez‑vous que j’écrive un exemple de wrapper pour QwenTTS (websocket) qui fait du streaming si vous ne l’avez pas ?
Dites‑moi les réponses; je vous fournis le code détaillé et adapté (wrapper TTS si nécessaire, conversion audio → AudioFrame, et la logique exacte pour forwarder les tokens du vLLM vers le TTS en temps réel).
Sources: Models overview | LiveKit Documentation | Agent Builder | LiveKit Documentation