Skip to main content

LiveKit client for saa: consume attention events inside your voice agent.

Project description

saa-livekit-client

LiveKit client for the saa.

Adds attention-aware gating, barge-in, and proactive interjection to any LiveKit voice agent.

Install

pip install saa-livekit-client

Quickstart: existing voice agent

import asyncio
import os

from livekit.agents import Agent, AgentServer, AgentSession, JobContext, cli
from livekit.plugins import openai
from saa_livekit_client import (
    AttentionEngine, attention_agent_token, start_attention_session,
)


class MyAssistant(Agent):
    def __init__(self):
        super().__init__(instructions="You are a helpful assistant")


server = AgentServer()


@server.rtc_session()
async def entrypoint(ctx: JobContext):
    await ctx.connect()
    user = await ctx.wait_for_participant()

    # summon the saa hosted agent into the room
    saa = await start_attention_session(
        api_key=os.environ["SAA_API_KEY"],
        livekit_url=os.environ["LIVEKIT_URL"],
        agent_token=attention_agent_token(
            api_key=os.environ["LIVEKIT_API_KEY"],
            api_secret=os.environ["LIVEKIT_API_SECRET"],
            room_name=ctx.room.name,
        ),
        room_name=ctx.room.name,
        participant_identity=user.identity,
    )
    ctx.add_shutdown_callback(saa.stop)

    # stand up the voice agent, then wire saa on top of the running session
    # (session.input / session.interrupt() are only valid once started)
    voice = AgentSession(llm=openai.realtime.RealtimeModel(voice="alloy"))
    await voice.start(agent=MyAssistant(), room=ctx.room)

    engine = AttentionEngine(ctx.room, agent_identity=saa.agent_identity)
    ctx.add_shutdown_callback(engine.stop)

    @engine.on_prediction
    def _(p):
        # gate the mic, only class 2 (talking-to-device) reaches the model
        voice.input.set_audio_enabled(p.aligned_class == 2)

    @engine.on_interrupt
    def _(ev):
        voice.interrupt()

    @engine.on_interjection
    async def _(ev):
        await voice.generate_reply(instructions="Briefly offer to help")

    # tell saa when the agent is speaking, arms interrupt, suppresses interjection
    @voice.on("agent_state_changed")
    def _(ev):
        if ev.new_state == "speaking":
            asyncio.create_task(engine.responding_start())
        elif ev.old_state == "speaking":
            asyncio.create_task(engine.responding_stop())

    await engine.start()


if __name__ == "__main__":
    cli.run_app(server)

That's the whole integration. Works with any LiveKit pipeline, including RealtimeModel speech-to-speech. Runnable variants are in the examples/livekit/ samples. (WorkerOptions(entrypoint_fnc=...) also works on 1.5.x, the older idiom.)

Greenfield, build_attention_entrypoint

For new voice agents that don't have an existing pipeline:

from livekit.agents import AgentServer, JobContext, cli
from saa_livekit_client import build_attention_entrypoint, TurnReadyEvent

async def handle_turn(event: TurnReadyEvent, ctx: JobContext):
    # event.audio_pcm16 = int16 mono 16 kHz; event.frames = list[TurnFrame]
    response_pcm = await my_llm.respond(event.audio_pcm16, frames=event.frames)
    await publish_response_audio(ctx.room, response_pcm)

entrypoint = build_attention_entrypoint(on_turn=handle_turn)

server = AgentServer()
server.rtc_session()(entrypoint)
# or the older idiom: cli.run_app(WorkerOptions(entrypoint_fnc=entrypoint))

if __name__ == "__main__":
    cli.run_app(server)

Environment: SAA_API_KEY, LIVEKIT_API_KEY, LIVEKIT_API_SECRET, LIVEKIT_URL.

Event types

Event Fires Payload
PredictionEvent every 250 ms raw_class, aligned_class (0/1/2), confidence, source, num_faces, responding
VADEvent every 250 ms is_speech, probability
warmup model warmed up, predictions begin none
listening_start / listening_cancelled state edges none
TurnReadyEvent end of user turn audio_pcm16, duration, frames, context
InterruptEvent user barges in during AI playback confidence
InterjectionEvent humans went quiet after side-chat reason, audio_pcm16, duration
ErrorEvent out-of-band errors code, message

Classes: 0=silent, 1=human-to-human, 2=human-to-device. responding is True while the AI is mid-playback.

Each is delivered through an @engine.on_* callback: on_prediction, on_vad, on_warmup, on_listening_start, on_listening_cancelled, on_turn_ready, on_interrupt, on_interjection, on_error.

Upstream actions

await attention.mute()                       # stop feeding mic to processor
await attention.unmute()
await attention.responding_start()           # AI is now speaking
await attention.responding_stop()
await attention.set_threshold(0.65)          # model class-2 confidence threshold

These are routed only to the SAA agent (destination_identities=[...]) so they never leak to other room participants.

Requirements

  • Python 3.10+
  • LiveKit URL must be publicly reachable from our cloud (no private VPC)
  • Audio + video tracks must both be available (the model is multimodal)
  • Customer voice agent and hosted attention agent share the same LiveKit room

License

Apache-2.0. © Attention Labs.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

saa_livekit_client-0.3.1.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

saa_livekit_client-0.3.1-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

Details for the file saa_livekit_client-0.3.1.tar.gz.

File metadata

  • Download URL: saa_livekit_client-0.3.1.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for saa_livekit_client-0.3.1.tar.gz
Algorithm Hash digest
SHA256 06f277fe0bd22a090f80d470951627a9d8e5b49a75b4d50ef97370260cbfd208
MD5 2fb63df4976f1882c46d75a4f2a2f890
BLAKE2b-256 cab3e3b39586640407e98868c461683e43a253a73ce5c5f2399e892064d8ff73

See more details on using hashes here.

File details

Details for the file saa_livekit_client-0.3.1-py3-none-any.whl.

File metadata

File hashes

Hashes for saa_livekit_client-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c9b9608d50a8f7cc18650b76bec6130b0db5a797c4ea6c53a1afc576531d42da
MD5 920dd1363332bcced8e535fe0f6d13ea
BLAKE2b-256 3c794e5febbaebecfdb8a7601c973dfe33a03658e8f4bc00adcd0526165a5844

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page