Metadata-Version: 2.5
Name: livekit-memorysync
Version: 1.0.0
Summary: MemorySync for LiveKit Agents: voice agents that remember callers — budgeted recall injection (never a stalled reply), both-role capture with idempotency seeds, and background prefetch.
Project-URL: Homepage, https://docs.memorysync.io/guides/livekit
Project-URL: Documentation, https://docs.memorysync.io/guides/livekit
Project-URL: Repository, https://github.com/Rafay121/memorysync-plugins
Author-email: MemorySync <support@memorysync.io>
License-Expression: MIT
Keywords: agents,livekit,long-term-memory,memory,memorysync,voice
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Communications :: Conferencing
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.25
Requires-Dist: livekit-agents<2,>=1.0.0
Description-Content-Type: text/markdown

# livekit-memorysync

[MemorySync](https://memorysync.io) for [LiveKit Agents](https://docs.livekit.io/agents/) —
voice agents that remember callers across calls, without ever stalling a reply.

```bash
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_completed`
  under 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.
- **Both-role capture.** User *and* assistant turns are persisted (with
  interruption metadata) via `conversation_item_added` — competitors that only
  store user turns lose half the conversation.
- **Delta-only, idempotent writes.** Every stored turn carries a deterministic
  seed, so retries and reconnects never duplicate memories.
- **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:

```python
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(self, turn_ctx, new_message)

session = AgentSession(...)          # your STT/LLM/TTS choices
memory.attach(session)               # capture both roles as they finalize
await session.start(agent=Assistant(), ...)
```

## Quick start (drop-in agent)

```python
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

```python
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` | `None` | Scope memories to one room/call thread |
| `recall_timeout` | `1.2` | Hard budget (seconds) for recall injection |
| `top_k` | `5` | 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 excluded from capture, so recalled context is
  never re-stored as a new memory.
- Interrupted assistant turns are stored with `interrupted: true` metadata.
- Free-tier quota exhaustion is silent by design (empty recall, accepted-but-
  dropped writes); evaluation keys surface strict `429`s instead.

## Development

```bash
python -m venv venv && venv/Scripts/pip install -e . livekit-agents pytest pytest-asyncio
venv/Scripts/python -m pytest tests -q     # 16 tests, run against the real framework
```

## License

MIT
