LangChain / ChromaDB adapter

This documentation is for an as-yet unreleased version of Cerbos. Choose 0.50.0 from the version picker at the top right or navigate to https://docs.cerbos.dev for the latest version.

The @cerbos/langchain-chromadb package converts a Cerbos PlanResources response into a ChromaDB Where filter object compatible with the LangChain.js Chroma vector store. This enables authorization-aware similarity searches where Cerbos policy conditions are pushed down as metadata filters.

Requirements

  • Cerbos >= v0.16

  • @cerbos/http or @cerbos/grpc client

  • ChromaDB 3.x

  • Node.js >= 20.0.0

Installation

npm install @cerbos/langchain-chromadb

Supported operators

Category Operators

Logical

and, or — $and, $or

Negation

not — operator inversion via De Morgan’s law (see below)

Comparison

eq, ne, lt, le, gt, ge — $eq, $ne, $lt, $lte, $gt, $gte

Membership

in — $in

Negation handling

ChromaDB has no $not or $nor filter. The adapter handles not expressions by inverting the inner operator:

  • not(eq) becomes $ne; not(ne) becomes $eq

  • not(lt) becomes $gte; not(gt) becomes $lte; not(le) becomes $gt; not(ge) becomes $lt

  • not(in) becomes $nin

  • not(and(A, B)) becomes $or[not(A), not(B)] (De Morgan’s law)

  • not(or(A, B)) becomes $and[not(A), not(B)] (De Morgan’s law)

  • not(not(X)) becomes X (double negation elimination)

Unsupported operators

ChromaDB stores flat scalar metadata. The following operators cannot be mapped and cause queryPlanToChromaDB to throw an error:

  • String: contains, startsWith, endsWith

  • Existence: isSet

  • Collection: hasIntersection, exists, exists_one, all, filter, map, lambda, size

Usage

import { queryPlanToChromaDB, PlanKind } from "@cerbos/langchain-chromadb";
import { Chroma } from "@langchain/community/vectorstores/chroma";
import { OpenAIEmbeddings } from "@langchain/openai";

const queryPlan = await cerbos.planResources({
  principal: { id: "user1", roles: ["USER"] },
  resource: { kind: "document" },
  action: "view",
});

const result = queryPlanToChromaDB({
  queryPlan,
  fieldNameMapper: {
    "request.resource.attr.department": "department",
    "request.resource.attr.public": "public",
  },
});

if (result.kind === PlanKind.ALWAYS_DENIED) {
  return [];
}

const chroma = await Chroma.fromExistingCollection(
  new OpenAIEmbeddings(),
  { collectionName: "my_collection" },
);

const filters =
  result.kind === PlanKind.CONDITIONAL ? result.filters : undefined;
const matches = await chroma.similaritySearch("query", 10, filters);

Field name mapper

The mapper translates Cerbos attribute references to ChromaDB metadata field names. It accepts an object or a function.

// Object mapper
const result = queryPlanToChromaDB({
  queryPlan,
  fieldNameMapper: {
    "request.resource.attr.aBool": "aBool",
    "request.resource.attr.aString": "title",
  },
});

// Function mapper
const result = queryPlanToChromaDB({
  queryPlan,
  fieldNameMapper: (fieldName) =>
    fieldName.replace("request.resource.attr.", ""),
});

If a field is not found in the object mapper, the original Cerbos path is used as-is.