Metadata-Version: 2.5
Name: agno-memorysync
Version: 1.0.0
Summary: MemorySync memory backend for Agno agents: a memory-only db for MemoryManager with real semantic recall, guarded deletes, and an async-native twin.
Project-URL: Homepage, https://memorysync.io
Project-URL: Documentation, https://docs.memorysync.io/guides/agno
Project-URL: Changelog, https://docs.memorysync.io/release-notes
Author-email: MemorySync <support@memorysync.io>
License-Expression: MIT
Keywords: agents,agno,ai,llm,memory,memorysync,phidata
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Requires-Dist: agno>=2.8
Requires-Dist: httpx<1,>=0.25
Description-Content-Type: text/markdown

# agno-memorysync

MemorySync memory backend for [Agno](https://github.com/agno-agi/agno) agents.

A **memory-only db** for Agno's `MemoryManager`: user memories live in
MemorySync (persistent, semantic, cross-framework) while sessions and other
agent state stay in your local db. Ships a sync `MemorySyncDb` and an
async-native `AsyncMemorySyncDb` twin.

```bash
pip install agno-memorysync
```

## Quickstart

```python
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.memory import MemoryManager
from agno_memorysync import MemorySyncDb

agent = Agent(
    db=SqliteDb(db_file="agent.db"),                  # sessions: local
    memory_manager=MemoryManager(db=MemorySyncDb()),  # memories: MemorySync
    update_memory_on_run=True,   # extract + store memories after every run
    user_id="customer-42",
)
agent.run("I prefer teal dashboards and window seats")
agent.run("Which color should the new chart use?")   # remembers
```

The API key comes from the `MEMORYSYNC_API_KEY` environment variable (or
`MemorySyncDb(api_key=...)`). `add_memories_to_context` auto-enables when a
memory manager is set, so recalled memories are injected into context on
every run.

## Why this instead of the Mem0 toolkit?

Agno's ecosystem has one other memory SaaS: the `Mem0Tools` toolkit shipped
in agno core, plus a cookbook. Verified against their source:

| Behavior | Mem0 (`Mem0Tools` + cookbook) | **agno-memorysync** |
|---|---|---|
| Integration depth | LLM tools — the model must *decide* to recall | native `MemoryManager` backend — automatic extraction + injection |
| Async agents | ✗ sync client **blocks the event loop** | sync db with bounded budget + a true `AsyncBaseDb` twin |
| Missing user id | returns error **strings as tool output** | deterministic `default` namespace, never cross-user |
| Retries / re-runs | cookbook: *"comment out this line after running once"* | deterministic idempotency seeds — retries converge |
| Memory snapshot | cookbook injects a **static** snapshot fetched at construction | fresh recall every run |
| Agent scoping | `search`/`get_all` ignore `agent_id` | `agent_id` / `team_id` stored and filterable |
| Semantic search | — | **real vector search** via `search_content` (agno itself has only last_n / first_n / an extra LLM round-trip) |
| Whole-store wipe | — | `clear_memories()` **refuses**; per-user wipe is explicit |

## The memory-only contract

`BaseDb` covers sessions, evals, knowledge, metrics, and traces too.
`MemorySyncDb` implements **every memory method for real** and makes every
other surface raise `MemorySyncMemoryOnlyError` with the fix in the message —
a backend that silently pretended to store sessions would lose them.

```python
Agent(
    db=SqliteDb(...),                                # sessions, evals, ...
    memory_manager=MemoryManager(db=MemorySyncDb())  # memories only
)
```

## Semantic recall

```python
db = MemorySyncDb()
memories = db.get_user_memories(
    user_id="customer-42",
    search_content="what does the user like to eat?",  # real vector search
    limit=5,
)
```

Agno's built-in `search_user_memories` offers `last_n`, `first_n`, and
`agentic` (an extra LLM call that reads *all* memories). `search_content`
here is served by MemorySync's vector index — no LLM round-trip, ranked by
similarity.

## Async agents

```python
from agno_memorysync import AsyncMemorySyncDb

manager = MemoryManager(db=AsyncMemorySyncDb())
# MemoryManager awaits AsyncBaseDb natively on Agent.arun paths.
```

## Delete semantics — designed against data loss

| Call | What happens |
|---|---|
| `delete_user_memory(id, user_id=...)` | deletes that row; already-gone id is an idempotent no-op; a FAILED delete raises |
| `delete_user_memories([ids], user_id=...)` | bulk variant |
| `clear_memories()` | **always raises** — a nullary everything-wipe is how accounts get destroyed |
| `forget_user_memories(user_id)` | the explicit, scoped, loud per-user wipe |

## Failure policy

- **Reads fail open** under a hard budget (`recall_timeout`, default 1.2 s):
  a slow or down memory service degrades to no memories, never a stalled or
  crashed turn.
- **Writes fail open by default** (`fail_open_writes=True`): post-run
  extraction never turns a successful agent run into a failure. The failure
  is logged loudly and the call returns `None` — an honest contract value.
  Set `fail_open_writes=False` to raise instead.
- **Deletes are never fail-open.** A delete that did not happen raises.

## Configuration

| Parameter | Default | Meaning |
|---|---|---|
| `api_key` | `MEMORYSYNC_API_KEY` env var | API key |
| `base_url` | `https://api.memorysync.io` | Override for staging |
| `project_id` | – | Optional `X-Project-ID` header |
| `tenant_id` | auto-discovered | Skip discovery |
| `default_user_id` | `"default"` | Namespace when agno passes `user_id=None` |
| `recall_timeout` | `1.2` | Hard read budget (seconds) |
| `fail_open_writes` | `True` | Post-run extraction failures log instead of raise |
| `source` | `"agno"` | Source label on stored rows |

## Multimodal memories

Images flow to the model (`Agent.run(images=[Image(...)])`), the model's
understanding is extracted by `MemoryManager` as text, and the memory lands
here with its source `input` — image-derived memories work through agno's
NATIVE pipeline. (The Mem0 docs demo bypasses agno's memory system entirely
and pushes raw base64 into their cloud.)

## Tests

```bash
pip install -e . pytest pytest-asyncio
pytest tests -q   # 30+ checks against the real agno at latest
```

The suite drives the real `MemoryManager` and a REAL `Agent` run (stub
model, local session db) and reproduces each named competitor bug as a
regression test.

## License

MIT © MemorySync.
