Skip to content
Open
1 change: 1 addition & 0 deletions livekit-agents/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ baseten = ["livekit-plugins-baseten>=1.7.1"]
bey = ["livekit-plugins-bey>=1.7.1"]
bithuman = ["livekit-plugins-bithuman>=1.7.1"]
bland = ["livekit-plugins-bland>=1.7.1"]
boson-avatar = ["livekit-plugins-boson-avatar>=1.7.1"]
browser = ["livekit-plugins-browser>=0.3.1"]
cambai = ["livekit-plugins-cambai>=1.7.1"]
cartesia = ["livekit-plugins-cartesia>=1.7.1"]
Expand Down
117 changes: 117 additions & 0 deletions livekit-plugins/livekit-plugins-boson-avatar/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Boson Higgs Avatar plugin for LiveKit Agents

Use Boson's Higgs Audio-Driven Avatar as the video output for a LiveKit voice
agent. This is an Avatar plugin: it composes with your existing voice/LLM
plugin and does not replace or fork it.

The plugin has no dependency on Boson Voice, Boson Audio, or a particular TTS
provider. Its input is the standard audio output of a LiveKit `AgentSession`,
so it works with any TTS plugin, realtime model, or custom source that produces
LiveKit audio frames. Only Avatar rendering and Avatar session lifecycle are
Boson-specific.

## Installation

```shell
pip install livekit-plugins-boson-avatar
```

Set the credentials used by your LiveKit Agent Worker:

```shell
export BOSON_API_KEY="..."
export BOSON_AVATAR_API_URL="https://your-avatar-session-service.example/v1"
export LIVEKIT_URL="wss://..."
export LIVEKIT_API_KEY="..."
export LIVEKIT_API_SECRET="..."
```

The plugin deliberately has no hard-coded provider endpoint. Your application
or deployment environment supplies the base URL exposed by its Boson Avatar
deployment/operator. The plugin appends `POST /sessions` when starting an
Avatar and `DELETE /sessions/{id}` during cleanup, so do not include
`/sessions` itself in `BOSON_AVATAR_API_URL`.

Load the project-scoped Avatar catalog on your application server and use it
to populate the face picker. The returned `avatar_id` is passed unchanged to
`AvatarSession`; browser users never need to type or remember it:

```python
from livekit.plugins import boson_avatar


async def avatar_options():
# Returns [AvatarInfo(avatar_id="...", name="...")]
return await boson_avatar.list_avatars()
```

This calls `GET {BOSON_AVATAR_API_URL}/avatars` with `BOSON_API_KEY`. Keep the
key server-side and cache the result according to your application's needs.

## Usage

Create the voice `AgentSession` with the audio provider of your choice, then
start the Avatar before starting the agent session:

```python
from livekit.agents import Agent, AgentSession, JobContext, inference
from livekit.plugins import boson_avatar


async def entrypoint(ctx: JobContext) -> None:
await ctx.connect()

session = AgentSession(
stt=inference.STT("deepgram/nova-3"),
llm=inference.LLM("openai/gpt-4.1-mini"),
tts=inference.TTS("cartesia/sonic-3"),
)
avatar = boson_avatar.AvatarSession(
avatar_id="your-avatar-id",
)

await avatar.start(session, room=ctx.room)
await session.start(
agent=Agent(instructions="You are a helpful assistant."),
room=ctx.room,
)
```

`BOSON_AVATAR_ID` can supply `avatar_id` instead. `AvatarSession` also accepts
optional `width`, `height`, `max_duration_seconds`,
`avatar_participant_identity`, `idempotency_key`, and `APIConnectOptions`.
`max_duration_seconds` must be an integer from 1 through 14400.

Inside a LiveKit Agent job, provider-session creation automatically derives a
stable UUID idempotency key from the LiveKit job ID and Avatar session binding.
If LiveKit redelivers that job after a worker crash, the plugin recovers the
existing provider session instead of allocating another one. The standard
model is one Avatar lifecycle per LiveKit job. If one job intentionally starts
another Avatar after closing the first, pass a new explicit UUID
`idempotency_key` for that lifecycle.

The `avatar_id` is the value returned by `list_avatars()` behind the Avatar
selected in your application; end users do not need to type or remember it.

The plugin handles the provider API call, LiveKit participant token, PCM data
stream routing, interruption buffer clears, and provider-session cleanup. A
developer does not need to call Boson's Avatar REST API or combine voice and
Avatar responses in an application server. The application server only needs
its normal responsibility: create a LiveKit room and dispatch the Agent Worker.

When started inside a LiveKit job, cleanup is registered automatically. When
using the plugin in a standalone script or test, open LiveKit's HTTP context and
close the Avatar explicitly:

```python
from livekit.agents import utils


async with utils.http_context.open():
await avatar.start(session, room)
try:
# Run the standalone session.
...
finally:
await avatar.aclose()
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2026 LiveKit, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Boson Higgs Audio-Driven Avatar plugin for LiveKit Agents."""

from livekit.agents import Plugin

from .api import AvatarInfo, list_avatars
from .avatar import AvatarSession
from .errors import BosonAvatarException
from .log import logger
from .version import __version__

__all__ = [
"AvatarInfo",
"AvatarSession",
"BosonAvatarException",
"__version__",
"list_avatars",
]


class BosonAvatarPlugin(Plugin):
def __init__(self) -> None:
super().__init__(__name__, __version__, __package__, logger)


Plugin.register_plugin(BosonAvatarPlugin())
Loading