Skip to main content

rostam-client

A dependency-free Python client and LangChain adapter for the Rostam vector store.

The core client uses only the Python standard library — no requests, no gRPC, nothing to pull in. The optional LangChain adapter requires langchain-core.

Install

pip install rostam-client            # core client only (zero dependencies)
pip install rostam-client[langchain] # + the LangChain VectorStore adapter

Run a server

The client talks to a Rostam HTTP server. Build and run one from this repo:

go build -o rostam-server ./cmd/rostam-server
./rostam-server -http :8080 -data ./data                    # REST/JSON
./rostam-server -http :8080 -grpc :9090 -tcp :7000          # all three over ONE store
./rostam-server -http :8080 -api-key "$KEY"                 # require a bearer token

A single server can expose REST, gRPC, and the binary TCP protocol at once over one shared store — a write on any transport is visible on the others. Set a transport's flag to "" to disable it.

Quickstart

from rostam import RostamClient
from rostam import filters as f

c = RostamClient("http://localhost:8080", api_key="optional-bearer-token")

c.create_collection("docs", dim=384, metric="cosine")   # metric: cosine|l2|dot

# Upsert points. Metadata is plain Python — the client encodes it to Rostam's
# tagged wire form for you (and decodes it back on the way out).
c.upsert("docs", 1, embedding, content="the chunk text", metadata={"doc_id": 7, "lang": "en"})

# k-NN with content + metadata, filtered.
hits = c.search_docs("docs", query_embedding, k=5, filter=f.eq("doc_id", 7))
for d in hits:
    print(d.id, d.distance, d.content, d.metadata)

# Group-by-document: the top-k distinct documents, best chunk(s) each.
for g in c.search_groups("docs", query_embedding, k=5, group_by="doc_id", group_size=2):
    print(g.key, [h.content for h in g.hits])

# Compound filters.
hits = c.search_docs("docs", query_embedding, k=5,
                     filter=f.and_(f.gte("price", 10.0), f.eq("in_stock", True)))

c.delete("docs", 1)
c.delete_by_filter("docs", f.eq("doc_id", 7))   # purge a whole document

The client also exposes insert (rejects duplicate ids), hybrid_search (dense + sparse fusion), drop_collection, and health.

Embeddings (work in text, not vectors)

The core client takes vectors. TextStore adds the text-first ergonomics — embedding happens client-side, so no model dependency touches Rostam's engine.

from rostam import RostamClient, TextStore, OpenAIEmbedder

store = TextStore(RostamClient("http://localhost:8080"), "docs", OpenAIEmbedder())
store.create_collection()                       # dim inferred from the embedder
store.add(["first chunk", "second chunk"], metadatas=[{"doc_id": 1}, {"doc_id": 1}])

docs = store.search("a question", k=4)                       # embeds the query for you
groups = store.search_groups("a question", k=4, group_by="doc_id")

Embedder options:

  • OpenAIEmbedder — calls any OpenAI-compatible /embeddings endpoint using only the standard library (no openai package). Works with OpenAI, Azure OpenAI, and local servers (Ollama, LM Studio, text-embeddings-inference) via base_url. Reads OPENAI_API_KEY by default.
  • FunctionEmbedder — wraps any callable, e.g. a local model:
    from sentence_transformers import SentenceTransformer
    m = SentenceTransformer("all-MiniLM-L6-v2")
    embedder = FunctionEmbedder(lambda ts: m.encode(ts).tolist())
    

Embedders implement the same interface as LangChain's Embeddings, so the same object feeds both TextStore and RostamVectorStore.

Multi-vector / late interaction (ColBERT MaxSim)

For late-interaction retrieval, a document is represented by many token vectors and scored by MaxSim (Σ_q max_d cos(q,d)) rather than a single pooled vector. Multi-vector collections are in-memory.

# quant ("sq8"/"bq1") quantizes the first-stage graph; persistent=True keeps the
# float32 token vectors off-heap in an mmap file and survives restart.
c.mv_create_collection("docs", dim=128, quant="sq8", persistent=True)
c.mv_add("docs", 1, doc_token_vectors, metadata={"doc_id": 1})   # token matrix
c.mv_add("docs", 2, other_token_vectors)

hits = c.mv_search("docs", query_token_vectors, k=5)             # MaxSim ranking
for h in hits:
    print(h.id, h.score, h.metadata)

c.mv_delete("docs", 1)

You supply token vectors yourself (e.g. from a ColBERT/late-interaction model); the client handles the wire encoding and decodes results (including native metadata). Persistent collections are flushed server-side (embedded CollectionStore.FlushMultiVector).

