Skip to main content

RetrievalKit Python Wrapper

This is a thin Python wrapper around the RetrievalKit Rust retrieval core. Python provides an ergonomic API, while Rust handles indexing, filtering, ranking, persistence, and result tracing.

For the Project Apollo walkthrough and guidance on choosing hybrid, semantic, graph scope, and metadata filters, start with the canonical Python guide.

The wrapper does not include an embedding model. Callers provide embeddings from the same local or remote provider for indexing and querying.

Requires CPython 3.10 through 3.14.

Capability API

Use RetrievalDatabase for new code. It matches the Rust and Swift system architecture while keeping Python naming and data structures idiomatic:

from retrievalkit import (
    Document,
    RetrievalDatabaseBuilder,
)

builder = RetrievalDatabaseBuilder(
    corpus_id="notes",
)
builder.upsert(
    Document(
        id="note-42",
        text="Local retrieval architecture",
        metadata={"project": "retrievalkit"},
    ),
    embedding=embedding,
)
database = builder.build()

hits = database.retrieval.semantic_search(
    query_embedding,
    where={"project": "retrievalkit"},
)

The first embedding fixes the dimension in Rust. Rust also derives the hidden canonical record and chunk identity, so the common path has no dimension, chunk-key, or embedding-map bookkeeping. Every retrieval database exposes both semantic_search(...) and hybrid_search(...).

The existing RecordInput plus embeddings={chunk_key: vector} methods remain available as an advanced compatibility surface for applications that already own stable multi-chunk identities.

The lower-level Index API remains available for compatibility, pipeline integration, mutation, compaction, and direct BM25 benchmarking. It is not the canonical capability-oriented database API.

Local Development

From this directory:

maturin develop

From the repository root, run the full local wrapper check:

scripts/check-python-wrapper.sh

Run tests:

python -m pytest

Run optional type checks when mypy is installed in your development environment:

python -m mypy

Run optional lint checks when ruff is installed:

python -m ruff check .

mypy and ruff are developer tools only. They are not runtime dependencies of the retrievalkit package.

Build A Local Wheel

From the repository root:

scripts/build-python-wheel.sh

The script builds the wheel, installs that exact wheel into a clean smoke-test virtual environment, and runs:

python wrappers/python/tests/smoke_installed.py

The wheel is written to:

target/wheels/

Install it into another local environment:

python -m pip install "target/wheels/retrievalkit-*.whl"

The wheel contains a compiled Rust extension, so it is specific to the platform and Python version used to build it. For example, a macOS arm64 CPython 3.14 wheel is only for compatible macOS arm64 CPython 3.14 environments.

Use --skip-smoke-test only when you need to produce a wheel without validating the installed package.

Persistence Safety

Index.save(path) writes and syncs a complete immutable generation before atomically publishing it through manifest.json. If a save is interrupted, the previously published generation remains loadable. The next successful save removes abandoned and superseded generations. Existing V1 root-file indexes remain readable and migrate automatically on their next save.

Only one writer may save a given directory at a time. RetrievalKit uses an OS-released lock, so a process crash does not leave the directory permanently locked; a competing save fails with an actionable PersistenceError.

Treat the index directory as RetrievalKit-owned; do not modify .snapshots or manifest.json directly.

from pathlib import Path

from retrievalkit import Index

path = Path("./search-index")
index = Index(dimension=384)
index.save(path)

# On the next app launch:
loaded_index = Index.load(path)

New saves use a checksummed V4 manifest. Validate a stored index without keeping it loaded for search:

from retrievalkit import CorruptIndexError, Index

try:
    Index.validate(path)
except CorruptIndexError as error:
    print(error)  # restore a known-good copy or rebuild the index

V1, V2, and V3 indexes remain readable; their next save publishes a checksummed V4 snapshot. V1 and V2 do not have payload checksums; V3 and V4 do. Corrupt payloads fail with CorruptIndexError; invalid or unsupported manifests fail with UnsupportedFormatError.

