Background Fetch API ile yapay zeka modellerini indirme

Yayınlanma tarihi: 20 Şubat 2025

Büyük yapay zeka modellerini güvenilir bir şekilde indirmek zor bir iştir. Kullanıcılar internet bağlantılarını kaybederse veya web sitenizi ya da web uygulamanızı kapatırsa kısmen indirilen model dosyalarını kaybeder ve sayfanıza geri döndüklerinde baştan başlamak zorunda kalırlar. Background Fetch API'yi aşamalı bir iyileştirme olarak kullanarak kullanıcı deneyimini önemli ölçüde iyileştirebilirsiniz.

Browser Support

  • Chrome: 74.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

Hizmet çalışanı kaydetme

Arka Planda Getirme API'si, uygulamanızın bir Service Worker kaydettirmesini gerektirir.

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);
  });
}

Arka planda getirmeyi tetikleme

Tarayıcı, verileri getirirken kullanıcıya ilerleme durumunu gösterir ve indirme işlemini iptal etmeleri için bir yöntem sunar. İndirme işlemi tamamlandığında tarayıcı servis çalışanını başlatır ve uygulama, yanıtla işlem yapabilir.

Arka Planda Getirme API'si, getirme işlemini çevrimdışıyken başlatılacak şekilde bile hazırlayabilir. Kullanıcı yeniden bağlanır bağlanmaz indirme işlemi başlar. Kullanıcı çevrimdışı olursa işlem, kullanıcı tekrar çevrimdışı olana kadar duraklatılır.

Aşağıdaki örnekte, kullanıcı Gemma 2B'yi indirmek için bir düğmeyi tıklar. Göndermeden önce, modelin daha önce indirilip önbelleğe alınıp alınmadığını kontrol ederiz. Böylece gereksiz kaynakları kullanmayız. Önbelleğe alınmamışsa arka planda getirme işlemini başlatırız.

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),
    });
  }
});

getResourceSize() işlevi, indirme işleminin bayt boyutunu döndürür. Bunu HEAD isteği göndererek uygulayabilirsiniz.

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;
  }
};

Rapor indirme işleminin ilerleme durumu

Arka planda getirme işlemi başladığında tarayıcı bir BackgroundFetchRegistration döndürür. progress etkinliğini kullanarak kullanıcıyı indirme işleminin ilerleme durumu hakkında bilgilendirebilirsiniz.

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}`);
});

Kullanıcıları ve istemciyi getirme işleminin tamamlandığı konusunda bilgilendirme

Arka planda getirme işlemi başarılı olduğunda uygulamanızın servis çalışanı bir backgroundfetchsuccess etkinliği alır.

Aşağıdaki kod, hizmet işleyiciye dahil edilmiştir. Sonlara doğru yapılan updateUI() çağrısı, kullanıcıyı arka planda başarılı bir şekilde getirilen veri hakkında bilgilendirmek için tarayıcının arayüzünü güncellemenize olanak tanır. Son olarak, indirme işleminin tamamlandığı hakkında müşteriyi bilgilendirin (ör. postMessage() kullanarak).

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,
          });
        }
      });
    })(),
  );
});

Hizmet çalışanından mesaj alma

İndirme işleminin tamamlanmasıyla ilgili gönderilen başarı mesajını istemcide almak için message etkinliklerini dinleyin. Hizmet çalışanından mesajı aldıktan sonra yapay zeka modeliyle çalışabilir ve Cache API ile saklayabilirsiniz.

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);
  }
});

Arka planda getirme işlemini iptal etme

Kullanıcının devam eden bir indirme işlemini iptal etmesine izin vermek için BackgroundFetchRegistration sınıfının abort() yöntemini kullanın.

const registration = await navigator.serviceWorker.ready;
const bgFetch = await registration.backgroundFetch.get(FETCH_ID);
if (!bgFetch) {
  return;
}
await bgFetch.abort();

Modeli önbelleğe alma

Kullanıcılarınızın modeli yalnızca bir kez indirmesi için indirilen modelleri önbelleğe alın. Arka Planda Getirme API'si indirme deneyimini iyileştirse de istemci tarafı yapay zekada her zaman mümkün olan en küçük modeli kullanmayı hedeflemeniz gerekir.

Bu API'ler birlikte, kullanıcılarınıza daha iyi bir istemci tarafı yapay zeka deneyimi sunmanıza yardımcı olur.

Demo

Bu yaklaşımın tam bir uygulamasını demo ve kaynak kodunda görebilirsiniz.

Arka Planda Getirme indirme işleminin açık olduğu Chrome Geliştirici Araçları Uygulama paneli.
Chrome Geliştirici Araçları'nı kullanarak devam eden arka planda getirme işlemiyle ilgili etkinlikleri önizleyebilirsiniz. Demoda, toplam 1,26 gigabaytlık bir indirme işleminin 17,54 megabaytlık kısmının tamamlandığı gösterilmektedir. Devam eden indirme işlemi, tarayıcının indirme göstergesinde de gösterilir.

Teşekkür ederiz

Bu kılavuz, François Beaufort, Andre Bandarra, Sebastian Benz, Maud Nalpas ve Alexandra Klepper tarafından incelendi.