LangChain

RostamVectorStore implements the standard LangChain VectorStore interface, so it drops into existing retrieval chains. You bring the embeddings; Rostam stores the vector, the chunk text, and metadata.

from langchain_openai import OpenAIEmbeddings
from rostam import RostamClient
from rostam.langchain import RostamVectorStore

client = RostamClient("http://localhost:8080")
client.create_collection("docs", dim=1536, metric="cosine")

store = RostamVectorStore.from_texts(
    texts=["first chunk", "second chunk"],
    embedding=OpenAIEmbeddings(),
    metadatas=[{"doc_id": 1}, {"doc_id": 1}],
    client=client,
    collection="docs",
)

docs = store.similarity_search("a question", k=4, filter={"doc_id": 1})
docs_scored = store.similarity_search_with_score("a question", k=4)

# Rostam-specific extension: retrieve the top-k distinct documents.
groups = store.search_grouped("a question", k=4, group_by="doc_id", group_size=2)

filter accepts either a native Rostam filter (rostam.filters) or a simple {field: value} map (translated to an AND of equalities). Relevance scores map Rostam's distance to a 0..1 range.

Hybrid retrieval

Fuse dense KNN with BM25 full-text search by enabling full_text on the store (the collection must have the full-text index enabled):

store = RostamVectorStore(
    client, "docs", embedding, full_text=True, auto_create=True
)
# Dense + server-side BM25 over the raw query string (default):
docs = store.hybrid_search("apple pie", k=4)

# Dense + SPLADE-style sparse (pass a callable that returns a sparse vector):
store = RostamVectorStore(
    client, "docs", embedding, full_text=True, sparse_embedding=my_splade_fn
)
docs = store.hybrid_search("apple pie", k=4)

hybrid_search signature: hybrid_search(query, k=4, *, filter=None, method="rrf", alpha=0.0). method and alpha are forwarded to Rostam's fusion endpoint unchanged.

Maximal Marginal Relevance (MMR)

Retrieve diverse results by trading off relevance against redundancy:

docs = store.max_marginal_relevance_search(
    "apple pie", k=4, fetch_k=20, lambda_mult=0.5
)

fetch_k candidates are fetched first; MMR re-ranks them to the final k. lambda_mult=1.0 is pure relevance; 0.0 is pure diversity. The async variant is await store.amax_marginal_relevance_search(...).

Fetch by id

Retrieve documents by their original string ids (missing ids are silently omitted):

docs = store.get_by_ids(["id-1", "id-2"])
# Async:
docs = await store.aget_by_ids(["id-1", "id-2"])

Async methods

Every retrieval and write method has an a-prefixed async counterpart. They offload to a thread pool over the synchronous client — no extra dependency is required:

docs = await store.asimilarity_search("a question", k=4)
docs_scored = await store.asimilarity_search_with_score("a question", k=4)
ids = await store.aadd_texts(["chunk one", "chunk two"])
ok = await store.adelete(["id-1"])

Auto-create

By default (auto_create=True) the collection is created on the first write. Dimensionality is inferred from the first batch of embeddings. If the store is configured with full_text=True the collection is created with the full-text index enabled (required for hybrid search):

# Collection created automatically on first add_texts / from_texts call:
store = RostamVectorStore(client, "docs", embedding, auto_create=True, full_text=True)
store.add_texts(["chunk one"])   # collection created here

# Manage the collection yourself:
client.create_collection("docs", dim=1536, metric="cosine")
store = RostamVectorStore(client, "docs", embedding, auto_create=False)

from_texts forwards auto_create, metric, and full_text to the constructor, so the class method works the same way.

LlamaIndex

rostam.llamaindex.RostamVectorStore implements the LlamaIndex VectorStore interface (pip install rostam-client[llamaindex]).

from rostam import RostamClient
from rostam.llamaindex import RostamVectorStore
from llama_index.core import VectorStoreIndex, StorageContext

client = RostamClient("http://localhost:8080")
client.create_collection("docs", dim=1536, metric="cosine")
store = RostamVectorStore(client=client, collection="docs")
index = VectorStoreIndex.from_documents(
    documents, storage_context=StorageContext.from_defaults(vector_store=store)
)
results = index.as_retriever().retrieve("a question")

Nodes are serialized with LlamaIndex's own metadata utils; delete(ref_doc_id) purges every node of a document via a metadata filter; metadata filters on the query translate to Rostam filters.

Hybrid mode