save returns actual persisted file sizes. It raises PersistenceError with the failed operation, path, operating-system cause, and a recovery hint when the directory cannot be written.

Concurrency

Exact, keyword, and hybrid search release the Python GIL while Rust performs retrieval. Multiple Python threads may search the same Index concurrently:

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=4) as pool:
    futures = [pool.submit(index.search, embedding, limit=10) for _ in range(4)]
    result_sets = [future.result() for future in futures]

Load, validation, add, delete, save, and compaction also release the GIL during Rust-only work. Search methods hold a shared PyO3 borrow; add, delete, save, and compaction require an exclusive mutable borrow. If mutation is attempted while a search is active—or a search while mutation is active—PyO3 rejects the call with RuntimeError: Already borrowed. Coordinate those operations in the application instead of retrying blindly. The Index remains alive until every active native call returns.

Compaction

Updates and deletes create tombstones so search results change immediately without rewriting the full index. Reclaim their memory before saving a smaller snapshot:

if index.tombstoned_chunk_count > 0:
    report = index.compact()
    print(
        f"removed {report['chunks_removed']} chunks; "
        f"reclaimed about {report['estimated_bytes_reclaimed']} bytes"
    )
    index.save(path)

compact() is an inexpensive no-op when there are no tombstones. It preserves all active chunk IDs and never reuses removed IDs. The byte report estimates in-memory payload savings; call save() afterward to publish a compacted disk snapshot and receive actual persisted file sizes.

Compaction is synchronous and temporarily holds both the current and replacement structures. Run it away from latency-sensitive work and leave memory headroom, especially near the 50K-chunk V1 ceiling. The estimate reports retained payload before and after compaction; it is not a peak-RSS measurement.

Platform Wheels

The local scripts/build-python-wheel.sh helper builds for the platform and Python interpreter running the command. To produce macOS, Linux, and Windows wheels, run the wheel build on each target operating system and smoke-test the installed package. The smoke test runs by default.

PYTHON_BIN=python3 scripts/preflight-python-wrapper.sh
scripts/build-python-wheel.sh

The preflight requires CPython 3.10-3.14, venv, and Rust cargo, and prints the detected versions and host. Passing source validation outside macOS arm64 does not expand the initial public wheel support claim.

This keeps the repository ready for platform-specific wheels without requiring a GitHub CI workflow yet.

Example

Run the complete local example after maturin develop:

python examples/pipeline_quickstart.py

Pipeline defaults to the shared Rust sentence chunker. If your embedding provider exposes its tokenizer, pass both its exact token counter and model limit so oversized chunks are recursively subdivided before embedding:

pipeline = Pipeline(
    index,
    embed=embed,
    count_tokens=tokenizer.count_tokens,
    max_tokens=model.max_input_tokens,
)

Character limits are only a fallback because token counts depend on the model's tokenizer.

from retrievalkit import Index, hybrid_search_text, search_text
from retrievalkit.ingest import chunk_text


def embed(texts):
    # Replace with a real embedding provider. The returned vectors must match
    # the index dimension.
    return [[1.0, 0.0, 0.0, 0.0] for _ in texts]


index = Index(dimension=4, metric="cosine")

chunks = chunk_text(
    "Python wrapper stays thin. Rust performs shared ingestion and retrieval.",
    max_characters=48,
    overlap_characters=8,
    strategy="sentence",
)
texts = [chunk["text"] for chunk in chunks]
embeddings = embed(texts)

index.add(
    documents=[
        {
            "id": "doc-1",
            "metadata": {"project": "retrievalkit"},
            "chunks": [
                {
                    "text": text,
                    "embedding": embedding,
                    "metadata": {"archived": False},
                }
                for text, embedding in zip(texts, embeddings)
            ],
        }
    ]
)

hits = search_text(
    index,
    "How does the wrapper work?",
    embed=embed,
    limit=5,
    where={"project": "retrievalkit", "archived": False},
)

