Data di pubblicazione: 20 febbraio 2025
Scaricare in modo affidabile modelli di AI di grandi dimensioni è un'attività impegnativa. Se gli utenti perdono la connessione a internet o chiudono il tuo sito web o la tua applicazione web, perdono i file del modello scaricati parzialmente e devono ricominciare quando tornano alla tua pagina. Utilizzando l'API Background Fetch come miglioramento progressivo, puoi migliorare significativamente l'esperienza utente.
Registrare un service worker
L'API Background Fetch richiede che la tua app registri un service worker.
if ('serviceWorker' in navigator) {
window.addEventListener('load', async () => {
const registration = await navigator.serviceWorker.register('sw.js');
console.log('Service worker registered for scope', registration.scope);
});
}
Attivare un recupero dello sfondo
Man mano che il browser recupera i dati, mostra all'utente lo stato di avanzamento e gli offre un metodo per annullare il download. Al termine del download, il browser avvia il service worker e l'applicazione può intervenire con la risposta.
L'API Background Fetch può persino preparare il recupero per l'avvio in modalità offline. Non appena l'utente si riconnette, il download inizia. Se l'utente va offline, la procedura si interrompe finché l'utente non torna online.
Nell'esempio seguente, l'utente fa clic su un pulsante per scaricare Gemma 2B. Prima di recuperare, controlliamo se il modello è stato scaricato e memorizzato nella cache in precedenza, in modo da non utilizzare risorse non necessarie. Se non è memorizzato nella cache, avviamo il recupero in background.
const FETCH_ID = 'gemma-2b';
const MODEL_URL =
'https://storage.googleapis.com/jmstore/kaggleweb/grader/g-2b-it-gpu-int4.bin';
downloadButton.addEventListener('click', async (event) => {
// If the model is already downloaded, return it from the cache.
const modelAlreadyDownloaded = await caches.match(MODEL_URL);
if (modelAlreadyDownloaded) {
const modelBlob = await modelAlreadyDownloaded.blob();
// Do something with the model.
console.log(modelBlob);
return;
}
// The model still needs to be downloaded.
// Feature detection and fallback to classic `fetch()`.
if (!('BackgroundFetchManager' in self)) {
try {
const response = await fetch(MODEL_URL);
if (!response.ok || response.status !== 200) {
throw new Error(`Download failed ${MODEL_URL}`);
}
const modelBlob = await response.blob();
// Do something with the model.
console.log(modelBlob);
return;
} catch (err) {
console.error(err);
}
}
// The service worker registration.
const registration = await navigator.serviceWorker.ready;
// Check if there's already a background fetch running for the `FETCH_ID`.
let bgFetch = await registration.backgroundFetch.get(FETCH_ID);
// If not, start a background fetch.
if (!bgFetch) {
bgFetch = await registration.backgroundFetch.fetch(FETCH_ID, MODEL_URL, {
title: 'Gemma 2B model',
icons: [
{
src: 'icon.png',
size: '128x128',
type: 'image/png',
},
],
downloadTotal: await getResourceSize(MODEL_URL),
});
}
});
La funzione getResourceSize() restituisce le dimensioni in byte del download. Puoi
implementare questa operazione effettuando una
richiesta HEAD.
const getResourceSize = async (url) => {
try {
const response = await fetch(url, { method: 'HEAD' });
if (response.ok) {
return response.headers.get('Content-Length');
}
console.error(`HTTP error: ${response.status}`);
return 0;
} catch (error) {
console.error('Error fetching content size:', error);
return 0;
}
};
Avanzamento del download del report
Una volta avviato il recupero in background, il browser restituisce un
BackgroundFetchRegistration.
Puoi utilizzare questo evento per informare l'utente sullo stato di avanzamento del download.progress
bgFetch.addEventListener('progress', (e) => {
// There's no download progress yet.
if (!bgFetch.downloadTotal) {
return;
}
// Something went wrong.
if (bgFetch.failureReason) {
console.error(bgFetch.failureReason);
}
if (bgFetch.result === 'success') {
return;
}
// Update the user about progress.
console.log(`${bgFetch.downloaded} / ${bgFetch.downloadTotal}`);
});
Notifica agli utenti e al cliente il completamento del recupero
Quando il recupero in background va a buon fine, il service worker dell'app riceve un evento
backgroundfetchsuccess.
Il seguente codice è incluso nel service worker. La chiamata
updateUI()
quasi alla fine ti consente di aggiornare l'interfaccia del browser per comunicare all'utente
il recupero in background riuscito. Infine, informa il client
del download completato, ad esempio utilizzando
postMessage().
self.addEventListener('backgroundfetchsuccess', (event) => {
// Get the background fetch registration.
const bgFetch = event.registration;
event.waitUntil(
(async () => {
// Open a cache named 'downloads'.
const cache = await caches.open('downloads');
// Go over all records in the background fetch registration.
// (In the running example, there's just one record, but this way
// the code is future-proof.)
const records = await bgFetch.matchAll();
// Wait for the response(s) to be ready, then cache it/them.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
await Promise.all(promises);
// Update the browser UI.
event.updateUI({ title: 'Model downloaded' });
// Inform the clients that the model was downloaded.
self.clients.matchAll().then((clientList) => {
for (const client of clientList) {
client.postMessage({
message: 'download-complete',
id: bgFetch.id,
});
}
});
})(),
);
});
Ricevere messaggi dal service worker
Per ricevere il messaggio di conferma dell'avvenuto download sul client,
ascolta gli eventi
message. Una volta ricevuto il messaggio dal service worker, puoi lavorare con
il modello di AI e memorizzarlo con l'API Cache.
navigator.serviceWorker.addEventListener('message', async (event) => {
const cache = await caches.open('downloads');
const keys = await cache.keys();
for (const key of keys) {
const modelBlob = await cache
.match(key)
.then((response) => response.blob());
// Do something with the model.
console.log(modelBlob);
}
});
Annullare un recupero in background
Per consentire all'utente di annullare un download in corso, utilizza il metodo abort() di
BackgroundFetchRegistration.
const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
return;
}
await bgFetch.abort();
Memorizzare nella cache il modello
Memorizza nella cache i modelli scaricati, in modo che gli utenti scarichino il modello una sola volta. Sebbene l'API Background Fetch migliori l'esperienza di download, dovresti sempre cercare di utilizzare il modello più piccolo possibile nell'AI lato client.
Insieme, queste API ti aiutano a creare una migliore esperienza di AI lato client per i tuoi utenti.
Demo
Puoi vedere un'implementazione completa di questo approccio nella demo e nel relativo codice sorgente.
Ringraziamenti
Questa guida è stata esaminata da François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas e Alexandra Klepper.