ब्राउज़र में कैश एआई मॉडल को कैश मेमोरी में सेव करना

ज़्यादातर एआई मॉडल में एक बात कॉमन होती है: इंटरनेट पर ट्रांसफ़र किए जाने वाले संसाधन के लिए, वे काफ़ी बड़े होते हैं. सबसे छोटे MediaPipe ऑब्जेक्ट डिटेक्शन मॉडल (SSD MobileNetV2 float16) का वज़न 5.6 एमबी है और सबसे बड़े मॉडल का साइज़ 25 एमबी है.

ओपन-सोर्स एलएलएम का साइज़ 1.35 जीबी है, जो एलएलएम के लिए बहुत कम माना जाता है. जनरेटिव एआई मॉडल का इस्तेमाल कई तरह से किया जा सकता है. इसलिए, एआई का ज़्यादातर इस्तेमाल क्लाउड में किया जाता है. ऐप्लिकेशन में, सीधे तौर पर डिवाइस पर ही बहुत ज़्यादा ऑप्टिमाइज़ किए गए मॉडल इस्तेमाल किए जा रहे हैं. ब्राउज़र में चल रहे एलएलएम के डेमो मौजूद हैं. हालांकि, यहां ब्राउज़र में चल रहे अन्य मॉडल के कुछ प्रोडक्शन-ग्रेड उदाहरण दिए गए हैं:

वेब पर Adobe Photoshop में, एआई की मदद से ऑब्जेक्ट चुनने वाला टूल खुला है. इसमें तीन ऑब्जेक्ट चुने गए हैं: दो जिराफ़ और एक चाँद.

आने वाले समय में अपने ऐप्लिकेशन को तेज़ी से लॉन्च करने के लिए, आपको डिवाइस पर मॉडल डेटा को साफ़ तौर पर कैश मेमोरी में सेव करना चाहिए. इसके लिए, एचटीटीपी ब्राउज़र के कैश मेमोरी पर निर्भर न रहें.

इस गाइड में चैटबॉट बनाने के लिए gemma-2b-it-gpu-int4.bin model का इस्तेमाल किया गया है. हालांकि, इस तरीके को डिवाइस पर मौजूद अन्य मॉडल और इस्तेमाल के अन्य उदाहरणों के हिसाब से बनाया जा सकता है. किसी ऐप्लिकेशन को मॉडल से कनेक्ट करने का सबसे आम तरीका यह है कि ऐप्लिकेशन के बाकी संसाधनों के साथ-साथ मॉडल को भी दिखाया जाए. डिलीवरी को ऑप्टिमाइज़ करना ज़रूरी है.

सही कैश मेमोरी हेडर कॉन्फ़िगर करना

अगर आपके सर्वर से एआई मॉडल दिखाए जाते हैं, तो सही Cache-Control हेडर कॉन्फ़िगर करना ज़रूरी है. यहां दिए गए उदाहरण में एक अच्छी डिफ़ॉल्ट सेटिंग दिखाई गई है. इसे अपने ऐप्लिकेशन की ज़रूरतों के हिसाब से बनाया जा सकता है.

Cache-Control: public, max-age=31536000, immutable

एआई मॉडल का रिलीज़ किया गया हर वर्शन, स्टैटिक रिसॉर्स होता है. ऐसे कॉन्टेंट के लिए, अनुरोध यूआरएल में max-age का लंबा वर्शन और कैश मेमोरी को हटाने की सुविधा का इस्तेमाल करें जो कभी नहीं बदलता. अगर आपको मॉडल को अपडेट करना है, तो आपको उसे एक नया यूआरएल देना होगा.

जब उपयोगकर्ता पेज को फिर से लोड करता है, तो क्लाइंट फिर से पुष्टि करने का अनुरोध भेजता है. भले ही, सर्वर को पता हो कि कॉन्टेंट स्थिर है. immutable के डायरेक्टिव में साफ़ तौर पर बताया गया है कि दोबारा पुष्टि करना ज़रूरी नहीं है, क्योंकि कॉन्टेंट में कोई बदलाव नहीं होगा. ब्राउज़र और इंटरमीडियरी कैश या प्रॉक्सी सर्वर, immutable डायरेक्टिव के साथ ज़्यादातर काम नहीं करते. हालांकि, इसे max-age डायरेक्टिव के साथ जोड़कर, ज़्यादा से ज़्यादा काम करने की सुविधा मिल सकती है. max-age डायरेक्टिव को सभी ब्राउज़र समझते हैं. public रिस्पॉन्स डायरेक्टिव से पता चलता है कि रिस्पॉन्स को शेयर किए गए कैश मेमोरी में सेव किया जा सकता है.

एआई मॉडल का अनुरोध करने पर, Chrome DevTools में Hugging Face से भेजे गए प्रॉडक्शन Cache-Control हेडर दिखते हैं. (सोर्स)

क्लाइंट-साइड पर एआई मॉडल कैश मेमोरी में सेव करना

एआई मॉडल दिखाते समय, यह ज़रूरी है कि मॉडल को ब्राउज़र में साफ़ तौर पर कैश मेमोरी में सेव किया जाए. इससे यह पक्का होता है कि उपयोगकर्ता के ऐप्लिकेशन को फिर से लोड करने के बाद, मॉडल का डेटा तुरंत उपलब्ध हो.

ऐसा करने के लिए, कई तकनीकों का इस्तेमाल किया जा सकता है. यहां दिए गए कोड सैंपल के लिए, मान लें कि हर मॉडल फ़ाइल को मेमोरी में blob नाम वाले Blob ऑब्जेक्ट में सेव किया गया है.

परफ़ॉर्मेंस को समझने के लिए, हर कोड सैंपल को performance.mark() और performance.measure() तरीकों के साथ एनोटेट किया जाता है. ये उपाय, डिवाइस पर निर्भर करते हैं और इन्हें सभी डिवाइसों पर लागू नहीं किया जा सकता.

Chrome DevTools ऐप्लिकेशन > स्टोरेज में, IndexedDB, कैश स्टोरेज, और फ़ाइल सिस्टम के लिए सेगमेंट के साथ, इस्तेमाल का डायग्राम देखें. हर सेगमेंट में 1,354 मेगाबाइट डेटा का इस्तेमाल किया गया है. कुल 4,063 मेगाबाइट डेटा का इस्तेमाल किया गया है.

ब्राउज़र में एआई मॉडल को कैश मेमोरी में सेव करने के लिए, इनमें से किसी एक एपीआई का इस्तेमाल किया जा सकता है: Cache API, Origin Private File System API, और IndexedDB API. सामान्य तौर पर, Cache API का इस्तेमाल करने का सुझाव दिया जाता है. हालांकि, इस गाइड में सभी विकल्पों के फ़ायदों और नुकसानों के बारे में बताया गया है.

Cache API

Cache API, Request और Response ऑब्जेक्ट के ऐसे पेयर के लिए, स्टोरेज की सुविधा देता है जिन्हें लंबे समय तक चलने वाली मेमोरी में कैश मेमोरी के तौर पर सेव किया जाता है. हालांकि, इसे सेवा वर्कर के स्पेसिफ़िकेशन में बताया गया है, लेकिन इस एपीआई का इस्तेमाल मुख्य थ्रेड या सामान्य वर्कर से किया जा सकता है. इसे सेवा वर्कर कॉन्टेक्स्ट के बाहर इस्तेमाल करने के लिए, Request ऑब्जेक्ट के बजाय सिंथेटिक यूआरएल के साथ जोड़े गए सिंथेटिक Response ऑब्जेक्ट के साथ, Cache.put() तरीके को कॉल करें.

इस गाइड में, मेमोरी में सेव blob का इस्तेमाल किया गया है. कैश मेमोरी कुंजी के तौर पर किसी फ़र्ज़ी यूआरएल और blob के आधार पर बनाए गए Response का इस्तेमाल करें. अगर आपको मॉडल को सीधे डाउनलोड करना है, तो आपको fetch() अनुरोध करने पर मिलने वाले Response का इस्तेमाल करना होगा.

उदाहरण के लिए, Cache API की मदद से मॉडल फ़ाइल को सेव और वापस लाने का तरीका यहां बताया गया है.

const storeFileInSWCache = async (blob) => {
  try {
    performance.mark('start-sw-cache-cache');
    const modelCache = await caches.open('models');
    await modelCache.put('model.bin', new Response(blob));
    performance.mark('end-sw-cache-cache');

    const mark = performance.measure(
      'sw-cache-cache',
      'start-sw-cache-cache',
      'end-sw-cache-cache'
    );
    console.log('Model file cached in sw-cache.', mark.name, mark.duration.toFixed(2));
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const restoreFileFromSWCache = async () => {
  try {
    performance.mark('start-sw-cache-restore');
    const modelCache = await caches.open('models');
    const response = await modelCache.match('model.bin');
    if (!response) {
      throw new Error(`File model.bin not found in sw-cache.`);
    }
    const file = await response.blob();
    performance.mark('end-sw-cache-restore');
    const mark = performance.measure(
      'sw-cache-restore',
      'start-sw-cache-restore',
      'end-sw-cache-restore'
    );
    console.log(mark.name, mark.duration.toFixed(2));
    console.log('Cached model file found in sw-cache.');
    return file;
  } catch (err) {    
    throw err;
  }
};

Origin Private File System API

Origin Private File System (OPFS), स्टोरेज एंडपॉइंट के लिए, तुलनात्मक तौर पर नया स्टैंडर्ड है. यह पेज के ऑरिजिन के लिए निजी होता है. इसलिए, यह उपयोगकर्ता को नहीं दिखता. यह सामान्य फ़ाइल सिस्टम से अलग होता है. यह एक खास फ़ाइल का ऐक्सेस देता है, जो परफ़ॉर्मेंस के लिए काफ़ी ऑप्टिमाइज़ की गई होती है. साथ ही, इस फ़ाइल के कॉन्टेंट को लिखने का ऐक्सेस भी देता है.

उदाहरण के लिए, यहां OPFS में मॉडल फ़ाइल को सेव और वापस लाने का तरीका बताया गया है.

const storeFileInOPFS = async (blob) => {
  try {
    performance.mark('start-opfs-cache');
    const root = await navigator.storage.getDirectory();
    const handle = await root.getFileHandle('model.bin', { create: true });
    const writable = await handle.createWritable();
    await blob.stream().pipeTo(writable);
    performance.mark('end-opfs-cache');
    const mark = performance.measure(
      'opfs-cache',
      'start-opfs-cache',
      'end-opfs-cache'
    );
    console.log('Model file cached in OPFS.', mark.name, mark.duration.toFixed(2));
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const restoreFileFromOPFS = async () => {
  try {
    performance.mark('start-opfs-restore');
    const root = await navigator.storage.getDirectory();
    const handle = await root.getFileHandle('model.bin');
    const file = await handle.getFile();
    performance.mark('end-opfs-restore');
    const mark = performance.measure(
      'opfs-restore',
      'start-opfs-restore',
      'end-opfs-restore'
    );
    console.log('Cached model file found in OPFS.', mark.name, mark.duration.toFixed(2));
    return file;
  } catch (err) {    
    throw err;
  }
};

IndexedDB API

IndexedDB, ब्राउज़र में किसी भी तरह के डेटा को लगातार स्टोर करने के लिए एक जाना-पहचाना स्टैंडर्ड है. यह अपने कुछ हद तक जटिल एपीआई के लिए बदनाम है. हालांकि, idb-keyval जैसी रैपर लाइब्रेरी का इस्तेमाल करके, IndexedDB को किसी क्लासिक की-वैल्यू स्टोर की तरह इस्तेमाल किया जा सकता है.

उदाहरण के लिए:

import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@latest/+esm';

const storeFileInIDB = async (blob) => {
  try {
    performance.mark('start-idb-cache');
    await set('model.bin', blob);
    performance.mark('end-idb-cache');
    const mark = performance.measure(
      'idb-cache',
      'start-idb-cache',
      'end-idb-cache'
    );
    console.log('Model file cached in IDB.', mark.name, mark.duration.toFixed(2));
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const restoreFileFromIDB = async () => {
  try {
    performance.mark('start-idb-restore');
    const file = await get('model.bin');
    if (!file) {
      throw new Error('File model.bin not found in IDB.');
    }
    performance.mark('end-idb-restore');
    const mark = performance.measure(
      'idb-restore',
      'start-idb-restore',
      'end-idb-restore'
    );
    console.log('Cached model file found in IDB.', mark.name, mark.duration.toFixed(2));
    return file;
  } catch (err) {    
    throw err;
  }
};

स्टोरेज को 'बने रहेगा' के तौर पर मार्क करना

कैश मेमोरी के इनमें से किसी भी तरीके के आखिर में, navigator.storage.persist() को कॉल करें. इससे, हमेशा मौजूद रहने वाले स्टोरेज का इस्तेमाल करने की अनुमति का अनुरोध किया जा सकता है. यह तरीका एक प्रॉमिस दिखाता है. अगर अनुमति मिल जाती है, तो यह true पर रिज़ॉल्व होता है. अगर अनुमति नहीं मिलती है, तो यह false पर रिज़ॉल्व होता है. ब्राउज़र के नियमों के आधार पर, ब्राउज़र अनुरोध को स्वीकार कर सकता है या नहीं.

if ('storage' in navigator && 'persist' in navigator.storage) {
  try {
    const persistent = await navigator.storage.persist();
    if (persistent) {
      console.log("Storage will not be cleared except by explicit user action.");
      return;
    }
    console.log("Storage may be cleared under storage pressure.");  
  } catch (err) {
    console.error(err.name, err.message);
  }
}

खास मामला: हार्ड डिस्क पर मॉडल का इस्तेमाल करना

ब्राउज़र स्टोरेज के विकल्प के तौर पर, सीधे उपयोगकर्ता की हार्ड डिस्क से एआई मॉडल का रेफ़रंस दिया जा सकता है. इस तकनीक से, रिसर्च पर फ़ोकस करने वाले ऐप्लिकेशन, ब्राउज़र में दिए गए मॉडल चलाने की संभावना दिखा सकते हैं. इसके अलावा, कलाकारों को विशेषज्ञता वाले क्रिएटिव ऐप्लिकेशन में, अपने-आप ट्रेन किए गए मॉडल इस्तेमाल करने की अनुमति मिल सकती है.

फ़ाइल सिस्टम को ऐक्सेस करने का एपीआई

File System Access API की मदद से हार्ड डिस्क से फ़ाइलें खोलकर, एक FileSystemFileHandle पाएं जिसे IndexedDB पर जारी रखना है.

इस पैटर्न के साथ, उपयोगकर्ता को सिर्फ़ एक बार मॉडल फ़ाइल का ऐक्सेस देना होगा. बने रहने वाली अनुमतियों की मदद से, उपयोगकर्ता फ़ाइल का ऐक्सेस हमेशा के लिए दे सकता है. ऐप्लिकेशन को फिर से लोड करने और उपयोगकर्ता के किसी जेस्चर, जैसे कि माउस क्लिक करने के बाद, FileSystemFileHandle को हार्ड डिस्क पर मौजूद फ़ाइल के ऐक्सेस की मदद से, IndexedDB से वापस लाया जा सकता है.

फ़ाइल को ऐक्सेस करने की अनुमतियों के लिए क्वेरी की जाती है और ज़रूरत पड़ने पर अनुरोध किया जाता है. इससे, आने वाले समय में फ़ाइल को फिर से लोड करने में आसानी होती है. यहां दिए गए उदाहरण में, हार्ड डिस्क से किसी फ़ाइल का हैंडल पाने और फिर हैंडल को सेव और वापस लाने का तरीका बताया गया है.

import { fileOpen } from 'https://cdn.jsdelivr.net/npm/browser-fs-access@latest/dist/index.modern.js';
import { get, set } from 'https://cdn.jsdelivr.net/npm/idb-keyval@latest/+esm';

button.addEventListener('click', async () => {
  try {
    const file = await fileOpen({
      extensions: ['.bin'],
      mimeTypes: ['application/octet-stream'],
      description: 'AI model files',
    });
    if (file.handle) {
      // It's an asynchronous method, but no need to await it.
      storeFileHandleInIDB(file.handle);
    }
    return file;
  } catch (err) {
    if (err.name !== 'AbortError') {
      console.error(err.name, err.message);
    }
  }
});

const storeFileHandleInIDB = async (handle) => {
  try {
    performance.mark('start-file-handle-cache');
    await set('model.bin.handle', handle);
    performance.mark('end-file-handle-cache');
    const mark = performance.measure(
      'file-handle-cache',
      'start-file-handle-cache',
      'end-file-handle-cache'
    );
    console.log('Model file handle cached in IDB.', mark.name, mark.duration.toFixed(2));
  } catch (err) {
    console.error(err.name, err.message);
  }
};

const restoreFileFromFileHandle = async () => {
  try {
    performance.mark('start-file-handle-restore');
    const handle = await get('model.bin.handle');
    if (!handle) {
      throw new Error('File handle model.bin.handle not found in IDB.');
    }
    if ((await handle.queryPermission()) !== 'granted') {
      const decision = await handle.requestPermission();
      if (decision === 'denied' || decision === 'prompt') {
        throw new Error(Access to file model.bin.handle not granted.');
      }
    }
    const file = await handle.getFile();
    performance.mark('end-file-handle-restore');
    const mark = performance.measure(
      'file-handle-restore',
      'start-file-handle-restore',
      'end-file-handle-restore'
    );
    console.log('Cached model file handle found in IDB.', mark.name, mark.duration.toFixed(2));
    return file;
  } catch (err) {    
    throw err;
  }
};

ये तरीके एक-दूसरे से अलग नहीं हैं. ऐसा भी हो सकता है कि आप दोनों, ब्राउज़र में मॉडल को साफ़ तौर पर कैश मेमोरी में सेव करें और उपयोगकर्ता की हार्ड डिस्क से मॉडल का इस्तेमाल करें.

डेमो

MediaPipe LLM डेमो में, केस स्टोरेज के तीनों सामान्य तरीके और हार्ड डिस्क का तरीका देखा जा सकता है.

बोनस: बड़ी फ़ाइल को अलग-अलग हिस्सों में डाउनलोड करना

अगर आपको इंटरनेट से कोई बड़ा एआई मॉडल डाउनलोड करना है, तो डाउनलोड को अलग-अलग हिस्सों में एक साथ ले जाएं. इसके बाद, इन्हें क्लाइंट पर फिर से जोड़ें.

यहां एक हेल्पर फ़ंक्शन दिया गया है, जिसका इस्तेमाल अपने कोड में किया जा सकता है. आपको सिर्फ़ url को पास करना होगा. chunkSize (डिफ़ॉल्ट: 5 एमबी), maxParallelRequests (डिफ़ॉल्ट: 6), progressCallback फ़ंक्शन (जो downloadedBytes और कुल fileSize की रिपोर्ट करता है), और AbortSignal सिग्नल के लिए signal, ये सभी वैकल्पिक हैं.

अपने प्रोजेक्ट में नीचे दिए गए फ़ंक्शन को कॉपी किया जा सकता है या npm से fetch-in-chunks पैकेज इंस्टॉल किया जा सकता है.

async function fetchInChunks(
  url,
  chunkSize = 5 * 1024 * 1024,
  maxParallelRequests = 6,
  progressCallback = null,
  signal = null
) {
  // Helper function to get the size of the remote file using a HEAD request
  async function getFileSize(url, signal) {
    const response = await fetch(url, { method: 'HEAD', signal });
    if (!response.ok) {
      throw new Error('Failed to fetch the file size');
    }
    const contentLength = response.headers.get('content-length');
    if (!contentLength) {
      throw new Error('Content-Length header is missing');
    }
    return parseInt(contentLength, 10);
  }

  // Helper function to fetch a chunk of the file
  async function fetchChunk(url, start, end, signal) {
    const response = await fetch(url, {
      headers: { Range: `bytes=${start}-${end}` },
      signal,
    });
    if (!response.ok && response.status !== 206) {
      throw new Error('Failed to fetch chunk');
    }
    return await response.arrayBuffer();
  }

  // Helper function to download chunks with parallelism
  async function downloadChunks(
    url,
    fileSize,
    chunkSize,
    maxParallelRequests,
    progressCallback,
    signal
  ) {
    let chunks = [];
    let queue = [];
    let start = 0;
    let downloadedBytes = 0;

    // Function to process the queue
    async function processQueue() {
      while (start < fileSize) {
        if (queue.length < maxParallelRequests) {
          let end = Math.min(start + chunkSize - 1, fileSize - 1);
          let promise = fetchChunk(url, start, end, signal)
            .then((chunk) => {
              chunks.push({ start, chunk });
              downloadedBytes += chunk.byteLength;

              // Update progress if callback is provided
              if (progressCallback) {
                progressCallback(downloadedBytes, fileSize);
              }

              // Remove this promise from the queue when it resolves
              queue = queue.filter((p) => p !== promise);
            })
            .catch((err) => {              
              throw err;              
            });
          queue.push(promise);
          start += chunkSize;
        }
        // Wait for at least one promise to resolve before continuing
        if (queue.length >= maxParallelRequests) {
          await Promise.race(queue);
        }
      }

      // Wait for all remaining promises to resolve
      await Promise.all(queue);
    }

    await processQueue();

    return chunks.sort((a, b) => a.start - b.start).map((chunk) => chunk.chunk);
  }

  // Get the file size
  const fileSize = await getFileSize(url, signal);

  // Download the file in chunks
  const chunks = await downloadChunks(
    url,
    fileSize,
    chunkSize,
    maxParallelRequests,
    progressCallback,
    signal
  );

  // Stitch the chunks together
  const blob = new Blob(chunks);

  return blob;
}

export default fetchInChunks;

अपने लिए सही तरीका चुनें

इस गाइड में, ब्राउज़र में एआई मॉडल को असरदार तरीके से कैश करने के कई तरीके बताए गए हैं. यह एक ऐसा टास्क है जो आपके ऐप्लिकेशन की परफ़ॉर्मेंस और उपयोगकर्ता के अनुभव को बेहतर बनाने के लिए बहुत ज़रूरी है. Chrome की स्टोरेज टीम, बेहतरीन परफ़ॉर्मेंस के लिए कैश एपीआई का सुझाव देती है. इससे एआई मॉडल का तुरंत ऐक्सेस मिलता है, लोड होने में लगने वाला समय कम होता है, और रिस्पॉन्स बेहतर होता है.

OPFS और IndexedDB, कम इस्तेमाल किए जाने वाले विकल्प हैं. डेटा को सेव करने से पहले, OPFS और IndexedDB API को उसे सीरियलाइज़ करना होगा. IndexedDB को डेटा वापस पाने के लिए, उसे डिसीरियलाइज़ भी करना पड़ता है. इसलिए, बड़े मॉडल को सेव करने के लिए, यह सबसे खराब जगह है.

खास ऐप्लिकेशन के लिए, File System Access API, उपयोगकर्ता के डिवाइस पर फ़ाइलों को सीधे ऐक्सेस करने की सुविधा देता है. यह उन उपयोगकर्ताओं के लिए सही है जो अपने एआई मॉडल खुद मैनेज करते हैं.

अगर आपको अपने एआई मॉडल को सुरक्षित रखना है, तो उसे सर्वर पर सेव करें. क्लाइंट पर सेव हो जाने के बाद, DevTools या OFPS DevTools एक्सटेंशन की मदद से कैश और IndexedDB, दोनों से डेटा निकालना आसान नहीं है. ये Storage API साफ़ तौर पर सुरक्षा के बराबर होते हैं. ऐसा हो सकता है कि आप मॉडल के एन्क्रिप्ट (सुरक्षित) किए गए वर्शन को सेव करना चाहें, लेकिन इसके बाद आपको क्लाइंट से डिक्रिप्शन कुंजी लेनी होगी. इससे, इस मॉडल को बीच में ही रोका जा सकता है. इसका मतलब है कि किसी बैड ऐक्टर की आपके मॉडल को चुराने की कोशिश थोड़ी मुश्किल होती है, लेकिन नामुमकिन नहीं.

हमारा सुझाव है कि आप कैश मेमोरी सेव करने की ऐसी रणनीति चुनें जो आपके ऐप्लिकेशन की ज़रूरतों, टारगेट ऑडियंस के व्यवहार, और इस्तेमाल किए जा रहे एआई मॉडल की विशेषताओं के हिसाब से हो. इससे यह पक्का होता है कि आपके ऐप्लिकेशन, नेटवर्क की अलग-अलग स्थितियों और सिस्टम की पाबंदियों के तहत, रिस्पॉन्सिव और बेहतर तरीके से काम करते हैं.


धन्यवाद

इसकी समीक्षा जोशुआ बेल, रेइली ग्रांट, इवान स्टेड, नैथन मेमोरीमट, ऑस्टिन सुलिवन, एटिएन नोएल, आंद्रे बंडारा, एलेक्ज़ेंड्रा क्लेपर, फ़्रैंकोइस बोफ़र्ट, पॉल किनलान, और रेचल एंड्रयू ने की है.