Skip to main content
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 AgentSession pipeline
  • 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 livekit-agent-2 with Docker Compose:
This service mounts three additional paths compared to 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 that AvatarSession expects. Use these values exactly when building your own sender:

Token Requirements

The sender participant must use with_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.