docs(livekit): add a runnable voice agent example

examples/voice_agent.py runs in console mode or joins LiveKit rooms. It
scopes memory from dispatch metadata, SUPERMEMORY_CONTAINER_TAG, or the
participant, preloads the caller's profile, and greets returning callers
by name. Tested on LiveKit Cloud: a second call greeted the caller by name
and used a fact from the first call.

The example is not included in the wheel or sdist.
This commit is contained in:
Ishaan Gupta 2026-09-28 21:15:39 +05:30
parent 2d281f8cbc
commit 5b2a435e37
5 changed files with 127 additions and 0 deletions

View file

@ -93,6 +93,8 @@ if __name__ == "__main__":
`preload` puts the caller's profile into the first turn, so the greeting can use it. `attach` stores the conversation after each agent reply, and stores anything left when the session closes, including a caller who hangs up mid-turn. `SupermemoryAgent` recalls inside `llm_node` before each reply and adds the memory tools. That covers voice turns, text turns from `generate_reply` or `session.run`, and LiveKit's preemptive generation. Each user message is recalled once, and tool follow-ups reuse it.
A runnable version, with a greeting that uses the caller's memory, is in the [example agent](https://github.com/supermemoryai/supermemory/tree/main/packages/livekit-sdk-python/examples).
`agent_name` turns on explicit dispatch, which is how job metadata reaches the agent. Remove it to join every new room automatically, for example when testing in the [Agents Playground](https://agents-playground.livekit.io).
## Your own agent

View file

@ -71,6 +71,8 @@ if __name__ == "__main__":
agents.cli.run_app(server)
```
A runnable version with a greeting that uses memory is in [examples/](examples/).
If you already have an `Agent` subclass, pass `tools=memory.tools()` and recall from `llm_node`:
```python

View file

@ -0,0 +1,14 @@
# LiveKit Cloud project: Settings > API keys. The same keys cover STT, LLM, and TTS
# through LiveKit Inference.
LIVEKIT_URL=wss://your-project.livekit.cloud
LIVEKIT_API_KEY=
LIVEKIT_API_SECRET=
# https://console.supermemory.ai
SUPERMEMORY_API_KEY=
# Optional: pin every call to one caller while testing.
# SUPERMEMORY_CONTAINER_TAG=demo_caller
# Optional: require explicit dispatch, so your backend can pass {"container_tag": ...}.
# LIVEKIT_AGENT_NAME=memory-agent

View file

@ -0,0 +1,38 @@
# Voice agent example
A LiveKit voice agent that remembers each caller. Tell it something on one call, hang up, and it knows it on the next.
## Setup
```bash
pip install supermemory-livekit python-dotenv
cp .env.example .env
```
Fill in `.env` with your LiveKit Cloud project keys and a [Supermemory API key](https://console.supermemory.ai). LiveKit Inference provides speech-to-text, the LLM, and text-to-speech, so no other keys are needed.
From a checkout of this repo, install the local package instead: `pip install -e .. python-dotenv`.
## Talk to it
In your terminal, through your mic:
```bash
python voice_agent.py console
```
In the browser: run `python voice_agent.py dev`, open the [Agents Playground](https://agents-playground.livekit.io), and connect to your project.
## Try memory
1. Say "My name is Priya, and please remember I'm vegetarian."
2. Hang up and start a new call.
3. The agent greets you by name. Ask "What should I order for dinner?"
Memory is scoped per caller:
- Console mode uses the container tag `console_user`.
- In rooms, the agent uses the participant attribute `supermemory_container_tag`, or else the participant identity. The Playground gives each session a new identity, so set `SUPERMEMORY_CONTAINER_TAG` in `.env` to keep one caller across Playground calls.
- In production, dispatch the agent from your backend with `{"container_tag": "<your user id>"}` as job metadata, and set `LIVEKIT_AGENT_NAME`.
Each call is stored as one document, and facts the caller asks it to remember are saved right away. See the [integration docs](https://supermemory.ai/docs/integrations/livekit) for configuration.

View file

@ -0,0 +1,71 @@
"""Voice agent that remembers each caller across calls.
Run from this folder after filling in .env (see README.md):
python voice_agent.py console # talk through your mic in the terminal
python voice_agent.py dev # join LiveKit rooms, e.g. from the Agents Playground
"""
import json
import logging
import os
from dotenv import load_dotenv
from livekit import agents
from livekit.agents import AgentServer, AgentSession, ChatContext, JobContext
from supermemory_livekit import SupermemoryAgent, SupermemoryLiveKit
load_dotenv()
logger = logging.getLogger("voice-agent")
INSTRUCTIONS = (
"You are a friendly voice assistant. You remember this caller across calls. "
"Use what you know naturally, and do not mention the memory system. "
"When the caller asks you to remember something, call remember. Keep replies short."
)
server = AgentServer()
# An agent_name turns on explicit dispatch, which is how job metadata reaches the agent.
# Leave it empty to join every new room, which the Agents Playground needs.
@server.rtc_session(agent_name=os.getenv("LIVEKIT_AGENT_NAME", ""))
async def entrypoint(ctx: JobContext):
memory = SupermemoryLiveKit(api_key=os.environ["SUPERMEMORY_API_KEY"])
# Your backend can pass the caller id in dispatch metadata. SUPERMEMORY_CONTAINER_TAG
# pins every call to one caller while testing. Console mode has no remote participant.
metadata = json.loads(ctx.job.metadata or "{}")
container_tag = metadata.get("container_tag") or os.getenv("SUPERMEMORY_CONTAINER_TAG")
if not container_tag and ctx.is_fake_job():
container_tag = "console_user"
await ctx.connect()
if container_tag:
memory.bind(container_tag=container_tag, session_id=ctx.room.name)
else:
participant = await ctx.wait_for_participant()
memory.bind(participant=participant, session_id=ctx.room.name)
logger.info("memory scoped to container tag %s", memory.container_tag)
# The caller's profile goes into the first turn, so the greeting can use it.
chat_ctx = ChatContext()
await memory.preload(chat_ctx)
session = AgentSession(
stt="deepgram/nova-3:en",
llm="openai/gpt-4.1-mini",
tts="cartesia/sonic-3",
)
memory.attach(session)
await session.start(
room=ctx.room,
agent=SupermemoryAgent(memory, chat_ctx=chat_ctx, instructions=INSTRUCTIONS),
)
await session.generate_reply(
instructions="Greet the caller. If you already know them, welcome them back by name."
)
if __name__ == "__main__":
agents.cli.run_app(server)