vexra 1.1.1__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 (38) hide show
  1. vexra-1.1.1/Cargo.toml +70 -0
  2. vexra-1.1.1/PKG-INFO +74 -0
  3. vexra-1.1.1/README.md +50 -0
  4. vexra-1.1.1/crates/vexra-core/Cargo.toml +25 -0
  5. vexra-1.1.1/crates/vexra-core/benches/search_bench.rs +185 -0
  6. vexra-1.1.1/crates/vexra-core/src/collection.rs +578 -0
  7. vexra-1.1.1/crates/vexra-core/src/config.rs +273 -0
  8. vexra-1.1.1/crates/vexra-core/src/db.rs +541 -0
  9. vexra-1.1.1/crates/vexra-core/src/error.rs +38 -0
  10. vexra-1.1.1/crates/vexra-core/src/lib.rs +39 -0
  11. vexra-1.1.1/crates/vexra-index/Cargo.toml +19 -0
  12. vexra-1.1.1/crates/vexra-index/src/distance.rs +581 -0
  13. vexra-1.1.1/crates/vexra-index/src/error.rs +22 -0
  14. vexra-1.1.1/crates/vexra-index/src/flat.rs +195 -0
  15. vexra-1.1.1/crates/vexra-index/src/hnsw/builder.rs +92 -0
  16. vexra-1.1.1/crates/vexra-index/src/hnsw/graph.rs +567 -0
  17. vexra-1.1.1/crates/vexra-index/src/hnsw/mod.rs +93 -0
  18. vexra-1.1.1/crates/vexra-index/src/hnsw/node.rs +123 -0
  19. vexra-1.1.1/crates/vexra-index/src/hnsw/search.rs +91 -0
  20. vexra-1.1.1/crates/vexra-index/src/lib.rs +85 -0
  21. vexra-1.1.1/crates/vexra-metadata/Cargo.toml +16 -0
  22. vexra-1.1.1/crates/vexra-metadata/src/error.rs +22 -0
  23. vexra-1.1.1/crates/vexra-metadata/src/filter.rs +344 -0
  24. vexra-1.1.1/crates/vexra-metadata/src/lib.rs +13 -0
  25. vexra-1.1.1/crates/vexra-metadata/src/store.rs +137 -0
  26. vexra-1.1.1/crates/vexra-storage/Cargo.toml +20 -0
  27. vexra-1.1.1/crates/vexra-storage/src/encoding.rs +130 -0
  28. vexra-1.1.1/crates/vexra-storage/src/error.rs +46 -0
  29. vexra-1.1.1/crates/vexra-storage/src/format.rs +516 -0
  30. vexra-1.1.1/crates/vexra-storage/src/lib.rs +35 -0
  31. vexra-1.1.1/crates/vexra-storage/src/page_cache.rs +486 -0
  32. vexra-1.1.1/crates/vexra-storage/src/wal.rs +504 -0
  33. vexra-1.1.1/pyproject.toml +36 -0
  34. vexra-1.1.1/sdk/python/Cargo.lock +431 -0
  35. vexra-1.1.1/sdk/python/Cargo.toml +25 -0
  36. vexra-1.1.1/sdk/python/README.md +50 -0
  37. vexra-1.1.1/sdk/python/src/lib.rs +107 -0
  38. vexra-1.1.1/vexra/__init__.py +7 -0
