agent-2 demonstrates how to drive an Avaluma avatar from an external audio source, completely bypassing the LiveKit Agents voice pipeline. Instead of routing through STT, LLM, and TTS, you connect to the LiveKit room as an independent participant and stream raw PCM audio bytes directly to the avatar using the lk.audio_stream DataStream topic. This pattern gives you full control over the audio content — use your own TTS service, play pre-recorded files, or pipe in any audio source you choose.
When to Use This Pattern
Use the external audio pattern when:- You have an existing TTS or audio generation service you want to keep
- You need to play pre-recorded audio segments through the avatar
- You want to drive the avatar independently of an
AgentSessionpipeline - You are integrating Avaluma into a system that already manages its own audio routing
Pipeline
Setup
1
Set your avatar ID
Open
agents/2-agent-with-external-audio/agent-2.py and set avatar_id to your .hvia filename without the extension:agent-2.py
2
Start the agent
Launch This service mounts three additional paths compared to
livekit-agent-2 with Docker Compose:livekit-agent-1: the agent script, the external sender module, and the assets/ directory containing sample WAV files.Agent Code
agent-2.py sets up the AvatarSession and AgentSession, then spawns the external audio sender as an asyncio task for local simulation:
agent-2.py
External Audio Sender
external_audio_sender.py is the heart of the pattern. It connects to the LiveKit room as an independent participant and streams a WAV file to the avatar via DataStream every few seconds:
external_audio_sender.py
DataStream Audio Protocol
The external sender communicates with the avatar over LiveKit DataStream using the protocol thatAvatarSession expects. Use these values exactly when building your own sender:
Token Requirements
The sender participant must usewith_kind("agent") when creating its LiveKit access token. The avatar only accepts audio streams from participants with agent kind — streams from other participant types are ignored.
Waiting for the Avatar Participant
Before streaming, you must resolve the avatar’s participant identity. The_wait_for_avatar() helper handles both the case where the avatar has already joined and the case where it joins after your sender connects:
In the
agent-2 example, external_audio_sender.run() is launched as an asyncio task inside the agent process for convenience during local development. In production, run the external sender as a completely separate service with its own LiveKit token — it needs no access to the agent process or the AgentSession. The only thing it needs is the room name and valid LiveKit credentials.