Skip to content

phillipsmark434-ux/saa-sdk

 
 

Repository files navigation

SAA: Selective Auditory Attention

Tells your voice agent which speech is actually for it.

One decision per utterance: only addressee speech reaches your STT, LLM, and TTS. No wake word.

npm PyPI License

Drop-in for the voice-agent stack you already use:

Pipecat    LiveKit    ElevenLabs Conversational AI    Twilio Media Streams

What is SAA?

A voice agent's microphone hears every voice in the room: yours, a coworker's, the kids, a podcast playing on the laptop, the agent's own TTS bleeding back through the speakers. Most pipelines respond to any of it, paying STT for every transcribed second and triggering the LLM on speech that was never directed at the device.

Single device · robot Single device · laptop Multi-device · two robots
Pollen Robotics Reachy robot listening; SAA fires only when the speaker addresses it. Live laptop session. SAA gates speech in a real browser tab; the pill flips green only when the user is addressing the laptop, ambient room speech stays gray. Two Pollen Robotics Reachy robots in the same room, hearing the same audio. Only the addressed robot acts; the other stays still.
Only addressed speech wakes the robot. Pill flips green only when the user addresses the screen. Same room, same audio. Only the addressed robot acts.

SAA (Selective Auditory Attention) is a hosted classifier that runs before STT and decides, per utterance, whether the speech was directed at the device. Side talk, background media, and the agent's own playback are filtered out, so your STT / LLM / TTS only see audio meant for the agent.

  • No wake word. SAA decides per-utterance from the audio (and optionally low-rate video) stream.
  • Hosted. A real-time WebSocket to attention labs' cloud; the open SDKs are thin clients. Because it gates before STT, only addressed speech reaches the STT, LLM, and TTS you already run, so your downstream services and logs see less audio, not more. On-device deployment is a separate enterprise licence.

The architecture and evaluation are described in the technical report.

Ways to integrate

Shape Package Use it when
Streaming SDK @attenlabs/saa-js, attenlabs-saa your app captures the audio/video itself and you want typed attention events to gate your own pipeline. Good for web agents, mall kiosks, drive-through agents, and robots.
LiveKit saa-livekit-client you run a LiveKit Agents voice agent. SAA joins your room and gates the session.
Pipecat (Daily) saa-pipecat-client you run a Pipecat voice agent on Daily. SAA joins your Daily room and gates the pipeline through the "saa" app-message topic.
ElevenLabs attenlabs-saa you run an ElevenLabs Conversational AI agent. SAA gates it via the streaming SDK's feed_audio (its room is sealed, so SAA can't join it directly).
Twilio attenlabs-saa you run a Twilio Media Streams telephony agent. SAA gates inbound/outbound call audio (μ-law 8 kHz resampled to PCM16) via the streaming SDK's feed_audio.

Install

npm install @attenlabs/saa-js     # JavaScript / browser
pip install attenlabs-saa          # Python (streaming SDK)
pip install saa-livekit-client     # Python (LiveKit)
pip install saa-pipecat-client     # Python (Pipecat on Daily)

Get an API key at attentionlabs.ai/dashboard.

Streaming SDK

You capture the media; SAA emits typed events. The key event is turnReady / turn_ready, one device-directed utterance, captured and ready to forward to your STT or LLM.

import { AttentionClient } from "@attenlabs/saa-js";

const client = new AttentionClient({ token: process.env.SAA_API_KEY });

// fires once per device-directed turn; turn.audioBase64 is PCM16 @ 16 kHz
client.on("turnReady", (turn) => yourSTT.send(turn.audioBase64));

await client.start({ videoElement: document.querySelector("video") });
import os
from saa import AttentionClient

client = AttentionClient(token=os.environ["SAA_API_KEY"])

@client.on_turn_ready
def _(turn):
    # turn.audio_base64, PCM16 @ 16 kHz mono; turn.audio_pcm16, np.int16 array
    your_stt.send(turn.audio_base64)

client.start()

For audio-only deployments, omit videoElement (browser) or pass enable_video=False (Python).

Both SDKs also emit prediction, vad, state, interrupt, config, and stats events, and expose mute() / unmute(), setThreshold() / set_threshold(), and markResponding() / mark_responding(). See packages/saa-js and packages/saa-py.

Runnable end-to-end demos are in examples/web/ (browser) and examples/python/ (terminal).

LiveKit

For LiveKit Agents, saa-livekit-client brings SAA into your room to run the classifier and publish events on the "saa" data topic. Your agent consumes them through AttentionEngine and gates the session.

from saa_livekit_client import AttentionEngine, attention_agent_token, start_attention_session

