memarena 0.1.2__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 (49) hide show
  1. memarena-0.1.2/.gitignore +14 -0
  2. memarena-0.1.2/CHANGELOG.md +33 -0
  3. memarena-0.1.2/LICENSE +21 -0
  4. memarena-0.1.2/PKG-INFO +280 -0
  5. memarena-0.1.2/README.es.md +253 -0
  6. memarena-0.1.2/README.md +246 -0
  7. memarena-0.1.2/pyproject.toml +65 -0
  8. memarena-0.1.2/src/memarena/__init__.py +3 -0
  9. memarena-0.1.2/src/memarena/cli.py +318 -0
  10. memarena-0.1.2/src/memarena/data/demo/LICENSE-LongMemEval.txt +21 -0
  11. memarena-0.1.2/src/memarena/data/demo/dataset.json +8 -0
  12. memarena-0.1.2/src/memarena/data/demo/history.json +1879 -0
  13. memarena-0.1.2/src/memarena/data/demo/questions.json +360 -0
  14. memarena-0.1.2/src/memarena/data/demo/results.json +2638 -0
  15. memarena-0.1.2/src/memarena/errors.py +25 -0
  16. memarena-0.1.2/src/memarena/importers.py +173 -0
  17. memarena-0.1.2/src/memarena/judge.py +47 -0
  18. memarena-0.1.2/src/memarena/llm.py +252 -0
  19. memarena-0.1.2/src/memarena/longmemeval.py +107 -0
  20. memarena-0.1.2/src/memarena/metrics.py +48 -0
  21. memarena-0.1.2/src/memarena/models.py +141 -0
  22. memarena-0.1.2/src/memarena/prompts.py +80 -0
  23. memarena-0.1.2/src/memarena/qa.py +124 -0
  24. memarena-0.1.2/src/memarena/report.py +123 -0
  25. memarena-0.1.2/src/memarena/runner.py +202 -0
  26. memarena-0.1.2/src/memarena/systems/__init__.py +42 -0
  27. memarena-0.1.2/src/memarena/systems/base.py +71 -0
  28. memarena-0.1.2/src/memarena/systems/baselines.py +142 -0
  29. memarena-0.1.2/src/memarena/systems/letta_adapter.py +99 -0
  30. memarena-0.1.2/src/memarena/systems/mem0_adapter.py +209 -0
  31. memarena-0.1.2/src/memarena/systems/supermemory_adapter.py +63 -0
  32. memarena-0.1.2/src/memarena/viewer.py +115 -0
  33. memarena-0.1.2/src/memarena/viewer_dist/assets/index-C-JbZZPk.js +9 -0
  34. memarena-0.1.2/src/memarena/viewer_dist/assets/index-lrIwlJgU.css +1 -0
  35. memarena-0.1.2/src/memarena/viewer_dist/favicon.svg +1 -0
  36. memarena-0.1.2/src/memarena/viewer_dist/index.html +24 -0
  37. memarena-0.1.2/tests/__init__.py +0 -0
  38. memarena-0.1.2/tests/conftest.py +113 -0
  39. memarena-0.1.2/tests/fixtures/chatgpt_conversations.json +23 -0
  40. memarena-0.1.2/tests/fixtures/claude_conversations.json +11 -0
  41. memarena-0.1.2/tests/fixtures/generic.jsonl +4 -0
  42. memarena-0.1.2/tests/fixtures/longmemeval_mini.json +17 -0
  43. memarena-0.1.2/tests/test_adapters.py +111 -0
  44. memarena-0.1.2/tests/test_baselines.py +56 -0
  45. memarena-0.1.2/tests/test_cli_viewer.py +160 -0
  46. memarena-0.1.2/tests/test_importers.py +60 -0
  47. memarena-0.1.2/tests/test_llm.py +107 -0
  48. memarena-0.1.2/tests/test_qa.py +79 -0
  49. memarena-0.1.2/tests/test_runner.py +78 -0
