Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Qenlo Python SDK

Type-safe Python bindings for Qenlo — the embedded, durable vector database written in Rust.

Qenlo provides exact filtered cosine vector search with atomic commits, write-ahead logging (WAL), and portable .qn snapshot files. Every search returns an execution report with routing and resource measurements.

Installation

pip install "qenlo==0.1.0a11"

Pre-built binary wheels bundle the native Rust engine for:

  • Linux (x86_64, manylinux 2.28)
  • macOS (Apple Silicon arm64, macOS 14+)
  • Windows (x86_64)

For source checkouts or development builds, set QENLO_LIBRARY_PATH to point to your compiled qenlo_ffi.dll, libqenlo_ffi.so, or libqenlo_ffi.dylib.


Quickstart

In-Memory Collection

from qenlo import Collection, Filter, Record

# Create an in-memory collection with 3-dimensional vectors
with Collection.memory(dimension=3) as db:
    # Insert records
    db.add(Record(id=1, user_id=42, timestamp=100, vector=(1.0, 0.0, 0.0)))
    db.add(Record(id=2, user_id=42, timestamp=200, vector=(0.0, 1.0, 0.0)))
    db.add(Record(id=3, user_id=99, timestamp=150, vector=(0.7, 0.7, 0.0)))

    # Search with combined user and timestamp filters
    response = db.search(
        query=(1.0, 0.0, 0.0),
        filter=Filter(user_id=42, timestamp_lower=50, timestamp_upper=150),
        k=5,
    )

    for hit in response.results:
        print(f"ID: {hit.id}, Cosine Distance: {hit.distance:.4f}")

    # Inspect the execution report
    report = response.report
    print(f"Backend: {report.actual_backend}, Algorithm: {report.algorithm}")
    print(f"Total Duration: {report.total_duration_ns} ns")

Durable Storage & Restarts

Qenlo collections can be persisted to disk with crash-safe write-ahead logging (WAL) and atomic compaction:

from qenlo import Collection, Record, Filter

path = "./my_collection.qenlo"

# 1. Create a new durable collection directory
with Collection.create(path, dimension=128) as db:
    db.add(Record(id=1, user_id=7, timestamp=10, vector=my_vector))
    db.flush()  # write a snapshot and delete the WAL files it covers

# 2. Reopen across application restarts
with Collection.open(path, dimension=128) as db:
    response = db.search(query=my_query, filter=Filter(user_id=7), k=10)
    print(f"Found {len(response.results)} matches")

Every durable add, delete, or batch call commits one WAL file, and open replays the WAL files newer than the last snapshot. Call flush() now and then (for example every few hundred writes) to fold them into a snapshot. close() does not snapshot, so it stays cheap.


Portable .qn Interchange Files

Export and import standalone, checksummed, immutable .qn snapshots:

# Export an existing collection to a .qn file
db.export_qn("snapshots/v1.qn")

# Import from a .qn file into a fast in-memory collection
with Collection.import_qn("snapshots/v1.qn", dimension=128) as snapshot_db:
    stats = snapshot_db.stats()
    print(f"Loaded {stats.live_rows} rows from generation {stats.generation}")

Batch Operations

Qenlo supports high-throughput atomic batch mutations:

records = [
    Record(id=10, user_id=1, timestamp=1000, vector=(0.1, 0.2, 0.3)),
    Record(id=11, user_id=1, timestamp=1001, vector=(0.4, 0.5, 0.6)),
    Record(id=12, user_id=2, timestamp=1002, vector=(0.7, 0.8, 0.9)),
]

# Insert all atomically (all-or-nothing validation)
db.add_batch(records)

# Delete multiple records by ID
db.delete_batch([10, 11])

For an existing C-contiguous native float32 matrix, add_buffer avoids per-component Python assignment. Writable buffers are borrowed for the native call; read-only buffers incur one bulk copy. The Rust core copies and validates the complete batch before return.

Optional PyTorch index

Install the optional dependency only in desktop applications that already need PyTorch:

pip install 'qenlo[torch]==0.1.0a11'

TorchIndex is an exhaustive, resident FP32 matrix index. It is derived from a canonical collection and is not a second durable store:

from qenlo import Filter, TorchIndex

index = TorchIndex.from_collection(
    db,
    Filter(user_id=42),
    device="cuda",       # "cpu" and "mps" are also explicit choices
    max_bytes=256 << 20,
)
ids, distances = index.search(query_tensor, k=10)

