Framework integrations

polign_db works as a vector store in LangChain and LlamaIndex, and Recall gives LiveKit voice agents long-term memory. Each integration is a small Python package that talks to the same server and needs nothing extra on the server side.

FrameworkPackageClass
LangChainlangchain-polignlangchain_polign.PolignVectorStore
LlamaIndexllama-index-vector-stores-polignllama_index.vector_stores.polign.PolignVectorStore
LiveKit Agentsrecall-livekitrecall_livekit.RecallAgent

All three need a running server. For a first try, start one on your machine:

polign-server -store fs:/var/lib/polign

It listens on http://localhost:23000. Get started covers installing the binary and pointing it at a bucket.

LangChain

pip install langchain-polign
from langchain_openai import OpenAIEmbeddings
from langchain_polign import PolignVectorStore

store = PolignVectorStore(
    embedding=OpenAIEmbeddings(),
    collection="docs",
    url="http://localhost:23000",
)

store.add_texts(
    ["cats purr", "dogs bark"],
    metadatas=[{"lang": "en", "score": 0.9}, {"lang": "en", "score": 0.4}],
)

store.similarity_search("purring", k=1, filter={"score": {"$gte": 0.5}})
store.similarity_search_with_relevance_scores("barking", k=2)
store.max_marginal_relevance_search("animals", k=2, fetch_k=10)

store.delete(filter={"lang": "fr"})      # or store.delete(ids=[...])

retriever = store.as_retriever(search_kwargs={"k": 4, "filter": {"lang": "en"}})

filter is the same metadata language as the Python client: a plain mapping is equality, and $eq, $ne, $in, $gt, $gte, $lt, $lte, $exists, $and, $or, and $not build richer predicates.

similarity_search_with_score returns the distance, where smaller is closer. similarity_search_with_relevance_scores turns it into a score between 0 and 1. The package passes LangChain's standard vector store test suite. Source and the full API are in the langchain-polign README.

LlamaIndex

pip install llama-index-vector-stores-polign
from llama_index.core import StorageContext, VectorStoreIndex
from llama_index.vector_stores.polign import PolignVectorStore

store = PolignVectorStore(collection_name="docs", url="http://localhost:23000")
index = VectorStoreIndex.from_documents(
    documents, storage_context=StorageContext.from_defaults(vector_store=store)
)

index.as_retriever(similarity_top_k=5).retrieve("what purrs?")
index.delete_ref_doc("document-id")     # removes every chunk of that document

# reopen later without re-indexing
index = VectorStoreIndex.from_vector_store(store)

LlamaIndex MetadataFilters are translated for you. Supported operators are EQ, NE, GT, GTE, LT, LTE, IN, NIN, ANY, ALL, CONTAINS, and IS_EMPTY, combined with AND, OR, and NOT. TEXT_MATCH is not supported.

Query modeWhat runs
DEFAULTVector search.
TEXT_SEARCH, SPARSEBM25 keyword search over the node text.
HYBRIDVector and BM25 search combined on the server. Set alpha to weight the vector side; leave it unset for rank fusion.
MMRNearest nodes re-ranked for variety.

Source and the full API are in the llama-index-vector-stores-polign README.

LiveKit Agents

A voice agent that remembers the caller. Facts are typed: each one is a predicate from a closed registry with a value, so "call me Sam" replaces the old name instead of adding a second one, and the history of the change is kept. Nothing is searched per turn and no second model runs: the caller's facts are a short list, loaded whole before the first reply.

pip install recall-livekit "livekit-agents[openai,deepgram,cartesia,silero]"
from livekit.agents import AgentServer, AgentSession, JobContext, JobProcess
from recall_livekit import VOICE_REGISTRY, RecallAgent, RecallMemory

server = AgentServer()

def setup(proc: JobProcess):
    # one Recall subprocess per worker process
    proc.userdata["recall"] = RecallMemory.open(
        url="http://localhost:23000", predicates=VOICE_REGISTRY,
    )

server.setup_fnc = setup

@server.rtc_session()
async def entrypoint(ctx: JobContext):
    participant = await ctx.wait_for_participant()
    memory = ctx.proc.userdata["recall"].for_subject(participant.identity)
    session = AgentSession(stt=..., llm=..., tts=..., vad=...)
    await session.start(
        agent=RecallAgent(memory=memory, instructions="You are the Acme support line."),
        room=ctx.room,
    )

What happens on a call:

The subject should be a stable, auth-derived identifier such as the participant identity your token server issued, never the room name. Reads fail open, so a slow memory store never drops a call; writes tell the model the fact was not saved. An existing Agent subclass gets the same block and tool through await attach(agent, memory).

Recall runs as a polign mcp subprocess, so the polign CLI has to be in the worker image. The example ships a Dockerfile that copies it from the published server image, plus a keyword knowledge-base agent for the retrieval side. Custom predicates are a JSON file; see the recall-livekit README for the format and every option.

Keyword and hybrid search

The two vector store packages store the document text in the metadata key text. That is the field the server's keyword index reads, so keyword and hybrid search work without setup. In LangChain, call them directly:

store.lexical_search("brown fox", k=5)            # keyword only
store.hybrid_search("brown fox", k=5, alpha=0.6)  # vector and keyword, weighted
store.hybrid_search("brown fox", k=5)             # vector and keyword, rank fusion

In LlamaIndex, pick the TEXT_SEARCH or HYBRID query mode.

Keyword search needs a server started with -store. A new document becomes searchable by keyword once the server has written it to the bucket, about half a minute with default settings. Vector search sees it right away.

What gets stored

Limits

Source & license

All three packages are open source under the Apache License 2.0 and live next to the client in github.com/Polign/polign. Bug reports and pull requests go there.

Using another framework?

Anything that can call Python can use the Python client directly, and every other language can use the HTTP API.