saa = await start_attention_session(
    api_key=SAA_API_KEY, livekit_url=LIVEKIT_URL,
    agent_token=attention_agent_token(api_key=LK_KEY, api_secret=LK_SECRET, room_name=ctx.room.name),
    room_name=ctx.room.name, participant_identity=user.identity,
)
engine = AttentionEngine(ctx.room, agent_identity=saa.agent_identity)

@engine.on_prediction
def _(p):
    session.input.set_audio_enabled(p.aligned_class == 2)   # the gate

await engine.start()

Two runnable samples, an OpenAI Realtime agent and a vanilla-JS web client, are in examples/livekit/.

Pipecat on Daily

For Pipecat voice agents running on Daily, saa-pipecat-client brings SAA into your Daily room and publishes events on Daily's app-message channel under the "saa" topic. Your bot consumes them through AttentionEngine (which subscribes via your DailyTransport) and gates the pipeline.

from saa_pipecat_client import AttentionEngine, attention_agent_token, start_attention_session

saa = await start_attention_session(
    api_key=SAA_API_KEY, room_url=ROOM_URL,
    agent_token=attention_agent_token(daily_api_key=DAILY_API_KEY, room_name=room_name),
    participant_identity=human_identity,
)
engine = AttentionEngine(transport, agent_identity=saa.agent_identity)
engine.bind_task(task)

@engine.on_prediction
def _(p):
    addressee_gate.suppressed = (p.aligned_class == 1 and p.confidence > 0.7)

await engine.start()

A runnable web-client sample is in examples/pipecat/.

ElevenLabs

ElevenLabs Conversational AI runs its agent in a sealed WebRTC room, so SAA can't join it directly. Instead the streaming SDK runs in feed mode: you hand it the agent's microphone audio through feed_audio and gate the agent on SAA's prediction events.

from saa import AttentionClient

# feed mode: the SDK captures nothing itself; you supply the audio
saa = AttentionClient(token=SAA_API_KEY, enable_audio=False, enable_video=False)

@saa.on_prediction
def _(p):
    mic_to_agent.enabled = (p.aligned_class == 2)   # 2 = addressed to the device

saa.start()
# in ElevenLabs' AudioInterface input callback:
saa.feed_audio(mic_pcm16)

A runnable sample is in examples/elevenlabs/.

Twilio

For Twilio Media Streams telephony agents, the streaming SDK runs in feed mode over the call audio. The adapter transcodes Twilio's μ-law 8 kHz frames to PCM16 16 kHz, feeds them to SAA, and forwards only device-directed turns to your bridge, so side talk, hold music, and the agent's own TTS echo are gated out.

from saa import AttentionClient

saa = AttentionClient(token=SAA_API_KEY, enable_audio=False, enable_video=False)

@saa.on_turn_ready
def _(turn):
    bridge.on_speech(turn.audio_base64)   # only device-directed call audio continues

saa.start()
# in the Twilio media handler, after decoding μ-law -> PCM16:
saa.feed_audio(pcm16_frames)

A runnable Media Streams bridge (codec, paced outbound, automatic mark_responding) is in examples/twilio/.

Proactive agents (speak first)

The streaming SDKs expose markResponding(true) / mark_responding(True) so the agent can assert when it is the one speaking, suppressing the gate during its own TTS and resuming once the tail clears. The LiveKit and Pipecat bridges expose the same lifecycle via engine.responding_start() / responding_stop(), identical surface.

How it composes

SAA is the model-agnostic addressee decision between your VAD and STT. It answers a different question than VAD (is anyone speaking), speaker diarization (which voice it is), turn detection (have they finished), or a wake word (did they say the phrase), so it composes with those layers and can replace the wake word outright.

Where SAA sits in your voice stack: noise suppression and VAD upstream, SAA addressee gate, then STT → LLM → TTS downstream

On-device deployment

The open SDKs stream to the SAA cloud. For deployments where audio must stay on the device (telephony, embedded systems, wearables, robotics, kiosks), request on-device and embedded access at attentionlabs.ai.

Documentation

License

Apache-2.0 across the repo, each package and the examples ship under it (see each subtree's LICENSE). The hosted cloud service is governed by the attention labs Terms of Service.

SECURITY.md · CONTRIBUTING.md · CODE_OF_CONDUCT.md · CHANGELOG.md · NOTICE · CITATION.cff


An attention labs project. © 2026 Socero Inc.

About

The addressee layer for voice agents: only speech meant for your agent reaches your STT, LLM, or TTS, on any stack.

Resources

License

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Python 75.5%
  • TypeScript 22.6%
  • JavaScript 1.5%
  • Makefile 0.4%