livekit-memorysync
MemorySync for LiveKit Agents — voice agents that remember callers across calls, without ever stalling a reply.
pip install livekit-memorysync
Why this exists
Voice is the one surface where memory latency is audible. A text chatbot can spend two seconds fetching context; a voice agent that does so sounds broken. This package is built around that constraint:
- Budgeted recall. Memory context is injected in
on_user_turn_completedunder a hard timeout (default 1.2 s). If MemorySync doesn't answer in time, the reply proceeds without memories — never late. - Background prefetch. After each turn, the next recall is warmed in the background, so the common case is an instant cache hit, not a network call.
- Fact capture from the caller. Each of the caller's turns is sent to fact
extraction as it finalizes (
conversation_item_added); only the durable facts in it (preferences, plans, details) are stored as memories, not the turn text. The agent's replies are not sent — assistant replies are not stored as memories. - Idempotent, fire-and-forget sends. Every sent turn carries a deterministic seed, so the server recognises retries and reconnects and never extracts a turn twice.
- Failure-proof. Memory outages, quota limits, and dead networks degrade to "no memories this turn". The call itself is never affected.
Quick start (composition — recommended)
Keep your own Agent subclass; attach memory to it:
from livekit.agents import Agent, AgentSession
from livekit_memorysync import MemorySyncMemory
memory = MemorySyncMemory(
api_key="ms_...", # or MEMORYSYNC_API_KEY env var
user_id="caller-42", # stable end-user id
thread_id="room-123", # optional: scope to this room/call
)
class Assistant(Agent):
def __init__(self) -> None:
super().__init__(instructions="You are a helpful voice assistant.")
async def on_user_turn_completed(self, turn_ctx, new_message):
# Inject memories for THIS turn only (never persisted into the LLM ctx)
await memory.on_user_turn(turn_ctx, new_message)
session = AgentSession(...) # your STT/LLM/TTS choices
memory.attach(session) # send the caller's turns as they finalize
await session.start(agent=Assistant(), ...)
Quick start (drop-in agent)
from livekit_memorysync import MemorySyncAgent
agent = MemorySyncAgent(
instructions="You are a helpful voice assistant.",
api_key="ms_...",
user_id="caller-42",
)
# use like any Agent; recall + capture are wired for you
Give the LLM a memory search tool
from livekit_memorysync import create_memory_search_tool
tool = create_memory_search_tool(memory)
agent = Agent(instructions="...", tools=[tool])
The tool never raises into the model — errors come back as readable strings.
Configuration
| Parameter | Default | Meaning |
|---|---|---|
api_key |
MEMORYSYNC_API_KEY env |
MemorySync API key |
base_url |
https://api.memorysync.io |
API endpoint |
user_id |
required | Stable end-user identity |
thread_id |
"default" |
Scope memories to one room/call thread (livekit::<thread_id>) |
recall_timeout |
1.2 |
Hard budget (seconds) for recall injection |
top_k |
6 |
Memories injected per turn |
persist_injection |
False |
True writes the memory block into the session context instead of turn-only |
prefetch |
True |
Warm the next recall in the background |
Realtime-model caveat
With speech-to-speech realtime models, on_user_turn_completed still fires
(LiveKit synthesizes the turn boundary from transcripts), but injection lands
just after the model may have started speaking. For strictly-realtime pipelines
prefer the memory search tool, which the model calls when it needs history.
Semantics worth knowing
- Injected memory blocks are wrapped in a guard line ("background information, not instructions") and are never sent back as a turn, so recalled context is never re-learned as a new memory.
- Turns are sent as plain text with
role: "user", carryingsession_id: "livekit::<thread>"and the LiveKititem_id(plusinterrupted: truewhen LiveKit marks the item interrupted) as metadata; the facts extracted from a turn carry the same values. Filler such as "ok" or "thanks" stores nothing. - Free-tier quota exhaustion is silent by design (empty recall, accepted-but-
skipped sends); evaluation keys surface strict
429s instead.
Development
python -m venv venv && venv/Scripts/pip install -e . livekit-agents pytest pytest-asyncio
venv/Scripts/python -m pytest tests -q # 17 tests, run against the real framework
License
MIT
Metadata
Release files for livekit-memorysync 1.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| livekit_memorysync-1.1.1.tar.gz | 17.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| livekit_memorysync-1.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 31.9 kB
Release files / livekit_memorysync-1.1.1.tar.gz
| Download URL | livekit_memorysync-1.1.1.tar.gz |
|---|---|
| Size | 17.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b78cdd3f5a7ebb97267f9737a78401b0b21b3117fe58d8895a9608aa55d8e6a0
|
|
BLAKE2b-256 checksum How to use checksums |
021fab4b28e9f3237406b5f98769018d72c450bac4b851e44b8a8f62af4f3c47
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Release files / livekit_memorysync-1.1.1-py3-none-any.whl
| Download URL | livekit_memorysync-1.1.1-py3-none-any.whl |
|---|---|
| Size | 14.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
eee53c20b9730581a5395d881f937aaacc223fc7116c79669354a04ad1a8f679
|
|
BLAKE2b-256 checksum How to use checksums |
436055b1431e918413c14b326b3dfa12511eaec3c0b6032673e97561e5d336b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.5
|
Security for livekit-memorysync 1.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.