Tracking Model Loading Progress with the `progress_callback` Option in Transformers.js
The progress_callback option in Transformers.js enables real-time tracking of model file downloads through a type-safe callback that receives granular ProgressInfo events including per-file bytes loaded, total size, and overall completion percentage.
When loading ONNX models from the Hugging Face Hub using the huggingface/transformers.js library, developers can monitor download status in real-time by supplying a progress_callback function. This mechanism provides fine-grained visibility into the loading pipeline, reporting progress for individual files like model.onnx and tokenizer.json as well as aggregate completion percentages. The implementation leverages a discriminated union type system defined in the core utilities to ensure type safety across both browser and Node.js environments.
How the Progress Callback Architecture Works
Source Files and Type Definitions
The callback system originates in src/utils/core.js, where the ProgressCallback type is defined as a function receiving a ProgressInfo discriminated union (lines 70-78). This union includes status variants: initiate, download, progress, done, ready, and progress_total. The dispatchCallback helper (lines 89-91) serves as the central dispatcher that invokes the callback when present.
In src/utils/hub.js, the PretrainedOptions interface includes the optional progress_callback property (lines 25-28). The high-level entry point getModelFile (lines 64-71) forwards this callback to lower-level loaders, while loadResourceFile orchestrates the actual fetching logic and emits per-file progress events.
The Event Flow from HTTP to Callback
When pipeline() initiates model loading, the flow proceeds through several stages. First, loadResourceFile checks the cache; on a miss, it fetches the remote URL. The readResponse function in src/utils/hub/utils.js (lines 84-106) streams the response body, calculating loaded bytes and percentage completion. Each chunk triggers a {status: 'progress', loaded, total, progress} event through dispatchCallback. Upon completion, a {status: 'done'} event fires.
For multi-file models, src/pipelines.js (lines 124-166) aggregates individual file progress into progress_total events, providing a unified percentage across all required assets.
Implementing progress_callback in Practice
Console Logging for Debugging
The simplest implementation logs per-file progress to the console. This approach captures both individual file downloads and the aggregate progress_total event.
import { pipeline } from '@huggingface/transformers';
function consoleProgress(info) {
if (info.status === 'progress') {
console.log(
`${info.name} – ${info.file}: ${info.progress.toFixed(1)}% (${info.loaded}/${info.total} bytes)`
);
} else if (info.status === 'progress_total') {
console.log(`Overall: ${info.progress.toFixed(1)}%`);
} else if (info.status === 'done') {
console.log(`${info.name} – ${info.file} loaded`);
}
}
const pipe = await pipeline(
'text-classification',
'Xenova/distilbert-base-uncased-finetuned-sst-2-english',
{ progress_callback: consoleProgress }
);
This callback receives events from loadResourceFile in hub.js (lines 84-90) and the aggregation logic in pipelines.js.
React Progress Bar Integration
For UI applications, the callback can drive progress indicators. The following React component maintains state for each file's percentage:
import React, { useState, useEffect } from 'react';
import { pipeline } from '@huggingface/transformers';
export default function ModelLoader() {
const [progress, setProgress] = useState({});
const onProgress = (info) => {
if (info.status === 'progress') {
setProgress((p) => ({
...p,
[info.file]: info.progress,
}));
}
};
useEffect(() => {
(async () => {
await pipeline('sentiment-analysis', 'Xenova/bert-base-uncased', {
progress_callback: onProgress,
});
})();
}, []);
return (
<div>
{Object.entries(progress).map(([file, pct]) => (
<div key={file}>
<p>{file}</p>
<div style={{ background: '#eee', width: '100%', height: '8px' }}>
<div
style={{
background: '#3b82f6',
width: `${pct}%`,
height: '100%',
}}
/>
</div>
</div>
))}
</div>
);
}
The per-file progress values originate from the readResponse streaming logic in hub/utils.js (lines 84-106), while the React state updates mirror patterns found in the official React tutorial documentation.
Web Worker Message Passing
To avoid blocking the main thread during large model downloads, forward progress events through a Web Worker:
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'load', model: 'Xenova/gpt2' });
worker.onmessage = (e) => {
if (e.data.status === 'progress_total') {
console.log(`Overall ${e.data.progress.toFixed(1)}%`);
}
};
// worker.js
import { pipeline } from '@huggingface/transformers';
self.onmessage = async (e) => {
if (e.data.type === 'load') {
await pipeline('text-generation', e.data.model, {
progress_callback: (info) => self.postMessage(info),
});
self.postMessage({ status: 'ready' });
}
};
This pattern utilizes the dispatchCallback helper in core.js (lines 89-91) to serialize ProgressInfo objects across thread boundaries.
Understanding Progress Event Types
Per-File vs. Aggregate Progress
The callback receives two distinct progress granularities. Per-file events (status: 'progress') originate from loadResourceFile and readResponse, reporting individual asset downloads with properties like name, file, loaded, and total. These events enable detailed per-file progress bars as shown in the React example.
Aggregate events (status: 'progress_total') are computed in src/pipelines.js (lines 124-166), combining multiple file downloads into a single percentage. This event includes a files map tracking each asset's byte count, providing a holistic view of model readiness.
Summary
- The
progress_callbackoption accepts a function conforming to theProgressCallbacktype defined insrc/utils/core.js(lines 70-78). - Per-file progress events stream from
readResponseinsrc/utils/hub/utils.js(lines 84-106), reporting byte-level download status. - Aggregate progress events (
progress_total) are generated insrc/pipelines.js(lines 124-166) to indicate overall loading completion. - The callback flows through
PretrainedOptionsinsrc/utils/hub.js(lines 25-28) and is dispatched viadispatchCallbackinsrc/utils/core.js(lines 89-91). - Implementation patterns range from simple console logging to complex UI bindings in React or Web Worker architectures.
Frequently Asked Questions
What parameters does the progress callback receive?
The callback receives a ProgressInfo object, a discriminated union with a status property indicating the event type. For progress events, it includes name (model ID), file (filename), loaded (bytes received), total (total bytes), and progress (percentage). For progress_total events, it provides aggregate loaded, total, and a files map containing per-file byte counts, as implemented in src/pipelines.js (lines 124-166).
Does the progress callback work with cached models?
Yes. When a model file is served from the cache, storeCachedResource in src/utils/hub.js (lines 96-108) re-wraps the callback to emit a final progress event showing 100% completion. This ensures consistent UI updates regardless of whether the data originates from the network or local storage.
Can I use progress_callback in Node.js environments?
Absolutely. The progress system relies on standard web APIs (fetch and Response.body streaming) that Transformers.js polyfills for Node.js. The readResponse implementation in src/utils/hub/utils.js handles both browser and Node.js streaming contexts, ensuring progress events fire correctly in either environment.
How do I track progress for specific files versus total loading?
Inspect the status property of the ProgressInfo object. When status equals 'progress', the event represents a single file download (e.g., model.onnx). When status equals 'progress_total', the event represents the aggregate progress across all model files, computed by the pipeline's aggregation logic in src/pipelines.js.
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 →