zaniidb-agent-memory 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. zaniidb_agent_memory-0.6.0/.claude/settings.local.json +34 -0
  2. zaniidb_agent_memory-0.6.0/.dockerignore +12 -0
  3. zaniidb_agent_memory-0.6.0/.github/workflows/ci.yml +42 -0
  4. zaniidb_agent_memory-0.6.0/.gitignore +14 -0
  5. zaniidb_agent_memory-0.6.0/Dockerfile +18 -0
  6. zaniidb_agent_memory-0.6.0/LICENSE +21 -0
  7. zaniidb_agent_memory-0.6.0/PKG-INFO +392 -0
  8. zaniidb_agent_memory-0.6.0/README.md +360 -0
  9. zaniidb_agent_memory-0.6.0/assets/demo.gif +0 -0
  10. zaniidb_agent_memory-0.6.0/assets/hero.png +0 -0
  11. zaniidb_agent_memory-0.6.0/pyproject.toml +49 -0
  12. zaniidb_agent_memory-0.6.0/skills/zanii/README.md +49 -0
  13. zaniidb_agent_memory-0.6.0/skills/zanii/SKILL.md +158 -0
  14. zaniidb_agent_memory-0.6.0/skills/zanii/reference/api.md +61 -0
  15. zaniidb_agent_memory-0.6.0/skills/zanii/reference/ecosystem.md +313 -0
  16. zaniidb_agent_memory-0.6.0/skills/zanii/reference/patterns.md +98 -0
  17. zaniidb_agent_memory-0.6.0/skills/zanii/reference/python.md +89 -0
  18. zaniidb_agent_memory-0.6.0/skills/zanii/reference/typescript.md +105 -0
  19. zaniidb_agent_memory-0.6.0/src/zanii_memory/__init__.py +9 -0
  20. zaniidb_agent_memory-0.6.0/src/zanii_memory/adapters.py +69 -0
  21. zaniidb_agent_memory-0.6.0/src/zanii_memory/autooffload.py +58 -0
  22. zaniidb_agent_memory-0.6.0/src/zanii_memory/bench.py +101 -0
  23. zaniidb_agent_memory-0.6.0/src/zanii_memory/cli.py +244 -0
  24. zaniidb_agent_memory-0.6.0/src/zanii_memory/config.py +109 -0
  25. zaniidb_agent_memory-0.6.0/src/zanii_memory/core.py +307 -0
  26. zaniidb_agent_memory-0.6.0/src/zanii_memory/dashboard.py +91 -0
  27. zaniidb_agent_memory-0.6.0/src/zanii_memory/embedding.py +63 -0
  28. zaniidb_agent_memory-0.6.0/src/zanii_memory/gateway.py +250 -0
  29. zaniidb_agent_memory-0.6.0/src/zanii_memory/llm.py +108 -0
  30. zaniidb_agent_memory-0.6.0/src/zanii_memory/llm_cache.py +58 -0
  31. zaniidb_agent_memory-0.6.0/src/zanii_memory/mcp_server.py +117 -0
  32. zaniidb_agent_memory-0.6.0/src/zanii_memory/offload.py +83 -0
  33. zaniidb_agent_memory-0.6.0/src/zanii_memory/personamem.py +415 -0
  34. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/__init__.py +3 -0
  35. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/consolidate.py +45 -0
  36. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/extractor.py +220 -0
  37. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/persona.py +106 -0
  38. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/scenes.py +81 -0
  39. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/scheduler.py +132 -0
  40. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/skills.py +77 -0
  41. zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/supersede.py +117 -0
  42. zaniidb_agent_memory-0.6.0/src/zanii_memory/recall.py +73 -0
  43. zaniidb_agent_memory-0.6.0/src/zanii_memory/store/__init__.py +36 -0
  44. zaniidb_agent_memory-0.6.0/src/zanii_memory/store/base.py +70 -0
  45. zaniidb_agent_memory-0.6.0/src/zanii_memory/store/postgres.py +465 -0
  46. zaniidb_agent_memory-0.6.0/src/zanii_memory/store/sqlite.py +601 -0
  47. zaniidb_agent_memory-0.6.0/src/zanii_memory/types.py +45 -0
  48. zaniidb_agent_memory-0.6.0/tasks/todo.md +200 -0
  49. zaniidb_agent_memory-0.6.0/tests/conftest.py +20 -0
  50. zaniidb_agent_memory-0.6.0/tests/test_export_import.py +92 -0
  51. zaniidb_agent_memory-0.6.0/tests/test_gateway.py +100 -0
  52. zaniidb_agent_memory-0.6.0/tests/test_llm.py +47 -0
  53. zaniidb_agent_memory-0.6.0/tests/test_llm_cache.py +67 -0
  54. zaniidb_agent_memory-0.6.0/tests/test_mcp.py +39 -0
  55. zaniidb_agent_memory-0.6.0/tests/test_offload.py +40 -0
  56. zaniidb_agent_memory-0.6.0/tests/test_personamem.py +31 -0
  57. zaniidb_agent_memory-0.6.0/tests/test_pipeline.py +59 -0
  58. zaniidb_agent_memory-0.6.0/tests/test_pipeline_e2e.py +223 -0
  59. zaniidb_agent_memory-0.6.0/tests/test_postgres.py +62 -0
  60. zaniidb_agent_memory-0.6.0/tests/test_store.py +75 -0
  61. zaniidb_agent_memory-0.6.0/tests/test_supersede.py +123 -0
  62. zaniidb_agent_memory-0.6.0/tests/test_v04_features.py +240 -0
