Run local reranking models directly in your JavaScript/TypeScript application.
In RAG and other retrieval workflows, returning accurate results is a trade off between search capabilities and speed. Embedding models and vector indexes are good at returning semantically similar results fast. Cross-encoding rerankers are much more accurate, but slower, because they compare the query with each document at ranking time.
This library uses @huggingface/transformers to make it easy to rerank documents against queries using local models.
You can see this library in action in a live reranking demo.
Install the library from npm.
npm install rerankersThis package uses @huggingface/transformers. Models are downloaded on first use and cached by Transformers.js.
import { Reranker } from "rerankers";
const query = "How can I reduce the initial load time of a JavaScript web application?";
const documents = [
{
id: "doc-1",
text: "Code splitting lets a JavaScript application load only the code needed for the current page. Dynamic imports and route-based chunks can significantly reduce the initial bundle size.",
},
{
id: "doc-2",
text: "JavaScript is a programming language commonly used to add interactive behaviour to websites and build server-side applications with Node.js.",
},
{
id: "doc-3",
text: "Compressing images, serving modern formats such as WebP or AVIF, and lazy-loading below-the-fold media can improve page load performance.",
},
{
id: "doc-4",
text: "Tree shaking removes unused exports from a production bundle. It works best with ES modules and can reduce the amount of JavaScript downloaded during the initial page load.",
},
{
id: "doc-5",
text: "A service worker can cache application assets after the first visit, making repeat visits faster and allowing some functionality to work offline.",
},
];
const reranker = await Reranker.create({ model: "mixedbread-ai/mxbai-rerank-base-v1" });
const results = await reranker.rank(query, documents, { topK: 3 });
console.log(results);
// => [
// {
// document: {
// id: "doc-1",
// text: "Code splitting lets a JavaScript application load only the code needed for the current page. Dynamic imports and route-based chunks can significantly reduce the initial bundle size."
// },
// index: 0,
// score: 0.9616439938545227
// },
// {
// document: {
// id: "doc-4",
// text: "Tree shaking removes unused exports from a production bundle. It works best with ES modules and can reduce the amount of JavaScript downloaded during the initial page load."
// },
// index: 3,
// score: 0.9472419619560242
// },
// {
// document: {
// id: "doc-5",
// text: "A service worker can cache application assets after the first visit, making repeat visits faster and allowing some functionality to work offline."
// },
// index: 4,
// score: 0.4266827702522278
// }
// ]You can just pass a Hugging Face model ID and default options will be used to load the model. Specifically, the dtype will be set to auto.
const reranker = await Reranker.create({
model: "mixedbread-ai/mxbai-rerank-large-v1",
});If you want to pass more options, such as a specific dtype or device, you can pass a transformerOptions property like this:
const reranker = await Reranker.create({
model: "mixedbread-ai/mxbai-rerank-large-v1",
transformerOptions: {
device: "wasm",
dtype: "q8",
},
});For more on dtypes or device options check the Transformers.js documentation.
These models have been tested and work with rerankers. Other reranking models with ONNX weights should also work.
| Model | Parameters | Languages | Context |
|---|---|---|---|
Xenova/ms-marco-TinyBERT-L-2-v2 |
4.4M | English | 512 |
Xenova/ms-marco-MiniLM-L-2-v2 |
15.6M | English | 512 |
Xenova/ms-marco-MiniLM-L-4-v2 |
19.2M | English | 512 |
Xenova/ms-marco-MiniLM-L-6-v2 |
22.7M | English | 512 |
Xenova/ms-marco-MiniLM-L-12-v2 |
33.4M | English | 512 |
jinaai/jina-reranker-v1-tiny-en |
33.0M | English | 8,192 |
jinaai/jina-reranker-v1-turbo-en |
37.8M | English | 8,192 |
mixedbread-ai/mxbai-rerank-xsmall-v1 |
70.8M | English | 512 |
mixedbread-ai/mxbai-rerank-base-v1 |
184M | English | 512 |
Xenova/bge-reranker-base |
~278M | Chinese and English | 512 |
mixedbread-ai/mxbai-rerank-large-v1 |
435M | English | 512 |
Xenova/bge-reranker-large |
~560M | Chinese and English | 512 |
onnx-community/bge-reranker-v2-m3-ONNX |
568M | Multilingual | 8,192 |
Documents can be strings or objects. Object documents are returned unchanged.
const results = await reranker.rank("red planet", [
{ id: "venus", text: "Venus is hot.", metadata: { source: "encyclopedia" } },
{ id: "mars", text: "Mars is called the Red Planet.", metadata: { source: "encyclopedia" } },
]);Each result includes:
type RerankResult<TDocument> = {
document: TDocument;
index: number;
score: number;
};A reranker keeps its model loaded so it can be reused across calls. Models can be large and occupy a significant amount of memory. When you no longer need it, call dispose() to release the model's inference sessions and regain the memory:
const reranker = await Reranker.create({
model: "mixedbread-ai/mxbai-rerank-base-v1",
});
try {
const results = await reranker.rank(query, documents);
} finally {
await reranker.dispose();
}Reranker also implements AsyncDisposable, so runtimes that support explicit resource management can dispose it automatically:
await using reranker = await Reranker.create({
model: "mixedbread-ai/mxbai-rerank-base-v1",
});
const results = await reranker.rank(query, documents);Disposal waits for active ranking calls to finish and is safe to call more than once. A disposed reranker cannot be used again, trying to do so will throw a RerankerDisposedError.
Install LangChain core alongside this package:
npm install rerankers @langchain/coreUse the LocalReranker adapter from the rerankers/langchain subpath anywhere LangChain expects a document compressor.
import { LocalReranker } from "rerankers/langchain";
import { Document } from "@langchain/core/documents";
// Using the documents from the first example
const lcDocs = documents.map(
(doc) =>
new Document({
pageContent: doc.text,
metadata: { id: doc.id },
}),
);
const reranker = new LocalReranker({
model: "mixedbread-ai/mxbai-rerank-base-v1",
topK: 5,
});
try {
const documents = await reranker.compressDocuments(lcDocs, query);
} finally {
await reranker.dispose();
}Returned LangChain documents are the original document objects in reranked order. Each returned document receives metadata.relevanceScore.
You can pass transformerOptions in the same initializer object
const reranker = new LocalReranker({
model: "mixedbread-ai/mxbai-rerank-base-v1",
transformerOptions: {
dtype: "q8",
device: "gpu",
},
topK: 5,
});For full lifecycle control, pass an already-created reranker:
import { Reranker } from "rerankers";
const coreReranker = await Reranker.create({ model: "mixedbread-ai/mxbai-rerank-base-v1" });
const reranker = new LocalReranker({ reranker: coreReranker, topK: 5 });LocalReranker.dispose() delegates to its core reranker, including when the core reranker was injected. LocalReranker also supports await using through Symbol.asyncDispose.
Use rerank() when you want raw scores without mutating LangChain document metadata:
const results = await reranker.rerank(documents, "red planet", { topK: 5 });
// [{ index: 1, relevanceScore: 0.92 }, ...]Install the AI SDK alongside this package, then use the rerankers/ai-sdk adapter anywhere AI SDK expects a reranking model.
npm install rerankers aiimport { rerank } from "ai";
import { rerankers } from "rerankers/ai-sdk";
await using model = rerankers.rerankingModel({
model: "mixedbread-ai/mxbai-rerank-base-v1",
});
const { ranking, rerankedDocuments } = await rerank({
model,
query: "Which document mentions Mars?",
documents: [
"Venus has a thick atmosphere.",
"Mars is called the Red Planet.",
"Jupiter is the largest planet.",
],
topN: 2,
});The returned model owns its local reranker and supports both dispose() and Symbol.asyncDispose for releasing it.
AI SDK object documents are supported with a text extractor. If you do not provide one, the adapter uses a string text property when present and falls back to JSON.stringify(document).
const model = rerankers.rerankingModel(
{ model: "Xenova/bge-reranker-base" },
{
documentText: (document) => `${document.title}: ${document.body}`,
},
);The package is ESM and keeps the runtime path compatible with browsers and Node.js. Use Transformers.js options such as device and dtype to tune runtime behavior.
MIT