LangChain#
pip install "needtoknow[langchain]"
A permission-aware LangChain retriever#
NeedToKnowRetriever is a LangChain BaseRetriever, so it works anywhere a retriever does:
chains, agents and LangGraph tools.
from needtoknow import MetadataACLAuthorizer, PermissionedRetriever, Subject
from needtoknow.langchain import NeedToKnowRetriever
retriever = NeedToKnowRetriever(retriever=PermissionedRetriever(backend, MetadataACLAuthorizer()), k=4)
docs = retriever.invoke("What changed in Q3 pricing?", subject=Subject("bob", ["sales"]))
Pass the subject on each call, or bind one with for_subject, which returns a copy:
bobs_retriever = retriever.for_subject(Subject("bob", ["sales"]))
docs = bobs_retriever.invoke("What changed in Q3 pricing?")
A call with no subject raises ValueError instead of returning unfiltered results. Each returned
Document carries the chunk’s metadata plus relevance_score. ainvoke uses aretrieve.
In an agent: bind the subject, do not expose it#
An agent’s tool arguments are chosen by the model. If the subject were one of them, a prompt could change whose documents are searched. Build the tool per session with the subject already bound:
from langchain_core.tools import tool
def tools_for(session):
retriever = NeedToKnowRetriever(retriever=permissioned).for_subject(session.subject)
@tool
def search_documents(query: str) -> str:
"""Search the company documents the current user may read."""
return "\n\n".join(doc.page_content for doc in retriever.invoke(query))
return [search_documents]
Any LangChain vector store as a backend#
LangChainVectorStoreBackend goes the other way: it lets a LangChain VectorStore act as a
needtoknow backend.
from needtoknow.langchain import LangChainVectorStoreBackend
backend = LangChainVectorStoreBackend(vectorstore, id_key="chunk_id")
retriever = PermissionedRetriever(backend, authorizer) # post-filtered
Filter syntax differs from store to store, so this backend post-filters. Every document needs an
id: Document.id, or the metadata field named by id_key. Scores follow the store’s ranking
(1 / (1 + rank)), since stores disagree on what their raw scores mean. For pre-filtering, use a
native backend such as pgvector or Chroma.