hybrid_hits = hybrid_search_text(
    index,
    "How does the wrapper work?",
    embed=embed,
    limit=5,
    where={"project": "retrievalkit", "archived": False},
    vector_candidates=10,
    keyword_candidates=25,
)

Chunk limits and overlap are measured in Unicode characters. start_byte and end_byte are UTF-8 byte offsets into the original string. Sentence mode prefers sentence endings, then whitespace, and falls back to the hard character limit. The implementation lives in Rust and is shared with Swift's RetrievalKit.TextChunker API.

Graph capabilities are intentionally absent from this base distribution. Use the separate retrievalkit-graph distribution in wrappers/python-graph when an application needs graph-only or combined graph-and-retrieval databases. The two native distributions must not be imported in the same Python process.

Use TimestampMillis when metadata must remain distinct from an ordinary integer across ingestion, filtering, persistence, and result hydration:

from retrievalkit import TimestampMillis

metadata = {"captured_at": TimestampMillis(120_000)}
where = {"captured_at": {"$gte": TimestampMillis(60_000)}}

Document Pipeline

The optional pipeline module composes chunking, a caller-provided embedding provider, indexing, and hybrid text search:

from retrievalkit import Index
from retrievalkit.pipeline import Pipeline

index = Index(dimension=384)
pipeline = Pipeline(
    index,
    embed=embed,
)

pipeline.add("note-42", note_text, metadata={"source": "notes"})
hits = pipeline.search("pricing decisions", limit=5)

All chunk embeddings are created and validated before the existing document is replaced. Empty input and embedding count or dimension mismatches fail without mutating the index.

Applications can instead pass any object implementing chunks(text). This allows Markdown-, transcript-, email-, or source-code-aware chunking while the pipeline continues to own embedding validation and atomic document upsert. The customization protocol lives in retrievalkit.pipeline; the concrete RustTextChunker remains available from retrievalkit.ingest when callers want non-default limits.

Filter Syntax

Common filters use where={...}:

from retrievalkit import Filter

filters: Filter = {
    "project": "retrievalkit",
    "archived": False,
    "created_at": {"$gte": 1710000000000},
    "source": {"$in": ["notes", "docs"]},
}

index.search(
    query_embedding,
    limit=10,
    where=filters,
)

Hybrid Search

Hybrid search combines vector and BM25 keyword candidates in the Rust core:

hits = index.hybrid_search(
    "python wrapper",
    query_embedding,
    limit=10,
    where={"project": "retrievalkit"},
    alpha=0.6,
)

limit is the final number of fused hits. vector_candidates and keyword_candidates control how many candidates each retrieval mode contributes before fusion. alpha directly controls weighted normalized fusion: 1 is vector-only, 0 is BM25-only, and the default 0.6 gives vector search 60% of the blend. The candidate defaults are 50 vector and 50 keyword results. Pass encoding="f32" for correctness-reference indexes. Every hit includes effective metadata. Hybrid traces expose alpha, source ranks, normalized scores, and matched terms rather than the internal Rust fusion enum.

Inputs and search results are plain dictionaries, with public TypedDict shapes available for annotations:

from retrievalkit import DocumentInput, HybridHit, SearchHit

documents: list[DocumentInput] = [
    {
        "id": "doc-1",
        "chunks": [{"text": "python wrapper", "embedding": query_embedding}],
    }
]

index.add(documents)
vector_hits: list[SearchHit] = index.search(query_embedding)
hybrid_hits: list[HybridHit] = index.hybrid_search("python wrapper", query_embedding)

Complex filters can use helper constructors:

from retrievalkit import where

index.search(
    query_embedding,
    limit=10,
    where=where.all(
        where.eq("project", "retrievalkit"),
        where.range("created_at", gte=1710000000000),
    ),
)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

retrievalkit-0.1.0-cp314-cp314-macosx_11_0_arm64.whl (957.9 kB view details)