@@ -0,0 +1,34 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(python -c \"import json;d=json.load\\(open\\('/c/Users/Hp/Desktop/project_for_all_time/TencentDB-Agent-Memory/package.json'\\)\\);print\\(json.dumps\\(d.get\\('dependencies'\\),indent=1\\)\\);print\\(json.dumps\\(d.get\\('devDependencies'\\),indent=1\\)\\)\")",
5
+ "Bash(python -m pip install -e \".[dev]\" -q)",
6
+ "Bash(python -m pytest -q)",
7
+ "Bash(python -m pip install -e \".[dev]\")",
8
+ "Bash(python *)",
9
+ "Bash(docker run *)",
10
+ "Bash(docker exec *)",
11
+ "Bash(break)",
12
+ "Bash(export ZANIIT_TEST_PG_DSN=\"postgresql://postgres:test@localhost:55432/zaniit_test\")",
13
+ "Bash(docker stop *)",
14
+ "WebSearch",
15
+ "WebFetch(domain:huggingface.co)",
16
+ "WebFetch(domain:github.com)",
17
+ "Bash(curl -s \"https://huggingface.co/api/datasets/bowen-upenn/PersonaMem-v1/tree/main\")",
18
+ "Bash(mkdir -p ~/.zaniit/personamem)",
19
+ "Bash(cp \"/c/Users/Hp/AppData/Local/Temp/claude/C--Users-Hp-Desktop-project-for-all-time-zaniitDB-Agent-Memory/48663eac-19f7-4951-a79c-d4ab0d216065/scratchpad/pm/\"* ~/.zaniit/personamem/)",
20
+ "Read(//c/Users/Hp/AppData/Local/Temp/claude/C--Users-Hp-Desktop-project-for-all-time-zaniitDB-Agent-Memory/48663eac-19f7-4951-a79c-d4ab0d216065/tasks/**)",
21
+ "Bash(gh auth *)",
22
+ "Bash(gh api *)",
23
+ "Bash(docker build *)",
24
+ "Bash(curl -s http://127.0.0.1:18520/health)",
25
+ "Bash(curl -s -X POST http://127.0.0.1:18520/seed -H \"Content-Type: application/json\" -d '{\"memories\":[{\"content\":\"docker smoke test fact\"}]}')",
26
+ "Bash(curl -s -X POST http://127.0.0.1:18520/search/memories -H \"Content-Type: application/json\" -d '{\"query\":\"docker smoke\"}')",
27
+ "Bash(docker rm *)",
28
+ "Bash(TWINE_USERNAME=__token__ TWINE_PASSWORD=$\\(tr -d '\\\\r\\\\n ' < secrets/pypi_key.txt\\) python -m twine upload --non-interactive dist/*)",
29
+ "Bash(grep -vE \"^\\\\s*$\")",
30
+ "Bash(gh repo *)",
31
+ "Skill(dataviz)"
32
+ ]
33
+ }
34
+ }
@@ -0,0 +1,12 @@
1
+ .git
2
+ .env
3
+ __pycache__
4
+ *.pyc
5
+ *.egg-info
6
+ dist
7
+ build
8
+ tests
9
+ tasks
10
+ .pytest_cache
11
+ secrets
12
+ *.env
@@ -0,0 +1,42 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ${{ matrix.os }}
11
+ strategy:
12
+ matrix:
13
+ os: [ubuntu-latest, windows-latest]
14
+ python: ["3.11", "3.12"]
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python }}
20
+ - run: pip install -e ".[dev]"
21
+ - run: pytest -q
22
+
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - uses: actions/setup-python@v5
28
+ with:
29
+ python-version: "3.12"
30
+ - run: pip install build twine
31
+ - run: python -m build
32
+ - run: twine check dist/*
33
+ - uses: actions/upload-artifact@v4
34
+ with:
35
+ name: dist
36
+ path: dist/
37
+
38
+ docker:
39
+ runs-on: ubuntu-latest
40
+ steps:
41
+ - uses: actions/checkout@v4
42
+ - run: docker build -t zaniidb-agent-memory:ci .
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .env
7
+ .venv/
8
+ venv/
9
+ .pytest_cache/
10
+ *.db
11
+ *.db-wal
12
+ *.db-shm
13
+ *.env
14
+ secrets/
@@ -0,0 +1,18 @@
1
+ FROM python:3.12-slim
2
+
3
+ WORKDIR /app
4
+ COPY pyproject.toml README.md LICENSE ./
5
+ COPY src ./src
6
+ RUN pip install --no-cache-dir .
7
+
8
+ # Data lives in a volume; SQLite + scenes + persona + refs under /data
9
+ ENV ZANII_DATA_DIR=/data \
10
+ ZANII_GATEWAY_HOST=0.0.0.0 \
11
+ ZANII_GATEWAY_PORT=8520
12
+ VOLUME /data
13
+ EXPOSE 8520
14
+
15
+ HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
16
+ CMD python -c "import urllib.request,sys;sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8520/health',timeout=4).status==200 else 1)"
17
+
18
+ CMD ["zanii-memory", "serve"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zanii
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,392 @@
1
+ Metadata-Version: 2.4
2
+ Name: zaniidb-agent-memory
3
+ Version: 0.6.0
4
+ Summary: ZaniiDB Agent Memory - layered long-term memory (L0-L3) for AI agents: SQLite + hybrid BM25/vector recall, LLM extraction pipeline, FastAPI gateway, Python SDK.
5
+ Project-URL: Homepage, https://github.com/vigilancetrent/zaniiDB-Agent-Memory
6
+ Project-URL: Repository, https://github.com/vigilancetrent/zaniiDB-Agent-Memory
7
+ Author: Zanii
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agent,llm,mcp,memory,personamem,rag,sqlite,vector-search
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Database
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.11
19
+ Requires-Dist: fastapi>=0.115
20
+ Requires-Dist: httpx>=0.27
21
+ Requires-Dist: mcp>=1.2
22
+ Requires-Dist: pydantic-settings>=2.3
23
+ Requires-Dist: pydantic>=2.7
24
+ Requires-Dist: sqlite-vec>=0.1.6
25
+ Requires-Dist: uvicorn>=0.30
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Provides-Extra: postgres
30
+ Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ZaniiDB Agent Memory
34
+
35
+ <p align="center">
36
+ <img src="assets/hero.png" alt="ZaniiDB Agent Memory — layered long-term memory for AI agents" width="920" />
37
+ </p>
38
+
39
+ <p align="center">
40
+ <img src="assets/demo.gif" alt="ZaniiDB Agent Memory demo: install, serve, capture, search, benchmark" width="760" />
41
+ </p>
42
+
43
+ **Layered long-term memory for AI agents.** Agents remember; humans stop repeating themselves.
44
+
45
+ ZaniiDB Agent Memory captures conversations, distills them into structured memories with an LLM, and recalls the right context before each turn — as a Python SDK, an HTTP gateway, or a CLI.
46
+
47
+ ## Architecture: the memory pyramid
48
+
49
+ Memory is never flat. Both formation and recall are hierarchical:
50
+
51
+ | Layer | What | Where |
52
+ | :--- | :--- | :--- |
53
+ | **L3 Persona** | Narrative user profile (archetype, preferences, interaction protocol) | `persona.md` — human-readable |
54
+ | **L2 Scenes** | Scene blocks grouping related facts | `scenes/*.md` — human-readable |
55
+ | **L1 Atoms** | Atomic memories: `persona` / `episodic` / `instruction`, priority-scored | SQLite (+ vectors) |
56
+ | **L0 Conversations** | Raw captured turns | SQLite, BM25-searchable |
57
+
58
+ - **Hybrid recall**: FTS5 BM25 + sqlite-vec cosine KNN, fused with Reciprocal Rank Fusion.
59
+ - **Self-correcting memory**: new facts supersede outdated ones (LLM conflict resolution with a same-type guard); paraphrase re-extractions are dropped, not hoarded. Superseded history stays auditable via `superseded_by`.
60
+ - **Scene synthesis**: oversized scene ledgers are LLM-condensed into resolved current-state narratives ("newest wins", update history preserved).
61
+ - **Provenance guard**: the client records which model the endpoint *reports serving* and warns on mismatch — benchmark and production results can't be silently mislabeled.
62
+ - **White-box**: the top layers are plain markdown files — open them and read what the agent knows.
63
+ - **Graceful degradation**: no embedding key → keyword-only search; no LLM key → capture and search still work, extraction pauses.
64
+ - **Zero infra**: one SQLite file. No servers required to start.
65
+
66
+ ## Install
67
+
68
+ ```bash
69
+ pip install -e . # SQLite backend (default)
70
+ pip install -e ".[postgres]" # + PostgreSQL/pgvector backend
71
+ ```
72
+
73
+ Requires Python 3.11+.
74
+
75
+ ## Storage backends
76
+
77
+ **SQLite (default)** — zero config, one file, ideal for local agents and single-user deployments.
78
+
79
+ **PostgreSQL + pgvector** — for production/server deployments:
80
+
81
+ ```bash
82
+ export ZANII_DATABASE_URL="postgresql://user:pass@host:5432/dbname"
83
+ ```
84
+
85
+ The store creates its schema (and the `vector` extension) on first start. If pgvector isn't available on the server, it degrades to keyword-only search — same contract as SQLite. Works with Neon, Supabase, RDS, or any Postgres 14+.
86
+
87
+ **Multi-tenancy**: run one database (or one `ZANII_DATA_DIR`) per tenant — hard isolation, no cross-tenant leak surface, and per-tenant export/deletion for free. Point each gateway/MCP instance at its tenant's database.
88
+
89
+ **Migration between backends** is export/import:
90
+
91
+ ```bash
92
+ zanii-memory export backup.json # from the current backend
93
+ ZANII_DATABASE_URL=postgresql://... zanii-memory import backup.json # into Postgres
94
+ ```
95
+
96
+ ## Configuration (environment variables)
97
+
98
+ Everything has a default; the system runs with zero configuration. Set `ZANII_LLM_*` to enable memory extraction.
99
+
100
+ | Variable | Default | Description |
101
+ | :--- | :--- | :--- |
102
+ | `ZANII_DATA_DIR` | `~/.zanii/memory` | Data directory (db, scenes, persona, refs, canvas) |
103
+ | `ZANII_DATABASE_URL` | — | `postgresql://...` selects the Postgres backend; empty = SQLite |
104
+ | `ZANII_LLM_BASE_URL` | — | OpenAI-compatible endpoint, e.g. `https://api.openai.com/v1` |
105
+ | `ZANII_LLM_API_KEY` | — | API key |
106
+ | `ZANII_LLM_MODEL` | — | Model name, e.g. `gpt-4o-mini` |
107
+ | `ZANII_EMBEDDING_BASE_URL` | LLM base URL | Embeddings endpoint (falls back to LLM endpoint) |
108
+ | `ZANII_EMBEDDING_API_KEY` | LLM key | Embeddings key |
109
+ | `ZANII_EMBEDDING_MODEL` | — | e.g. `text-embedding-3-small` |
110
+ | `ZANII_EMBEDDING_DIMENSIONS` | `1536` | Vector dimensions |
111
+ | `ZANII_RECALL_STRATEGY` | `hybrid` | `keyword` / `embedding` / `hybrid` |
112
+ | `ZANII_RECALL_MAX_RESULTS` | `5` | Memories injected per recall |
113
+ | `ZANII_RECALL_MAX_TOTAL_CHARS` | `4000` | Char budget for recalled memories |
114
+ | `ZANII_PIPELINE_EVERY_N_TURNS` | `5` | Extract memories every N turns |
115
+ | `ZANII_PIPELINE_WARMUP` | `true` | New sessions extract at turn 1, doubling up to N |
116
+ | `ZANII_PIPELINE_IDLE_TIMEOUT_S` | `600` | Flush pending turns after idle |
117
+ | `ZANII_PIPELINE_PERSONA_EVERY_N` | `50` | Regenerate persona every N new memories |
118
+ | `ZANII_PIPELINE_SKILLS` | `true` | Distill skill/SOP docs after each persona regeneration |
119
+ | `ZANII_FTS_TOKENIZER` | `unicode61` | SQLite FTS tokenizer: `unicode61` / `trigram` (CJK) |
120
+ | `ZANII_PG_TEXT_SEARCH_CONFIG` | `simple` | Postgres text-search config name |
121
+ | `ZANII_DEDUP_MAX_DISTANCE` | `0.08` | Cosine distance for near-duplicate consolidation |
122
+ | `ZANII_RETENTION_EPISODIC_DAYS` | `0` | Episodic memory retention (0 = forever) |
123
+ | `ZANII_RETENTION_KEEP_PRIORITY` | `90` | Episodic memories at/above this priority never decay |
124
+ | `ZANII_AUDIT_ENABLED` | `false` | Record every memory operation to the audit log |
125
+ | `ZANII_LLM_CACHE_PATH` | — | Exact-request LLM/embedding response cache (SQLite); identical requests replay free. The benchmark enables it automatically |
126
+ | `ZANII_GATEWAY_HOST` / `_PORT` | `127.0.0.1:8520` | Gateway bind |
127
+ | `ZANII_GATEWAY_API_KEY` | — | When set, all routes except `/health` require `Authorization: Bearer <key>` |
128
+ | `ZANII_CORS_ORIGINS` | — | Comma-separated CORS allow-list (empty = none) |
129
+
130
+ A `.env` file in the working directory is also read.
131
+
132
+ ## SDK
133
+
134
+ ```python
135
+ import asyncio
136
+ from zanii_memory import ZaniiMemory
137
+
138
+ async def main():
139
+ memory = ZaniiMemory() # config from ZANII_* env vars
140
+ await memory.initialize()
141
+
142
+ # Before each agent turn: recall relevant context
143
+ recall = await memory.recall("what stack does the user prefer?", session_key="s1")
144
+ print(recall.prepend_context) # relevant memories -> prepend to the user prompt
145
+ print(recall.append_system_context) # persona -> append to the system prompt
146
+
147
+ # After each completed turn: capture it
148
+ await memory.capture("s1", [
149
+ {"role": "user", "content": "From now on always answer in French."},
150
+ {"role": "assistant", "content": "Bien sûr!"},
151
+ ])
152
+
153
+ # On session end: flush extraction immediately
154
+ await memory.end_session("s1")
155
+ await memory.close()
156
+
157
+ asyncio.run(main())
158
+ ```
159
+
160
+ ## HTTP gateway
161
+
162
+ ```bash
163
+ zanii-memory serve # http://127.0.0.1:8520 — OpenAPI docs at /docs
164
+ ```
165
+
166
+ | Route | Body |
167
+ | :--- | :--- |
168
+ | `GET /health` | — (always open, no auth) |
169
+ | `POST /recall` | `{"query", "session_key"}` |
170
+ | `POST /capture` | `{"session_key", "messages": [{"role", "content", "timestamp?"}], "session_id?"}` |
171
+ | `POST /search/memories` | `{"query", "limit?", "type?"}` |
172
+ | `POST /search/conversations` | `{"query", "limit?", "session_key?"}` |
173
+ | `POST /session/end` | `{"session_key"}` |
174
+ | `POST /seed` | `{"memories": [{"content", "type?", "priority?"}]}` |
175
+ | `POST /offload` | `{"session_key", "content", "label?"}` |
176
+ | `GET /offload/{node_id}` | — |
177
+ | `GET /canvas/{session_key}` | — |
178
+ | `POST /export` | — |
179
+ | `POST /import` | an `export` snapshot |
180
+
181
+ ```bash
182
+ curl -X POST http://127.0.0.1:8520/recall \
183
+ -H "Content-Type: application/json" \
184
+ -d '{"query": "user preferences", "session_key": "s1"}'
185
+ ```
186
+
187
+ ## MCP server
188
+
189
+ Give any MCP-capable agent (Claude Code, IDE agents, custom clients) direct access to the user's memory:
190
+
191
+ ```bash
192
+ # Claude Code
193
+ claude mcp add zanii-memory -- zanii-memory mcp
194
+ ```
195
+
196
+ Or in a generic MCP client config:
197
+
198
+ ```json
199
+ {
200
+ "mcpServers": {
201
+ "zanii-memory": {
202
+ "command": "zanii-memory",
203
+ "args": ["mcp"],
204
+ "env": { "ZANII_DATA_DIR": "~/.zanii/memory" }
205
+ }
206
+ }
207
+ }
208
+ ```
209
+
210
+ | Tool | Purpose |
211
+ | :--- | :--- |
212
+ | `memory_search` | Hybrid search over long-term memories (optional `type` filter) |
213
+ | `conversation_search` | Keyword search over raw captured conversations |
214
+ | `save_memory` | Store a durable fact / instruction directly |
215
+ | `get_persona` | The user's narrative persona profile |
216
+
217
+ The server runs over stdio and shares the same `ZANII_*` configuration and data directory as the SDK and gateway — memories are shared across all three surfaces.
218
+
219
+ ## Observability dashboard
220
+
221
+ `zanii-memory serve`, then open **http://127.0.0.1:8520/dashboard** — live stats, memory search, recent memories, persona, scenes, skills, and the audit trail on one page. When `ZANII_GATEWAY_API_KEY` is set, append `?token=<key>`.
222
+
223
+ ## Benchmark
224
+
225
+ Measure retrieval quality of your exact configuration (backend, tokenizer, keyword vs hybrid) on a built-in eval of 20 queries against seeded facts + distractors:
226
+
227
+ ```bash
228
+ zanii-memory bench
229
+ ```
230
+
231
+ Measured results (SQLite backend):
232
+
233
+ | Mode | recall@1 | recall@5 | MRR |
234
+ | :--- | :---: | :---: | :---: |
235
+ | keyword only (zero config) | 80% | 95% | 0.867 |
236
+ | hybrid (OpenAI `text-embedding-3-small`) | **100%** | **100%** | **1.000** |
237
+
238
+ Uses a throwaway data dir — never touches real memory.
239
+
240
+ ### Public benchmark: PersonaMem
241
+
242
+ `zanii-memory personamem` runs the [PersonaMem](https://github.com/bowen-upenn/PersonaMem) (COLM 2025) benchmark against the live product — multi-session conversations with evolving personas, 4-way multiple-choice questions about the user's *current* profile. Our protocol is **stricter than the official full-context eval**: the dataset's ground-truth persona system messages are excluded (memory is built from dialogue alone), and questions are answered from a few hundred tokens of *recalled memory* instead of the full 32k context.
243
+
244
+ ```bash
245
+ zanii-memory personamem --contexts 1 --max-questions 15 --baseline
246
+ ```
247
+
248
+ `--baseline` also scores a no-memory control to show the uplift. Scale up with `--contexts` / `--max-questions` / `--size 128k` (LLM cost scales with ingested contexts).
249
+
250
+ Measured (gpt-4o, seven independent 150-question runs, 2026-07-18; recommended configuration = `--scenes`, which adds the L2 chronological fact ledger to the answering context):
251
+
252
+ | Setup | Accuracy |
253
+ | :--- | :---: |
254
+ | No memory (control), pooled n=750 | 43.1% |
255
+ | Frontier models with the FULL 32k context (paper) | ~52% |
256
+ | ZaniiDB memory, all runs pooled n=1050 | **55.2%** |
257
+ | — recommended config (`--scenes`), pooled n=600 | 56.2% (best single run 61.3%) |
258
+
259
+ On `gpt-5.6-luna` with v0.5.x (conflict resolution + scene synthesis): **58.0–58.7%** across two runs, with record per-type results (preference-aligned recommendations 9/9, reasons-behind-updates 82–93% on every run across both models).
260
+
261
+ A robust, many-times-replicated **+12–16-point uplift** over the no-memory control, matching or exceeding frontier full-context reading from ~100× less context per answer. Run-to-run variance on this eval is ±4pp — we report pooled numbers and label the 61.3% for what it is: the best single run, not the expected value. Every number here is reproducible with:
262
+
263
+ ```bash
264
+ zanii-memory personamem --contexts 12 --max-questions 150 --baseline --scenes
265
+ ```
266
+
267
+ ## Agent integration
268
+
269
+ **Hooks for any framework** (LangGraph, CrewAI, Pydantic-AI, OpenAI Agents — recipes in `adapters.py`):
270
+
271
+ ```python
272
+ from zanii_memory.adapters import AgentMemoryHooks
273
+
274
+ hooks = AgentMemoryHooks(memory, session_key="user-42")
275
+ injection = await hooks.before_turn(user_text) # memories + persona
276
+ messages = hooks.inject(messages, injection) # OpenAI-style message list
277
+ ...
278
+ await hooks.after_turn(user_text, assistant_text) # capture the turn
279
+ ```
280
+
281
+ **Automatic context offload** — stubs out oversized tool outputs transparently:
282
+
283
+ ```python
284
+ from zanii_memory.autooffload import AutoOffloader
285
+
286
+ auto = AutoOffloader(memory, "task-1", threshold_chars=4000)
287
+ messages = await auto.filter_messages(messages) # before each LLM call
288
+ ```
289
+
290
+ ## Temporal search
291
+
292
+ `search_memories` (SDK/gateway/MCP) accepts `since`/`until` bounds — "what did the user decide last week?":
293
+
294
+ ```bash
295
+ curl -X POST .../search/memories -d '{"query": "deploy decision", "since": "2026-07-10"}'
296
+ ```
297
+
298
+ ## Team memory
299
+
300
+ Memories with `scope: "team"` are shared org knowledge — injected into every session's system context alongside the persona:
301
+
302
+ ```bash
303
+ zanii-memory seed team_sops.json # entries: {"content": ..., "scope": "team"}
304
+ ```
305
+
306
+ The MCP `save_memory` tool takes the same `scope` parameter.
307
+
308
+ ## Skills (SOPs distilled from memory)
309
+
310
+ The pipeline distills recurring task patterns from episodic + instruction memories into reusable procedure docs in `skills/*.md` — automatically after each persona regeneration, or on demand with `zanii-memory skills`. Disable auto mode with `ZANII_PIPELINE_SKILLS=false`.
311
+
312
+ ## Consolidation & retention
313
+
314
+ `zanii-memory consolidate` (also `POST /consolidate`, and automatic each persona cycle):
315
+
316
+ - merges semantically near-duplicate memories (vector distance ≤ `ZANII_DEDUP_MAX_DISTANCE`, higher priority wins)
317
+ - deletes episodic memories older than `ZANII_RETENTION_EPISODIC_DAYS` (default 0 = keep forever) unless priority ≥ `ZANII_RETENTION_KEEP_PRIORITY` — persona and instruction memories never decay
318
+
319
+ ## Security & compliance
320
+
321
+ - **Audit log**: `ZANII_AUDIT_ENABLED=true` records every capture/recall/search/seed/consolidate with timestamps — `zanii-memory audit` or `GET /audit`.
322
+ - **Encryption at rest**: delegate to the storage layer — full-disk encryption for the SQLite file, or Postgres-native options (cloud-provider TDE, encrypted volumes, `pgcrypto`). The app layer stays encryption-agnostic by design.
323
+ - **Right to erasure**: per-tenant database + `export`/delete covers GDPR data portability and deletion.
324
+
325
+ ## Multilingual / CJK search
326
+
327
+ - SQLite: `ZANII_FTS_TOKENIZER=trigram` enables CJK-friendly matching. Queries shorter than 3 contiguous characters (e.g. 2-char Chinese words) automatically fall back to a substring scan, so nothing is unfindable. Applies at database creation.
328
+ - Postgres: `ZANII_PG_TEXT_SEARCH_CONFIG` selects the text-search config (`simple`, `english`, or a custom CJK config like zhparser).
329
+ - Hybrid mode with embeddings is language-agnostic and the best option for multilingual corpora.
330
+
331
+ ## Context offload (short-term memory)
332
+
333
+ In long tasks, verbose tool outputs are the biggest token drain. Offload them and keep only a compact stub + a symbolic task canvas in context:
334
+
335
+ ```python
336
+ result = await memory.offload("task-1", huge_tool_output, label="build logs")
337
+ # result["stub"] -> '[offloaded:N1a2b3c4d] build logs' (keep this in context)
338
+ full = await memory.retrieve_ref(result["node_id"]) # drill down on demand
339
+ print(await memory.get_canvas("task-1")) # Mermaid graph of task steps
340
+ ```
341
+
342
+ The canvas is a Mermaid graph (`canvas/<session>.mmd`) where each node is an offloaded step — the agent reasons over the symbol graph and retrieves raw text by `node_id` only when needed. Gateway routes: `POST /offload`, `GET /offload/{node_id}`, `GET /canvas/{session_key}`. All artifacts are plain files under the data directory: `refs/*.md` and `canvas/*.mmd`.
343
+
344
+ ## Export / import
345
+
346
+ Portable, idempotent memory snapshots — backup, migration, and cross-device sync:
347
+
348
+ ```bash
349
+ zanii-memory export backup.json # memories + conversations + persona + scenes
350
+ zanii-memory import backup.json # skips entries that already exist
351
+ ```
352
+
353
+ Also available as `POST /export` / `POST /import` on the gateway and `export_memory()` / `import_memory()` in the SDK. Imports re-embed memories when embeddings are enabled, so vectors survive backend or model changes.
354
+
355
+ ## CLI
356
+
357
+ ```bash
358
+ zanii-memory serve # run the gateway
359
+ zanii-memory mcp # run the MCP server (stdio)
360
+ zanii-memory seed facts.json # bulk-insert memories (JSON array)
361
+ zanii-memory search "coffee" # search L1 memories
362
+ zanii-memory search -c "kafka" # search raw conversations
363
+ zanii-memory export backup.json # portable memory snapshot
364
+ zanii-memory import backup.json # idempotent restore / migration
365
+ zanii-memory inspect # stats + persona
366
+ ```
367
+
368
+ ## Development
369
+
370
+ ```bash
371
+ pip install -e ".[dev]"
372
+ pytest
373
+ ```
374
+
375
+ ## Roadmap
376
+
377
+ - [x] L0→L3 layered memory, hybrid RRF recall, gateway, CLI
378
+ - [x] MCP server (`memory_search`, `conversation_search`, `save_memory`, `get_persona`)
379
+ - [x] Postgres + pgvector backend behind the store interface
380
+ - [x] Short-term context offload (symbolic Mermaid task canvas) + automatic offload middleware
381
+ - [x] Memory export / import / migration (idempotent, re-embedding)
382
+ - [x] Multi-tenancy via database-per-tenant isolation
383
+ - [x] Retrieval benchmark harness (`zanii-memory bench`)
384
+ - [x] Observability dashboard (`/dashboard`)
385
+ - [x] Framework-agnostic agent hooks (`adapters.py`)
386
+ - [x] Consolidation (near-duplicate merge) + retention decay
387
+ - [x] Temporal search (`since`/`until`)
388
+ - [x] Skill/SOP generation from memories
389
+ - [x] Team memory scope (shared org knowledge)
390
+ - [x] CJK tokenizer options, audit log, encryption guidance
391
+
392
+ MIT © Zanii