vexra-1.1.1/Cargo.toml ADDED
@@ -0,0 +1,70 @@
1
+ [workspace]
2
+ resolver = "2"
3
+ members = [
4
+ "crates/vexra-core",
5
+ "crates/vexra-storage",
6
+ "crates/vexra-index",
7
+ "crates/vexra-metadata",
8
+ "crates/vexra-query",
9
+ "crates/vexra-ffi",
10
+ "crates/vexra-cli",
11
+ "crates/vexra-server",
12
+ "crates/vexra-embedding",
13
+ ]
14
+
15
+ [workspace.package]
16
+ version = "1.0.0-rc1"
17
+ edition = "2021"
18
+ license = "MIT"
19
+ repository = "https://github.com/embeddb/embeddb"
20
+ authors = ["EmbedDB Contributors"]
21
+
22
+ [workspace.dependencies]
23
+ # Serialization
24
+ serde = { version = "1", features = ["derive"] }
25
+ serde_json = "1"
26
+ bytemuck = { version = "1", features = ["derive"] }
27
+
28
+ # Error handling
29
+ thiserror = "2"
30
+
31
+ # Logging
32
+ log = "0.4"
33
+ tracing = "0.1"
34
+
35
+ # Concurrency
36
+ parking_lot = "0.12"
37
+ arc-swap = "1"
38
+
39
+ # CLI
40
+ clap = { version = "4", features = ["derive"] }
41
+
42
+ # Storage
43
+ memmap2 = "0.9"
44
+ crc32fast = "1"
45
+
46
+ # Big arrays / SIMD
47
+ rustix = "0.38"
48
+
49
+ # Web server (Phase 2)
50
+ # axum = "0.7"
51
+ # tokio = { version = "1", features = ["full"] }
52
+ # tower-http = { version = "0.5", features = ["cors", "fs"] }
53
+ # rust-embed = "8"
54
+
55
+ # ONNX (Phase 2, feature-gated)
56
+ # ort = "2"
57
+
58
+ # Text search (Phase 3)
59
+ # tantivy = "0.22"
60
+
61
+ [profile.release]
62
+ opt-level = 3
63
+ lto = true
64
+ codegen-units = 1
65
+ strip = true
66
+
67
+ [profile.bench]
68
+ opt-level = 3
69
+ lto = true
70
+ codegen-units = 1
vexra-1.1.1/PKG-INFO ADDED
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: vexra
3
+ Version: 1.1.1
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: License :: OSI Approved :: MIT License
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: Programming Language :: Python :: 3.9
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Rust
13
+ Classifier: Topic :: Database
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Requires-Dist: pytest>=7 ; extra == 'dev'
16
+ Provides-Extra: dev
17
+ Summary: Vexra Python SDK — embedded vector database
18
+ License: MIT
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
21
+ Project-URL: Homepage, https://github.com/Mengxun326/embeddb
22
+ Project-URL: Issues, https://github.com/Mengxun326/embeddb/issues
23
+
24
+ # EmbedDB Python SDK
25
+
26
+ Python bindings for EmbedDB — the embedded vector database.
27
+
28
+ ## Installation
29
+
30
+ ```bash
31
+ pip install embeddb
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```python
37
+ import embeddb
38
+
39
+ # Open or create a database
40
+ db = embeddb.Database("data.embeddb")
41
+
42
+ # Create a collection
43
+ col = db.create_collection("docs", dimension=384, distance=embeddb.DistanceMetric.COSINE)
44
+
45
+ # Insert vectors
46
+ col.insert({"id": "doc1", "vector": [0.1] * 384, "metadata": {"title": "Hello"}})
47
+ col.insert({"id": "doc2", "vector": [0.2] * 384, "metadata": {"title": "World"}})
48
+
49
+ # Search
50
+ results = col.search(vector=[0.15] * 384, top_k=10)
51
+ for hit in results:
52
+ print(f"{hit.id}: {hit.score:.4f}")
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### Database
58
+
59
+ - `Database(path)` — Open or create a database
60
+ - `db.create_collection(name, dimension, distance)` — Create a collection
61
+ - `db.get_collection(name)` — Get an existing collection
62
+ - `db.close()` — Close the database
63
+
64
+ ### Collection
65
+
66
+ - `col.insert(doc)` — Insert a document `{"id": str, "vector": [float], "metadata": dict}`
67
+ - `col.search(vector, top_k=10)` — Search for nearest neighbors
68
+
69
+ ### Distance Metrics
70
+
71
+ - `DistanceMetric.COSINE` — Cosine distance
72
+ - `DistanceMetric.EUCLIDEAN` — Euclidean (L2) distance
73
+ - `DistanceMetric.DOT_PRODUCT` — Dot product similarity
74
+
vexra-1.1.1/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # EmbedDB Python SDK
2
+
3
+ Python bindings for EmbedDB — the embedded vector database.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install embeddb
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ import embeddb
15
+
16
+ # Open or create a database
17
+ db = embeddb.Database("data.embeddb")
18
+
19
+ # Create a collection
20
+ col = db.create_collection("docs", dimension=384, distance=embeddb.DistanceMetric.COSINE)
21
+
22
+ # Insert vectors
23
+ col.insert({"id": "doc1", "vector": [0.1] * 384, "metadata": {"title": "Hello"}})
24
+ col.insert({"id": "doc2", "vector": [0.2] * 384, "metadata": {"title": "World"}})
25
+
26
+ # Search
27
+ results = col.search(vector=[0.15] * 384, top_k=10)
28
+ for hit in results:
29
+ print(f"{hit.id}: {hit.score:.4f}")
30
+ ```
31
+
32
+ ## API
33
+
34
+ ### Database
35
+
36
+ - `Database(path)` — Open or create a database
37
+ - `db.create_collection(name, dimension, distance)` — Create a collection
38
+ - `db.get_collection(name)` — Get an existing collection
39
+ - `db.close()` — Close the database
40
+
41
+ ### Collection
42
+
43
+ - `col.insert(doc)` — Insert a document `{"id": str, "vector": [float], "metadata": dict}`
44
+ - `col.search(vector, top_k=10)` — Search for nearest neighbors
45
+
46
+ ### Distance Metrics
47
+
48
+ - `DistanceMetric.COSINE` — Cosine distance
49
+ - `DistanceMetric.EUCLIDEAN` — Euclidean (L2) distance
50
+ - `DistanceMetric.DOT_PRODUCT` — Dot product similarity
@@ -0,0 +1,25 @@
1
+ [package]
2
+ name = "vexra-core"
3
+ version.workspace = true
4
+ edition.workspace = true
5
+ license.workspace = true
6
+ description = "EmbedDB core: public API, Database and Collection handles"
7
+
8
+ [dependencies]
9
+ vexra-storage = { path = "../vexra-storage" }
10
+ vexra-index = { path = "../vexra-index" }
11
+ vexra-metadata = { path = "../vexra-metadata" }
12
+ serde.workspace = true
13
+ serde_json.workspace = true
14
+ thiserror.workspace = true
15
+ log.workspace = true
16
+ parking_lot.workspace = true
17
+
18
+ [dev-dependencies]
19
+ tempfile = "3"
20
+ rand = "0.8"
21
+ criterion = { version = "0.5", features = ["html_reports"] }
22
+
23
+ [[bench]]
24
+ name = "search_bench"
25
+ harness = false
@@ -0,0 +1,185 @@
1
+ //! EmbedDB benchmarks: Flat vs HNSW insert and search performance.
2
+ //!
3
+ //! Run with: cargo bench
4
+
5
+ use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
6
+ use vexra_core::collection::{Collection, IndexType};
7
+ use vexra_core::config::{CollectionConfig, SearchQuery};
8
+ use vexra_core::DistanceMetric;
9
+ use vexra_index::flat::FlatIndex;
10
+ use vexra_index::hnsw::graph::HnswGraph;
11
+ use vexra_index::hnsw::HnswConfig;
12
+ use vexra_index::{SearchResult, VectorIndex};
13
+ use rand::Rng;
14
+ use std::time::Duration;
15
+
16
+ const DIM: usize = 128;
17
+
18
+ /// Generate `n` random normalized vectors of dimension `DIM`.
19
+ fn random_vectors(n: usize) -> Vec<Vec<f32>> {
20
+ let mut rng = rand::thread_rng();
21
+ (0..n)
22
+ .map(|_| (0..DIM).map(|_| rng.gen::<f32>()).collect())
23
+ .collect()
24
+ }
25
+
26
+ /// Build a random FlatIndex with `n` vectors.
27
+ fn build_flat(n: usize) -> FlatIndex {
28
+ let mut idx = FlatIndex::new(DIM, DistanceMetric::Euclidean);
29
+ for (i, v) in random_vectors(n).into_iter().enumerate() {
30
+ idx.insert(i as u64, &v).unwrap();
31
+ }
32
+ idx
33
+ }
34
+
35
+ /// Build a random HNSW graph with `n` vectors.
36
+ fn build_hnsw(n: usize) -> HnswGraph {
37
+ let config = HnswConfig::new(16).with_ef_construction(200);
38
+ let mut graph = HnswGraph::new(DIM, DistanceMetric::Euclidean, config);
39
+ for (i, v) in random_vectors(n).into_iter().enumerate() {
40
+ graph.insert(i as u64, &v).unwrap();
41
+ }
42
+ graph
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Insert benchmarks
47
+ // ---------------------------------------------------------------------------
48
+
49
+ fn bench_insert_flat(c: &mut Criterion) {
50
+ let mut group = c.benchmark_group("insert_flat");
51
+ for n in [1000, 10000, 50000] {
52
+ group.throughput(Throughput::Elements(n as u64));
53
+ group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
54
+ b.iter(|| build_flat(black_box(n)));
55
+ });
56
+ }
57
+ }
58
+
59
+ fn bench_insert_hnsw(c: &mut Criterion) {
60
+ let mut group = c.benchmark_group("insert_hnsw");
61
+ for n in [1000, 10000, 50000] {
62
+ group.throughput(Throughput::Elements(n as u64));
63
+ group.bench_with_input(BenchmarkId::from_parameter(n), &n, |b, &n| {
64
+ b.iter(|| build_hnsw(black_box(n)));
65
+ });
66
+ }
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Search benchmarks (100 queries, avg latency)
71
+ // ---------------------------------------------------------------------------
72
+
73
+ fn bench_search_flat(c: &mut Criterion) {
74
+ let mut group = c.benchmark_group("search_flat");
75
+ for n in [1000, 10000, 50000] {
76
+ let idx = build_flat(n);
77
+ let queries = random_vectors(100);
78
+ group.throughput(Throughput::Elements(100));
79
+ group.bench_with_input(BenchmarkId::from_parameter(n), &(idx, queries), |b, (idx, queries)| {
80
+ b.iter(|| {
81
+ for q in queries {
82
+ black_box(idx.search(q, 10).unwrap());
83
+ }
84
+ });
85
+ });
86
+ }
87
+ }
88
+
89
+ fn bench_search_hnsw(c: &mut Criterion) {
90
+ let mut group = c.benchmark_group("search_hnsw");
91
+ for n in [1000, 10000, 50000] {
92
+ let graph = build_hnsw(n);
93
+ let queries = random_vectors(100);
94
+ group.throughput(Throughput::Elements(100));
95
+ group.bench_with_input(BenchmarkId::from_parameter(n), &(graph, queries), |b, (graph, queries)| {
96
+ b.iter(|| {
97
+ for q in queries {
98
+ black_box(graph.search(q, 10).unwrap());
99
+ }
100
+ });
101
+ });
102
+ }
103
+ }
104
+
105
+ // ---------------------------------------------------------------------------
106
+ // Recall benchmark
107
+ // ---------------------------------------------------------------------------
108
+
109
+ fn bench_recall_hnsw(c: &mut Criterion) {
110
+ let mut group = c.benchmark_group("recall_hnsw");
111
+ let n = 10000;
112
+ let k = 10;
113
+
114
+ // Build both indexes
115
+ let flat = build_flat(n);
116
+ let hnsw = build_hnsw(n);
117
+ let queries = random_vectors(100);
118
+
119
+ // Compute ground truth with Flat (exact)
120
+ let ground_truth: Vec<Vec<SearchResult>> = queries
121
+ .iter()
122
+ .map(|q| flat.search(q, k).unwrap())
123
+ .collect();
124
+
125
+ group.bench_function("recall@10_vs_flat", |b| {
126
+ b.iter(|| {
127
+ let mut total_hits = 0usize;
128
+ let mut total = 0usize;
129
+ for (q, gt) in queries.iter().zip(ground_truth.iter()) {
130
+ let approx = hnsw.search(q, k).unwrap();
131
+ // Count intersection
132
+ let gt_ids: std::collections::HashSet<u64> = gt.iter().map(|r| r.id).collect();
133
+ total_hits += approx.iter().filter(|r| gt_ids.contains(&r.id)).count();
134
+ total += k;
135
+ }
136
+ let recall = total_hits as f64 / total as f64;
137
+ black_box(recall);
138
+ });
139
+ });
140
+ }
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // Collection persistence benchmark
144
+ // ---------------------------------------------------------------------------
145
+
146
+ fn bench_persist_collection(c: &mut Criterion) {
147
+ let mut group = c.benchmark_group("persist_collection");
148
+ let n = 1000;
149
+
150
+ group.bench_function("insert_1000_persist", |b| {
151
+ b.iter(|| {
152
+ let dir = tempfile::tempdir().unwrap();
153
+ let path = dir.path().join("bench.embeddb");
154
+ let pc = std::sync::Arc::new(
155
+ vexra_storage::page_cache::PageCache::open(&path, Default::default()).unwrap(),
156
+ );
157
+ let config = CollectionConfig::new("bench", DIM);
158
+ let mut col = Collection::new_persistent(config, IndexType::Flat, pc).unwrap();
159
+ let vectors = random_vectors(n);
160
+ for (i, v) in vectors.iter().enumerate() {
161
+ col.insert(vexra_core::config::Document::with_vector(
162
+ format!("doc_{}", i), v.clone(),
163
+ ))
164
+ .unwrap();
165
+ }
166
+ black_box(col.vector_count());
167
+ });
168
+ });
169
+ }
170
+
171
+ criterion_group!(
172
+ name = benches;
173
+ config = Criterion::default()
174
+ .sample_size(30)
175
+ .measurement_time(Duration::from_secs(10))
176
+ .warm_up_time(Duration::from_secs(2));
177
+ targets =
178
+ bench_insert_flat,
179
+ bench_search_flat,
180
+ bench_insert_hnsw,
181
+ bench_search_hnsw,
182
+ bench_recall_hnsw,
183
+ bench_persist_collection,
184
+ );
185
+ criterion_main!(benches);