Uploaded CPython 3.14macOS 11.0+ ARM64

retrievalkit-0.1.0-cp313-cp313-macosx_11_0_arm64.whl (958.6 kB view details)

Uploaded CPython 3.13macOS 11.0+ ARM64

retrievalkit-0.1.0-cp312-cp312-macosx_11_0_arm64.whl (958.4 kB view details)

Uploaded CPython 3.12macOS 11.0+ ARM64

retrievalkit-0.1.0-cp311-cp311-macosx_11_0_arm64.whl (960.6 kB view details)

Uploaded CPython 3.11macOS 11.0+ ARM64

retrievalkit-0.1.0-cp310-cp310-macosx_11_0_arm64.whl (960.6 kB view details)

Uploaded CPython 3.10macOS 11.0+ ARM64

File details

Details for the file retrievalkit-0.1.0-cp314-cp314-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for retrievalkit-0.1.0-cp314-cp314-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 d1bc1feec33135f507653f97cdd357f9907241a4f2664a7cc0effbb551d69c21
MD5 5802a80a37491f29e9c4ec6499d6fa27
BLAKE2b-256 d24f90ffec75cb9a9adaf2aaf1ee86865e4a5cfe01a0f0ce1e41a9ca57df335b

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrievalkit-0.1.0-cp314-cp314-macosx_11_0_arm64.whl:

Publisher: publish-release.yml on gungorbasa/RetrievalKit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file retrievalkit-0.1.0-cp313-cp313-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for retrievalkit-0.1.0-cp313-cp313-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3b5b675eeeeb263a29c6b6041cbcd53636dbe30c212b3901929e7ed7b6d36b70
MD5 ab05561cb453d477d4e9b56e1fb0b7dd
BLAKE2b-256 62442fd54816ad80ee3ae76a00793acf3bb8fc2826ff83139f8e436cea010814

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrievalkit-0.1.0-cp313-cp313-macosx_11_0_arm64.whl:

Publisher: publish-release.yml on gungorbasa/RetrievalKit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file retrievalkit-0.1.0-cp312-cp312-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for retrievalkit-0.1.0-cp312-cp312-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 812ac8b70682794f2192efc18876a580fb6c301f86526ede281b4c011e3456a3
MD5 7202500a243011f6a2b6507dd9de7cdb
BLAKE2b-256 4d87b153fee516e01573425e94c22a64ffabbca0c86887e16e21a67ae062725d

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrievalkit-0.1.0-cp312-cp312-macosx_11_0_arm64.whl:

Publisher: publish-release.yml on gungorbasa/RetrievalKit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file retrievalkit-0.1.0-cp311-cp311-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for retrievalkit-0.1.0-cp311-cp311-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 384850c94c40dd04a4db290b6ac0ee94e832cba1d51e65b021ef738e860af5b6
MD5 238e6c5437e345e68dcf1d46f8fa67e3
BLAKE2b-256 836a78a863fca646dc8c29304a1841e9c9ffe42ebbec0f70fc48c652afc77360

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrievalkit-0.1.0-cp311-cp311-macosx_11_0_arm64.whl:

Publisher: publish-release.yml on gungorbasa/RetrievalKit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file retrievalkit-0.1.0-cp310-cp310-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for retrievalkit-0.1.0-cp310-cp310-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 a72a0796ae7f88eea74ba8054a846629bbd953c65f85efa0c73c8b57888800d7
MD5 8b6e047a5ce76ede2052cda8aa7fc5fe
BLAKE2b-256 9f3e27bd8f2eb2765084a2011e31f910e8e4edc3c10a97950e9289273404f58e

See more details on using hashes here.

Provenance

The following attestation bundles were made for retrievalkit-0.1.0-cp310-cp310-macosx_11_0_arm64.whl:

Publisher: publish-release.yml on gungorbasa/RetrievalKit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.1.0 This release

5 files

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