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.
- zaniidb_agent_memory-0.6.0/.claude/settings.local.json +34 -0
- zaniidb_agent_memory-0.6.0/.dockerignore +12 -0
- zaniidb_agent_memory-0.6.0/.github/workflows/ci.yml +42 -0
- zaniidb_agent_memory-0.6.0/.gitignore +14 -0
- zaniidb_agent_memory-0.6.0/Dockerfile +18 -0
- zaniidb_agent_memory-0.6.0/LICENSE +21 -0
- zaniidb_agent_memory-0.6.0/PKG-INFO +392 -0
- zaniidb_agent_memory-0.6.0/README.md +360 -0
- zaniidb_agent_memory-0.6.0/assets/demo.gif +0 -0
- zaniidb_agent_memory-0.6.0/assets/hero.png +0 -0
- zaniidb_agent_memory-0.6.0/pyproject.toml +49 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/README.md +49 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/SKILL.md +158 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/reference/api.md +61 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/reference/ecosystem.md +313 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/reference/patterns.md +98 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/reference/python.md +89 -0
- zaniidb_agent_memory-0.6.0/skills/zanii/reference/typescript.md +105 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/__init__.py +9 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/adapters.py +69 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/autooffload.py +58 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/bench.py +101 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/cli.py +244 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/config.py +109 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/core.py +307 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/dashboard.py +91 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/embedding.py +63 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/gateway.py +250 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/llm.py +108 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/llm_cache.py +58 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/mcp_server.py +117 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/offload.py +83 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/personamem.py +415 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/__init__.py +3 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/consolidate.py +45 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/extractor.py +220 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/persona.py +106 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/scenes.py +81 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/scheduler.py +132 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/skills.py +77 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/pipeline/supersede.py +117 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/recall.py +73 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/store/__init__.py +36 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/store/base.py +70 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/store/postgres.py +465 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/store/sqlite.py +601 -0
- zaniidb_agent_memory-0.6.0/src/zanii_memory/types.py +45 -0
- zaniidb_agent_memory-0.6.0/tasks/todo.md +200 -0
- zaniidb_agent_memory-0.6.0/tests/conftest.py +20 -0
- zaniidb_agent_memory-0.6.0/tests/test_export_import.py +92 -0
- zaniidb_agent_memory-0.6.0/tests/test_gateway.py +100 -0
- zaniidb_agent_memory-0.6.0/tests/test_llm.py +47 -0
- zaniidb_agent_memory-0.6.0/tests/test_llm_cache.py +67 -0
- zaniidb_agent_memory-0.6.0/tests/test_mcp.py +39 -0
- zaniidb_agent_memory-0.6.0/tests/test_offload.py +40 -0
- zaniidb_agent_memory-0.6.0/tests/test_personamem.py +31 -0
- zaniidb_agent_memory-0.6.0/tests/test_pipeline.py +59 -0
- zaniidb_agent_memory-0.6.0/tests/test_pipeline_e2e.py +223 -0
- zaniidb_agent_memory-0.6.0/tests/test_postgres.py +62 -0
- zaniidb_agent_memory-0.6.0/tests/test_store.py +75 -0
- zaniidb_agent_memory-0.6.0/tests/test_supersede.py +123 -0
- 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,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,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
|