onemem 0.1.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.
- onemem-0.1.0/.github/workflows/ci.yml +41 -0
- onemem-0.1.0/.gitignore +27 -0
- onemem-0.1.0/DESIGN.md +130 -0
- onemem-0.1.0/LICENSE +21 -0
- onemem-0.1.0/PKG-INFO +171 -0
- onemem-0.1.0/README.md +126 -0
- onemem-0.1.0/bench/README.md +60 -0
- onemem-0.1.0/bench/answer_accuracy.py +138 -0
- onemem-0.1.0/bench/freeze.py +236 -0
- onemem-0.1.0/bench/metrics.py +84 -0
- onemem-0.1.0/bench/reduction.py +73 -0
- onemem-0.1.0/bench/reembed_strong.py +86 -0
- onemem-0.1.0/bench/run_bench.py +111 -0
- onemem-0.1.0/docs/.nojekyll +0 -0
- onemem-0.1.0/docs/demo-poster.jpg +0 -0
- onemem-0.1.0/docs/demo.mp4 +0 -0
- onemem-0.1.0/docs/index.html +5506 -0
- onemem-0.1.0/docs/meniscus-logo.png +0 -0
- onemem-0.1.0/onemem/__init__.py +3 -0
- onemem-0.1.0/onemem/api/__init__.py +1 -0
- onemem-0.1.0/onemem/api/app.py +42 -0
- onemem-0.1.0/onemem/api/events.py +66 -0
- onemem-0.1.0/onemem/chunker.py +72 -0
- onemem-0.1.0/onemem/cli/__init__.py +1 -0
- onemem-0.1.0/onemem/cli/main.py +2011 -0
- onemem-0.1.0/onemem/config.py +141 -0
- onemem-0.1.0/onemem/db.py +229 -0
- onemem-0.1.0/onemem/embedding_interface.py +28 -0
- onemem-0.1.0/onemem/entity_extractor.py +153 -0
- onemem-0.1.0/onemem/event_intake.py +143 -0
- onemem-0.1.0/onemem/exceptions.py +29 -0
- onemem-0.1.0/onemem/fact_retrieval.py +784 -0
- onemem-0.1.0/onemem/home.py +49 -0
- onemem-0.1.0/onemem/mcp_server.py +214 -0
- onemem-0.1.0/onemem/model_interface.py +22 -0
- onemem-0.1.0/onemem/models.py +19 -0
- onemem-0.1.0/onemem/onemem_types.py +12 -0
- onemem-0.1.0/onemem/pipeline.py +392 -0
- onemem-0.1.0/onemem/provider_defaults.toml +9 -0
- onemem-0.1.0/onemem/providers/__init__.py +167 -0
- onemem-0.1.0/onemem/providers/_schema.py +34 -0
- onemem-0.1.0/onemem/providers/anthropic.py +116 -0
- onemem-0.1.0/onemem/providers/local_embedding.py +99 -0
- onemem-0.1.0/onemem/providers/openai_compat.py +147 -0
- onemem-0.1.0/onemem/schema.sql +102 -0
- onemem-0.1.0/onemem/spend_gate.py +55 -0
- onemem-0.1.0/onemem/startup.py +24 -0
- onemem-0.1.0/onemem/time_bounds.py +53 -0
- onemem-0.1.0/onemem/tokens.py +8 -0
- onemem-0.1.0/onemem/transcript_ingest.py +132 -0
- onemem-0.1.0/onemem/vocab_reconciliation.py +85 -0
- onemem-0.1.0/onemem-plugin/.claude-plugin/plugin.json +9 -0
- onemem-0.1.0/pyproject.toml +67 -0
- onemem-0.1.0/scripts/dev-onemem +7 -0
- onemem-0.1.0/tests/__init__.py +1 -0
- onemem-0.1.0/tests/conftest.py +31 -0
- onemem-0.1.0/tests/test_chunker.py +19 -0
- onemem-0.1.0/tests/test_cli.py +805 -0
- onemem-0.1.0/tests/test_config.py +46 -0
- onemem-0.1.0/tests/test_db.py +268 -0
- onemem-0.1.0/tests/test_entity_extractor.py +91 -0
- onemem-0.1.0/tests/test_event_intake.py +65 -0
- onemem-0.1.0/tests/test_fact_retrieval.py +257 -0
- onemem-0.1.0/tests/test_mcp_contract.py +43 -0
- onemem-0.1.0/tests/test_models.py +28 -0
- onemem-0.1.0/tests/test_pipeline.py +124 -0
- onemem-0.1.0/tests/test_providers.py +115 -0
- onemem-0.1.0/tests/test_retrieval.py +116 -0
- onemem-0.1.0/tests/test_spend_gate.py +94 -0
- onemem-0.1.0/tests/test_time_bounds.py +24 -0
- onemem-0.1.0/tests/test_types.py +8 -0
- onemem-0.1.0/tests/test_vocab_reconciliation.py +103 -0
- onemem-0.1.0/tests/test_workflow.py +90 -0
- onemem-0.1.0/uv.lock +2397 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, release-prep]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: actions/setup-python@v5
|
|
14
|
+
with:
|
|
15
|
+
python-version: "3.11"
|
|
16
|
+
- name: Install (all extras + dev)
|
|
17
|
+
run: pip install -e ".[all,dev]"
|
|
18
|
+
- name: Run tests
|
|
19
|
+
run: pytest -q
|
|
20
|
+
- name: CLI entry point
|
|
21
|
+
run: men --help
|
|
22
|
+
|
|
23
|
+
clean-install:
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
steps:
|
|
26
|
+
- uses: actions/checkout@v4
|
|
27
|
+
- uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: "3.11"
|
|
30
|
+
- name: Build wheel
|
|
31
|
+
run: |
|
|
32
|
+
pip install build
|
|
33
|
+
python -m build
|
|
34
|
+
- name: Install the built wheel with extras
|
|
35
|
+
run: |
|
|
36
|
+
WHEEL=$(ls dist/*.whl)
|
|
37
|
+
pip install "${WHEEL}[all]"
|
|
38
|
+
- name: Verify entry points and imports
|
|
39
|
+
run: |
|
|
40
|
+
men --help
|
|
41
|
+
python -c "import onemem, onemem.config, onemem.db, onemem.fact_retrieval, onemem.mcp_server, onemem.pipeline"
|
onemem-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
.env
|
|
2
|
+
|
|
3
|
+
# python
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.pyc
|
|
6
|
+
.venv/
|
|
7
|
+
venv/
|
|
8
|
+
.meniscus-dev/
|
|
9
|
+
.pytest_cache/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
.DS_Store
|
|
12
|
+
|
|
13
|
+
.mcp.json
|
|
14
|
+
|
|
15
|
+
# benchmark data & frozen memories (large; regenerable via bench/)
|
|
16
|
+
bench/data/
|
|
17
|
+
bench/out/
|
|
18
|
+
bench/memories*/
|
|
19
|
+
bench/*.log
|
|
20
|
+
|
|
21
|
+
demo/
|
|
22
|
+
|
|
23
|
+
notes/
|
|
24
|
+
|
|
25
|
+
# build artifacts
|
|
26
|
+
/dist/
|
|
27
|
+
/build/
|
onemem-0.1.0/DESIGN.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# oneMEM — Design
|
|
2
|
+
|
|
3
|
+
A local, structured, long-term memory for AI agents and people. oneMEM receives
|
|
4
|
+
unstructured text (chat turns, notes, imported files), distills it into **atomic facts**,
|
|
5
|
+
stores everything **append-only** in one SQLite file, and makes it retrievable through a
|
|
6
|
+
**deterministic hybrid search** — no LLM in the read path. Any MCP-capable agent reads and
|
|
7
|
+
writes the same memory.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 1. Principles
|
|
12
|
+
|
|
13
|
+
- **Append-only.** Raw events are never overwritten or deleted. Facts are only ever added.
|
|
14
|
+
Every fact traces back to the exact source event it came from, so nothing is lost and
|
|
15
|
+
everything is recoverable.
|
|
16
|
+
- **Deterministic retrieval.** Reading memory is a fixed formula over indexes — no model
|
|
17
|
+
call. The same query against the same data always returns the same result, and every
|
|
18
|
+
ranking decision is inspectable with plain SQL.
|
|
19
|
+
- **Local and single-file.** One SQLite database on the user's machine. No server, no
|
|
20
|
+
cloud, no account. Back it up by copying a file.
|
|
21
|
+
- **Small models at the edges.** An LLM is used only at *write* time (to distill facts) and
|
|
22
|
+
optionally at *read* time (to phrase an answer). The retrieval itself never calls a model,
|
|
23
|
+
so a small/cheap model is enough and nothing heavy runs locally.
|
|
24
|
+
|
|
25
|
+
## 2. Data model
|
|
26
|
+
|
|
27
|
+
All state lives in one SQLite file:
|
|
28
|
+
|
|
29
|
+
- **`events`** — raw ingested content, verbatim, timestamped, with a `source`. The source of
|
|
30
|
+
truth. `extraction_status` tracks whether facts have been distilled yet.
|
|
31
|
+
- **`facts`** — atomic statements distilled from an event by the LLM. Each fact links to its
|
|
32
|
+
`event_id` (provenance) and carries its text and position.
|
|
33
|
+
- **`entities`** — canonical named things (people, projects, tools) with a normalized form
|
|
34
|
+
for matching; `entity_aliases` maps surface variants to a canonical entity.
|
|
35
|
+
- **`fact_entity_edges`** — which entities each fact mentions (the entity retrieval door).
|
|
36
|
+
- **`extractions`** — one row per distillation run (provider, model, prompt version), so the
|
|
37
|
+
provenance of every fact is auditable.
|
|
38
|
+
- **`fact_embeddings`** — a `sqlite-vec` (vec0) virtual table holding each fact's vector.
|
|
39
|
+
- **`facts_fts`** — an FTS5 virtual table over fact text (the keyword door), kept in sync.
|
|
40
|
+
|
|
41
|
+
Facts and their embeddings/edges are only appended. Correcting the record means adding a new
|
|
42
|
+
event, never mutating an old one.
|
|
43
|
+
|
|
44
|
+
## 3. Ingestion pipeline
|
|
45
|
+
|
|
46
|
+
1. **Intake** — content is stored as one or more `events` (large inputs are chunked). Intake
|
|
47
|
+
always commits first, so an event is durable even if distillation later fails. Duplicate
|
|
48
|
+
content is deduplicated by a content hash of `(source, content)`.
|
|
49
|
+
2. **Distillation** — a small LLM (via whichever provider is configured — see §7) reads each
|
|
50
|
+
pending event and extracts atomic facts. Filler turns with nothing worth keeping produce
|
|
51
|
+
zero facts and are marked complete (not retried forever).
|
|
52
|
+
3. **Entity reconciliation** — entities named by the facts are resolved to canonical rows
|
|
53
|
+
(via normalization + aliases) and linked through `fact_entity_edges`.
|
|
54
|
+
4. **Embedding** — each fact is embedded locally with `bge-base-en-v1.5` (768-d) and written
|
|
55
|
+
to `fact_embeddings`; `facts_fts` indexes the text.
|
|
56
|
+
|
|
57
|
+
Processing runs as a parallel batch and is resumable: if the model becomes unavailable,
|
|
58
|
+
processed events stay done and the rest remain pending for the next run.
|
|
59
|
+
|
|
60
|
+
## 4. Retrieval — deterministic hybrid fusion
|
|
61
|
+
|
|
62
|
+
Retrieval scores candidate facts through three independent **doors**, then fuses them. No
|
|
63
|
+
model is involved.
|
|
64
|
+
|
|
65
|
+
- **Vector door** — cosine similarity between the query embedding and each fact embedding.
|
|
66
|
+
Queries are embedded with an instruction prefix; facts are embedded plain. A wide candidate
|
|
67
|
+
pool is fetched (far more than the return limit) so fusion sees a broad set.
|
|
68
|
+
- **Keyword door** — FTS5 (BM25) over fact text. The query is tokenized and OR-matched, so a
|
|
69
|
+
fact matching *any* term is a candidate; the fact's *rank position* feeds fusion.
|
|
70
|
+
- **Entity door** — facts linked to an explicitly named entity (used when a caller passes an
|
|
71
|
+
entity argument).
|
|
72
|
+
|
|
73
|
+
**Fusion (magnitude noisy-OR).** Each door contributes independently; a single strong door
|
|
74
|
+
can win on its own merit:
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
fused = 1 − (1 − W_VECTOR·vector) · (1 − W_FTS·fts) · (1 − W_ENTITY·entity)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
where `vector` is the raw cosine, `fts` is a reciprocal-rank term `RRF_K/(RRF_K + position)`,
|
|
81
|
+
and `entity` is 1 if the entity door matched. Vector is the senior partner; keyword is a
|
|
82
|
+
booster/tiebreaker.
|
|
83
|
+
|
|
84
|
+
**Adaptive cut.** Results are sorted by fused score and cut on the *shape* of the curve, not
|
|
85
|
+
a fixed `k`: keep every fact scoring at least a ratio of the top score, bounded to
|
|
86
|
+
`[MIN_RETURN, limit]`. A sharp, specific query returns a tight set; a broad query returns
|
|
87
|
+
more. Counting/enumeration questions (detected by trigger phrases like "how many", "every")
|
|
88
|
+
bypass the ratio cut and return the whole plateau.
|
|
89
|
+
|
|
90
|
+
**Source collapse.** If the facts selected from one event cost as many tokens as the raw
|
|
91
|
+
event, the raw event is returned instead — full fidelity when distillation didn't save
|
|
92
|
+
anything.
|
|
93
|
+
|
|
94
|
+
## 5. Episodic reconstruction
|
|
95
|
+
|
|
96
|
+
oneMEM does **not** store episodes or threads. Instead, "what happened around this moment"
|
|
97
|
+
is reconstructed *at read time*: given an anchor (a matched fact, an event id, or a
|
|
98
|
+
timestamp), events in a time window are segmented into sessions by gaps longer than
|
|
99
|
+
`SESSION_GAP_SECONDS`, and the relevant segment is returned with its facts. Episodes are a
|
|
100
|
+
read-time view over the append-only events, never materialized state.
|
|
101
|
+
|
|
102
|
+
## 6. Interfaces
|
|
103
|
+
|
|
104
|
+
- **CLI (`men`)** — `init` (interactive setup: provider, key, capture, MCP wiring), `add`,
|
|
105
|
+
`import`, `process`, `ask`, `watch` (silent transcript capture), `sql`, `tables`, `status`,
|
|
106
|
+
`doctor`, `help`, and event inspection (`list events`, `show event`). `men ask` plans a
|
|
107
|
+
query, retrieves deterministically, and phrases an answer.
|
|
108
|
+
- **MCP** — exactly **two** tools, so an agent's surface stays minimal:
|
|
109
|
+
- `onemem_recall` — the single read entry point. One call; the argument selects the
|
|
110
|
+
operation (topic search, a time window, a reconstructed session, or the raw source
|
|
111
|
+
behind a fact). Returns structured facts with timestamps and source ids — not a finished
|
|
112
|
+
answer.
|
|
113
|
+
- `onemem_log` — an invisible background write; the agent logs what's worth remembering
|
|
114
|
+
without announcing it.
|
|
115
|
+
|
|
116
|
+
## 7. Providers
|
|
117
|
+
|
|
118
|
+
- **LLM** — bring your own key. Any OpenAI-compatible backend works (OpenRouter, OpenAI,
|
|
119
|
+
Gemini, Groq, xAI, Hugging Face, a local Ollama server, or any other OpenAI-compatible
|
|
120
|
+
endpoint via `provider = "custom"`); Anthropic is wired separately against Claude's native
|
|
121
|
+
API.
|
|
122
|
+
- **Embeddings** — `bge-base-en-v1.5` run **locally** (768-d), no API. Query embeddings use a
|
|
123
|
+
retrieval instruction prefix.
|
|
124
|
+
|
|
125
|
+
## 8. What makes it inspectable
|
|
126
|
+
|
|
127
|
+
Because it is one SQLite file and retrieval is a fixed formula, nothing is hidden. `men sql`
|
|
128
|
+
and `men tables` expose the raw events, facts, entities, edges, and scores — the exact data
|
|
129
|
+
an agent sees. Every fact links to its source event; every extraction records the model that
|
|
130
|
+
produced it. Memory you can audit is memory you can trust.
|
onemem-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 magic_bubblez
|
|
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.
|
onemem-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: onemem
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: One memory. Every AI. You own it. — Local structured memory for AI agents
|
|
5
|
+
Project-URL: Homepage, https://github.com/YOUR_USERNAME/onemem
|
|
6
|
+
Project-URL: Repository, https://github.com/YOUR_USERNAME/onemem
|
|
7
|
+
Project-URL: Issues, https://github.com/YOUR_USERNAME/onemem/issues
|
|
8
|
+
Author: oneMEM
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: ai-agents,llm,long-term-memory,mcp,memory,retrieval,sqlite
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: click>=8.0
|
|
23
|
+
Requires-Dist: pydantic>=2.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0
|
|
25
|
+
Requires-Dist: requests>=2.0
|
|
26
|
+
Provides-Extra: all
|
|
27
|
+
Requires-Dist: fastapi>=0.100; extra == 'all'
|
|
28
|
+
Requires-Dist: mcp[cli]<2,>=1.27; extra == 'all'
|
|
29
|
+
Requires-Dist: sentence-transformers>=2.0; extra == 'all'
|
|
30
|
+
Requires-Dist: sqlite-vec>=0.1; extra == 'all'
|
|
31
|
+
Requires-Dist: uvicorn>=0.20; extra == 'all'
|
|
32
|
+
Provides-Extra: api
|
|
33
|
+
Requires-Dist: fastapi>=0.100; extra == 'api'
|
|
34
|
+
Requires-Dist: uvicorn>=0.20; extra == 'api'
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
38
|
+
Provides-Extra: local-embedding
|
|
39
|
+
Requires-Dist: sentence-transformers>=2.0; extra == 'local-embedding'
|
|
40
|
+
Provides-Extra: mcp
|
|
41
|
+
Requires-Dist: mcp[cli]<2,>=1.27; extra == 'mcp'
|
|
42
|
+
Provides-Extra: vec
|
|
43
|
+
Requires-Dist: sqlite-vec>=0.1; extra == 'vec'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
<div align="center">
|
|
47
|
+
|
|
48
|
+
# oneMEM
|
|
49
|
+
|
|
50
|
+
### One memory. Every AI. You own it.
|
|
51
|
+
|
|
52
|
+
Local, structured memory for AI agents—in one SQLite file on your machine.
|
|
53
|
+
|
|
54
|
+
[](https://pypi.org/project/onemem/)
|
|
55
|
+
[](LICENSE)
|
|
56
|
+
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
oneMEM gives AI tools a shared local memory. It turns useful context into compact facts and surfaces the minimum amount of memory sufficient for a query. Every connected agent reads and writes the same SQLite file.
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
AI agents ───┐
|
|
63
|
+
Code editors ┼── MCP ── oneMEM ── ~/.onemem/onemem.db
|
|
64
|
+
Local tools ─┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Install
|
|
68
|
+
|
|
69
|
+
oneMEM requires Python 3.11 or newer and an API key for any supported language-model provider. Embeddings run locally; there is no embedding service or API key to configure.
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
uv tool install "onemem[all]"
|
|
73
|
+
onemem init
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`onemem init` does the rest:
|
|
77
|
+
|
|
78
|
+
1. asks which model provider you want to use;
|
|
79
|
+
2. recommends a model or lets you enter another model ID;
|
|
80
|
+
3. verifies the key and model before saving them;
|
|
81
|
+
4. initializes the local database and embedding model;
|
|
82
|
+
5. offers background capture; and
|
|
83
|
+
6. connects detected AI tools over MCP.
|
|
84
|
+
|
|
85
|
+
Nothing is uploaded to a oneMEM server. Configuration and credentials stay under `~/.onemem/`; the API key is sent only to the provider you select.
|
|
86
|
+
|
|
87
|
+
## Try it
|
|
88
|
+
|
|
89
|
+
Add something directly:
|
|
90
|
+
|
|
91
|
+
```console
|
|
92
|
+
onemem add "Chose SQLite because it needs zero operations and one-file backups."
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Ask for it later:
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
onemem ask "What storage did I choose, and why?"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or ask from a connected agent. It receives two MCP tools:
|
|
102
|
+
|
|
103
|
+
- `onemem_recall` retrieves relevant memory.
|
|
104
|
+
- `onemem_log` stores something worth remembering.
|
|
105
|
+
|
|
106
|
+
## MCP setup
|
|
107
|
+
|
|
108
|
+
oneMEM works with any client that can run a local `stdio` MCP server, including Claude Code, Codex, Cursor, and Windsurf. `onemem init` automatically configures clients whose command-line tools it detects; other clients only need the `onemem-mcp` executable path.
|
|
109
|
+
|
|
110
|
+
```console
|
|
111
|
+
claude mcp add --scope user onemem -- "$(command -v onemem-mcp)"
|
|
112
|
+
codex mcp add onemem -- "$(command -v onemem-mcp)"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Commands
|
|
116
|
+
|
|
117
|
+
| Command | Purpose |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `onemem init` | Complete interactive setup |
|
|
120
|
+
| `onemem add "memory"` | Store one note or observation |
|
|
121
|
+
| `onemem ask "question"` | Retrieve relevant facts and answer a question |
|
|
122
|
+
| `onemem watch --start` | Start capturing in the background |
|
|
123
|
+
| `onemem doctor` | Check the environment |
|
|
124
|
+
| `onemem status` | Show event, fact, entity counts |
|
|
125
|
+
|
|
126
|
+
## Where data lives
|
|
127
|
+
|
|
128
|
+
| Path | Contents |
|
|
129
|
+
|---|---|
|
|
130
|
+
| `~/.onemem/onemem.db` | Events, facts, entities, embeddings |
|
|
131
|
+
| `~/.onemem/config.toml` | Active provider, model, and runtime settings |
|
|
132
|
+
| `~/.onemem/.env` | Provider API keys |
|
|
133
|
+
|
|
134
|
+
## How retrieval works
|
|
135
|
+
|
|
136
|
+
```text
|
|
137
|
+
session or imported text
|
|
138
|
+
↓
|
|
139
|
+
append-only raw event
|
|
140
|
+
↓
|
|
141
|
+
compact facts + local embeddings + entity anchors
|
|
142
|
+
↓
|
|
143
|
+
deterministic fusion of semantic, keyword, and entity retrieval
|
|
144
|
+
↓
|
|
145
|
+
minimum relevant memory returned to the agent
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
oneMEM uses an LLM only to interpret and compact language and, optionally, to synthesize the final answer. Storage, indexing, and retrieval are ordinary code.
|
|
149
|
+
|
|
150
|
+
## Benchmarks
|
|
151
|
+
|
|
152
|
+
Measured on a 100-instance stratified sample of [LongMemEval-S](https://arxiv.org/abs/2410.10813):
|
|
153
|
+
|
|
154
|
+
| Metric | Result |
|
|
155
|
+
|---|---:|
|
|
156
|
+
| Retrieval recall | **0.89** |
|
|
157
|
+
| Context reduction | **99.1%** |
|
|
158
|
+
| End-to-end answer accuracy | **72%** |
|
|
159
|
+
|
|
160
|
+
## Development
|
|
161
|
+
|
|
162
|
+
```console
|
|
163
|
+
git clone https://github.com/YOUR_USERNAME/onemem.git
|
|
164
|
+
cd onemem
|
|
165
|
+
uv sync --all-extras
|
|
166
|
+
uv run pytest -q
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## License
|
|
170
|
+
|
|
171
|
+
MIT — Based on [Meniscus](https://github.com/magic-bubblez/meniscus) by magic_bubblez.
|
onemem-0.1.0/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# oneMEM
|
|
4
|
+
|
|
5
|
+
### One memory. Every AI. You own it.
|
|
6
|
+
|
|
7
|
+
Local, structured memory for AI agents—in one SQLite file on your machine.
|
|
8
|
+
|
|
9
|
+
[](https://pypi.org/project/onemem/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
oneMEM gives AI tools a shared local memory. It turns useful context into compact facts and surfaces the minimum amount of memory sufficient for a query. Every connected agent reads and writes the same SQLite file.
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
AI agents ───┐
|
|
18
|
+
Code editors ┼── MCP ── oneMEM ── ~/.onemem/onemem.db
|
|
19
|
+
Local tools ─┘
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
oneMEM requires Python 3.11 or newer and an API key for any supported language-model provider. Embeddings run locally; there is no embedding service or API key to configure.
|
|
25
|
+
|
|
26
|
+
```console
|
|
27
|
+
uv tool install "onemem[all]"
|
|
28
|
+
onemem init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`onemem init` does the rest:
|
|
32
|
+
|
|
33
|
+
1. asks which model provider you want to use;
|
|
34
|
+
2. recommends a model or lets you enter another model ID;
|
|
35
|
+
3. verifies the key and model before saving them;
|
|
36
|
+
4. initializes the local database and embedding model;
|
|
37
|
+
5. offers background capture; and
|
|
38
|
+
6. connects detected AI tools over MCP.
|
|
39
|
+
|
|
40
|
+
Nothing is uploaded to a oneMEM server. Configuration and credentials stay under `~/.onemem/`; the API key is sent only to the provider you select.
|
|
41
|
+
|
|
42
|
+
## Try it
|
|
43
|
+
|
|
44
|
+
Add something directly:
|
|
45
|
+
|
|
46
|
+
```console
|
|
47
|
+
onemem add "Chose SQLite because it needs zero operations and one-file backups."
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Ask for it later:
|
|
51
|
+
|
|
52
|
+
```console
|
|
53
|
+
onemem ask "What storage did I choose, and why?"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or ask from a connected agent. It receives two MCP tools:
|
|
57
|
+
|
|
58
|
+
- `onemem_recall` retrieves relevant memory.
|
|
59
|
+
- `onemem_log` stores something worth remembering.
|
|
60
|
+
|
|
61
|
+
## MCP setup
|
|
62
|
+
|
|
63
|
+
oneMEM works with any client that can run a local `stdio` MCP server, including Claude Code, Codex, Cursor, and Windsurf. `onemem init` automatically configures clients whose command-line tools it detects; other clients only need the `onemem-mcp` executable path.
|
|
64
|
+
|
|
65
|
+
```console
|
|
66
|
+
claude mcp add --scope user onemem -- "$(command -v onemem-mcp)"
|
|
67
|
+
codex mcp add onemem -- "$(command -v onemem-mcp)"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
| Command | Purpose |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `onemem init` | Complete interactive setup |
|
|
75
|
+
| `onemem add "memory"` | Store one note or observation |
|
|
76
|
+
| `onemem ask "question"` | Retrieve relevant facts and answer a question |
|
|
77
|
+
| `onemem watch --start` | Start capturing in the background |
|
|
78
|
+
| `onemem doctor` | Check the environment |
|
|
79
|
+
| `onemem status` | Show event, fact, entity counts |
|
|
80
|
+
|
|
81
|
+
## Where data lives
|
|
82
|
+
|
|
83
|
+
| Path | Contents |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `~/.onemem/onemem.db` | Events, facts, entities, embeddings |
|
|
86
|
+
| `~/.onemem/config.toml` | Active provider, model, and runtime settings |
|
|
87
|
+
| `~/.onemem/.env` | Provider API keys |
|
|
88
|
+
|
|
89
|
+
## How retrieval works
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
session or imported text
|
|
93
|
+
↓
|
|
94
|
+
append-only raw event
|
|
95
|
+
↓
|
|
96
|
+
compact facts + local embeddings + entity anchors
|
|
97
|
+
↓
|
|
98
|
+
deterministic fusion of semantic, keyword, and entity retrieval
|
|
99
|
+
↓
|
|
100
|
+
minimum relevant memory returned to the agent
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
oneMEM uses an LLM only to interpret and compact language and, optionally, to synthesize the final answer. Storage, indexing, and retrieval are ordinary code.
|
|
104
|
+
|
|
105
|
+
## Benchmarks
|
|
106
|
+
|
|
107
|
+
Measured on a 100-instance stratified sample of [LongMemEval-S](https://arxiv.org/abs/2410.10813):
|
|
108
|
+
|
|
109
|
+
| Metric | Result |
|
|
110
|
+
|---|---:|
|
|
111
|
+
| Retrieval recall | **0.89** |
|
|
112
|
+
| Context reduction | **99.1%** |
|
|
113
|
+
| End-to-end answer accuracy | **72%** |
|
|
114
|
+
|
|
115
|
+
## Development
|
|
116
|
+
|
|
117
|
+
```console
|
|
118
|
+
git clone https://github.com/YOUR_USERNAME/onemem.git
|
|
119
|
+
cd onemem
|
|
120
|
+
uv sync --all-extras
|
|
121
|
+
uv run pytest -q
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT — Based on [Meniscus](https://github.com/magic-bubblez/meniscus) by magic_bubblez.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Benchmarks
|
|
2
|
+
|
|
3
|
+
oneMEM is measured on [LongMemEval-S](https://arxiv.org/abs/2410.10813), a long-term-memory
|
|
4
|
+
benchmark of long chat histories with labelled evidence turns. The headline numbers in the
|
|
5
|
+
top-level README come from the scripts here, on a 100-instance stratified sample.
|
|
6
|
+
|
|
7
|
+
## Reproducing the numbers
|
|
8
|
+
|
|
9
|
+
### 1. Get the dataset
|
|
10
|
+
|
|
11
|
+
Download LongMemEval-S (`longmemeval_s.json`) from the official release linked in the
|
|
12
|
+
[paper](https://arxiv.org/abs/2410.10813) and place it at:
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
bench/data/longmemeval_s.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Build the frozen memories (one-time; costs LLM calls)
|
|
19
|
+
|
|
20
|
+
Building a memory runs one extraction call per chat turn, so we build once and persist each
|
|
21
|
+
instance to `bench/memories/<id>.db` (plus a `.json` sidecar). Every later retrieval experiment
|
|
22
|
+
runs against these frozen memories with **zero** LLM calls, so any measured difference is purely
|
|
23
|
+
the retrieval config.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
export OPENROUTER_API_KEY="sk-..."
|
|
27
|
+
# The published numbers used deepseek-v4-flash as the extraction model:
|
|
28
|
+
ONEMEM_FREEZE_MODEL=deepseek/deepseek-v4-flash python bench/freeze.py bench/data/longmemeval_s.json 100
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The run is spend-guarded (`MAX_RUN_COST_USD` in `config.py`) and resumable — rerun to continue.
|
|
32
|
+
Frozen memories are gitignored (large); you rebuild them locally.
|
|
33
|
+
|
|
34
|
+
### 3. Retrieval metrics (free — no LLM)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
python bench/metrics.py # recall + set-complete (all@k) per question type -> 0.89 recall
|
|
38
|
+
python bench/reduction.py # injected vs full-history tokens -> ~99.1% reduction
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 4. End-to-end answer accuracy (small; spend-capped)
|
|
42
|
+
|
|
43
|
+
A reader model answers from the retrieved facts; a judge model grades it against the gold answer.
|
|
44
|
+
Both are set via env vars, and the run is capped at `$2` (`MAX_EVAL_COST_USD`).
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
EVAL_READER=openai/gpt-5 python bench/answer_accuracy.py # strong reader -> 72%
|
|
48
|
+
python bench/answer_accuracy.py # default small reader -> ~54%
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Which script substantiates which number
|
|
52
|
+
|
|
53
|
+
| Claim | Script | Notes |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Retrieval recall 0.89 | `bench/metrics.py` | fraction of gold evidence in the returned set |
|
|
56
|
+
| Context reduction ~99.1% | `bench/reduction.py` | 1 − injected/full-history tokens |
|
|
57
|
+
| Answer accuracy 72% / ~54% | `bench/answer_accuracy.py` | 72% with a GPT-5 reader, ~54% with a small reader; LLM-judged |
|
|
58
|
+
|
|
59
|
+
`metrics.py` and `reduction.py` call `search_facts` directly and make no LLM calls, so they are
|
|
60
|
+
fully deterministic and reproduce exactly on identical frozen memories.
|