lodedb 1.3.2__tar.gz → 2.0.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-1.3.2 → lodedb-2.0.0}/Cargo.toml +4 -2
- lodedb-1.3.2/README.md → lodedb-2.0.0/PKG-INFO +138 -1
- lodedb-1.3.2/PKG-INFO → lodedb-2.0.0/README.md +81 -53
- lodedb-2.0.0/crates/lodedb-cloud-core/Cargo.toml +41 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/artifact_store.rs +127 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/blob_layout.rs +119 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/client_ops.rs +621 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/digest.rs +46 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/error.rs +133 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/generation_inventory.rs +502 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/lib.rs +83 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/local_artifact_store.rs +190 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/managed.rs +440 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/manifest_transfer.rs +245 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/object_artifact_store.rs +495 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/paths.rs +153 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/snapshot_identity.rs +72 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/status.rs +143 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/store_target.rs +58 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/sync_plan.rs +225 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/sync_state.rs +212 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/transfer_policy.rs +81 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/src/verify.rs +160 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/artifact_store.rs +236 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/client_ops.rs +105 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/common/mod.rs +276 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/generation_inventory.rs +413 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/managed.rs +268 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/manifest_transfer.rs +317 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/object_artifact_store.rs +285 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/restore.rs +283 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/snapshot_identity.rs +67 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/store_target.rs +35 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/sync_ops.rs +663 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/sync_plan.rs +273 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/sync_state.rs +125 -0
- lodedb-2.0.0/crates/lodedb-cloud-core/tests/verify_status.rs +183 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/engine/mod.rs +161 -43
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/commit_manifest.rs +47 -9
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/mod.rs +1 -1
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/util.rs +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/wal.rs +8 -8
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/turbovec.rs +103 -10
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/version.rs +6 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/core_engine.rs +143 -2
- lodedb-2.0.0/crates/lodedb-core/tests/turbovec_adapter.rs +191 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-gpu/src/lib.rs +4 -4
- lodedb-2.0.0/crates/lodedb-graph/Cargo.toml +28 -0
- lodedb-2.0.0/crates/lodedb-graph/src/error.rs +61 -0
- lodedb-2.0.0/crates/lodedb-graph/src/graph.rs +809 -0
- lodedb-2.0.0/crates/lodedb-graph/src/index.rs +883 -0
- lodedb-2.0.0/crates/lodedb-graph/src/lib.rs +51 -0
- lodedb-2.0.0/crates/lodedb-graph/src/model.rs +231 -0
- lodedb-2.0.0/crates/lodedb-graph/src/search.rs +278 -0
- lodedb-2.0.0/crates/lodedb-graph/src/temporal.rs +126 -0
- lodedb-2.0.0/crates/lodedb-graph/src/topology.rs +1372 -0
- lodedb-2.0.0/crates/lodedb-graph/tests/temporal_graph.rs +608 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/pyproject.toml +19 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/__init__.py +2 -2
- lodedb-2.0.0/src/lodedb/cloud/__init__.py +113 -0
- lodedb-2.0.0/src/lodedb/cloud/_agents_scaffold.py +204 -0
- lodedb-2.0.0/src/lodedb/cloud/_config.py +159 -0
- lodedb-2.0.0/src/lodedb/cloud/_env_file.py +120 -0
- lodedb-2.0.0/src/lodedb/cloud/_sealing.py +60 -0
- lodedb-2.0.0/src/lodedb/cloud/cli.py +1845 -0
- lodedb-2.0.0/src/lodedb/cloud/client.py +447 -0
- lodedb-2.0.0/src/lodedb/cloud/serving.py +972 -0
- lodedb-2.0.0/src/lodedb/cloud/transfer.py +1086 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/_atomic_io.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/_commit_manifest.py +6 -6
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/_filelock.py +4 -4
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/_lexical.py +3 -3
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/_predicate.py +4 -4
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/embedding_backends.py +63 -34
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/gpu_turbovec.py +3 -3
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/route_registry.py +1 -1
- lodedb-2.0.0/src/lodedb/graph/__init__.py +27 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/graph/_store.py +2 -2
- lodedb-2.0.0/src/lodedb/graph/temporal.py +362 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/__init__.py +5 -5
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/backends.py +1 -1
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/benchmark.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/cli.py +36 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/db.py +99 -25
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/__init__.py +9 -9
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/kotaemon.py +5 -5
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/langchain.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/llama_index.py +1 -1
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/llama_index_graph.py +3 -3
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/privategpt.py +5 -5
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/mcp_install.py +5 -5
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/mcp_server.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/detect.py +4 -4
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/report.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/runner.py +1 -1
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/__init__.py +4 -4
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/base.py +2 -2
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/onnx_artifacts.py +3 -3
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/segments.py +10 -10
- lodedb-2.0.0/third_party/turbovec/Cargo.lock +3095 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/Cargo.toml +1 -1
- lodedb-2.0.0/third_party/turbovec/turbovec-python/Cargo.toml +29 -0
- lodedb-2.0.0/third_party/turbovec/turbovec-python/src/cloud.rs +455 -0
- lodedb-2.0.0/third_party/turbovec/turbovec-python/src/graph.rs +510 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/src/lib.rs +8 -0
- lodedb-1.3.2/crates/lodedb-core/tests/turbovec_adapter.rs +0 -90
- lodedb-1.3.2/src/lodedb/graph/__init__.py +0 -13
- lodedb-1.3.2/third_party/turbovec/Cargo.lock +0 -1455
- lodedb-1.3.2/third_party/turbovec/turbovec-python/Cargo.toml +0 -21
- {lodedb-1.3.2 → lodedb-2.0.0}/LICENSE +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/NOTICE +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/Cargo.toml +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/examples/filter_planner_bench.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/examples/turbovec_adapter_smoke.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/examples/write_generation_fixture.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/examples/write_wal_fixture.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/error.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/ast.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/doc_set.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/field_index.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/mod.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/predicate.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/resolve.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/filter/validate.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/lexical/bm25.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/lexical/mod.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/lexical/rrf.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/lexical/tokenize.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/lib.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/legacy.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/lexical_store.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/lsn.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/multivec_store.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/state_journal.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/text_store.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/tvann_store.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/tvim_delta.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/storage/tvvf_store.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/text/chunk.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/text/hash.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/text/mod.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/types.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/ann.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/index.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/math.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/mod.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/src/vector/stable_id.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/ann_build_bench.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/block_skip_layout.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/filter_plan_fixtures.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/identity_fixtures.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/lexical_fixtures.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/operator_observability.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/predicate_fixtures.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-core/tests/storage_fixtures.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/crates/lodedb-gpu/Cargo.toml +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/__main__.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/_native_core.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/config.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/__init__.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/core.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/native_adapter.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/route_profiles.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/runtime_policy.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/state_journal_store.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/turbovec_delta_store.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/engine/turbovec_index.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/graph/knowledge_graph.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/appender.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/checkpointer.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/collection.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/doctor.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/cognee.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/integrations/mem0.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/late_interaction.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/__init__.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/cli.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/plan.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/langchain_inmemory.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/llama_index_simple.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/mem0_qdrant.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/migrate/sources/pgvector.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/presets.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/local/server.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/src/lodedb/py.typed +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/Cargo.toml +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/README.md +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/build.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/examples/dump_state.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/examples/kernel_xtest.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/codebook.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/encode.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/error.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/id_map.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/io.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/lib.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/maxsim.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/pack.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/rotation.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/src/search.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/codebook.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/concurrent_search.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/distortion.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/encode.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/encoded_rows.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/filtering.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/id_map.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/input_validation.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/io_versioning.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/kernel_correctness.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/lazy_init.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/reconstruction.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/rotation.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/state_sequences.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/swap_remove.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec/tests/tqplus_calibration.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/README.md +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/build.rs +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/__init__.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/_dedup.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/_persist.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/agno.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/haystack.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/langchain.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/python/turbovec/llama_index.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_agno.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_dedup.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_filtering.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_haystack.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_id_map.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_index.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_langchain.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_llama_index.py +0 -0
- {lodedb-1.3.2 → lodedb-2.0.0}/third_party/turbovec/turbovec-python/tests/test_security.py +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
[workspace]
|
|
2
2
|
members = [
|
|
3
|
+
"crates/lodedb-cloud-core",
|
|
3
4
|
"crates/lodedb-core",
|
|
4
5
|
"crates/lodedb-ffi",
|
|
5
6
|
"crates/lodedb-gpu",
|
|
7
|
+
"crates/lodedb-graph",
|
|
6
8
|
]
|
|
7
9
|
resolver = "2"
|
|
8
10
|
|
|
9
11
|
[workspace.package]
|
|
10
|
-
version = "
|
|
12
|
+
version = "2.0.0"
|
|
11
13
|
edition = "2021"
|
|
12
|
-
rust-version = "1.
|
|
14
|
+
rust-version = "1.88"
|
|
13
15
|
license = "Apache-2.0"
|
|
14
16
|
repository = "https://github.com/Egoist-Machines/LodeDB"
|
|
15
17
|
|
|
@@ -1,3 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lodedb
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Intended Audience :: Developers
|
|
6
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
+
Classifier: Topic :: Database :: Database Engines/Servers
|
|
8
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
9
|
+
Requires-Dist: numpy>=2.0.0,<3
|
|
10
|
+
Requires-Dist: typer>=0.12.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
12
|
+
Requires-Dist: lodedb[embeddings,torch,image,mcp,langchain,llama-index,mem0] ; extra == 'all'
|
|
13
|
+
Requires-Dist: httpx>=0.27 ; extra == 'cloud'
|
|
14
|
+
Requires-Dist: pynacl>=1.5 ; extra == 'cloud'
|
|
15
|
+
Requires-Dist: cryptography>=47 ; extra == 'cloud-sealed'
|
|
16
|
+
Requires-Dist: cognee>=1.1.0,<2 ; python_full_version < '3.15' and extra == 'cognee'
|
|
17
|
+
Requires-Dist: pytest>=8.3.0 ; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff==0.15.17 ; extra == 'dev'
|
|
19
|
+
Requires-Dist: onnxruntime>=1.20.0,<2 ; extra == 'embeddings'
|
|
20
|
+
Requires-Dist: transformers>=4.40.0,<5 ; extra == 'embeddings'
|
|
21
|
+
Requires-Dist: cupy-cuda12x>=13.0.0 ; platform_system == 'Linux' and extra == 'gpu'
|
|
22
|
+
Requires-Dist: pillow>=10.0.0 ; extra == 'image'
|
|
23
|
+
Requires-Dist: lodedb[torch] ; extra == 'image'
|
|
24
|
+
Requires-Dist: langchain-core>=0.3.0 ; extra == 'langchain'
|
|
25
|
+
Requires-Dist: llama-index-core>=0.11.0 ; extra == 'llama-index'
|
|
26
|
+
Requires-Dist: mcp>=1.0.0 ; extra == 'mcp'
|
|
27
|
+
Requires-Dist: mem0ai>=2.0.0 ; extra == 'mem0'
|
|
28
|
+
Requires-Dist: optimum[exporters]>=1.23.0,<2.0.0 ; extra == 'onnx-export'
|
|
29
|
+
Requires-Dist: sentence-transformers>=3.0.0,<5 ; extra == 'torch'
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Provides-Extra: cloud
|
|
32
|
+
Provides-Extra: cloud-sealed
|
|
33
|
+
Provides-Extra: cognee
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Provides-Extra: embeddings
|
|
36
|
+
Provides-Extra: gpu
|
|
37
|
+
Provides-Extra: image
|
|
38
|
+
Provides-Extra: langchain
|
|
39
|
+
Provides-Extra: llama-index
|
|
40
|
+
Provides-Extra: mcp
|
|
41
|
+
Provides-Extra: mem0
|
|
42
|
+
Provides-Extra: onnx-export
|
|
43
|
+
Provides-Extra: torch
|
|
44
|
+
License-File: LICENSE
|
|
45
|
+
License-File: NOTICE
|
|
46
|
+
Summary: Local-first, privacy-by-default embedded vector database. Your data never leaves your machine.
|
|
47
|
+
Keywords: vector-database,embeddings,local-first,privacy,semantic-search,rag,cuda,gpu-acceleration,turbovec
|
|
48
|
+
Author-email: Egoist Machines <oss@egoistmachines.com>
|
|
49
|
+
License-Expression: Apache-2.0
|
|
50
|
+
Requires-Python: >=3.11
|
|
51
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
52
|
+
Project-URL: Changelog, https://github.com/Egoist-Machines/LodeDB/blob/main/CHANGELOG.md
|
|
53
|
+
Project-URL: Homepage, https://github.com/Egoist-Machines/LodeDB
|
|
54
|
+
Project-URL: Issues, https://github.com/Egoist-Machines/LodeDB/issues
|
|
55
|
+
Project-URL: Repository, https://github.com/Egoist-Machines/LodeDB
|
|
56
|
+
|
|
1
57
|
<h1 align="center">LodeDB</h1>
|
|
2
58
|
<p align="center">🔥 the <b>fastest</b> and <i>most compact</i> embedded vector database in the world 🌍</p>
|
|
3
59
|
|
|
@@ -605,6 +661,86 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
605
661
|
|
|
606
662
|
</details>
|
|
607
663
|
|
|
664
|
+
## Managed cloud (OreCloud)
|
|
665
|
+
|
|
666
|
+
LodeDB stores can live in [OreCloud](https://db.egoistmachines.com), the managed-cloud
|
|
667
|
+
companion: durable sync for local stores, hosted read serving, and a per-user memory
|
|
668
|
+
data plane for agent applications. The client ships as the `[cloud]` extra:
|
|
669
|
+
|
|
670
|
+
```sh
|
|
671
|
+
pip install "lodedb[cloud]"
|
|
672
|
+
|
|
673
|
+
lodedb cloud login # one browser approval
|
|
674
|
+
lodedb cloud init ./my-store # link the directory to a managed remote
|
|
675
|
+
lodedb cloud keys ./my-store # list the directory's index keys
|
|
676
|
+
lodedb cloud sync ./my-store cloud <key> # mirror the local store to the cloud
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
A cloud store opens through the same class as a local one, via the
|
|
680
|
+
`LodeDB.cloud` alternate constructor:
|
|
681
|
+
|
|
682
|
+
```python
|
|
683
|
+
from lodedb import LodeDB
|
|
684
|
+
|
|
685
|
+
db = LodeDB("./notes") # local, as always
|
|
686
|
+
db = LodeDB.cloud("user-42") # managed cloud, same verbs
|
|
687
|
+
db.add("the quick brown fox") # embedded server-side
|
|
688
|
+
db.search("fox", k=5)
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
A bare store id is enough. The org/environment half resolves from the
|
|
692
|
+
credential (`token=`, the `ORECLOUD_TOKEN` environment variable, or
|
|
693
|
+
`lodedb cloud login`); pass the full `"org/environment/store"` triple in
|
|
694
|
+
cross-environment scripts. For config-driven code, where one string field (an
|
|
695
|
+
env var, a YAML value) must express either a local path or a cloud store, the
|
|
696
|
+
plain constructor accepts the explicit URL form
|
|
697
|
+
`LodeDB("orecloud://org/environment/store")` and returns the same handle.
|
|
698
|
+
An application serving many end users should hold one `Client` and open
|
|
699
|
+
per-user handles from it (`Client().store(user_id)`); the credential resolves
|
|
700
|
+
once and the handles share one HTTP pool. `from lodedb.cloud import Client`
|
|
701
|
+
works as the import root.
|
|
702
|
+
|
|
703
|
+
Work locally with `lodedb` exactly as before. The client is first-party code
|
|
704
|
+
in this package (`lodedb.cloud` and the `lodedb cloud` CLI), and push/pull/
|
|
705
|
+
sync run on the same bundled native core as the engine. The extra installs
|
|
706
|
+
only its dependencies (`httpx`, `pynacl`). Everything cloud loads lazily, so
|
|
707
|
+
a plain `import lodedb` stays network-free.
|
|
708
|
+
|
|
709
|
+
### Sealed stores
|
|
710
|
+
|
|
711
|
+
OreCloud can host a server-side encrypted store whose 32-byte wrapping key
|
|
712
|
+
remains with your application. Install the additional lazy crypto support,
|
|
713
|
+
keep that material outside source control, and keep a recovery copy: losing
|
|
714
|
+
it makes the store unreadable and the service never persists it.
|
|
715
|
+
|
|
716
|
+
```sh
|
|
717
|
+
pip install "lodedb[cloud,cloud-sealed]"
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
```python
|
|
721
|
+
import secrets
|
|
722
|
+
|
|
723
|
+
from lodedb.cloud import Client
|
|
724
|
+
|
|
725
|
+
material = secrets.token_bytes(32)
|
|
726
|
+
client = Client()
|
|
727
|
+
client.create_store(
|
|
728
|
+
"user-42",
|
|
729
|
+
encrypted=True,
|
|
730
|
+
key_material=material,
|
|
731
|
+
mode="cloud_writer",
|
|
732
|
+
preset="minilm",
|
|
733
|
+
)
|
|
734
|
+
expires_at = client.unseal_store("user-42", material, ttl_seconds=900)
|
|
735
|
+
# Query or write while the grant is live, then remove it when finished.
|
|
736
|
+
client.reseal_store("user-42")
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
`Client.rotate_store_key("user-42", fresh_material)` replaces the caller-held
|
|
740
|
+
material after the store is unsealed. A sealed data-plane read returns
|
|
741
|
+
`CloudError` with status 423 and a `store_sealed:` detail; applications decide
|
|
742
|
+
when to request an unseal grant.
|
|
743
|
+
|
|
608
744
|
## Concurrency & durability
|
|
609
745
|
|
|
610
746
|
- **Single writer, many readers, per path.** One handle holds the path open for *writing* at
|
|
@@ -648,7 +784,7 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
648
784
|
read-your-writes against an append's returned LSN). On Windows the shared lock degrades to an
|
|
649
785
|
exclusive hold, so appenders serialize there rather than coexisting. A record is a precomputed
|
|
650
786
|
vector plus metadata (with an optional caption, e.g. for an image, retained only when the appender
|
|
651
|
-
opts into `store_text
|
|
787
|
+
opts into `store_text`; it is off by default, so no raw text reaches the WAL); with an embedder
|
|
652
788
|
configured, the appender also ingests full text (chunked by the core, embedded in the binding
|
|
653
789
|
layer, then logged as a post-embedding record) so text writes are multi-producer too, without a
|
|
654
790
|
captured base generation. It requires WAL commit mode. Exposed as the native `CoreAppender`, over
|
|
@@ -723,3 +859,4 @@ Enterprise licensing and commercial support are available from
|
|
|
723
859
|
PRs welcome; see [`CONTRIBUTING.md`](CONTRIBUTING.md). Report security issues **privately**
|
|
724
860
|
per [`SECURITY.md`](SECURITY.md), not in public issues. Other bugs and requests go to the
|
|
725
861
|
[issue tracker](https://github.com/Egoist-Machines/LodeDB/issues).
|
|
862
|
+
|
|
@@ -1,54 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: lodedb
|
|
3
|
-
Version: 1.3.2
|
|
4
|
-
Classifier: Development Status :: 4 - Beta
|
|
5
|
-
Classifier: Intended Audience :: Developers
|
|
6
|
-
Classifier: Programming Language :: Python :: 3 :: Only
|
|
7
|
-
Classifier: Topic :: Database :: Database Engines/Servers
|
|
8
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
9
|
-
Requires-Dist: numpy>=2.0.0,<3
|
|
10
|
-
Requires-Dist: typer>=0.12.0
|
|
11
|
-
Requires-Dist: pyyaml>=6.0.0
|
|
12
|
-
Requires-Dist: lodedb[embeddings,torch,image,mcp,langchain,llama-index,mem0] ; extra == 'all'
|
|
13
|
-
Requires-Dist: cognee>=1.1.0,<2 ; python_full_version < '3.15' and extra == 'cognee'
|
|
14
|
-
Requires-Dist: pytest>=8.3.0 ; extra == 'dev'
|
|
15
|
-
Requires-Dist: ruff==0.15.17 ; extra == 'dev'
|
|
16
|
-
Requires-Dist: onnxruntime>=1.20.0,<2 ; extra == 'embeddings'
|
|
17
|
-
Requires-Dist: transformers>=4.40.0,<5 ; extra == 'embeddings'
|
|
18
|
-
Requires-Dist: cupy-cuda12x>=13.0.0 ; platform_system == 'Linux' and extra == 'gpu'
|
|
19
|
-
Requires-Dist: pillow>=10.0.0 ; extra == 'image'
|
|
20
|
-
Requires-Dist: lodedb[torch] ; extra == 'image'
|
|
21
|
-
Requires-Dist: langchain-core>=0.3.0 ; extra == 'langchain'
|
|
22
|
-
Requires-Dist: llama-index-core>=0.11.0 ; extra == 'llama-index'
|
|
23
|
-
Requires-Dist: mcp>=1.0.0 ; extra == 'mcp'
|
|
24
|
-
Requires-Dist: mem0ai>=2.0.0 ; extra == 'mem0'
|
|
25
|
-
Requires-Dist: optimum[exporters]>=1.23.0,<2.0.0 ; extra == 'onnx-export'
|
|
26
|
-
Requires-Dist: sentence-transformers>=3.0.0,<5 ; extra == 'torch'
|
|
27
|
-
Provides-Extra: all
|
|
28
|
-
Provides-Extra: cognee
|
|
29
|
-
Provides-Extra: dev
|
|
30
|
-
Provides-Extra: embeddings
|
|
31
|
-
Provides-Extra: gpu
|
|
32
|
-
Provides-Extra: image
|
|
33
|
-
Provides-Extra: langchain
|
|
34
|
-
Provides-Extra: llama-index
|
|
35
|
-
Provides-Extra: mcp
|
|
36
|
-
Provides-Extra: mem0
|
|
37
|
-
Provides-Extra: onnx-export
|
|
38
|
-
Provides-Extra: torch
|
|
39
|
-
License-File: LICENSE
|
|
40
|
-
License-File: NOTICE
|
|
41
|
-
Summary: Local-first, privacy-by-default embedded vector database. Your data never leaves your machine.
|
|
42
|
-
Keywords: vector-database,embeddings,local-first,privacy,semantic-search,rag,cuda,gpu-acceleration,turbovec
|
|
43
|
-
Author-email: Egoist Machines <oss@egoistmachines.com>
|
|
44
|
-
License-Expression: Apache-2.0
|
|
45
|
-
Requires-Python: >=3.11
|
|
46
|
-
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
47
|
-
Project-URL: Changelog, https://github.com/Egoist-Machines/LodeDB/blob/main/CHANGELOG.md
|
|
48
|
-
Project-URL: Homepage, https://github.com/Egoist-Machines/LodeDB
|
|
49
|
-
Project-URL: Issues, https://github.com/Egoist-Machines/LodeDB/issues
|
|
50
|
-
Project-URL: Repository, https://github.com/Egoist-Machines/LodeDB
|
|
51
|
-
|
|
52
1
|
<h1 align="center">LodeDB</h1>
|
|
53
2
|
<p align="center">🔥 the <b>fastest</b> and <i>most compact</i> embedded vector database in the world 🌍</p>
|
|
54
3
|
|
|
@@ -656,6 +605,86 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
656
605
|
|
|
657
606
|
</details>
|
|
658
607
|
|
|
608
|
+
## Managed cloud (OreCloud)
|
|
609
|
+
|
|
610
|
+
LodeDB stores can live in [OreCloud](https://db.egoistmachines.com), the managed-cloud
|
|
611
|
+
companion: durable sync for local stores, hosted read serving, and a per-user memory
|
|
612
|
+
data plane for agent applications. The client ships as the `[cloud]` extra:
|
|
613
|
+
|
|
614
|
+
```sh
|
|
615
|
+
pip install "lodedb[cloud]"
|
|
616
|
+
|
|
617
|
+
lodedb cloud login # one browser approval
|
|
618
|
+
lodedb cloud init ./my-store # link the directory to a managed remote
|
|
619
|
+
lodedb cloud keys ./my-store # list the directory's index keys
|
|
620
|
+
lodedb cloud sync ./my-store cloud <key> # mirror the local store to the cloud
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
A cloud store opens through the same class as a local one, via the
|
|
624
|
+
`LodeDB.cloud` alternate constructor:
|
|
625
|
+
|
|
626
|
+
```python
|
|
627
|
+
from lodedb import LodeDB
|
|
628
|
+
|
|
629
|
+
db = LodeDB("./notes") # local, as always
|
|
630
|
+
db = LodeDB.cloud("user-42") # managed cloud, same verbs
|
|
631
|
+
db.add("the quick brown fox") # embedded server-side
|
|
632
|
+
db.search("fox", k=5)
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
A bare store id is enough. The org/environment half resolves from the
|
|
636
|
+
credential (`token=`, the `ORECLOUD_TOKEN` environment variable, or
|
|
637
|
+
`lodedb cloud login`); pass the full `"org/environment/store"` triple in
|
|
638
|
+
cross-environment scripts. For config-driven code, where one string field (an
|
|
639
|
+
env var, a YAML value) must express either a local path or a cloud store, the
|
|
640
|
+
plain constructor accepts the explicit URL form
|
|
641
|
+
`LodeDB("orecloud://org/environment/store")` and returns the same handle.
|
|
642
|
+
An application serving many end users should hold one `Client` and open
|
|
643
|
+
per-user handles from it (`Client().store(user_id)`); the credential resolves
|
|
644
|
+
once and the handles share one HTTP pool. `from lodedb.cloud import Client`
|
|
645
|
+
works as the import root.
|
|
646
|
+
|
|
647
|
+
Work locally with `lodedb` exactly as before. The client is first-party code
|
|
648
|
+
in this package (`lodedb.cloud` and the `lodedb cloud` CLI), and push/pull/
|
|
649
|
+
sync run on the same bundled native core as the engine. The extra installs
|
|
650
|
+
only its dependencies (`httpx`, `pynacl`). Everything cloud loads lazily, so
|
|
651
|
+
a plain `import lodedb` stays network-free.
|
|
652
|
+
|
|
653
|
+
### Sealed stores
|
|
654
|
+
|
|
655
|
+
OreCloud can host a server-side encrypted store whose 32-byte wrapping key
|
|
656
|
+
remains with your application. Install the additional lazy crypto support,
|
|
657
|
+
keep that material outside source control, and keep a recovery copy: losing
|
|
658
|
+
it makes the store unreadable and the service never persists it.
|
|
659
|
+
|
|
660
|
+
```sh
|
|
661
|
+
pip install "lodedb[cloud,cloud-sealed]"
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
```python
|
|
665
|
+
import secrets
|
|
666
|
+
|
|
667
|
+
from lodedb.cloud import Client
|
|
668
|
+
|
|
669
|
+
material = secrets.token_bytes(32)
|
|
670
|
+
client = Client()
|
|
671
|
+
client.create_store(
|
|
672
|
+
"user-42",
|
|
673
|
+
encrypted=True,
|
|
674
|
+
key_material=material,
|
|
675
|
+
mode="cloud_writer",
|
|
676
|
+
preset="minilm",
|
|
677
|
+
)
|
|
678
|
+
expires_at = client.unseal_store("user-42", material, ttl_seconds=900)
|
|
679
|
+
# Query or write while the grant is live, then remove it when finished.
|
|
680
|
+
client.reseal_store("user-42")
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
`Client.rotate_store_key("user-42", fresh_material)` replaces the caller-held
|
|
684
|
+
material after the store is unsealed. A sealed data-plane read returns
|
|
685
|
+
`CloudError` with status 423 and a `store_sealed:` detail; applications decide
|
|
686
|
+
when to request an unseal grant.
|
|
687
|
+
|
|
659
688
|
## Concurrency & durability
|
|
660
689
|
|
|
661
690
|
- **Single writer, many readers, per path.** One handle holds the path open for *writing* at
|
|
@@ -699,7 +728,7 @@ See [`examples/mcp_config.json`](examples/mcp_config.json) for a copy-paste star
|
|
|
699
728
|
read-your-writes against an append's returned LSN). On Windows the shared lock degrades to an
|
|
700
729
|
exclusive hold, so appenders serialize there rather than coexisting. A record is a precomputed
|
|
701
730
|
vector plus metadata (with an optional caption, e.g. for an image, retained only when the appender
|
|
702
|
-
opts into `store_text
|
|
731
|
+
opts into `store_text`; it is off by default, so no raw text reaches the WAL); with an embedder
|
|
703
732
|
configured, the appender also ingests full text (chunked by the core, embedded in the binding
|
|
704
733
|
layer, then logged as a post-embedding record) so text writes are multi-producer too, without a
|
|
705
734
|
captured base generation. It requires WAL commit mode. Exposed as the native `CoreAppender`, over
|
|
@@ -774,4 +803,3 @@ Enterprise licensing and commercial support are available from
|
|
|
774
803
|
PRs welcome; see [`CONTRIBUTING.md`](CONTRIBUTING.md). Report security issues **privately**
|
|
775
804
|
per [`SECURITY.md`](SECURITY.md), not in public issues. Other bugs and requests go to the
|
|
776
805
|
[issue tracker](https://github.com/Egoist-Machines/LodeDB/issues).
|
|
777
|
-
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "lodedb-cloud-core"
|
|
3
|
+
description = "Native transfer core for the LodeDB managed cloud — durable push/pull/sync over the open commit format."
|
|
4
|
+
version.workspace = true
|
|
5
|
+
edition.workspace = true
|
|
6
|
+
rust-version.workspace = true
|
|
7
|
+
license.workspace = true
|
|
8
|
+
repository.workspace = true
|
|
9
|
+
publish = false
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
# The engine core. The cloud transfer plane links its `storage` module directly
|
|
13
|
+
# so the managed cloud and the embedded product share ONE commit-format
|
|
14
|
+
# implementation (schema, layout, checksum, sub-manifests incl. `tvmv`). A path
|
|
15
|
+
# dep now that both live in this workspace — engine changes and the transfer
|
|
16
|
+
# plane move together under one suite.
|
|
17
|
+
lodedb-core = { path = "../lodedb-core" }
|
|
18
|
+
# Object-storage backend (S3/GCS/Azure) behind the same ArtifactStore trait. The
|
|
19
|
+
# `aws` feature adds the S3 builder; the crate's conditional-put API (Create /
|
|
20
|
+
# Update{e_tag}) is what makes the root-pointer compare-and-swap strongly
|
|
21
|
+
# consistent on object storage. Its InMemory backend also drives the tests.
|
|
22
|
+
object_store = { version = "0.12.5", features = ["aws"] }
|
|
23
|
+
serde_json = "1.0"
|
|
24
|
+
sha2 = "0.10"
|
|
25
|
+
# Streaming transfer plumbing: `bytes` for the object-store chunk type and
|
|
26
|
+
# `futures` for driving its download streams chunk-by-chunk from the sync
|
|
27
|
+
# ArtifactStore surface. Both are already in the tree via object_store.
|
|
28
|
+
bytes = "1"
|
|
29
|
+
futures = "0.3"
|
|
30
|
+
# The object_store backends are async; the runtime is enabled with net + time so a
|
|
31
|
+
# real S3 client works (not just the in-memory test backend). ObjectArtifactStore
|
|
32
|
+
# keeps the sync ArtifactStore trait by owning a current-thread runtime.
|
|
33
|
+
tokio = { version = "1.47.5", default-features = false, features = ["rt", "net", "time"] }
|
|
34
|
+
# The pointer serialize/validate round-trip reuses lodedb-core's own
|
|
35
|
+
# write/read_commit_manifest (which operate on file paths) via a scratch file, so
|
|
36
|
+
# object-store pointer documents are byte-identical to on-disk ones. Also used by
|
|
37
|
+
# the test fixtures to build committed generations in throwaway directories.
|
|
38
|
+
tempfile = "3"
|
|
39
|
+
|
|
40
|
+
[lints]
|
|
41
|
+
workspace = true
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//! Storage abstraction for committed, generation-addressed artifacts.
|
|
2
|
+
//!
|
|
3
|
+
//! The substrate is the engine's existing on-disk layout: a committed generation
|
|
4
|
+
//! is a set of immutable, sha256-addressed `g<epoch>.*` artifacts under
|
|
5
|
+
//! `<key>.gen/` pinned by an atomic `<key>.commit.json` root pointer. An
|
|
6
|
+
//! [`ArtifactStore`] exposes exactly the operations that layout needs:
|
|
7
|
+
//!
|
|
8
|
+
//! - **names** are store-relative paths mirroring the on-disk layout, e.g.
|
|
9
|
+
//! `idx.gen/g7.json` or `idx.gen/g7.json.json-delta/delta-00000000.jsd`;
|
|
10
|
+
//! - the **pointer key** is the index key (`idx` -> `idx.commit.json`).
|
|
11
|
+
//!
|
|
12
|
+
//! Implementations treat content artifacts as immutable and content-addressed
|
|
13
|
+
//! (written once, never overwritten), and commit a generation *only* by swapping
|
|
14
|
+
//! the root pointer. [`LocalArtifactStore`](crate::LocalArtifactStore) is the
|
|
15
|
+
//! default; object-storage backends (S3/GCS/Azure) land in later milestones with
|
|
16
|
+
//! the artifact names becoming object keys under a per-tenant prefix and the
|
|
17
|
+
//! pointer swap becoming a conditional write.
|
|
18
|
+
|
|
19
|
+
use crate::error::Result;
|
|
20
|
+
use serde_json::Value;
|
|
21
|
+
use std::io::Read;
|
|
22
|
+
|
|
23
|
+
/// Reads/writes immutable artifacts and swaps a root pointer atomically.
|
|
24
|
+
///
|
|
25
|
+
/// The interface is deliberately small: streaming artifact I/O plus a pointer
|
|
26
|
+
/// compare-and-swap. That is everything the generation-addressed commit format
|
|
27
|
+
/// needs as a cloud substrate. Streaming is the primitive. Vector bases run
|
|
28
|
+
/// to gigabytes, so a transfer's peak memory must be a fixed buffer, never a
|
|
29
|
+
/// function of artifact size; the buffered methods are conveniences for the
|
|
30
|
+
/// small payloads (pointer documents) built on top.
|
|
31
|
+
pub trait ArtifactStore {
|
|
32
|
+
/// Opens one artifact for streaming reads;
|
|
33
|
+
/// [`ArtifactStoreError::NotFound`] if the name is absent.
|
|
34
|
+
///
|
|
35
|
+
/// [`ArtifactStoreError::NotFound`]: crate::ArtifactStoreError::NotFound
|
|
36
|
+
fn open_read<'a>(&'a self, name: &str) -> Result<Box<dyn Read + 'a>>;
|
|
37
|
+
|
|
38
|
+
/// Returns one artifact's bytes, fully buffered, for small payloads
|
|
39
|
+
/// only; transfers and verification stream via [`open_read`](Self::open_read).
|
|
40
|
+
fn read_bytes(&self, name: &str) -> Result<Vec<u8>> {
|
|
41
|
+
let mut data = Vec::new();
|
|
42
|
+
self.open_read(name)?.read_to_end(&mut data)?;
|
|
43
|
+
Ok(data)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/// Streams one immutable artifact into the store unless it already
|
|
47
|
+
/// exists, hashing incrementally as it copies.
|
|
48
|
+
///
|
|
49
|
+
/// `sha256` is the expected lowercase-hex digest of the streamed bytes; a
|
|
50
|
+
/// mismatch is an integrity error and nothing is stored, so corruption can
|
|
51
|
+
/// never land, even when the source is another store's live stream. A
|
|
52
|
+
/// name already present with identical bytes is a no-op (idempotent
|
|
53
|
+
/// re-push); present with *different* bytes is a conflict. Artifacts are
|
|
54
|
+
/// immutable and are never overwritten in place. On the already-present
|
|
55
|
+
/// no-op path the incoming stream may be left partially (or wholly)
|
|
56
|
+
/// unread and unvalidated: success then attests that the STORED bytes
|
|
57
|
+
/// match `sha256`, not that the stream did.
|
|
58
|
+
///
|
|
59
|
+
/// `size_hint` is the expected byte count (0 = unknown), advisory only:
|
|
60
|
+
/// backends use it to pick an upload strategy (the object store's
|
|
61
|
+
/// conditional-claim path has a provider copy-size ceiling), never to
|
|
62
|
+
/// trust the stream's length. Transfers pass the manifest-recorded size.
|
|
63
|
+
fn write_stream_if_absent(
|
|
64
|
+
&self,
|
|
65
|
+
name: &str,
|
|
66
|
+
data: &mut dyn Read,
|
|
67
|
+
sha256: &str,
|
|
68
|
+
size_hint: u64,
|
|
69
|
+
) -> Result<()>;
|
|
70
|
+
|
|
71
|
+
/// Buffered convenience over
|
|
72
|
+
/// [`write_stream_if_absent`](Self::write_stream_if_absent), for small
|
|
73
|
+
/// payloads a caller already holds in memory.
|
|
74
|
+
fn write_bytes_if_absent(&self, name: &str, data: &[u8], sha256: &str) -> Result<()> {
|
|
75
|
+
let mut cursor = data;
|
|
76
|
+
self.write_stream_if_absent(name, &mut cursor, sha256, data.len() as u64)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Whether an artifact named `name` is present, without asserting anything
|
|
80
|
+
/// about its content.
|
|
81
|
+
///
|
|
82
|
+
/// The default opens the artifact and maps
|
|
83
|
+
/// [`NotFound`](crate::ArtifactStoreError::NotFound) to `false`; backends
|
|
84
|
+
/// with a cheap existence primitive (a filesystem stat, an object-store
|
|
85
|
+
/// HEAD) should override it to avoid opening a stream.
|
|
86
|
+
fn contains(&self, name: &str) -> Result<bool> {
|
|
87
|
+
match self.open_read(name) {
|
|
88
|
+
Ok(_) => Ok(true),
|
|
89
|
+
Err(crate::ArtifactStoreError::NotFound(_)) => Ok(false),
|
|
90
|
+
Err(error) => Err(error),
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// Returns the committed root-manifest body for `key`, or `None`.
|
|
95
|
+
///
|
|
96
|
+
/// The body carries the generation number, the base epoch, counts, and the
|
|
97
|
+
/// per-store sub-manifests that name every artifact in the generation. It is
|
|
98
|
+
/// the read side that [`compare_and_swap_pointer`](Self::compare_and_swap_pointer)
|
|
99
|
+
/// and the inventory helpers need to learn the currently-committed generation.
|
|
100
|
+
fn read_pointer(&self, key: &str) -> Result<Option<Value>>;
|
|
101
|
+
|
|
102
|
+
/// Publishes `new_body` as the root pointer iff the currently committed body is
|
|
103
|
+
/// exactly `old_body` (`None` means the pointer must not yet exist); otherwise
|
|
104
|
+
/// [`ArtifactStoreError::PointerConflict`]. This swap is the only commit point:
|
|
105
|
+
/// artifacts are published first, then the pointer flips all-or-nothing.
|
|
106
|
+
///
|
|
107
|
+
/// The precondition is the full committed body, not just its generation number.
|
|
108
|
+
/// A generation number is not a unique version token (two independent lineages
|
|
109
|
+
/// can share one with different content), so comparing the whole body is what
|
|
110
|
+
/// makes this a sound compare-and-swap rather than an ABA-prone check.
|
|
111
|
+
///
|
|
112
|
+
/// [`ArtifactStoreError::PointerConflict`]: crate::ArtifactStoreError::PointerConflict
|
|
113
|
+
fn compare_and_swap_pointer(
|
|
114
|
+
&self,
|
|
115
|
+
key: &str,
|
|
116
|
+
old_body: Option<&Value>,
|
|
117
|
+
new_body: &Value,
|
|
118
|
+
) -> Result<()>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// The generation number recorded in a committed body, if present.
|
|
122
|
+
///
|
|
123
|
+
/// Used only to annotate a [`PointerConflict`](crate::ArtifactStoreError::PointerConflict)
|
|
124
|
+
/// for readability; the swap precondition is the full body, not this number.
|
|
125
|
+
pub(crate) fn body_generation(body: &Value) -> Option<u64> {
|
|
126
|
+
body.get("generation").and_then(Value::as_u64)
|
|
127
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//! Content-addressed blob naming for the managed remote layout.
|
|
2
|
+
//!
|
|
3
|
+
//! A managed remote can store artifacts content-addressed
|
|
4
|
+
//! (`blobs/sha256/aa/<sha256>` under a per-tenant prefix) instead of under
|
|
5
|
+
//! their engine path names. Content addressing is what absorbs the
|
|
6
|
+
//! fork-collision case (two branches committing different artifacts under the
|
|
7
|
+
//! same engine name, e.g. `idx.gen/g7.json`, coexist as two blobs), and the
|
|
8
|
+
//! two-level `aa/` fan-out keeps listings and prefix operations tractable.
|
|
9
|
+
//!
|
|
10
|
+
//! These helpers are pure and tested; they pin the naming contract the
|
|
11
|
+
//! transfer plane builds on. The digest is always the lowercase-hex
|
|
12
|
+
//! SHA-256 of the blob's bytes, the same digest the engine records per
|
|
13
|
+
//! artifact and [`ArtifactStore::write_bytes_if_absent`] verifies.
|
|
14
|
+
//!
|
|
15
|
+
//! [`ArtifactStore::write_bytes_if_absent`]: crate::ArtifactStore::write_bytes_if_absent
|
|
16
|
+
|
|
17
|
+
use crate::error::{ArtifactStoreError, Result};
|
|
18
|
+
|
|
19
|
+
/// The fixed prefix every blob name lives under.
|
|
20
|
+
const BLOB_PREFIX: &str = "blobs/sha256";
|
|
21
|
+
|
|
22
|
+
/// Returns the store-relative blob name for a content digest:
|
|
23
|
+
/// `blobs/sha256/aa/<sha256>`, where `aa` is the digest's first two hex chars.
|
|
24
|
+
///
|
|
25
|
+
/// Rejects anything that is not exactly 64 lowercase hex characters. Blob
|
|
26
|
+
/// names participate in authorization paths, so a malformed digest must fail
|
|
27
|
+
/// closed rather than mint a name outside the contract.
|
|
28
|
+
pub fn blob_name(sha256: &str) -> Result<String> {
|
|
29
|
+
validate_sha256(sha256)?;
|
|
30
|
+
Ok(format!("{BLOB_PREFIX}/{}/{sha256}", &sha256[..2]))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/// Parses a blob name back to its content digest, the exact inverse of
|
|
34
|
+
/// [`blob_name`]. Rejects any name that deviates from the contract (wrong
|
|
35
|
+
/// prefix, fan-out directory disagreeing with the digest, malformed digest).
|
|
36
|
+
pub fn parse_blob_name(name: &str) -> Result<String> {
|
|
37
|
+
let malformed = || {
|
|
38
|
+
ArtifactStoreError::Integrity(format!(
|
|
39
|
+
"blob name {name:?} does not match {BLOB_PREFIX}/aa/<sha256>"
|
|
40
|
+
))
|
|
41
|
+
};
|
|
42
|
+
let rest = name.strip_prefix(BLOB_PREFIX).ok_or_else(malformed)?;
|
|
43
|
+
let rest = rest.strip_prefix('/').ok_or_else(malformed)?;
|
|
44
|
+
let (fan_out, sha256) = rest.split_once('/').ok_or_else(malformed)?;
|
|
45
|
+
validate_sha256(sha256).map_err(|_| malformed())?;
|
|
46
|
+
if fan_out != &sha256[..2] {
|
|
47
|
+
return Err(malformed());
|
|
48
|
+
}
|
|
49
|
+
Ok(sha256.to_string())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Requires exactly 64 lowercase hex characters. Also used by the generation
|
|
53
|
+
/// inventory to validate every manifest digest at the trust boundary: digests
|
|
54
|
+
/// become staging file names and object keys, so a non-hex "digest" must fail
|
|
55
|
+
/// closed before it can name a path.
|
|
56
|
+
pub(crate) fn validate_sha256(sha256: &str) -> Result<()> {
|
|
57
|
+
let valid = sha256.len() == 64
|
|
58
|
+
&& sha256
|
|
59
|
+
.bytes()
|
|
60
|
+
.all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte));
|
|
61
|
+
if valid {
|
|
62
|
+
Ok(())
|
|
63
|
+
} else {
|
|
64
|
+
Err(ArtifactStoreError::Integrity(format!(
|
|
65
|
+
"{sha256:?} is not a lowercase-hex sha256 digest"
|
|
66
|
+
)))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[cfg(test)]
|
|
71
|
+
mod tests {
|
|
72
|
+
use super::{blob_name, parse_blob_name};
|
|
73
|
+
use crate::error::ArtifactStoreError;
|
|
74
|
+
|
|
75
|
+
const SHA: &str = "aa30b1cc05c10ac8a1f4e6d46f7d4b1a9c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f";
|
|
76
|
+
|
|
77
|
+
#[test]
|
|
78
|
+
fn names_a_blob_under_its_two_char_fan_out() {
|
|
79
|
+
assert_eq!(blob_name(SHA).unwrap(), format!("blobs/sha256/aa/{SHA}"));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#[test]
|
|
83
|
+
fn parse_inverts_blob_name() {
|
|
84
|
+
assert_eq!(parse_blob_name(&blob_name(SHA).unwrap()).unwrap(), SHA);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[test]
|
|
88
|
+
fn rejects_malformed_digests() {
|
|
89
|
+
for bad in [
|
|
90
|
+
"",
|
|
91
|
+
"abc",
|
|
92
|
+
&SHA[..63], // too short
|
|
93
|
+
&format!("{SHA}0"), // too long
|
|
94
|
+
&SHA.to_uppercase(), // uppercase
|
|
95
|
+
&format!("g{}", &SHA[1..]), // non-hex
|
|
96
|
+
] {
|
|
97
|
+
assert!(matches!(
|
|
98
|
+
blob_name(bad).unwrap_err(),
|
|
99
|
+
ArtifactStoreError::Integrity(_)
|
|
100
|
+
));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#[test]
|
|
105
|
+
fn rejects_names_off_contract() {
|
|
106
|
+
for bad in [
|
|
107
|
+
format!("blobs/sha256/{SHA}"), // no fan-out level
|
|
108
|
+
format!("blobs/sha256/bb/{SHA}"), // fan-out disagrees
|
|
109
|
+
format!("blobs/md5/aa/{SHA}"), // wrong algorithm segment
|
|
110
|
+
format!("sha256/aa/{SHA}"), // wrong prefix
|
|
111
|
+
format!("blobs/sha256/aa/{}", &SHA[..63]), // malformed digest
|
|
112
|
+
] {
|
|
113
|
+
assert!(matches!(
|
|
114
|
+
parse_blob_name(&bad).unwrap_err(),
|
|
115
|
+
ArtifactStoreError::Integrity(_)
|
|
116
|
+
));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|