The capture includes only live rows matching the filter and records the canonical generation. A later add or delete makes the index stale; search then raises instead of serving the old snapshot. Inputs are copied, normalized, and owned by the index. Returned IDs and distances are tensors on the selected device. The reported allocation_bytes and max_bytes checks cover owned vectors, IDs, and the explicit search tensors; they do not measure PyTorch allocator caches or backend-private memory.

Current tensor IDs are restricted to 0..=2**63-1. Native collections accept the full unsigned 64-bit range, but PyTorch documents uint64 eager operations as having limited backend support. TorchIndex.from_collection rejects a snapshot outside the portable tensor range without truncating it. CPU is tested locally; CUDA and MPS require separate platform runs.


Data Model & Types

Record

  • id: int (unsigned 64-bit integer, unique and non-reusable)
  • user_id: int (unsigned 64-bit integer)
  • timestamp: int (signed 64-bit integer)
  • vector: Sequence[float] (normalized FP32 components)

Filter

  • user_id: Optional[int] (exact equality match)
  • timestamp_lower: Optional[int] (inclusive lower bound)
  • timestamp_upper: Optional[int] (exclusive upper bound)

ExecutionReport

  • operation_id: int — Unique monotonically increasing query ID
  • requested_backend: str — the configured policy, e.g. CpuExact, Automatic(GpuPredicate), or WgpuRequired(GpuPredicate)
  • actual_backend: str — the engine that ran the search: Cpu, Wgpu, or Usearch
  • algorithm: str — Search algorithm (Exact, IvfFlat, etc.)
  • filter_execution: str — Filter strategy evaluated
  • index_generation: int — Generation watermark observed
  • total_duration_ns: int — Total wall-clock time in nanoseconds
  • lock_wait_ns: int — Time spent acquiring read locks
  • eligible_rows: Optional[int] — Number of live rows passing metadata filters
  • upload_bytes: Optional[int] — Host-to-device bytes transferred
  • readback_bytes: Optional[int] — Device-to-host bytes read back

Error Handling

All native and validation failures raise QenloError or standard Python exceptions (ValueError):

from qenlo import Collection, QenloError, Record

try:
    with Collection.memory(3) as db:
        db.add(Record(1, 1, 0, (1.0, 0.0, 0.0)))
        # Duplicate IDs are strictly rejected
        db.add(Record(1, 1, 0, (0.0, 1.0, 0.0)))
except QenloError as e:
    print(f"Operation rejected: {e}")

Background work and networking

Importing or using the Python SDK starts no background thread and sends no network request. Applications may export ExecutionReport values through their own telemetry system.


License

Licensed under Apache-2.0.

Release files for qenlo 0.1.0a11

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for qenlo 0.1.0a11
File Interpreter ABI Platform
qenlo-0.1.0a11-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
qenlo-0.1.0a11-py3-none-manylinux_2_28_x86_64.whl Python 3 none Linux glibc 2.28+ x86-64 Details
qenlo-0.1.0a11-py3-none-macosx_14_0_arm64.whl Python 3 none macOS 14.0+ ARM64 Details

Total release size: 7.9 MB

Release files / qenlo-0.1.0a11-py3-none-win_amd64.whl

Download URL qenlo-0.1.0a11-py3-none-win_amd64.whl
Size 2.8 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
b099b6297ac805c69fbc97088582aac1b1bc10aa194c60ab887687fb5152ddbb
BLAKE2b-256 checksum
How to use checksums
133efba37b3976e1150ca036a608572592557d6afb487837adf2aef8193b9f2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / qenlo-0.1.0a11-py3-none-manylinux_2_28_x86_64.whl

Download URL qenlo-0.1.0a11-py3-none-manylinux_2_28_x86_64.whl
Size 2.9 MB
Tags Linux glibc 2.28+ x86-64 Python 3
SHA-256 checksum
How to use checksums
dd0e773cadb54c11361603a8842222270ec57c4de98a31e506e2f2c970ff4176
BLAKE2b-256 checksum
How to use checksums
6556118399510d5381cfbcc751669054e0f487be98f3b47f742b4bff156ba104
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / qenlo-0.1.0a11-py3-none-macosx_14_0_arm64.whl

Download URL qenlo-0.1.0a11-py3-none-macosx_14_0_arm64.whl
Size 2.2 MB
Tags Python 3 macOS 14.0+ ARM64
SHA-256 checksum
How to use checksums
653a0e59be0ea731c45fd7e60d431c283708222d45d3efd40779a45076e0e0ee
BLAKE2b-256 checksum
How to use checksums
2ecc4b90bc42da59202c2df5c179a8ac5a7864814e8937376d655a13cedfd413
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page