@@ -0,0 +1,14 @@
1
+ .env
2
+ .venv/
3
+ __pycache__/
4
+ *.pyc
5
+ dist/
6
+ build/
7
+ .pytest_cache/
8
+ .ruff_cache/
9
+ node_modules/
10
+ viewer/dist/
11
+ memarena-out/
12
+ site/
13
+ *.egg-info/
14
+ .DS_Store
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ ## 0.1.2 - 2026-11-03
4
+
5
+ - Include LongMemEval MIT notice with the bundled demo data
6
+ (`src/memarena/data/demo/LICENSE-LongMemEval.txt`, copied unmodified from xiaowu0162/LongMemEval) and cite it
7
+ in a "Third-party data" section of the README.
8
+
9
+ ## 0.1.1 - 2026-11-03
10
+
11
+ - Terminal: `memarena demo` and `run` fit narrow terminals (80 columns, CI logs, pipes). Below 150 columns the
12
+ per-category accuracy moves to a second table; below 100 columns p50 and ingestion time are dropped.
13
+ - Viewer: the judge audit is stored as structured data and shown in the viewer's language (EN/ES).
14
+ - Viewer: system cards share one layout (kind chip under the name), so they line up.
15
+ - Viewer: answers render the bold/italic/code markdown models emit instead of showing raw asterisks; latency and
16
+ token counts sit on their own line in every answer card; the question field is labelled "Question"; Spanish
17
+ cards say "correctas".
18
+ - Viewer: deep links `#questions`, `#method` and `#q=<question id>` open a tab or expand a question.
19
+
20
+ ## 0.1.0 - 2026-11-03
21
+
22
+ First release.
23
+
24
+ - Importers: ChatGPT export, Claude export (`conversations.json` or the `.zip`), generic JSONL.
25
+ - `memarena generate`: questions with gold answers and evidence pointers in LongMemEval's five categories
26
+ (single-session, multi-session, temporal, knowledge update, abstention), written as `pending` for review.
27
+ - `memarena run`: systems mem0 (OSS), Letta (REST API) and Supermemory (API), always next to three baselines:
28
+ full context, BM25 and embeddings. Same answer model and prompt for every system.
29
+ - Metrics: accuracy from an LLM judge (prompt published in `docs/judge-prompt.md`), retrieval latency p50/p95,
30
+ ingestion time, prompt tokens and cost, overall and per category.
31
+ - Viewer (React) bundled in the wheel: `memarena view`, question review and editing, static export for Pages.
32
+ - `memarena demo`: a recorded real run on a 20-question LongMemEval subset (MIT).
33
+ - `--max-cost` budget checked before every paid call, privacy warning before sending data to a cloud provider.
memarena-0.1.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Diego Pérez
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,280 @@
1
+ Metadata-Version: 2.5
2
+ Name: memarena
3
+ Version: 0.1.2
4
+ Summary: Benchmark memory layers on your own conversations, not on someone else's leaderboard.
5
+ Project-URL: Homepage, https://diegoperezg7.github.io/memarena/
6
+ Project-URL: Repository, https://github.com/diegoperezg7/memarena
7
+ Project-URL: Issues, https://github.com/diegoperezg7/memarena/issues
8
+ Project-URL: Changelog, https://github.com/diegoperezg7/memarena/blob/main/CHANGELOG.md
9
+ Author: Diego Pérez
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: agents,benchmark,evaluation,letta,llm,mem0,memory,rag
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: openai==3.42.1
24
+ Requires-Dist: rich==15.0.0
25
+ Requires-Dist: typer==0.27.3
26
+ Provides-Extra: letta
27
+ Requires-Dist: letta-client==1.12.1; extra == 'letta'
28
+ Provides-Extra: mem0
29
+ Requires-Dist: mem0ai==2.2.1; extra == 'mem0'
30
+ Requires-Dist: ollama==0.6.3; extra == 'mem0'
31
+ Provides-Extra: supermemory
32
+ Requires-Dist: supermemory==5.0.1; extra == 'supermemory'
33
+ Description-Content-Type: text/markdown
34
+
35
+ <p align="center">
36
+ <img src="docs/assets/banner.svg" alt="memarena" width="760">
37
+ </p>
38
+
39
+ <p align="center">
40
+ <a href="https://github.com/diegoperezg7/memarena/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/diegoperezg7/memarena/actions/workflows/ci.yml/badge.svg"></a>
41
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-7945c5"></a>
42
+ <a href="https://github.com/diegoperezg7/memarena/releases"><img alt="Release" src="https://img.shields.io/github/v/release/diegoperezg7/memarena?color=7945c5"></a>
43
+ <a href="https://github.com/diegoperezg7/memarena/stargazers"><img alt="Stars" src="https://img.shields.io/github/stars/diegoperezg7/memarena?style=flat&color=7945c5"></a>
44
+ </p>
45
+
46
+ <p align="center"><b>Benchmark memory layers on your own conversations, not on someone else's leaderboard.</b></p>
47
+
48
+ <p align="center">English · <a href="README.es.md">Español</a> · <a href="https://diegoperezg7.github.io/memarena/">Live example viewer</a></p>
49
+
50
+ memarena turns your ChatGPT or Claude export into a memory test: it writes questions with gold answers
51
+ from your own history (you review them), feeds the history to each memory layer, asks every question and
52
+ grades the answers with a published LLM-judge prompt. Every run also includes the two things a memory layer
53
+ has to beat: **pasting the whole history** and **simple retrieval** (BM25 and embeddings).
54
+
55
+ ![memarena viewer, recorded demo](docs/assets/screenshot-light.png)
56
+
57
+ ## Why
58
+
59
+ The public memory harnesses are each published by a memory vendor and are built around fixed datasets:
60
+ [supermemoryai/memorybench](https://github.com/supermemoryai/memorybench) (334 stars, Supermemory),
61
+ [vectorize-io/agent-memory-benchmark](https://github.com/vectorize-io/agent-memory-benchmark) (163, Vectorize) and
62
+ [mem0ai/memory-benchmarks](https://github.com/mem0ai/memory-benchmarks) (134, Mem0) (star counts checked 2026-11-03).
63
+
64
+ Vendor numbers do not agree with each other. Mem0's paper reports, on LoCoMo, a 26% relative improvement in
65
+ LLM-as-a-Judge over OpenAI's memory and 91% lower p95 latency with over 90% fewer tokens than full context
66
+ ([arXiv 2504.19413](https://arxiv.org/abs/2504.19413)). Letta then reported 74.0% on LoCoMo with GPT-4o mini for a
67
+ plain filesystem agent, against the 68.5% Mem0 published for itself, adding that
68
+ "Mem0 did not respond to requests for clarification"
69
+ ([Letta blog, 12 Aug 2025](https://www.letta.com/blog/benchmarking-ai-agent-memory)). Zep reports 94.8% on DMR vs
70
+ 93.4% for MemGPT and up to 18.5% higher accuracy on LongMemEval ([arXiv 2501.13956](https://arxiv.org/abs/2501.13956)).
71
+
72
+ Whichever is right on LoCoMo, none of it tells you how a memory layer does on **your** conversations, with
73
+ **your** model, against the cheap option of pasting everything. memarena is vendor-neutral, runs on your
74
+ exports, and always puts full context, BM25 and embeddings in the same table.
75
+
76
+ ## Quickstart
77
+
78
+ Recorded demo, no keys, no Docker, no model calls (under 2 minutes from a clean machine with `uv`):
79
+
80
+ ```bash
81
+ uvx --from git+https://github.com/diegoperezg7/memarena@v0.1.2 memarena demo
82
+ ```
83
+
84
+ or from a clone:
85
+
86
+ ```bash
87
+ git clone https://github.com/diegoperezg7/memarena && cd memarena && uv run memarena demo
88
+ ```
89
+
90
+ ![memarena demo in the terminal](docs/assets/demo.gif)
91
+
92
+ It prints the table below and opens the viewer on http://127.0.0.1:8765 (add `--no-view` to only print).
93
+ `uvx memarena` is coming to PyPI.
94
+
95
+ ## Example output
96
+
97
+ This is a **recorded real run** (`memarena run`, 2026-11-03), shipped in the package and shown by `memarena demo`:
98
+ 20 questions (4 per category) from [LongMemEval](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned)
99
+ (MIT), with the evidence sessions of all 20 merged into **one** history of 28 sessions (~75k tokens), the way a
100
+ single user's export looks.
101
+
102
+ | System | Accuracy | Single | Multi | Temporal | Update | Abstain | Retrieval p50 / p95 | Ingest | Prompt tokens / q | Cost |
103
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
104
+ | **mem0** (memory layer) | **75%** | 50% | 50% | 75% | 100% | 100% | 49 / 134 ms | 21.6 min | 611 | $0.174 |
105
+ | bm25 (baseline) | 70% | 75% | 25% | 50% | 100% | 100% | 0.9 / 3.9 ms | 0.03 s | 4,180 | $0.008 |
106
+ | embeddings (baseline) | 55% | 50% | 50% | 25% | 50% | 100% | 105 / 233 ms | 34 s | 3,537 | $0.007 |
107
+ | full-context (baseline) | 50% | 0% | 0% | 75% | 75% | 100% | 0.5 / 1.2 ms | 0 s | 69,297 | $0.139 |
108
+
109
+ Setup: answer model `mistral/ministral-8b-latest` (same for every system), judge `mistral/codestral-latest`,
110
+ mem0 2.2.1 with `ministral-8b-latest` for fact extraction, embeddings `nomic-embed-text` on local Ollama, top 10
111
+ items per retriever. Hardware: Apple silicon laptop, 10 CPUs, 16 GB RAM; the local Ollama server was shared with
112
+ other jobs during the run, so the embedding latencies are pessimistic. 20 questions is a small sample: treat
113
+ differences of one or two questions as noise.
114
+
115
+ What this small run shows:
116
+
117
+ - **Pasting everything lost.** With ~69k prompt tokens per question, the 8B answer model said "I don't know" to
118
+ every single-session and multi-session question, while answering the same questions from retrieved snippets.
119
+ - **mem0 sent about 1% of the full-context prompt** (611 vs 69,297 tokens per question) and scored highest, but
120
+ its ingestion took 21.6 minutes and $0.17 of LLM calls for 165 turns (one extraction call per turn), against
121
+ 0.03 s for BM25.
122
+ - **BM25 was 5 points behind mem0** at almost no cost. That gap is one question.
123
+ - **Judge audit.** I checked all 80 judge decisions by hand: 3 false positives, all temporal (one each for
124
+ full-context, bm25 and mem0, e.g. "about 1 week ago" accepted for "4 weeks"), and no false negatives. Corrected
125
+ accuracy: mem0 70%, bm25 65%, embeddings 55%, full-context 45%. The order does not change. The table shows the
126
+ judge's raw output, as the tool produced it.
127
+ - Total spent on this run: **$0.34** (Mistral API, priced from the token counts it returned).
128
+
129
+ Browse every answer, the judge's reasoning and what each system retrieved in the
130
+ [example viewer](https://diegoperezg7.github.io/memarena/).
131
+
132
+ ## Use it on your own history
133
+
134
+ ```bash
135
+ # 1. Import your export (ChatGPT or Claude conversations.json, the export .zip, or JSONL)
136
+ uvx --from git+https://github.com/diegoperezg7/memarena@v0.1.2 memarena import conversations.json --out history.json
137
+
138
+ # 2. Generate questions with gold answers and evidence pointers (written as "pending")
139
+ memarena generate history.json --out questions.json # local Ollama by default
140
+
141
+ # 3. Review them: edit, approve or reject in the viewer (saved back to questions.json)
142
+ memarena view --questions questions.json --history history.json
143
+
144
+ # 4. Run the benchmark on the approved questions
145
+ memarena run history.json questions.json --systems full-context,bm25,embeddings,mem0
146
+
147
+ # 5. Look at the results, or export a static site
148
+ memarena view results.json
149
+ memarena export results.json --questions questions.json --out site
150
+ ```
151
+
152
+ mem0, Letta and Supermemory are optional extras: run with
153
+ `uvx --from 'memarena[mem0] @ git+https://github.com/diegoperezg7/memarena@v0.1.2' memarena run ...`
154
+ (or `uv sync --extra mem0` in a clone).
155
+
156
+ **Privacy.** With `--provider ollama` and local systems nothing leaves your machine. With a cloud provider,
157
+ Supermemory or Letta Cloud, memarena lists where your conversations will go and asks before sending
158
+ (`--yes` skips the question). Generated questions must be reviewed: an unreviewed question set makes the
159
+ benchmark unreliable, so `run` only uses approved questions unless you pass `--include-pending`.
160
+
161
+ ## How it works
162
+
163
+ ```mermaid
164
+ flowchart LR
165
+ E["ChatGPT / Claude export<br/>or JSONL"] -->|memarena import| H[(history.json)]
166
+ H -->|memarena generate| Q[(questions.json<br/>pending)]
167
+ Q -->|review in viewer| QA[(questions.json<br/>approved)]
168
+ H --> I{ingest}
169
+ I --> FC[full context]
170
+ I --> BM[BM25]
171
+ I --> EM[embeddings]
172
+ I --> M0[mem0]
173
+ I --> LE[Letta]
174
+ I --> SM[Supermemory]
175
+ QA --> RQ{retrieve per question}
176
+ FC & BM & EM & M0 & LE & SM --> RQ
177
+ RQ -->|same answer model + prompt| A[answers]
178
+ A -->|LLM judge, published prompt| R[(results.json)]
179
+ R --> V[viewer / static site]
180
+ ```
181
+
182
+ - **Same answer model for everyone.** A system only decides what context reaches the answer model, so accuracy
183
+ differences come from memory, not prompting.
184
+ - **Questions** follow LongMemEval's categories: single-session, multi-session, temporal, knowledge update and
185
+ abstention (the answer is not in the history and the right reply is "I don't know").
186
+ - **Metrics**: accuracy from the LLM judge ([exact prompt](docs/judge-prompt.md)), retrieval latency p50/p95,
187
+ ingestion time, prompt tokens per question and cost, overall and per category.
188
+ - **Budget**: `--max-cost` (default 1 USD) is checked before every paid call; when it would be exceeded the run
189
+ stops and keeps partial results. Local models count as 0.
190
+
191
+ ## Systems
192
+
193
+ | System | Kind | Runs | Status in v0.1 |
194
+ |---|---|---|---|
195
+ | `full-context` | baseline | anywhere | whole history pasted, newest kept if over `--max-context-tokens` |
196
+ | `bm25` | baseline | local, no deps | Okapi BM25 over user/assistant turn pairs |
197
+ | `embeddings` | baseline | Ollama or any OpenAI-compatible embeddings | cosine search over turn pairs |
198
+ | `mem0` | memory | local (mem0ai 2.2.1 OSS, Qdrant on disk) | live-tested (recorded demo); also smoke-tested with Ollama |
199
+ | `letta` | memory | Letta REST API (letta-client 1.12.1) | **not live-tested**: mock-tested only, see below |
200
+ | `supermemory` | memory | Supermemory API (supermemory 5.0.1), needs `SUPERMEMORY_API_KEY` | **not live-tested**: no key was available, mock-tested only |
201
+
202
+ Notes found while building the adapters:
203
+
204
+ - **mem0 and OpenRouter.** mem0 2.2.1's OpenAI-compatible client switches to OpenRouter whenever
205
+ `OPENROUTER_API_KEY` is set in the environment, ignoring the configured base URL. memarena hides that variable
206
+ from mem0 unless you chose OpenRouter, so your history goes only where you said. mem0's telemetry is turned off.
207
+ - **mem0 defaults.** memarena tests mem0's default install. Its BM25 keyword search and spaCy lemmatization need
208
+ the `mem0ai[nlp,extras]` extras, which v0.1 does not install, so mem0 runs with semantic search only.
209
+ - **Letta.** Since 0.34 the `letta/letta` Docker image and the `letta` PyPI package ship "Letta Code"; the old
210
+ self-hosted Python REST server is end-of-life (the image says so when you start it the old way). The adapter
211
+ uses the REST API through `letta-client`, so it needs Letta Cloud (`LETTA_API_KEY`) or a self-hosted server
212
+ that still exposes `/v1`. It writes each turn to the agent's archival memory and searches it; it does not drive
213
+ the agent loop.
214
+
215
+ ## Configuration
216
+
217
+ | Option | Default | What it does |
218
+ |---|---|---|
219
+ | `--provider`, `--model` | `ollama`, `qwen3:4b` | answer model (also used by `generate`) |
220
+ | `--judge-provider`, `--judge-model` | same as answer model | judge |
221
+ | `--embed-provider`, `--embed-model` | `ollama`, `nomic-embed-text` | embeddings baseline and mem0's embedder |
222
+ | `--memory-provider`, `--memory-model` | `ollama`, `qwen3:4b` | LLM inside mem0 / Letta |
223
+ | `--systems` | `full-context,bm25,embeddings,mem0` | what to compare |
224
+ | `--top-k` | 10 | items each retriever returns |
225
+ | `--max-context-tokens` | 100000 | cap for the full-context baseline |
226
+ | `--max-cost` | 1.0 | USD budget, checked before every paid call |
227
+ | `--price in,out` | built-in table | override the USD-per-million-token price of the answer model |
228
+
229
+ Providers (one OpenAI-compatible client, keys only from environment variables, see [.env.example](.env.example)):
230
+ `ollama` (`OLLAMA_HOST`, default http://localhost:11434), `openai` (`OPENAI_API_KEY`), `openrouter`
231
+ (`OPENROUTER_API_KEY`), `mistral` (`MISTRAL_API_KEY`). Optional systems: `SUPERMEMORY_API_KEY`, `LETTA_API_KEY`,
232
+ `LETTA_BASE_URL`. memarena sends no telemetry.
233
+
234
+ **Ollama context window.** Ollama serves models with a small default window (4096 tokens for `qwen3:4b` here) and
235
+ its OpenAI-compatible `/v1` endpoint cannot change it per request, so long prompts are cut silently. Create a
236
+ variant with a bigger window once (it shares the weights, no extra disk) and point memarena at it:
237
+
238
+ ```bash
239
+ printf 'FROM qwen3:4b\nPARAMETER num_ctx 16384\n' > Modelfile && ollama create qwen3:4b-16k -f Modelfile
240
+ memarena run history.json questions.json --model qwen3:4b-16k --memory-model qwen3:4b-16k \
241
+ --max-context-tokens 12000 --num-ctx 16384
242
+ ```
243
+
244
+ Keep `--max-context-tokens` below the window (leave room for the question and the answer) so the full-context
245
+ baseline truncates visibly in the results instead of silently on the server, and keep `--num-ctx` equal to the
246
+ variant's window so mem0's calls do not force Ollama to reload the model. Local reasoning models such as qwen3
247
+ think before answering, which is slow on a laptop. The recorded demo did not use Ollama for chat: its answer model
248
+ (`ministral-8b-latest`, 128k window) received at most ~69.3k prompt tokens per question, so nothing was cut.
249
+
250
+ Reproduce the demo data and run:
251
+
252
+ ```bash
253
+ curl -LO https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_oracle.json
254
+ memarena longmemeval longmemeval_oracle.json --out-dir lme --per-category 4 --max-chars 16000
255
+ memarena run lme/history.json lme/questions.json --systems full-context,bm25,embeddings,mem0 \
256
+ --provider mistral --model ministral-8b-latest --judge-model codestral-latest \
257
+ --memory-provider mistral --memory-model ministral-8b-latest --yes --max-cost 2
258
+ ```
259
+
260
+ ## Roadmap
261
+
262
+ - More memory systems: Zep / Graphiti (need Neo4j or FalkorDB), cognee, MemOS.
263
+ - claude-mem is out of scope for now: it is a Claude Code session plugin, not an API to ingest conversations.
264
+ - Letta agent-loop mode (let the agent edit its own memory while reading the history).
265
+ - mem0 with its `nlp` and `extras` (hybrid search) as a separate configuration.
266
+ - Repeated runs with confidence intervals.
267
+ - LoCoMo is not bundled: its license (CC BY-NC 4.0) is not compatible with an MIT package.
268
+
269
+ ## Third-party data
270
+
271
+ The recorded demo bundles a 20-question subset of LongMemEval
272
+ ([xiaowu0162/longmemeval-cleaned](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned), code at
273
+ [xiaowu0162/LongMemEval](https://github.com/xiaowu0162/LongMemEval)), MIT licensed, Copyright (c) 2024 Di Wu.
274
+ Its license notice ships next to the data, unmodified, in
275
+ [`src/memarena/data/demo/LICENSE-LongMemEval.txt`](src/memarena/data/demo/LICENSE-LongMemEval.txt)
276
+ (copied from the LongMemEval repository at commit `9e0b455`), and is included in the wheel and the sdist.
277
+
278
+ ## License
279
+
280
+ [MIT](LICENSE) for memarena itself. The bundled demo data keeps its own MIT notice (see Third-party data).
@@ -0,0 +1,253 @@
1
+ <p align="center">
2
+ <img src="docs/assets/banner.svg" alt="memarena" width="760">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <a href="https://github.com/diegoperezg7/memarena/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/diegoperezg7/memarena/actions/workflows/ci.yml/badge.svg"></a>
7
+ <a href="LICENSE"><img alt="Licencia: MIT" src="https://img.shields.io/badge/license-MIT-7945c5"></a>
8
+ <a href="https://github.com/diegoperezg7/memarena/releases"><img alt="Release" src="https://img.shields.io/github/v/release/diegoperezg7/memarena?color=7945c5"></a>
9
+ <a href="https://github.com/diegoperezg7/memarena/stargazers"><img alt="Estrellas" src="https://img.shields.io/github/stars/diegoperezg7/memarena?style=flat&color=7945c5"></a>
10
+ </p>
11
+
12
+ <p align="center"><b>Compara capas de memoria con tus propias conversaciones, no con el ranking de otro.</b></p>
13
+
14
+ <p align="center"><a href="README.md">English</a> · Español · <a href="https://diegoperezg7.github.io/memarena/">Visor de ejemplo</a></p>
15
+
16
+ memarena convierte tu exportación de ChatGPT o Claude en un test de memoria: escribe preguntas con su
17
+ respuesta correcta a partir de tu propio historial (las revisas tú), mete el historial en cada capa de memoria,
18
+ hace cada pregunta y puntúa las respuestas con un juez LLM cuyo prompt está publicado. Cada ejecución incluye
19
+ siempre lo que una capa de memoria tiene que superar: **pegar el historial entero** y **una recuperación simple**
20
+ (BM25 y embeddings).
21
+
22
+ ![Visor de memarena, demo grabada](docs/assets/screenshot-light.png)
23
+
24
+ ## Por qué
25
+
26
+ Los arneses públicos de memoria los publica cada uno un fabricante de memoria y giran en torno a datasets fijos:
27
+ [supermemoryai/memorybench](https://github.com/supermemoryai/memorybench) (334 estrellas, Supermemory),
28
+ [vectorize-io/agent-memory-benchmark](https://github.com/vectorize-io/agent-memory-benchmark) (163, Vectorize) y
29
+ [mem0ai/memory-benchmarks](https://github.com/mem0ai/memory-benchmarks) (134, Mem0) (estrellas a 3 nov 2026).
30
+
31
+ Las cifras de los fabricantes no cuadran entre sí. El paper de Mem0 da, en LoCoMo, un 26 % de mejora relativa en
32
+ LLM-as-a-Judge frente a la memoria de OpenAI, y un 91 % menos de latencia p95 y más de un 90 % menos de tokens
33
+ que el contexto completo ([arXiv 2504.19413](https://arxiv.org/abs/2504.19413)). Letta publicó después un 74,0 %
34
+ en LoCoMo con GPT-4o mini para un agente con sistema de ficheros, frente al 68,5 % que Mem0 publicó para sí mismo,
35
+ y añadió «Mem0 did not respond to requests for clarification»
36
+ ([blog de Letta, 12 ago 2025](https://www.letta.com/blog/benchmarking-ai-agent-memory)). Zep da un 94,8 % en DMR
37
+ frente al 93,4 % de MemGPT y hasta un 18,5 % más de acierto en LongMemEval
38
+ ([arXiv 2501.13956](https://arxiv.org/abs/2501.13956)).
39
+
40
+ Tenga razón quien la tenga en LoCoMo, nada de eso dice cómo funciona una capa de memoria con **tus**
41
+ conversaciones, con **tu** modelo, frente a la opción barata de pegarlo todo. memarena es neutral, trabaja sobre
42
+ tus exportaciones y pone siempre contexto completo, BM25 y embeddings en la misma tabla.
43
+
44
+ ## Inicio rápido
45
+
46
+ Demo grabada, sin claves, sin Docker y sin llamadas a modelos (menos de 2 minutos en una máquina limpia con `uv`):
47
+
48
+ ```bash
49
+ uvx --from git+https://github.com/diegoperezg7/memarena@v0.1.2 memarena demo
50
+ ```
51
+
52
+ o desde un clon:
53
+
54
+ ```bash
55
+ git clone https://github.com/diegoperezg7/memarena && cd memarena && uv run memarena demo
56
+ ```
57
+
58
+ ![memarena demo in the terminal](docs/assets/demo.gif)
59
+
60
+ Imprime la tabla de abajo y abre el visor en http://127.0.0.1:8765 (`--no-view` para solo imprimir).
61
+ `uvx memarena` llegará con la publicación en PyPI.
62
+
63
+ ## Ejemplo de salida
64
+
65
+ Es una **ejecución real grabada** (`memarena run`, 3 nov 2026) que va dentro del paquete y que muestra
66
+ `memarena demo`: 20 preguntas (4 por categoría) de
67
+ [LongMemEval](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned) (MIT), con las sesiones de evidencia
68
+ de las 20 juntas en **un solo** historial de 28 sesiones (~75k tokens), como sería la exportación de un usuario.
69
+
70
+ | Sistema | Acierto | Una sesión | Varias | Temporal | Actualización | Abstención | Recuperación p50 / p95 | Ingesta | Tokens de prompt / p | Coste |
71
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
72
+ | **mem0** (memoria) | **75 %** | 50 % | 50 % | 75 % | 100 % | 100 % | 49 / 134 ms | 21,6 min | 611 | 0,174 $ |
73
+ | bm25 (línea base) | 70 % | 75 % | 25 % | 50 % | 100 % | 100 % | 0,9 / 3,9 ms | 0,03 s | 4.180 | 0,008 $ |
74
+ | embeddings (línea base) | 55 % | 50 % | 50 % | 25 % | 50 % | 100 % | 105 / 233 ms | 34 s | 3.537 | 0,007 $ |
75
+ | full-context (línea base) | 50 % | 0 % | 0 % | 75 % | 75 % | 100 % | 0,5 / 1,2 ms | 0 s | 69.297 | 0,139 $ |
76
+
77
+ Configuración: modelo que responde `mistral/ministral-8b-latest` (el mismo para todos), juez
78
+ `mistral/codestral-latest`, mem0 2.2.1 con `ministral-8b-latest` para extraer hechos, embeddings
79
+ `nomic-embed-text` en Ollama local, 10 elementos por recuperador. Hardware: portátil Apple silicon, 10 CPUs,
80
+ 16 GB de RAM; el servidor de Ollama estaba compartido con otros trabajos durante la ejecución, así que las
81
+ latencias de embeddings son pesimistas. 20 preguntas es una muestra pequeña: diferencias de una o dos preguntas
82
+ son ruido.
83
+
84
+ Lo que enseña esta ejecución pequeña:
85
+
86
+ - **Pegarlo todo perdió.** Con unos 69k tokens de prompt por pregunta, el modelo de 8B respondió «no lo sé» a todas
87
+ las preguntas de una y de varias sesiones, que sí respondía a partir de fragmentos recuperados.
88
+ - **mem0 envió alrededor del 1 % del prompt de contexto completo** (611 frente a 69.297 tokens por pregunta) y
89
+ sacó la nota más alta, pero su ingesta tardó 21,6 minutos y costó 0,17 $ en llamadas al LLM para 165 turnos (una
90
+ extracción por turno), frente a 0,03 s de BM25.
91
+ - **BM25 quedó a 5 puntos de mem0** casi sin coste. Esa diferencia es una pregunta.
92
+ - **Auditoría del juez.** Revisé a mano las 80 decisiones del juez: 3 falsos positivos, todos en temporal (uno en
93
+ full-context, otro en bm25 y otro en mem0; por ejemplo, aceptó «hace 1 semana» cuando eran «4 semanas») y ningún
94
+ falso negativo. Acierto corregido: mem0 70 %, bm25 65 %, embeddings 55 %, full-context 45 %. El orden no cambia.
95
+ La tabla muestra la salida tal cual del juez.
96
+ - Gasto total de esta ejecución: **0,34 $** (API de Mistral, calculado con los tokens que devolvió).
97
+
98
+ Cada respuesta, el razonamiento del juez y lo que recuperó cada sistema están en el
99
+ [visor de ejemplo](https://diegoperezg7.github.io/memarena/).
100
+
101
+ ## Úsalo con tu historial
102
+
103
+ ```bash
104
+ # 1. Importa tu exportación (conversations.json de ChatGPT o Claude, el .zip, o JSONL)
105
+ uvx --from git+https://github.com/diegoperezg7/memarena@v0.1.2 memarena import conversations.json --out history.json
106
+
107
+ # 2. Genera preguntas con respuesta y punteros a la evidencia (quedan como "pending")
108
+ memarena generate history.json --out questions.json # Ollama local por defecto
109
+
110
+ # 3. Revísalas: edita, aprueba o rechaza en el visor (se guardan en questions.json)
111
+ memarena view --questions questions.json --history history.json
112
+
113
+ # 4. Ejecuta el benchmark con las preguntas aprobadas
114
+ memarena run history.json questions.json --systems full-context,bm25,embeddings,mem0
115
+
116
+ # 5. Mira los resultados o expórtalos como web estática
117
+ memarena view results.json
118
+ memarena export results.json --questions questions.json --out site
119
+ ```
120
+
121
+ mem0, Letta y Supermemory son extras opcionales:
122
+ `uvx --from 'memarena[mem0] @ git+https://github.com/diegoperezg7/memarena@v0.1.2' memarena run ...`
123
+ (o `uv sync --extra mem0` en un clon).
124
+
125
+ **Privacidad.** Con `--provider ollama` y sistemas locales nada sale de tu máquina. Con un proveedor en la nube,
126
+ Supermemory o Letta Cloud, memarena dice adónde van a ir tus conversaciones y pregunta antes de enviarlas
127
+ (`--yes` se salta la pregunta). Las preguntas generadas hay que revisarlas: sin revisión el test no es fiable, así
128
+ que `run` solo usa las aprobadas salvo que pases `--include-pending`.
129
+
130
+ ## Cómo funciona
131
+
132
+ ```mermaid
133
+ flowchart LR
134
+ E["Exportación ChatGPT / Claude<br/>o JSONL"] -->|memarena import| H[(history.json)]
135
+ H -->|memarena generate| Q[(questions.json<br/>pendientes)]
136
+ Q -->|revisión en el visor| QA[(questions.json<br/>aprobadas)]
137
+ H --> I{ingesta}
138
+ I --> FC[contexto completo]
139
+ I --> BM[BM25]
140
+ I --> EM[embeddings]
141
+ I --> M0[mem0]
142
+ I --> LE[Letta]
143
+ I --> SM[Supermemory]
144
+ QA --> RQ{recuperación por pregunta}
145
+ FC & BM & EM & M0 & LE & SM --> RQ
146
+ RQ -->|mismo modelo y prompt| A[respuestas]
147
+ A -->|juez LLM, prompt publicado| R[(results.json)]
148
+ R --> V[visor / web estática]
149
+ ```
150
+
151
+ - **El mismo modelo responde para todos.** Un sistema solo decide qué contexto llega al modelo, así que las
152
+ diferencias de acierto vienen de la memoria y no del prompt.
153
+ - **Las preguntas** siguen las categorías de LongMemEval: una sesión, varias sesiones, temporal, actualización
154
+ de conocimiento y abstención (la respuesta no está en el historial y lo correcto es decir «no lo sé»).
155
+ - **Métricas**: acierto según el juez LLM ([prompt exacto](docs/judge-prompt.md)), latencia de recuperación
156
+ p50/p95, tiempo de ingesta, tokens de prompt por pregunta y coste, en total y por categoría.
157
+ - **Presupuesto**: `--max-cost` (1 USD por defecto) se comprueba antes de cada llamada de pago; si se superaría,
158
+ la ejecución para y guarda los resultados parciales. Los modelos locales cuentan como 0.
159
+
160
+ ## Sistemas
161
+
162
+ | Sistema | Tipo | Dónde corre | Estado en v0.1 |
163
+ |---|---|---|---|
164
+ | `full-context` | línea base | en cualquier sitio | historial entero; si no cabe en `--max-context-tokens`, se queda lo más reciente |
165
+ | `bm25` | línea base | local, sin dependencias | BM25 de Okapi sobre pares de turnos usuario/asistente |
166
+ | `embeddings` | línea base | Ollama o cualquier API de embeddings compatible con OpenAI | búsqueda por coseno sobre pares de turnos |
167
+ | `mem0` | memoria | local (mem0ai 2.2.1 OSS, Qdrant en disco) | probado en vivo (demo grabada); también probado con Ollama |
168
+ | `letta` | memoria | API REST de Letta (letta-client 1.12.1) | **sin probar en vivo**: solo con mocks, ver abajo |
169
+ | `supermemory` | memoria | API de Supermemory (supermemory 5.0.1), necesita `SUPERMEMORY_API_KEY` | **sin probar en vivo**: no había clave, solo con mocks |
170
+
171
+ Lo que salió al construir los adaptadores:
172
+
173
+ - **mem0 y OpenRouter.** El cliente compatible con OpenAI de mem0 2.2.1 se pasa a OpenRouter siempre que
174
+ `OPENROUTER_API_KEY` está en el entorno, ignorando la URL configurada. memarena le oculta esa variable a mem0
175
+ salvo que hayas elegido OpenRouter, para que tu historial vaya solo adonde dijiste. La telemetría de mem0 se
176
+ desactiva.
177
+ - **mem0 por defecto.** memarena prueba la instalación por defecto de mem0. Su búsqueda BM25 y la lematización
178
+ con spaCy necesitan los extras `mem0ai[nlp,extras]`, que la v0.1 no instala, así que mem0 usa solo búsqueda
179
+ semántica.
180
+ - **Letta.** Desde la 0.34 la imagen Docker `letta/letta` y el paquete `letta` de PyPI traen «Letta Code»; el
181
+ antiguo servidor REST en Python está retirado (la propia imagen lo dice al arrancarla como antes). El adaptador
182
+ usa la API REST con `letta-client`, así que necesita Letta Cloud (`LETTA_API_KEY`) o un servidor propio que
183
+ siga exponiendo `/v1`. Guarda cada turno en la memoria de archivo del agente y busca en ella; no ejecuta el
184
+ bucle del agente.
185
+
186
+ ## Configuración
187
+
188
+ | Opción | Por defecto | Qué hace |
189
+ |---|---|---|
190
+ | `--provider`, `--model` | `ollama`, `qwen3:4b` | modelo que responde (también lo usa `generate`) |
191
+ | `--judge-provider`, `--judge-model` | el mismo que responde | juez |
192
+ | `--embed-provider`, `--embed-model` | `ollama`, `nomic-embed-text` | línea base de embeddings y embedder de mem0 |
193
+ | `--memory-provider`, `--memory-model` | `ollama`, `qwen3:4b` | LLM dentro de mem0 / Letta |
194
+ | `--systems` | `full-context,bm25,embeddings,mem0` | qué comparar |
195
+ | `--top-k` | 10 | elementos que devuelve cada recuperador |
196
+ | `--max-context-tokens` | 100000 | tope de la línea base de contexto completo |
197
+ | `--max-cost` | 1.0 | presupuesto en USD, comprobado antes de cada llamada de pago |
198
+ | `--price in,out` | tabla interna | precio en USD por millón de tokens del modelo que responde |
199
+
200
+ Proveedores (un solo cliente compatible con OpenAI, claves solo desde variables de entorno, ver
201
+ [.env.example](.env.example)): `ollama` (`OLLAMA_HOST`, por defecto http://localhost:11434), `openai`
202
+ (`OPENAI_API_KEY`), `openrouter` (`OPENROUTER_API_KEY`), `mistral` (`MISTRAL_API_KEY`). Sistemas opcionales:
203
+ `SUPERMEMORY_API_KEY`, `LETTA_API_KEY`, `LETTA_BASE_URL`. memarena no envía telemetría.
204
+
205
+ **Ventana de contexto de Ollama.** Ollama sirve los modelos con una ventana pequeña por defecto (4096 tokens para
206
+ `qwen3:4b` aquí) y su endpoint `/v1`, compatible con OpenAI, no deja cambiarla por petición, así que los prompts
207
+ largos se recortan en silencio. Crea una vez una variante con más ventana (comparte los pesos, no ocupa disco) y
208
+ usa esa:
209
+
210
+ ```bash
211
+ printf 'FROM qwen3:4b\nPARAMETER num_ctx 16384\n' > Modelfile && ollama create qwen3:4b-16k -f Modelfile
212
+ memarena run history.json questions.json --model qwen3:4b-16k --memory-model qwen3:4b-16k \
213
+ --max-context-tokens 12000 --num-ctx 16384
214
+ ```
215
+
216
+ Deja `--max-context-tokens` por debajo de la ventana (con margen para la pregunta y la respuesta) para que la
217
+ línea base de contexto completo recorte de forma visible en los resultados y no en silencio en el servidor, y deja
218
+ `--num-ctx` igual a la ventana de la variante para que las llamadas de mem0 no obliguen a Ollama a recargar el
219
+ modelo. Los modelos locales que razonan, como qwen3, piensan antes de responder y en un portátil van lentos. La
220
+ demo grabada no usó Ollama para el chat: su modelo (`ministral-8b-latest`, ventana de 128k) recibió como mucho
221
+ ~69,3k tokens de prompt por pregunta, así que no se recortó nada.
222
+
223
+ Para reproducir los datos y la ejecución de la demo:
224
+
225
+ ```bash
226
+ curl -LO https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_oracle.json
227
+ memarena longmemeval longmemeval_oracle.json --out-dir lme --per-category 4 --max-chars 16000
228
+ memarena run lme/history.json lme/questions.json --systems full-context,bm25,embeddings,mem0 \
229
+ --provider mistral --model ministral-8b-latest --judge-model codestral-latest \
230
+ --memory-provider mistral --memory-model ministral-8b-latest --yes --max-cost 2
231
+ ```
232
+
233
+ ## Hoja de ruta
234
+
235
+ - Más sistemas de memoria: Zep / Graphiti (necesitan Neo4j o FalkorDB), cognee, MemOS.
236
+ - claude-mem queda fuera por ahora: es un plugin de sesiones de Claude Code, no una API para ingerir conversaciones.
237
+ - Modo agente de Letta (que el agente edite su propia memoria mientras lee el historial).
238
+ - mem0 con sus extras `nlp` y `extras` (búsqueda híbrida) como configuración aparte.
239
+ - Repeticiones con intervalos de confianza.
240
+ - LoCoMo no se incluye: su licencia (CC BY-NC 4.0) no es compatible con un paquete MIT.
241
+
242
+ ## Datos de terceros
243
+
244
+ La demo grabada incluye un subconjunto de 20 preguntas de LongMemEval
245
+ ([xiaowu0162/longmemeval-cleaned](https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned), código en
246
+ [xiaowu0162/LongMemEval](https://github.com/xiaowu0162/LongMemEval)), con licencia MIT, Copyright (c) 2024 Di Wu.
247
+ Su aviso de licencia va junto a los datos, sin modificar, en
248
+ [`src/memarena/data/demo/LICENSE-LongMemEval.txt`](src/memarena/data/demo/LICENSE-LongMemEval.txt)
249
+ (copiado del repositorio de LongMemEval en el commit `9e0b455`), y se incluye en el wheel y en el sdist.
250
+
251
+ ## Licencia
252
+
253
+ [MIT](LICENSE) para memarena. Los datos de la demo mantienen su propio aviso MIT (ver Datos de terceros).