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.
Files changed (106) hide show
  1. {lodedb-0.2.1 → lodedb-0.4.0}/PKG-INFO +113 -20
  2. {lodedb-0.2.1 → lodedb-0.4.0}/README.md +106 -19
  3. {lodedb-0.2.1 → lodedb-0.4.0}/pyproject.toml +20 -5
  4. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/__init__.py +5 -1
  5. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/core.py +919 -70
  6. lodedb-0.4.0/src/lodedb/engine/embedding_backends.py +570 -0
  7. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/gpu_turbovec.py +4 -1
  8. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/route_profiles.py +18 -0
  9. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/runtime_policy.py +46 -0
  10. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_index.py +15 -7
  11. lodedb-0.4.0/src/lodedb/engine/wal_store.py +282 -0
  12. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/knowledge_graph.py +17 -4
  13. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/__init__.py +9 -1
  14. lodedb-0.4.0/src/lodedb/local/backends.py +302 -0
  15. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/benchmark.py +40 -39
  16. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/cli.py +50 -6
  17. lodedb-0.4.0/src/lodedb/local/collection.py +411 -0
  18. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/db.py +325 -11
  19. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/doctor.py +74 -0
  20. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/__init__.py +6 -0
  21. lodedb-0.4.0/src/lodedb/local/integrations/privategpt.py +196 -0
  22. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/mcp_server.py +3 -0
  23. lodedb-0.4.0/src/lodedb/local/onnx_artifacts.py +381 -0
  24. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/presets.py +61 -0
  25. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/server.py +20 -8
  26. lodedb-0.2.1/src/lodedb/engine/embedding_backends.py +0 -152
  27. lodedb-0.2.1/src/lodedb/local/backends.py +0 -158
  28. {lodedb-0.2.1 → lodedb-0.4.0}/LICENSE +0 -0
  29. {lodedb-0.2.1 → lodedb-0.4.0}/NOTICE +0 -0
  30. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/__main__.py +0 -0
  31. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/config.py +0 -0
  32. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/__init__.py +0 -0
  33. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_atomic_io.py +0 -0
  34. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_commit_manifest.py +0 -0
  35. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_filelock.py +0 -0
  36. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_filter_plan.py +0 -0
  37. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_lexical.py +0 -0
  38. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/_predicate.py +0 -0
  39. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/document_text_store.py +0 -0
  40. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/index.py +0 -0
  41. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/lexical_index_store.py +0 -0
  42. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/mps_turbovec.py +0 -0
  43. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/route_registry.py +0 -0
  44. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/state_journal_store.py +0 -0
  45. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_delta_store.py +0 -0
  46. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/engine/turbovec_resident.py +0 -0
  47. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/__init__.py +0 -0
  48. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/graph/_store.py +0 -0
  49. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/langchain.py +0 -0
  50. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/llama_index.py +0 -0
  51. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/llama_index_graph.py +0 -0
  52. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/integrations/mem0.py +0 -0
  53. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/local/mcp_install.py +0 -0
  54. {lodedb-0.2.1 → lodedb-0.4.0}/src/lodedb/py.typed +0 -0
  55. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/Cargo.lock +0 -0
  56. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/Cargo.toml +0 -0
  57. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/Cargo.toml +0 -0
  58. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/README.md +0 -0
  59. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/build.rs +0 -0
  60. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/examples/dump_state.rs +0 -0
  61. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/examples/kernel_xtest.rs +0 -0
  62. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/codebook.rs +0 -0
  63. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/encode.rs +0 -0
  64. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/error.rs +0 -0
  65. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/id_map.rs +0 -0
  66. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/io.rs +0 -0
  67. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/lib.rs +0 -0
  68. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/pack.rs +0 -0
  69. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/rotation.rs +0 -0
  70. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/src/search.rs +0 -0
  71. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/codebook.rs +0 -0
  72. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/concurrent_search.rs +0 -0
  73. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/distortion.rs +0 -0
  74. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/encode.rs +0 -0
  75. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/encoded_rows.rs +0 -0
  76. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/filtering.rs +0 -0
  77. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/id_map.rs +0 -0
  78. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/input_validation.rs +0 -0
  79. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/io_versioning.rs +0 -0
  80. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/kernel_correctness.rs +0 -0
  81. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/lazy_init.rs +0 -0
  82. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/reconstruction.rs +0 -0
  83. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/rotation.rs +0 -0
  84. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/state_sequences.rs +0 -0
  85. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/swap_remove.rs +0 -0
  86. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec/tests/tqplus_calibration.rs +0 -0
  87. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/Cargo.toml +0 -0
  88. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/README.md +0 -0
  89. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/build.rs +0 -0
  90. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/__init__.py +0 -0
  91. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/_dedup.py +0 -0
  92. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/_persist.py +0 -0
  93. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/agno.py +0 -0
  94. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/haystack.py +0 -0
  95. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/langchain.py +0 -0
  96. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/python/turbovec/llama_index.py +0 -0
  97. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/src/lib.rs +0 -0
  98. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_agno.py +0 -0
  99. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_dedup.py +0 -0
  100. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_filtering.py +0 -0
  101. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_haystack.py +0 -0
  102. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_id_map.py +0 -0
  103. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_index.py +0 -0
  104. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_langchain.py +0 -0
  105. {lodedb-0.2.1 → lodedb-0.4.0}/third_party/turbovec/turbovec-python/tests/test_llama_index.py +0 -0
  106. {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.2.1
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
- # LodeDB
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: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
42
49
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](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
- Most embedded vector databases stop at the CPU. LodeDB runs the same on-disk index on the
50
- GPU when you have one: batched search hits **24k queries/sec on an A10 and 50k qps on an L40S**,
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**: `sentence-transformers` on CUDA, MPS, or CPU.
69
- - **Batteries included**: a `lodedb` CLI, a loopback dev server, an
70
- [MCP server](#use-as-an-mcp-server), and LangChain, LlamaIndex, and mem0 adapters
71
- (`VectorStore`s, plus a LlamaIndex `PropertyGraphStore`).
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) and `bge` (768-dim), with weights pulled from Hugging Face on first use. More in
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
- ### Prerequisites
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
- ### How it works
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
- ### Durable lexical index (`index_text=True`)
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 # loopback dev server (127.0.0.1, no auth)
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
- # LodeDB
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: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
4
5
  [![Python 3.11+](https://img.shields.io/badge/python-3.11+-blue.svg)](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
- Most embedded vector databases stop at the CPU. LodeDB runs the same on-disk index on the
12
- GPU when you have one: batched search hits **24k queries/sec on an A10 and 50k qps on an L40S**,
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**: `sentence-transformers` on CUDA, MPS, or CPU.
31
- - **Batteries included**: a `lodedb` CLI, a loopback dev server, an
32
- [MCP server](#use-as-an-mcp-server), and LangChain, LlamaIndex, and mem0 adapters
33
- (`VectorStore`s, plus a LlamaIndex `PropertyGraphStore`).
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) and `bge` (768-dim), with weights pulled from Hugging Face on first use. More in
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
- ### Prerequisites
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
- ### How it works
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
- ### Durable lexical index (`index_text=True`)
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 # loopback dev server (127.0.0.1, no auth)
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.2.1"
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; sentence-transformers = default embedding backend (pulls
37
- # torch/transformers); pyyaml = loads the bundled route registry. Heavy research
38
- # deps (faiss/modal/mteb/datasets/matplotlib/sklearn) are deliberately NOT here
39
- # and are guarded out by tests/test_import_boundary.py.
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.2.1"
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",