Implementing Custom Cache Systems with the CacheInterface in Transformers.js
The Transformers.js library abstracts model-file caching behind a minimal CacheInterface that mirrors the Web Cache API, allowing you to override default storage behavior by setting env.useCustomCache = true and assigning a custom object to env.customCache that implements match(), put(), and optionally delete() methods.
When loading models from the Hugging Face Hub, Transformers.js automatically manages file caching to avoid redundant downloads. While the library defaults to the browser's Cache API or a filesystem-based FileCache, production deployments often require specialized persistence layers such as Redis, IndexedDB, or encrypted stores. By implementing the CacheInterface defined in src/utils/cache.js, you can intercept all cache operations and route them through your own backend.
Understanding the CacheInterface Architecture
The caching subsystem selects a backend according to a strict priority order defined in src/utils/cache.js. When the library initializes via getCache(), it checks:
- Custom cache — Used when
env.useCustomCacheis enabled andenv.customCachecontains a valid implementation. - Browser Cache API — Invoked via
caches.openwhen running in a browser environment. - File-system cache — Falls back to
FileCache(implemented insrc/utils/hub/files.js) for Node.js, Deno, or Bun when a writable filesystem is available.
The interface contract requires only two mandatory methods and one optional:
async match(request): Accepts a string URL and returns aResponse(orundefinedif not cached).async put(request, response, progress_callback?): Stores the response, optionally reporting download progress.async delete?(request): (Optional) Removes an entry, enabling full cache-clearing support viasrc/utils/model_registry/clear_cache.js.
Enabling Custom Cache Mode
Before the library loads any model files, you must flag the environment to use a custom backend. This must be set before importing models or calling pipeline() to ensure getCache() selects your implementation.
import { env } from '@huggingface/transformers';
// Enable custom cache lookup
env.useCustomCache = true;
Implementing a Custom Cache
Your custom cache must be an object or class instance that conforms to the CacheInterface. Below is a complete in-memory implementation using JavaScript Maps, suitable for testing or ephemeral caching.
Minimal In-Memory Cache Example
// memoryCache.js
class MemoryCache {
constructor() {
this.store = new Map(); // key → {body, headers, status}
}
async match(request) {
const entry = this.store.get(request);
if (!entry) return undefined;
// Reconstruct a Web API compatible Response
return new Response(entry.body, {
status: entry.status,
headers: entry.headers,
});
}
async put(request, response, progress_callback) {
// Capture response body as ArrayBuffer
const body = await response.arrayBuffer();
// Preserve metadata
const headers = {};
response.headers.forEach((v, k) => (headers[k] = v));
this.store.set(request, { body, headers, status: response.status });
// Report completion
progress_callback?.({
progress: 100,
loaded: body.byteLength,
total: body.byteLength
});
}
async delete(request) {
return this.store.delete(request);
}
}
export const customCache = new MemoryCache();
Wiring the Custom Cache
After defining your implementation, attach it to the environment object. All subsequent calls to getCache() throughout src/utils/hub.js and the model registry will now delegate to your object.
import { env } from '@huggingface/transformers';
import { customCache } from './memoryCache.js';
env.useCustomCache = true;
env.customCache = customCache; // Must follow the enable flag
// Subsequent model loads use your cache
import { pipeline } from '@huggingface/transformers';
const classifier = await pipeline('sentiment-analysis');
Advanced Custom Cache Patterns
Distributed Caching with Redis
For server-side deployments, you can implement the interface using Redis to share cached model files across multiple Node.js instances. Store binary data as base64 to ensure safe JSON serialization.
// redisCache.js
import { createClient } from 'redis';
class RedisCache {
constructor() {
this.client = createClient();
this.client.connect();
}
async match(request) {
const data = await this.client.get(request);
if (!data) return undefined;
const { body, status, headers } = JSON.parse(data);
const uint8 = Uint8Array.from(atob(body), c => c.charCodeAt(0));
return new Response(uint8.buffer, { status, headers });
}
async put(request, response, progress_callback) {
const body = await response.arrayBuffer();
const headers = {};
response.headers.forEach((v, k) => (headers[k] = v));
const payload = JSON.stringify({
body: Buffer.from(body).toString('base64'),
status: response.status,
headers,
});
await this.client.set(request, payload);
progress_callback?.({
progress: 100,
loaded: body.byteLength,
total: body.byteLength
});
}
async delete(request) {
const result = await this.client.del(request);
return result > 0;
}
}
export const redisCache = new RedisCache();
Persistent Browser Storage
For browser applications requiring survival across page reloads, implement match and put using IndexedDB or localStorage. Store the raw ArrayBuffer and reconstruct the Response object in match, mirroring the in-memory example but with asynchronous IndexedDB transactions.
How the Library Consumes Your Cache
Your custom implementation integrates seamlessly with the library's internal workflows:
- Model loading: Files in
src/utils/model_registry/get_file_metadata.jscallgetCache()and invokematch()to check for local copies before downloading. - Hub utilities:
src/utils/hub.jsbuilds standardized cache keys and passes your cache tocheckCachedResource, delegating storage to yourput()method during downloads. - Cache clearing: When
clear_cache.jsis invoked, it iterates through cached files and callsdelete()on your object if implemented, allowing full eviction support.
Summary
- Enable custom caching by setting
env.useCustomCache = truebefore any model imports. - Implement three methods:
match(request),put(request, response, progress_callback?), and optionallydelete(request)to conform to the CacheInterface insrc/utils/cache.js. - Attach your instance to
env.customCacheto override the default browser or filesystem backends. - Support cache clearing by implementing the optional
deletemethod, whichsrc/utils/model_registry/clear_cache.jsuses during cache invalidation. - Handle binary data by storing
ArrayBuffercontents and reconstructingResponseobjects inmatch().
Frequently Asked Questions
What happens if I don't implement the delete method?
The delete method is optional according to the CacheInterface definition in src/utils/cache.js. If omitted, cache-clearing operations initiated via the library's utilities will skip your custom store, leaving entries intact. Your cache will still function normally for reading and writing model files.
Can I use a custom cache in Node.js environments?
Yes. When running in Node.js, the library normally defaults to FileCache defined in src/utils/hub/files.js. By enabling env.useCustomCache and providing your own implementation, you can redirect caching to Redis, an S3 bucket, or any other storage backend, completely bypassing the filesystem.
How do I report download progress with a custom cache?
The put method receives a third argument, progress_callback, which you should invoke with an object containing progress, loaded, and total values. This feeds progress bars in UI components. If your backend supports streaming (e.g., S3 multipart uploads), you can call this callback multiple times to report incremental progress rather than a single 100% completion.
Does the custom cache replace the browser Cache API entirely?
When env.useCustomCache is true and env.customCache is set, the library uses your implementation exclusively for all cache operations. It will not fall back to the browser's caches.open or the filesystem FileCache for that session. Your object becomes the single source of truth for getCache() calls throughout src/utils/hub.js and the model registry.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →