دانلود مدل های هوش مصنوعی با Background Fetch API

منتشر شده: ۲۰ فوریه ۲۰۲۵

دانلود قابل اعتماد مدل‌های بزرگ هوش مصنوعی یک کار چالش برانگیز است. اگر کاربران اتصال اینترنت خود را از دست بدهند یا وب‌سایت یا برنامه وب شما را ببندند، فایل‌های مدل نیمه دانلود شده را از دست می‌دهند و در بازگشت به صفحه شما باید دوباره از ابتدا شروع کنند. با استفاده از Background Fetch API به عنوان یک پیشرفت تدریجی، می‌توانید تجربه کاربری را به میزان قابل توجهی بهبود بخشید.

Browser Support

  • کروم: ۷۴.
  • لبه: ۷۹.
  • فایرفاکس: پشتیبانی نمی‌شود.
  • سافاری: پشتیبانی نمی‌شود.

Source

ثبت نام یک کارگر خدماتی

API مربوط به Background Fetch از برنامه شما می‌خواهد که یک 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);
  });
}

فعال کردن واکشی پس‌زمینه

همزمان با دریافت فایل توسط مرورگر، پیشرفت آن به کاربر نمایش داده می‌شود و روشی برای لغو دانلود به او داده می‌شود. پس از اتمام دانلود، مرورگر سرویس ورکر را اجرا می‌کند و برنامه می‌تواند با پاسخ دریافتی، اقدامات لازم را انجام دهد.

API مربوط به Background Fetch حتی می‌تواند فرآیند واکشی را برای شروع در حالت آفلاین آماده کند. به محض اینکه کاربر دوباره متصل شود، دانلود شروع می‌شود. اگر کاربر آفلاین شود، فرآیند تا زمانی که دوباره آنلاین شود، متوقف می‌شود.

در مثال زیر، کاربر برای دانلود Gemma 2B روی دکمه‌ای کلیک می‌کند. قبل از واکشی، بررسی می‌کنیم که آیا مدل قبلاً دانلود و ذخیره شده است یا خیر، تا از منابع غیرضروری استفاده نکنیم. اگر ذخیره نشده باشد، واکشی پس‌زمینه را شروع می‌کنیم.

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() اندازه بایت دانلود را برمی‌گرداند. می‌توانید این کار را با ارسال یک درخواست 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;
  }
};

گزارش پیشرفت دانلود

به محض اینکه فرآیند واکشی پس‌زمینه آغاز شود، مرورگر یک BackgroundFetchRegistration برمی‌گرداند. می‌توانید از این مورد برای اطلاع‌رسانی به کاربر در مورد پیشرفت دانلود، با استفاده از رویداد 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}`);
});

کاربران و کلاینت را از اتمام واکشی مطلع کنید

وقتی واکشی پس‌زمینه با موفقیت انجام شود، سرویس ورکر برنامه شما یک رویداد backgroundfetchsuccess دریافت می‌کند.

کد زیر در سرویس ورکر گنجانده شده است. فراخوانی updateUI() در نزدیکی انتها به شما امکان می‌دهد رابط مرورگر را به‌روزرسانی کنید تا کاربر را از دریافت موفقیت‌آمیز پس‌زمینه مطلع کنید. در نهایت، به عنوان مثال، با استفاده از 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,
          });
        }
      });
    })(),
  );
});

دریافت پیام از سرویس ورکر

برای دریافت پیام موفقیت‌آمیز ارسال شده در مورد تکمیل دانلود در کلاینت، به رویدادهای message گوش دهید. پس از دریافت پیام از سرویس ورکر، می‌توانید با مدل هوش مصنوعی کار کنید و آن را با Cache API ذخیره کنید.

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

لغو واکشی پس‌زمینه

برای اینکه به کاربر اجازه دهید دانلود در حال انجام را لغو کند، از متد abort() از BackgroundFetchRegistration استفاده کنید.

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

مدل را ذخیره کنید

مدل‌های دانلود شده را ذخیره کنید ، تا کاربران شما فقط یک بار مدل را دانلود کنند. در حالی که Background Fetch API تجربه دانلود را بهبود می‌بخشد، شما همیشه باید هدف خود را استفاده از کوچکترین مدل ممکن در هوش مصنوعی سمت کلاینت قرار دهید.

این APIها در کنار هم به شما کمک می‌کنند تا یک تجربه هوش مصنوعی سمت کلاینت بهتر برای کاربران خود ایجاد کنید.

نسخه آزمایشی

می‌توانید پیاده‌سازی کامل این رویکرد را در دمو و کد منبع آن مشاهده کنید.

پنل برنامه Chrome DevTools برای دانلود Background Fetch باز است.
با استفاده از Chrome DevTools، می‌توانید رویدادهای مربوط به واکشی مداوم در پس‌زمینه را پیش‌نمایش کنید. این نسخه آزمایشی، دانلودی در حال انجام را نشان می‌دهد که ۱۷.۵۴ مگابایت حجم دارد و در مجموع ۱.۲۶ گیگابایت است. نشانگر دانلود مرورگر نیز دانلود در حال انجام را نشان می‌دهد.

تقدیرنامه‌ها

این راهنما توسط فرانسوا بوفور ، آندره باندارا ، سباستین بنز ، مود نالپاس و الکساندرا کلپر بررسی شده است.