Transformers.js Adapter
Local Hugging Face Transformers.js ONNX embedding adapter for 100% offline semantic recall in MemoFS.
The @memofs/adapter-transformers adapter runs vector embeddings 100% locally and offline in-process using ONNX runtime and Hugging Face's Transformers.js (@huggingface/transformers).
It requires no API keys, no external services, and zero network traffic after the initial model weights are downloaded and cached in a shared user-level cache ($XDG_CACHE_HOME/memofs/models, or ~/.cache/memofs/models) — one download per machine, shared across projects and runtimes.
Subpath Exports
| Export Path | Target Environment | Description |
|---|---|---|
@memofs/adapter-transformers | Node.js (>= 22) | Root entry. Exposes TransformersEmbedder, createTransformersEmbedder, resolveModelCacheDir, error classes, and pipeline types. |
@memofs/adapter-transformers/testing | Test runners | Exposes createFakePipeline and createFakePipelineFactory for deterministic testing without loading ONNX weights. |
Installation
npm install @memofs/adapter-transformersRequires Node.js >= 22 when running under the Node.js runtime.
Usage
Instantiate the local embedder with createTransformersEmbedder():
import { createNodeMemoFs } from "@memofs/core/node-fs";
import { createTransformersEmbedder } from "@memofs/adapter-transformers";
const memo = createNodeMemoFs({
rootDir: ".",
embedder: createTransformersEmbedder({
model: "Xenova/bge-small-en-v1.5", // 384 dimensions
device: "cpu",
dtype: "q8",
}),
});import { MemoFS } from "@memofs/core";
import { createNodeFsMemoryStore } from "@memofs/core/node-fs";
import { createTransformersEmbedder } from "@memofs/adapter-transformers";
const memo = new MemoFS({
store: createNodeFsMemoryStore({ rootDir: "." }),
projectId: "offline-app",
mode: "local",
embedder: createTransformersEmbedder({
model: "Xenova/bge-small-en-v1.5",
device: "cpu",
dtype: "q8",
}),
});Lazy Loading & Eager Prewarming
To ensure fast CLI and server startup, @memofs/adapter-transformers defers loading the heavy ONNX WebAssembly binary and model weights until the first embedTexts() call.
If your application requires deterministic latency on the first search request, you can explicitly prewarm the model pipeline at startup:
const embedder = createTransformersEmbedder({
model: "Xenova/bge-small-en-v1.5",
});
// Eagerly compile the ONNX pipeline and load weights into memory
await embedder.prewarm();Model Cache
Downloaded weights live in one shared, user-level directory resolved by the exported resolveModelCacheDir():
$XDG_CACHE_HOME/memofs/modelswhenXDG_CACHE_HOMEis set to a non-empty value~/.cache/memofs/modelsotherwise
MemoFS runs from several working directories over the lifetime of a machine (memofs init from a project root, an MCP server launched by an IDE, test runners), and Transformers.js's default cache is relative to the process working directory — without an explicit shared path, each directory would re-download the same weights. The shared cache means the model that memofs init predownloads is the exact one an MCP server finds on connect. Pass an explicit cacheDir to createTransformersEmbedder() to override the location.
Configuration API (TransformersEmbedderOptions)
The createTransformersEmbedder(options) factory accepts TransformersEmbedderOptions:
| Option | Type | Default | Description |
|---|---|---|---|
model | string | "Xenova/bge-small-en-v1.5" | Hugging Face model repository identifier. Generates 384-dimensional vectors. |
cacheDir | string | resolveModelCacheDir() | Directory used to cache downloaded ONNX weights. Defaults to a shared user-level cache so weights download once per machine. |
device | "cpu" | "gpu" | "wasm" | "cpu" | Hardware backend target for the ONNX execution engine. |
dtype | "fp32" | "fp16" | "q8" | "int8" | "q8" | Tensor precision format for model weights. "q8" (quantized) is ~4x smaller to download and faster on CPU with negligible retrieval-quality loss; "fp32" is the most conservative choice. |
batchSize | number | 32 | Maximum number of text chunks processed in a single pipeline pass. |
retries | number | 2 | Maximum retry attempts when the initial model download/load fails with a transient network error. Set to 0 to disable. |
onProgress | TransformersProgressCallback | — | Callback invoked during model download with { status, file, progress }. Use it to show a one-time "warming up" notice. |
pipelineFactory | FeatureExtractionPipelineFactory | — | Custom pipeline factory function (primarily used in testing). |
Error Classes
All errors inherit from TransformersEmbedderError:
class TransformersEmbedderError extends Error {
readonly cause?: unknown;
}| Error Class | Cause |
|---|---|
TransformersValidationError | Thrown when an input text string exceeds MAX_TEXT_LENGTH = 8192 characters or input structure is malformed. |
TransformersInferenceError | Thrown when the ONNX runtime fails during forward inference or returns an unexpected tensor shape. |
Unit Testing with Fake Pipeline
Use the @memofs/adapter-transformers/testing subpath to test embeddings logic without downloading or running ONNX models:
import { describe, it, expect } from "vitest";
import { TransformersEmbedder } from "@memofs/adapter-transformers";
import { createFakePipelineFactory } from "@memofs/adapter-transformers/testing";
describe("Local transformer pipeline", () => {
it("generates deterministic embeddings with fake pipeline", async () => {
const pipelineFactory = createFakePipelineFactory({
dimensions: 384,
deterministic: true,
});
const embedder = new TransformersEmbedder({
pipelineFactory,
});
const result = await embedder.embedText("Refactor database pooling");
expect(result.dimensions).toBe(384);
expect(result.embedding).toHaveLength(384);
});
});