lodedb 0.2.1__tar.gz → 0.4.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.
- {lodedb-0.2.1 → lodedb-0.4.0}/PKG-INFO +113 -20
- {lodedb-0.2.1 → lodedb-0.4.0}/README.md +106 -19
- {lodedb-0.2.1 → lodedb-0.4.0}/pyproject.toml +20 -5
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/__init__.py +5 -1
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/core.py +919 -70
- lodedb-0.4.0/src/lodedb/engine/embedding_backends.py +570 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/gpu_turbovec.py +4 -1
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/route_profiles.py +18 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/runtime_policy.py +46 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_index.py +15 -7
- lodedb-0.4.0/src/lodedb/engine/wal_store.py +282 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/knowledge_graph.py +17 -4
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/__init__.py +9 -1
- lodedb-0.4.0/src/lodedb/local/backends.py +302 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/benchmark.py +40 -39
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/cli.py +50 -6
- lodedb-0.4.0/src/lodedb/local/collection.py +411 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/db.py +325 -11
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/doctor.py +74 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/__init__.py +6 -0
- lodedb-0.4.0/src/lodedb/local/integrations/privategpt.py +196 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/mcp_server.py +3 -0
- lodedb-0.4.0/src/lodedb/local/onnx_artifacts.py +381 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/presets.py +61 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/server.py +20 -8
- lodedb-0.2.1/src/lodedb/engine/embedding_backends.py +0 -152
- lodedb-0.2.1/src/lodedb/local/backends.py +0 -158
- {lodedb-0.2.1 → lodedb-0.4.0}/LICENSE +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/NOTICE +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/__main__.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/config.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/__init__.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_atomic_io.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_commit_manifest.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_filelock.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_filter_plan.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_lexical.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_predicate.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/document_text_store.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/index.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/lexical_index_store.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/mps_turbovec.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/route_registry.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/state_journal_store.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_delta_store.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_resident.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/__init__.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/_store.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/langchain.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/llama_index.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/llama_index_graph.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/mem0.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/mcp_install.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/py.typed +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/Cargo.lock +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/Cargo.toml +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/Cargo.toml +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/README.md +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/build.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/examples/dump_state.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/examples/kernel_xtest.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/codebook.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/encode.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/error.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/id_map.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/io.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/lib.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/pack.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/rotation.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/search.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/codebook.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/concurrent_search.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/distortion.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/encode.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/encoded_rows.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/filtering.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/id_map.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/input_validation.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/io_versioning.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/kernel_correctness.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/lazy_init.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/reconstruction.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/rotation.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/state_sequences.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/swap_remove.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/tqplus_calibration.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/Cargo.toml +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/README.md +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/build.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/__init__.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/_dedup.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/_persist.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/agno.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/haystack.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/langchain.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/llama_index.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/src/lib.rs +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_agno.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_dedup.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_filtering.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_haystack.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_id_map.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_index.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_langchain.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_llama_index.py +0 -0
- {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_security.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lodedb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
@@ -8,21 +8,27 @@ Classifier: Topic :: Database :: Database Engines/Servers
|
|
|
8
8
|
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
9
9
|
Requires-Dist: numpy>=2.0.0
|
|
10
10
|
Requires-Dist: typer>=0.12.0
|
|
11
|
+
Requires-Dist: onnxruntime>=1.20.0
|
|
12
|
+
Requires-Dist: transformers>=4.40.0
|
|
11
13
|
Requires-Dist: sentence-transformers>=3.0.0
|
|
12
14
|
Requires-Dist: pyyaml>=6.0.0
|
|
13
15
|
Requires-Dist: pytest>=8.3.0 ; extra == 'dev'
|
|
14
16
|
Requires-Dist: ruff==0.15.17 ; extra == 'dev'
|
|
15
17
|
Requires-Dist: cupy-cuda12x>=13.0.0 ; platform_system == 'Linux' and extra == 'gpu'
|
|
18
|
+
Requires-Dist: pillow>=10.0.0 ; extra == 'image'
|
|
16
19
|
Requires-Dist: langchain-core>=0.3.0 ; extra == 'langchain'
|
|
17
20
|
Requires-Dist: llama-index-core>=0.11.0 ; extra == 'llama-index'
|
|
18
21
|
Requires-Dist: mcp>=1.0.0 ; extra == 'mcp'
|
|
19
22
|
Requires-Dist: mem0ai>=2.0.0 ; extra == 'mem0'
|
|
23
|
+
Requires-Dist: optimum[exporters]>=1.23.0,<2.0.0 ; extra == 'onnx-export'
|
|
20
24
|
Provides-Extra: dev
|
|
21
25
|
Provides-Extra: gpu
|
|
26
|
+
Provides-Extra: image
|
|
22
27
|
Provides-Extra: langchain
|
|
23
28
|
Provides-Extra: llama-index
|
|
24
29
|
Provides-Extra: mcp
|
|
25
30
|
Provides-Extra: mem0
|
|
31
|
+
Provides-Extra: onnx-export
|
|
26
32
|
License-File: LICENSE
|
|
27
33
|
License-File: NOTICE
|
|
28
34
|
Summary: Local-first, privacy-by-default embedded vector database. Your data never leaves your machine.
|
|
@@ -36,23 +42,45 @@ Project-URL: Homepage, https://github.com/Egoist-Machines/LodeDB
|
|
|
36
42
|
Project-URL: Issues, https://github.com/Egoist-Machines/LodeDB/issues
|
|
37
43
|
Project-URL: Repository, https://github.com/Egoist-Machines/LodeDB
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
<h1 align="center">LodeDB</h1>
|
|
46
|
+
<p align="center">🔥 the <b>fastest</b> and <i>most compact</i> embedded vector database in the world 🌍</p>
|
|
40
47
|
|
|
41
48
|
[](LICENSE)
|
|
42
49
|
[](pyproject.toml)
|
|
43
50
|
|
|
44
|
-
**A fast, exact embedded vector database for local RAG: in-process, on-disk, no server.**
|
|
45
|
-
|
|
46
51
|
*Built by [Egoist Machines, Inc.](https://egoistmachines.com) - efficient full-stack infrastructure
|
|
47
52
|
for reliable AI systems.*
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
LodeDB is great for local RAG; it's _extremely fast_, exact, in-process, and on-disk. We're the **best drop-in** durable memory backend for **LangChain, LlamaIndex, and mem0** across all metrics that matter: the most
|
|
55
|
+
compact on disk, GPU-accelerated, and sub-millisecond on durable writes. Point any of them at
|
|
56
|
+
LodeDB instead of its default store. Over 17.5k documents, per framework default:
|
|
57
|
+
|
|
58
|
+
| vs the framework's default store | LangChain `InMemoryVectorStore` | LlamaIndex `SimpleVectorStore` | mem0 Qdrant |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| On-disk footprint | **7.2× smaller** (28 vs 199 MB) | **5.3× smaller** (28 vs 145 MB) | **4.6× smaller** (15 vs 70 MB) |
|
|
61
|
+
| Single-query p50 (CPU) | **~510× faster** (0.57 vs 294 ms) | **~610× faster** (0.57 vs 349 ms) | **~48× faster** (0.64 vs 31 ms) |
|
|
62
|
+
| Batched retrieval, 64 (GPU) | **~1,900×** (6,602 vs ~4 qps) | **~2,200×** (6,414 vs ~3 qps) | **~160×** (4,649 vs 29 qps) |
|
|
63
|
+
| Durable add of one memory | **~10,000× faster** (0.8 ms vs 8.6 s) | **~22,000× faster** (0.9 ms vs 19.3 s) | 0.6 vs 0.5 ms (both sub-ms) |
|
|
64
|
+
|
|
65
|
+
LodeDB matches sqlite-vec/qdrant's per-add latency with a significantly more compact footprint:
|
|
66
|
+
|
|
67
|
+
| **embedded stores** | **durable add p50** | **single-query p50** | **batch-64/query** | **memory footprint** |
|
|
68
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
69
|
+
| sqlite-vec | **0.4 ms** | 25.3 ms | 25.1 ms | 101 MB |
|
|
70
|
+
| qdrant | **0.5 ms** | 27.3 ms | 27.0 ms | 85 MB |
|
|
71
|
+
| **LodeDB** | **0.6 ms** | **0.49 ms** | **0.11 ms** | **29 MB** |
|
|
72
|
+
| pgvector | 2.3 ms | 30.4 ms | 30.6 ms | 50 MB |
|
|
73
|
+
| lancedb | 3.2 ms | 10.3 ms | 10.0 ms | 37 MB |
|
|
74
|
+
| chroma | 6.5 ms | 3.0 ms | 3.1 ms | 151 MB |
|
|
75
|
+
|
|
76
|
+
[Full benchmark, all backends
|
|
77
|
+
(FAISS, Chroma, Qdrant, LanceDB, sqlite-vec, pgvector), and method.](benchmarks/memory_integrations)
|
|
78
|
+
|
|
79
|
+
Most embedded vector databases stop at the CPU. LodeDB runs the same on-disk index **on the
|
|
80
|
+
GPU** when you have one: batched search hits *24k queries/sec on an A10 and 50k qps on an L40S*,
|
|
51
81
|
2.8× to 4.8× the all-CPU ceiling, with recall unchanged. It also persists changed rows
|
|
52
82
|
incrementally, so a commit stays **sub-millisecond even at 1M vectors**.
|
|
53
83
|
|
|
54
|
-
Fast on a laptop. Faster on a GPU. Exact every time. Never phones home.
|
|
55
|
-
|
|
56
84
|
- **GPU-resident batch search**: an fp16 copy of the index lives on the GPU, scored with a
|
|
57
85
|
tiled GEMM plus a streaming top-k (`[gpu]`, Linux/CUDA). [How it works](#gpu-resident-index).
|
|
58
86
|
- **O(changed) persistence**: commits only the rows that changed, 173× to 1,308× faster
|
|
@@ -65,10 +93,16 @@ Fast on a laptop. Faster on a GPU. Exact every time. Never phones home.
|
|
|
65
93
|
[How it works](#concurrency--durability).
|
|
66
94
|
- **Private by default**: text, ids, and vectors stay local; telemetry is metrics-only
|
|
67
95
|
(counts, bytes, latency), never raw payloads.
|
|
68
|
-
- **Local embeddings**:
|
|
69
|
-
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
- **Local embeddings**: ONNX Runtime by default (lower per-query latency), with a PyTorch
|
|
97
|
+
`sentence-transformers` fallback; runs on CPU, CUDA, or MPS. Pick with `embedding_runtime=`.
|
|
98
|
+
- **Multimodal**: index images and text in one shared CLIP space (`model="clip"`) for
|
|
99
|
+
cross-modal search, or bring your own vectors from any model.
|
|
100
|
+
[How it works](#multimodal--bring-your-own-vectors).
|
|
101
|
+
- **Batteries included**: a `lodedb` CLI, a loopback/private-network dev server, an
|
|
102
|
+
[MCP server](#use-as-an-mcp-server), LangChain, LlamaIndex, and mem0 adapters
|
|
103
|
+
(`VectorStore`s, plus a LlamaIndex `PropertyGraphStore`), and a one-line
|
|
104
|
+
[PrivateGPT](https://github.com/zylon-ai/private-gpt) vector-store provider built on the
|
|
105
|
+
LlamaIndex adapter.
|
|
72
106
|
|
|
73
107
|
> 🏢 **Enterprise** The LodeDB core is Apache-2.0 and free to use. Enterprise licensing is
|
|
74
108
|
> available for commercial support, managed and at-scale serving, and on-prem / BYOC
|
|
@@ -86,7 +120,9 @@ the install with `lodedb doctor`. Optional extras:
|
|
|
86
120
|
|
|
87
121
|
```bash
|
|
88
122
|
pip install "lodedb[gpu]" # GPU-resident scan (Linux/CUDA)
|
|
123
|
+
pip install "lodedb[image]" # image + text (CLIP) embedding (model="clip")
|
|
89
124
|
pip install "lodedb[mcp,langchain,llama-index,mem0]" # MCP server + LangChain/LlamaIndex/mem0 adapters
|
|
125
|
+
pip install "lodedb[onnx-export]" # export ONNX for a custom model (Optimum); presets need no export
|
|
90
126
|
```
|
|
91
127
|
|
|
92
128
|
Using LodeDB as memory for a coding assistant? After installing the `mcp` extra, register its
|
|
@@ -144,7 +180,7 @@ Run with `uv run` (e.g. `uv run lodedb doctor`).
|
|
|
144
180
|
```python
|
|
145
181
|
from lodedb import LodeDB
|
|
146
182
|
|
|
147
|
-
db = LodeDB(path="./data", model="minilm") # "minilm" (fast) | "bge" (quality)
|
|
183
|
+
db = LodeDB(path="./data", model="minilm") # "minilm" (fast) | "bge" (quality) | "clip" (image+text)
|
|
148
184
|
|
|
149
185
|
fox = db.add("the quick brown fox jumps", metadata={"topic": "animals"})
|
|
150
186
|
db.add("a lazy dog sleeps all day", metadata={"topic": "animals"})
|
|
@@ -168,8 +204,8 @@ db.persist() # durable .tvim/.tvd/.jsd snapshot; replays on reopen
|
|
|
168
204
|
|
|
169
205
|
Reopen with `LodeDB(path="./data")`; no migration step. Original text is kept in a
|
|
170
206
|
`.tvtext` sidecar for `db.get`; pass `store_text=False` to keep none. Presets are `minilm`
|
|
171
|
-
(384-dim)
|
|
172
|
-
[`examples/`](examples/).
|
|
207
|
+
(384-dim), `bge` (768-dim), and `clip` (512-dim, image+text), with weights pulled from Hugging
|
|
208
|
+
Face on first use. More in [`examples/`](examples/).
|
|
173
209
|
|
|
174
210
|
Need to read a store another process is writing to? Open it read-only. It takes no writer
|
|
175
211
|
lock, so it never blocks on (or is blocked by) the writer:
|
|
@@ -196,7 +232,8 @@ db.search("E1234", k=5, mode="hybrid") # BM25 + RRF: surfaces it in the top-k
|
|
|
196
232
|
db.search("E1234", k=5, mode="lexical") # BM25 ranking alone, no vector scan
|
|
197
233
|
```
|
|
198
234
|
|
|
199
|
-
|
|
235
|
+
<details>
|
|
236
|
+
<summary><b>Prerequisites</b></summary>
|
|
200
237
|
|
|
201
238
|
`mode="hybrid"` and `mode="lexical"` build a BM25 index over your text, so they need a text
|
|
202
239
|
source enabled when you open the database. `mode="vector"` (the default) needs nothing.
|
|
@@ -210,8 +247,10 @@ source enabled when you open the database. `mode="vector"` (the default) needs n
|
|
|
210
247
|
Either source is enough and you can enable both. `store_text=True` is the default, so hybrid
|
|
211
248
|
works out of the box. With neither source enabled, a hybrid or lexical query raises a clear,
|
|
212
249
|
actionable error rather than silently degrading.
|
|
250
|
+
</details>
|
|
213
251
|
|
|
214
|
-
|
|
252
|
+
<details>
|
|
253
|
+
<summary><b>How it works</b></summary>
|
|
215
254
|
|
|
216
255
|
A `filter` constrains both rankers, so `mode="hybrid"` with a filter returns the true top-k of
|
|
217
256
|
the matching subset. The vector half of a hybrid query runs on the same scan as `mode="vector"`,
|
|
@@ -219,8 +258,10 @@ including the GPU-resident batch scan that serves `search_many`; only the BM25 r
|
|
|
219
258
|
fusion run on the CPU, and the vector kernel and on-disk format are untouched. The serving BM25
|
|
220
259
|
index lives in memory and is maintained incrementally: a small mutation folds just the changed
|
|
221
260
|
chunks into the existing index, so a single `add` never forces a full re-tokenization.
|
|
261
|
+
</details>
|
|
222
262
|
|
|
223
|
-
|
|
263
|
+
<details>
|
|
264
|
+
<summary><b>Durable lexical index (`index_text=True`)</b></summary>
|
|
224
265
|
|
|
225
266
|
By default the BM25 index is rebuilt from the retained raw text, so it needs `store_text=True`
|
|
226
267
|
and is re-tokenized on the first hybrid query after opening. Pass `index_text=True` to persist
|
|
@@ -241,6 +282,40 @@ db.close()
|
|
|
241
282
|
reopened = LodeDB(path="./data", index_text=True, store_text=False)
|
|
242
283
|
reopened.search("E1234", k=5, mode="hybrid") # works after reopen, rebuilt from persisted terms
|
|
243
284
|
```
|
|
285
|
+
</details>
|
|
286
|
+
|
|
287
|
+
## Multimodal & bring-your-own vectors
|
|
288
|
+
|
|
289
|
+
The storage and scan are modality-agnostic: TurboVec stores any normalized float32
|
|
290
|
+
vector, so an image, audio, or video embedding is indexed and scanned exactly like a
|
|
291
|
+
text one. There are two ways to use that.
|
|
292
|
+
|
|
293
|
+
Bring your own vectors. Open a vector-only index at your dimension and pass the
|
|
294
|
+
embeddings you already computed with any model (CLIP, SigLIP, ImageBind, an audio or
|
|
295
|
+
video encoder, a hosted API). No embedding model is bundled on this path:
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
db = LodeDB.open_vector_store("./media", vector_dim=512)
|
|
299
|
+
db.add_vectors(image_vector, id="img-001", metadata={"path": "photos/img-001.jpg"})
|
|
300
|
+
db.search_by_vector(query_vector, k=10)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Or use the built-in `clip` preset for image and text in one shared space, so a text
|
|
304
|
+
query retrieves images and an image query retrieves images and text. It rides the base
|
|
305
|
+
sentence-transformers stack and adds only Pillow (`pip install 'lodedb[image]'`):
|
|
306
|
+
|
|
307
|
+
```python
|
|
308
|
+
db = LodeDB("./gallery", model="clip") # downloads clip-ViT-B-32 on first use
|
|
309
|
+
db.add_image("photos/beach.jpg", metadata={"path": "photos/beach.jpg"})
|
|
310
|
+
db.search("a beach at sunset", k=5) # text -> image, cross-modal
|
|
311
|
+
db.search_by_image("photos/beach.jpg", k=5) # image -> image
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
The raw image is never stored; keep it on disk and put its path in `metadata`. Keep one
|
|
315
|
+
embedding model per index (scores are only comparable within one space); the model
|
|
316
|
+
identity is pinned and re-enforced on reopen. To hold several encoders side by side, use
|
|
317
|
+
`LodeCollection` named spaces, and pass `embedder=` to drive an index with your own
|
|
318
|
+
model. See [`docs/multimodal.md`](docs/multimodal.md).
|
|
244
319
|
|
|
245
320
|
## GPU-resident index
|
|
246
321
|
|
|
@@ -325,7 +400,7 @@ and end-to-end single-query latency is 5.7 ms p50.
|
|
|
325
400
|
lodedb doctor # capability report: embedding / GPU / TurboVec backend
|
|
326
401
|
lodedb index ... # build / add to an on-disk index
|
|
327
402
|
lodedb query ... # search
|
|
328
|
-
lodedb serve #
|
|
403
|
+
lodedb serve # dev server (127.0.0.1 by default; private LAN only, no auth)
|
|
329
404
|
lodedb mcp # stdio MCP server for agent memory
|
|
330
405
|
lodedb benchmark # local, metrics-only benchmark
|
|
331
406
|
```
|
|
@@ -339,6 +414,9 @@ your host at `lodedb mcp`:
|
|
|
339
414
|
|
|
340
415
|
```bash
|
|
341
416
|
pip install "lodedb[mcp]"
|
|
417
|
+
|
|
418
|
+
# for coding assistant:
|
|
419
|
+
lodedb mcp install --client claude-code # or: claude-desktop | cursor | lm-studio | codex | all
|
|
342
420
|
```
|
|
343
421
|
|
|
344
422
|
It exposes `lodedb_add`, `lodedb_search`, `lodedb_remove`, and `lodedb_stats`, plus
|
|
@@ -351,7 +429,7 @@ the server with `--exclude-text` to return metrics only (this also withdraws `lo
|
|
|
351
429
|
`--no-store-text` to keep no text on disk at all. `lodedb_stats` is always metrics-only and raw
|
|
352
430
|
query text never leaves the process.
|
|
353
431
|
|
|
354
|
-
### One command
|
|
432
|
+
### One command install
|
|
355
433
|
|
|
356
434
|
`lodedb mcp install` writes the correct entry to a client's config for you, so you do not have to
|
|
357
435
|
find the file or hand-write the JSON/TOML:
|
|
@@ -423,6 +501,21 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
423
501
|
- **Durability is `fast` by default.** Commits are *atomic* but not fsync'd. Pass
|
|
424
502
|
`durability="fsync"` (or `--durability fsync` / `LODEDB_DURABILITY=fsync`) to fsync each
|
|
425
503
|
file and its directory on commit for power-loss durability, at some commit-throughput cost.
|
|
504
|
+
- **WAL commit by default for low-latency durable writes.** Each `add`/`remove` appends one
|
|
505
|
+
framed record to a `<key>.wal` log and a full generation is checkpointed periodically, so a
|
|
506
|
+
durable single add costs roughly an order of magnitude less than publishing a whole generation
|
|
507
|
+
per write, into the sqlite-vec/qdrant range (see the comparison up top). The WAL is replayed
|
|
508
|
+
crash-atomically on reopen (a half-written trailing record is discarded), every writable open
|
|
509
|
+
folds it into a clean committed generation, and `close()`/`persist()` checkpoint it. WAL is
|
|
510
|
+
single-writer: a concurrent `open_readonly` reader still loads a consistent committed
|
|
511
|
+
generation, but the last *checkpointed* one, not the writer's in-flight WAL. Pass
|
|
512
|
+
`commit_mode="generation"` (or `LODEDB_COMMIT_MODE=generation`) for the classic path that
|
|
513
|
+
publishes a crash-atomic, MVCC-readable generation on every write; pick it when many
|
|
514
|
+
out-of-process readers must see each write the instant it commits. Note `<key>.wal` is
|
|
515
|
+
**payload-bearing before a checkpoint** (raw text under `store_text=True`, otherwise embedding
|
|
516
|
+
deltas plus, with `index_text=True`, lexical tokens), so treat it as sensitively as the data it
|
|
517
|
+
indexes; `persist()`/`close()` checkpoint and truncate it, and `generation` mode keeps no WAL.
|
|
518
|
+
See the [payload boundary](docs/architecture.md#persistence--payload-boundary) docs.
|
|
426
519
|
- **Local filesystems only.** The OS advisory lock is unreliable on NFS/SMB.
|
|
427
520
|
|
|
428
521
|
## Limitations
|
|
@@ -1,20 +1,42 @@
|
|
|
1
|
-
|
|
1
|
+
<h1 align="center">LodeDB</h1>
|
|
2
|
+
<p align="center">🔥 the <b>fastest</b> and <i>most compact</i> embedded vector database in the world 🌍</p>
|
|
2
3
|
|
|
3
4
|
[](LICENSE)
|
|
4
5
|
[](pyproject.toml)
|
|
5
6
|
|
|
6
|
-
**A fast, exact embedded vector database for local RAG: in-process, on-disk, no server.**
|
|
7
|
-
|
|
8
7
|
*Built by [Egoist Machines, Inc.](https://egoistmachines.com) - efficient full-stack infrastructure
|
|
9
8
|
for reliable AI systems.*
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
LodeDB is great for local RAG; it's _extremely fast_, exact, in-process, and on-disk. We're the **best drop-in** durable memory backend for **LangChain, LlamaIndex, and mem0** across all metrics that matter: the most
|
|
11
|
+
compact on disk, GPU-accelerated, and sub-millisecond on durable writes. Point any of them at
|
|
12
|
+
LodeDB instead of its default store. Over 17.5k documents, per framework default:
|
|
13
|
+
|
|
14
|
+
| vs the framework's default store | LangChain `InMemoryVectorStore` | LlamaIndex `SimpleVectorStore` | mem0 Qdrant |
|
|
15
|
+
|---|---|---|---|
|
|
16
|
+
| On-disk footprint | **7.2× smaller** (28 vs 199 MB) | **5.3× smaller** (28 vs 145 MB) | **4.6× smaller** (15 vs 70 MB) |
|
|
17
|
+
| Single-query p50 (CPU) | **~510× faster** (0.57 vs 294 ms) | **~610× faster** (0.57 vs 349 ms) | **~48× faster** (0.64 vs 31 ms) |
|
|
18
|
+
| Batched retrieval, 64 (GPU) | **~1,900×** (6,602 vs ~4 qps) | **~2,200×** (6,414 vs ~3 qps) | **~160×** (4,649 vs 29 qps) |
|
|
19
|
+
| Durable add of one memory | **~10,000× faster** (0.8 ms vs 8.6 s) | **~22,000× faster** (0.9 ms vs 19.3 s) | 0.6 vs 0.5 ms (both sub-ms) |
|
|
20
|
+
|
|
21
|
+
LodeDB matches sqlite-vec/qdrant's per-add latency with a significantly more compact footprint:
|
|
22
|
+
|
|
23
|
+
| **embedded stores** | **durable add p50** | **single-query p50** | **batch-64/query** | **memory footprint** |
|
|
24
|
+
| --- | ---: | ---: | ---: | ---: |
|
|
25
|
+
| sqlite-vec | **0.4 ms** | 25.3 ms | 25.1 ms | 101 MB |
|
|
26
|
+
| qdrant | **0.5 ms** | 27.3 ms | 27.0 ms | 85 MB |
|
|
27
|
+
| **LodeDB** | **0.6 ms** | **0.49 ms** | **0.11 ms** | **29 MB** |
|
|
28
|
+
| pgvector | 2.3 ms | 30.4 ms | 30.6 ms | 50 MB |
|
|
29
|
+
| lancedb | 3.2 ms | 10.3 ms | 10.0 ms | 37 MB |
|
|
30
|
+
| chroma | 6.5 ms | 3.0 ms | 3.1 ms | 151 MB |
|
|
31
|
+
|
|
32
|
+
[Full benchmark, all backends
|
|
33
|
+
(FAISS, Chroma, Qdrant, LanceDB, sqlite-vec, pgvector), and method.](benchmarks/memory_integrations)
|
|
34
|
+
|
|
35
|
+
Most embedded vector databases stop at the CPU. LodeDB runs the same on-disk index **on the
|
|
36
|
+
GPU** when you have one: batched search hits *24k queries/sec on an A10 and 50k qps on an L40S*,
|
|
13
37
|
2.8× to 4.8× the all-CPU ceiling, with recall unchanged. It also persists changed rows
|
|
14
38
|
incrementally, so a commit stays **sub-millisecond even at 1M vectors**.
|
|
15
39
|
|
|
16
|
-
Fast on a laptop. Faster on a GPU. Exact every time. Never phones home.
|
|
17
|
-
|
|
18
40
|
- **GPU-resident batch search**: an fp16 copy of the index lives on the GPU, scored with a
|
|
19
41
|
tiled GEMM plus a streaming top-k (`[gpu]`, Linux/CUDA). [How it works](#gpu-resident-index).
|
|
20
42
|
- **O(changed) persistence**: commits only the rows that changed, 173× to 1,308× faster
|
|
@@ -27,10 +49,16 @@ Fast on a laptop. Faster on a GPU. Exact every time. Never phones home.
|
|
|
27
49
|
[How it works](#concurrency--durability).
|
|
28
50
|
- **Private by default**: text, ids, and vectors stay local; telemetry is metrics-only
|
|
29
51
|
(counts, bytes, latency), never raw payloads.
|
|
30
|
-
- **Local embeddings**:
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
52
|
+
- **Local embeddings**: ONNX Runtime by default (lower per-query latency), with a PyTorch
|
|
53
|
+
`sentence-transformers` fallback; runs on CPU, CUDA, or MPS. Pick with `embedding_runtime=`.
|
|
54
|
+
- **Multimodal**: index images and text in one shared CLIP space (`model="clip"`) for
|
|
55
|
+
cross-modal search, or bring your own vectors from any model.
|
|
56
|
+
[How it works](#multimodal--bring-your-own-vectors).
|
|
57
|
+
- **Batteries included**: a `lodedb` CLI, a loopback/private-network dev server, an
|
|
58
|
+
[MCP server](#use-as-an-mcp-server), LangChain, LlamaIndex, and mem0 adapters
|
|
59
|
+
(`VectorStore`s, plus a LlamaIndex `PropertyGraphStore`), and a one-line
|
|
60
|
+
[PrivateGPT](https://github.com/zylon-ai/private-gpt) vector-store provider built on the
|
|
61
|
+
LlamaIndex adapter.
|
|
34
62
|
|
|
35
63
|
> 🏢 **Enterprise** The LodeDB core is Apache-2.0 and free to use. Enterprise licensing is
|
|
36
64
|
> available for commercial support, managed and at-scale serving, and on-prem / BYOC
|
|
@@ -48,7 +76,9 @@ the install with `lodedb doctor`. Optional extras:
|
|
|
48
76
|
|
|
49
77
|
```bash
|
|
50
78
|
pip install "lodedb[gpu]" # GPU-resident scan (Linux/CUDA)
|
|
79
|
+
pip install "lodedb[image]" # image + text (CLIP) embedding (model="clip")
|
|
51
80
|
pip install "lodedb[mcp,langchain,llama-index,mem0]" # MCP server + LangChain/LlamaIndex/mem0 adapters
|
|
81
|
+
pip install "lodedb[onnx-export]" # export ONNX for a custom model (Optimum); presets need no export
|
|
52
82
|
```
|
|
53
83
|
|
|
54
84
|
Using LodeDB as memory for a coding assistant? After installing the `mcp` extra, register its
|
|
@@ -106,7 +136,7 @@ Run with `uv run` (e.g. `uv run lodedb doctor`).
|
|
|
106
136
|
```python
|
|
107
137
|
from lodedb import LodeDB
|
|
108
138
|
|
|
109
|
-
db = LodeDB(path="./data", model="minilm") # "minilm" (fast) | "bge" (quality)
|
|
139
|
+
db = LodeDB(path="./data", model="minilm") # "minilm" (fast) | "bge" (quality) | "clip" (image+text)
|
|
110
140
|
|
|
111
141
|
fox = db.add("the quick brown fox jumps", metadata={"topic": "animals"})
|
|
112
142
|
db.add("a lazy dog sleeps all day", metadata={"topic": "animals"})
|
|
@@ -130,8 +160,8 @@ db.persist() # durable .tvim/.tvd/.jsd snapshot; replays on reopen
|
|
|
130
160
|
|
|
131
161
|
Reopen with `LodeDB(path="./data")`; no migration step. Original text is kept in a
|
|
132
162
|
`.tvtext` sidecar for `db.get`; pass `store_text=False` to keep none. Presets are `minilm`
|
|
133
|
-
(384-dim)
|
|
134
|
-
[`examples/`](examples/).
|
|
163
|
+
(384-dim), `bge` (768-dim), and `clip` (512-dim, image+text), with weights pulled from Hugging
|
|
164
|
+
Face on first use. More in [`examples/`](examples/).
|
|
135
165
|
|
|
136
166
|
Need to read a store another process is writing to? Open it read-only. It takes no writer
|
|
137
167
|
lock, so it never blocks on (or is blocked by) the writer:
|
|
@@ -158,7 +188,8 @@ db.search("E1234", k=5, mode="hybrid") # BM25 + RRF: surfaces it in the top-k
|
|
|
158
188
|
db.search("E1234", k=5, mode="lexical") # BM25 ranking alone, no vector scan
|
|
159
189
|
```
|
|
160
190
|
|
|
161
|
-
|
|
191
|
+
<details>
|
|
192
|
+
<summary><b>Prerequisites</b></summary>
|
|
162
193
|
|
|
163
194
|
`mode="hybrid"` and `mode="lexical"` build a BM25 index over your text, so they need a text
|
|
164
195
|
source enabled when you open the database. `mode="vector"` (the default) needs nothing.
|
|
@@ -172,8 +203,10 @@ source enabled when you open the database. `mode="vector"` (the default) needs n
|
|
|
172
203
|
Either source is enough and you can enable both. `store_text=True` is the default, so hybrid
|
|
173
204
|
works out of the box. With neither source enabled, a hybrid or lexical query raises a clear,
|
|
174
205
|
actionable error rather than silently degrading.
|
|
206
|
+
</details>
|
|
175
207
|
|
|
176
|
-
|
|
208
|
+
<details>
|
|
209
|
+
<summary><b>How it works</b></summary>
|
|
177
210
|
|
|
178
211
|
A `filter` constrains both rankers, so `mode="hybrid"` with a filter returns the true top-k of
|
|
179
212
|
the matching subset. The vector half of a hybrid query runs on the same scan as `mode="vector"`,
|
|
@@ -181,8 +214,10 @@ including the GPU-resident batch scan that serves `search_many`; only the BM25 r
|
|
|
181
214
|
fusion run on the CPU, and the vector kernel and on-disk format are untouched. The serving BM25
|
|
182
215
|
index lives in memory and is maintained incrementally: a small mutation folds just the changed
|
|
183
216
|
chunks into the existing index, so a single `add` never forces a full re-tokenization.
|
|
217
|
+
</details>
|
|
184
218
|
|
|
185
|
-
|
|
219
|
+
<details>
|
|
220
|
+
<summary><b>Durable lexical index (`index_text=True`)</b></summary>
|
|
186
221
|
|
|
187
222
|
By default the BM25 index is rebuilt from the retained raw text, so it needs `store_text=True`
|
|
188
223
|
and is re-tokenized on the first hybrid query after opening. Pass `index_text=True` to persist
|
|
@@ -203,6 +238,40 @@ db.close()
|
|
|
203
238
|
reopened = LodeDB(path="./data", index_text=True, store_text=False)
|
|
204
239
|
reopened.search("E1234", k=5, mode="hybrid") # works after reopen, rebuilt from persisted terms
|
|
205
240
|
```
|
|
241
|
+
</details>
|
|
242
|
+
|
|
243
|
+
## Multimodal & bring-your-own vectors
|
|
244
|
+
|
|
245
|
+
The storage and scan are modality-agnostic: TurboVec stores any normalized float32
|
|
246
|
+
vector, so an image, audio, or video embedding is indexed and scanned exactly like a
|
|
247
|
+
text one. There are two ways to use that.
|
|
248
|
+
|
|
249
|
+
Bring your own vectors. Open a vector-only index at your dimension and pass the
|
|
250
|
+
embeddings you already computed with any model (CLIP, SigLIP, ImageBind, an audio or
|
|
251
|
+
video encoder, a hosted API). No embedding model is bundled on this path:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
db = LodeDB.open_vector_store("./media", vector_dim=512)
|
|
255
|
+
db.add_vectors(image_vector, id="img-001", metadata={"path": "photos/img-001.jpg"})
|
|
256
|
+
db.search_by_vector(query_vector, k=10)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Or use the built-in `clip` preset for image and text in one shared space, so a text
|
|
260
|
+
query retrieves images and an image query retrieves images and text. It rides the base
|
|
261
|
+
sentence-transformers stack and adds only Pillow (`pip install 'lodedb[image]'`):
|
|
262
|
+
|
|
263
|
+
```python
|
|
264
|
+
db = LodeDB("./gallery", model="clip") # downloads clip-ViT-B-32 on first use
|
|
265
|
+
db.add_image("photos/beach.jpg", metadata={"path": "photos/beach.jpg"})
|
|
266
|
+
db.search("a beach at sunset", k=5) # text -> image, cross-modal
|
|
267
|
+
db.search_by_image("photos/beach.jpg", k=5) # image -> image
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The raw image is never stored; keep it on disk and put its path in `metadata`. Keep one
|
|
271
|
+
embedding model per index (scores are only comparable within one space); the model
|
|
272
|
+
identity is pinned and re-enforced on reopen. To hold several encoders side by side, use
|
|
273
|
+
`LodeCollection` named spaces, and pass `embedder=` to drive an index with your own
|
|
274
|
+
model. See [`docs/multimodal.md`](docs/multimodal.md).
|
|
206
275
|
|
|
207
276
|
## GPU-resident index
|
|
208
277
|
|
|
@@ -287,7 +356,7 @@ and end-to-end single-query latency is 5.7 ms p50.
|
|
|
287
356
|
lodedb doctor # capability report: embedding / GPU / TurboVec backend
|
|
288
357
|
lodedb index ... # build / add to an on-disk index
|
|
289
358
|
lodedb query ... # search
|
|
290
|
-
lodedb serve #
|
|
359
|
+
lodedb serve # dev server (127.0.0.1 by default; private LAN only, no auth)
|
|
291
360
|
lodedb mcp # stdio MCP server for agent memory
|
|
292
361
|
lodedb benchmark # local, metrics-only benchmark
|
|
293
362
|
```
|
|
@@ -301,6 +370,9 @@ your host at `lodedb mcp`:
|
|
|
301
370
|
|
|
302
371
|
```bash
|
|
303
372
|
pip install "lodedb[mcp]"
|
|
373
|
+
|
|
374
|
+
# for coding assistant:
|
|
375
|
+
lodedb mcp install --client claude-code # or: claude-desktop | cursor | lm-studio | codex | all
|
|
304
376
|
```
|
|
305
377
|
|
|
306
378
|
It exposes `lodedb_add`, `lodedb_search`, `lodedb_remove`, and `lodedb_stats`, plus
|
|
@@ -313,7 +385,7 @@ the server with `--exclude-text` to return metrics only (this also withdraws `lo
|
|
|
313
385
|
`--no-store-text` to keep no text on disk at all. `lodedb_stats` is always metrics-only and raw
|
|
314
386
|
query text never leaves the process.
|
|
315
387
|
|
|
316
|
-
### One command
|
|
388
|
+
### One command install
|
|
317
389
|
|
|
318
390
|
`lodedb mcp install` writes the correct entry to a client's config for you, so you do not have to
|
|
319
391
|
find the file or hand-write the JSON/TOML:
|
|
@@ -385,6 +457,21 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
385
457
|
- **Durability is `fast` by default.** Commits are *atomic* but not fsync'd. Pass
|
|
386
458
|
`durability="fsync"` (or `--durability fsync` / `LODEDB_DURABILITY=fsync`) to fsync each
|
|
387
459
|
file and its directory on commit for power-loss durability, at some commit-throughput cost.
|
|
460
|
+
- **WAL commit by default for low-latency durable writes.** Each `add`/`remove` appends one
|
|
461
|
+
framed record to a `<key>.wal` log and a full generation is checkpointed periodically, so a
|
|
462
|
+
durable single add costs roughly an order of magnitude less than publishing a whole generation
|
|
463
|
+
per write, into the sqlite-vec/qdrant range (see the comparison up top). The WAL is replayed
|
|
464
|
+
crash-atomically on reopen (a half-written trailing record is discarded), every writable open
|
|
465
|
+
folds it into a clean committed generation, and `close()`/`persist()` checkpoint it. WAL is
|
|
466
|
+
single-writer: a concurrent `open_readonly` reader still loads a consistent committed
|
|
467
|
+
generation, but the last *checkpointed* one, not the writer's in-flight WAL. Pass
|
|
468
|
+
`commit_mode="generation"` (or `LODEDB_COMMIT_MODE=generation`) for the classic path that
|
|
469
|
+
publishes a crash-atomic, MVCC-readable generation on every write; pick it when many
|
|
470
|
+
out-of-process readers must see each write the instant it commits. Note `<key>.wal` is
|
|
471
|
+
**payload-bearing before a checkpoint** (raw text under `store_text=True`, otherwise embedding
|
|
472
|
+
deltas plus, with `index_text=True`, lexical tokens), so treat it as sensitively as the data it
|
|
473
|
+
indexes; `persist()`/`close()` checkpoint and truncate it, and `generation` mode keeps no WAL.
|
|
474
|
+
See the [payload boundary](docs/architecture.md#persistence--payload-boundary) docs.
|
|
388
475
|
- **Local filesystems only.** The OS advisory lock is unreliable on NFS/SMB.
|
|
389
476
|
|
|
390
477
|
## Limitations
|
|
@@ -5,7 +5,7 @@ build-backend = "maturin"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "lodedb"
|
|
7
7
|
# Static (maturin reads it here); keep in sync with src/lodedb/__init__.py __version__.
|
|
8
|
-
version = "0.
|
|
8
|
+
version = "0.4.0"
|
|
9
9
|
description = "Local-first, privacy-by-default embedded vector database. Your data never leaves your machine."
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
requires-python = ">=3.11"
|
|
@@ -33,13 +33,19 @@ classifiers = [
|
|
|
33
33
|
# Lean runtime set (see docs/architecture.md). The compact TurboVec CPU core +
|
|
34
34
|
# .tvim/.tvd persistence is the bundled `lodedb._turbovec` extension (compiled
|
|
35
35
|
# into the wheel, see [tool.maturin]), not a PyPI dependency. numpy = embedding/
|
|
36
|
-
# scan I/O; typer = CLI;
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
36
|
+
# scan I/O; typer = CLI; pyyaml = loads the bundled route registry.
|
|
37
|
+
# Embedding runtime: onnxruntime is the default (faster feature extraction; the
|
|
38
|
+
# preset models ship a prebuilt ONNX on the Hub, fetched on first use) with
|
|
39
|
+
# transformers for tokenization; sentence-transformers (which pulls torch) is the
|
|
40
|
+
# fallback when ONNX cannot be set up. Exporting ONNX for a model without a prebuilt
|
|
41
|
+
# graph is the opt-in [onnx-export] extra (Optimum), not part of the base install.
|
|
42
|
+
# Heavy research deps (faiss/modal/mteb/datasets/matplotlib/sklearn) are
|
|
43
|
+
# deliberately NOT here and are guarded out by tests/test_import_boundary.py.
|
|
40
44
|
dependencies = [
|
|
41
45
|
"numpy>=2.0.0",
|
|
42
46
|
"typer>=0.12.0",
|
|
47
|
+
"onnxruntime>=1.20.0",
|
|
48
|
+
"transformers>=4.40.0",
|
|
43
49
|
"sentence-transformers>=3.0.0",
|
|
44
50
|
"pyyaml>=6.0.0",
|
|
45
51
|
]
|
|
@@ -48,6 +54,10 @@ dependencies = [
|
|
|
48
54
|
# Opt-in GPU-resident exact scan on CUDA hosts (gpu_turbovec); absent CUDA, LodeDB uses
|
|
49
55
|
# the CPU scan. cupy wheels are Linux/CUDA-only, so this resolves to nothing elsewhere.
|
|
50
56
|
gpu = ["cupy-cuda12x>=13.0.0; platform_system == 'Linux'"]
|
|
57
|
+
# Export an ONNX graph for a model that does not ship a prebuilt one on the Hub
|
|
58
|
+
# (the built-in minilm/bge presets do, so this is only needed for custom models).
|
|
59
|
+
# Optimum runs the export in a subprocess, so it never imports into the lodedb process.
|
|
60
|
+
onnx-export = ["optimum[exporters]>=1.23.0,<2.0.0"]
|
|
51
61
|
# Optional stdio MCP server so coding agents can use LodeDB as local memory.
|
|
52
62
|
mcp = ["mcp>=1.0.0"]
|
|
53
63
|
# Optional LangChain VectorStore adapter.
|
|
@@ -56,6 +66,11 @@ langchain = ["langchain-core>=0.3.0"]
|
|
|
56
66
|
llama-index = ["llama-index-core>=0.11.0"]
|
|
57
67
|
# Optional mem0 vector-store adapter.
|
|
58
68
|
mem0 = ["mem0ai>=2.0.0"]
|
|
69
|
+
# Optional image + text (CLIP) embedding via the "clip" preset / add_image. The CLIP
|
|
70
|
+
# model runs on the existing sentence-transformers + torch stack (already in the base
|
|
71
|
+
# deps), so this adds only Pillow for decoding image files. Both are imported lazily,
|
|
72
|
+
# so a plain `import lodedb` pulls neither (guarded by tests/test_import_boundary.py).
|
|
73
|
+
image = ["pillow>=10.0.0"]
|
|
59
74
|
# ruff is pinned (not floored) so `ruff format`/`ruff check` are reproducible across
|
|
60
75
|
# contributors and CI — the formatter's output can change between ruff releases.
|
|
61
76
|
dev = ["pytest>=8.3.0", "ruff==0.15.17"]
|
|
@@ -11,6 +11,8 @@ from __future__ import annotations
|
|
|
11
11
|
from lodedb.local import (
|
|
12
12
|
LOCAL_MODEL_PRESETS,
|
|
13
13
|
ConcurrentWriterError,
|
|
14
|
+
ImageEmbeddingUnsupportedError,
|
|
15
|
+
LodeCollection,
|
|
14
16
|
LodeDB,
|
|
15
17
|
LodeSearchHit,
|
|
16
18
|
ReadOnlyError,
|
|
@@ -19,11 +21,13 @@ from lodedb.local import (
|
|
|
19
21
|
from lodedb.local.cli import app, main
|
|
20
22
|
|
|
21
23
|
# Keep in sync with `version` in pyproject.toml (the release workflow asserts they match).
|
|
22
|
-
__version__ = "0.
|
|
24
|
+
__version__ = "0.4.0"
|
|
23
25
|
|
|
24
26
|
__all__ = [
|
|
25
27
|
"LOCAL_MODEL_PRESETS",
|
|
26
28
|
"ConcurrentWriterError",
|
|
29
|
+
"ImageEmbeddingUnsupportedError",
|
|
30
|
+
"LodeCollection",
|
|
27
31
|
"LodeDB",
|
|
28
32
|
"LodeSearchHit",
|
|
29
33
|
"ReadOnlyError",
|