Rust · Python · serverless
An embedded engine whose database is a bucket. No cluster and no server: every process reads the index straight from storage, ranks exact, prefix, abbreviation, infix and misspelt matches together, and answers in a fraction of a millisecond.
pip install typemind
-
Every match kind
Exact, prefix, infix, abbreviation, spelling-tolerant and word-decomposing completion, ranked together with popularity.
-
Serverless by design
Storage is the only shared component. Commits are create-only writes, and every process syncs itself from the bucket.
-
Semantic and hybrid
Embeddings from any model, quantised to 2 to 4 bits, fused with lexical results by reciprocal rank, weighted blending or lexical-first ordering.
-
Override layers
One catalogue, many views: tenants, languages, listeners, promotions and takedowns as small layers over the shared data, overriding it per document id, without a copy.
-
Fast and deterministic
Memory-mapped, checksummed segments. Typed queries take 0.1 to 0.2 ms at the median on short titles, and identical inputs give bit-identical results.
-
Rust and Python
Documents as dicts,
Documentobjects, or pandas, polars and Arrow tables. Typed stubs, an asyncio API and a command-line tool.
Install¶
Quick start¶
import typemind
from typemind import Index
docs = [
{"id": "bohemian", "text": "Bohemian Rhapsody – Queen", "popularity": 0.95},
{"id": "dancing", "text": "Dancing Queen – ABBA", "popularity": 0.85},
{"id": "dark", "text": "Dancing in the Dark – Bruce Springsteen", "popularity": 0.7},
{"id": "bridge", "text": "Under the Bridge – Red Hot Chili Peppers", "popularity": 0.75, "abbreviations": ["RHCP"]},
]
index = Index.from_documents(docs) # in memory
for query in ["danc", "RHCP", "bohemain rapsody", "queen"]:
print(query, [(s.text, s.kind) for s in index.complete(query, limit=3)])
db = typemind.connect("./data") # or "s3://bucket/prefix", "gs://...", "az://..."
txn = db.begin()
txn.append("songs", docs)
txn.commit()
engine = db.engine() # loads new versions by itself, every 5 seconds
print([s.text for s in engine.complete("danc", ["songs"])])
use std::{sync::Arc, time::Duration};
use typemind::{Database, Document, Engine, Index, IndexOptions, Replica};
let docs = || [
Document::keyed("bohemian", "Bohemian Rhapsody – Queen", 0.95),
Document::keyed("dancing", "Dancing Queen – ABBA", 0.85),
Document::keyed("dark", "Dancing in the Dark – Bruce Springsteen", 0.7),
Document::keyed("bridge", "Under the Bridge – Red Hot Chili Peppers", 0.75).with_abbreviation("RHCP"),
];
let index = Index::from_documents(docs())?; // in memory
for s in index.complete("danc", 3) {
println!("{} {}", s.text, s.kind.as_str());
}
let database = Database::open("./data", Vec::<(String, String)>::new()).await?;
let mut txn = database.begin().await?;
txn.append_documents("songs", docs(), [])?;
txn.commit().await?;
let engine = Arc::new(Engine::new());
let replica = Arc::new(Replica::new(database, IndexOptions::default()));
replica.sync(&engine).await?;
let _follower = replica.follow(&engine, Duration::from_secs(5));
let hits = engine.complete(&["songs"], "danc", 10)?;
danc [('Dancing Queen – ABBA', MatchKind.PREFIX), ('Dancing in the Dark – Bruce Springsteen', MatchKind.PREFIX)]
RHCP [('Under the Bridge – Red Hot Chili Peppers', MatchKind.ABBREVIATION)]
bohemain rapsody [('Bohemian Rhapsody – Queen', MatchKind.FUZZY)]
queen [('Bohemian Rhapsody – Queen', MatchKind.INFIX), ('Dancing Queen – ABBA', MatchKind.INFIX)]
['Dancing Queen – ABBA', 'Dancing in the Dark – Bruce Springsteen']
Next, Getting started walks from an index in memory to a database in a bucket that several processes share, Concepts explains the model behind it, and Collections covers the convenience layer on top.