Pass mode=VectorStoreQueryMode.HYBRID and set query_str to enable hybrid retrieval. The collection must have the full-text index enabled (full_text=True). Note that query_embedding is always required — the hybrid path uses the dense vector unconditionally and only fuses it with BM25/sparse when query_str is also set:

from llama_index.core.vector_stores.types import VectorStoreQuery, VectorStoreQueryMode

store = RostamVectorStore(client=client, collection="docs", full_text=True)
q = VectorStoreQuery(
    query_embedding=embedding,
    query_str="apple pie",          # required for hybrid
    mode=VectorStoreQueryMode.HYBRID,
    similarity_top_k=4,
)
result = store.query(q)

With a sparse_embedding callable, dense + sparse (SPLADE-style) fusion is used instead of dense + BM25:

store = RostamVectorStore(
    client=client, collection="docs", full_text=True, sparse_embedding=my_splade_fn
)

Hybrid results are ranked by the server's fusion score; similarities in the returned VectorStoreQueryResult are rank-based (1/(1+rank)).

Async methods

async_add, aquery, and adelete are available. They offload to a thread pool over the sync client — no extra dependency is required:

ids = await store.async_add(nodes)
result = await store.aquery(query)
await store.adelete(ref_doc_id)

Auto-create

By default (auto_create=True) the collection is created on the first add call. Dimensionality is inferred from the first node's embedding. If the store is configured with full_text=True the collection is created with the full-text index enabled (required for hybrid mode):

# Collection created automatically on first add:
store = RostamVectorStore(client=client, collection="docs", full_text=True)
index = VectorStoreIndex.from_documents(
    documents, storage_context=StorageContext.from_defaults(vector_store=store)
)

# Manage the collection yourself:
client.create_collection("docs", dim=1536, metric="cosine")
store = RostamVectorStore(client=client, collection="docs", auto_create=False)

Haystack

rostam.haystack provides a RostamDocumentStore (Haystack 2.x DocumentStore) and a RostamEmbeddingRetriever component (pip install rostam-client[haystack]).

from haystack import Document
from rostam import RostamClient
from rostam.haystack import RostamDocumentStore, RostamEmbeddingRetriever

RostamClient("http://localhost:8080").create_collection("docs", dim=384, metric="cosine")
store = RostamDocumentStore(url="http://localhost:8080", collection="docs")
store.write_documents([Document(content="hello", embedding=[...], meta={"src": "a"})])

retriever = RostamEmbeddingRetriever(document_store=store, top_k=5)
docs = retriever.run(query_embedding=[...])["documents"]

count_documents / filter_documents are served by Rostam's scroll listing. Documents must carry embeddings; writes use overwrite semantics.

Notes

  • IDs. Rostam point ids are uint64. The client takes integers directly. The LangChain adapter accepts string ids: a purely-numeric string is used verbatim, anything else is hashed (BLAKE2b) to a stable 64-bit id, so repeated upserts/deletes of the same external id address the same point.
  • Metadata kinds. Supported value types: int, float, str, bool, and lists of int/float/str.

Download files

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

Source Distribution

rostam_client-0.1.0.tar.gz (44.8 kB view details)

Uploaded Source

Built Distribution

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

rostam_client-0.1.0-py3-none-any.whl (30.0 kB view details)

Uploaded Python 3

File details

Details for the file rostam_client-0.1.0.tar.gz.

File metadata

  • Download URL: rostam_client-0.1.0.tar.gz
  • Upload date:
  • Size: 44.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rostam_client-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c64fc80ec6117fd3c944228f951ee9047f374b858e78ce99aa30ff4b481b0ad0
MD5 8ea1b48c7b0bc5c38fd595a79cff66a2
BLAKE2b-256 3ef68092c590b51ae9fb77adb139fd1c0096c6a8292d0e6c341b574dba0efe3d

See more details on using hashes here.

Provenance

The following attestation bundles were made for rostam_client-0.1.0.tar.gz:

Publisher: publish-python.yml on rostamlabs/rostam

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

File details

Details for the file rostam_client-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: rostam_client-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 30.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for rostam_client-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 67bae96fee4c723b3c7a2ab98a414ea1f2853873b387868a92e94545ae564e18
MD5 998c0b8637166eee2f17c4fcfc0824bd
BLAKE2b-256 85e4f7d2045ca6e1b1af361a2187418b6918d675af5d95047b1cc2a34627fe92

See more details on using hashes here.

Provenance

The following attestation bundles were made for rostam_client-0.1.0-py3-none-any.whl:

Publisher: publish-python.yml on rostamlabs/rostam

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

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page