Implementing a web worker in WeWeb

I’m working on implementing some custom JS code that relies on a web worker to do some data processing in the background without jamming up the main thread. My code works great outside of WeWeb but I can’t figure out how to implement it in WeWeb so it’ll work.

I previously managed to implement the functionality I need in the main thread, using @raydeck 's awesome tool https://weweb-embed.statechange.ai/ to import the library I need on page load (this thread was very helpful in figuring that out) then executing the actual code as part of a button-click triggered workflow.

Now I’m struggling with how to adapt this approach (or find another approach) for a web worker context.

Specifically, here are the main things I’m stuck on:

  • Where to put the contents of my worker.js file so my main script can access it?
  • Conversely, where to put the contents of my script.js file so it can refer to my worker.js file, and so I can call the function it defines in a JS workflow action?

At this point I really only need this to work on one particular page.

Here’s the code from my script.js file:

// Create a new Worker instance, pointing to the path of our worker.js file
const myWorker = new Worker('js/worker.js', { type: 'module' });

// Function to send data to the worker for processing
function processText(text) {
    myWorker.postMessage({ text: text });
}

// Event listener to handle messages received from the worker
myWorker.addEventListener('message', function(event) {
    // Check the type of message received
    if (event.data.status && event.data.status === 'complete') {
        // Handle the completed output
        console.log('Feature extraction output:', event.data.output);
    } else {
        // This could be a progress update or any other message
        console.log('Worker message:', event.data);
    }
}, false);

// Example usage: Send some text to the worker for processing
processText('What is a large language model?');

And here is the code from my worker.js file:

import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@xenova/transformers';

// Skip local model check since we are downloading the model from the Hugging Face Hub.
env.allowLocalModels = false;

class PipelineSingleton {
    static task = 'text2text-generation';
    static model = 'Xenova/LaMini-Flan-T5-783M';
    static instance = null;

    static async getInstance(progress_callback = null) {
        if (this.instance === null) {
            this.instance = pipeline(this.task, this.model, {
                progress_callback,
            });
        }
        return this.instance;
    }
}

// Listen for messages from the main thread
self.addEventListener('message', async (event) => {
    // Retrieve the pipeline. When called for the first time,
    // this will load the pipeline and save it for future use.
    const generator = await PipelineSingleton.getInstance(x => {
        // We also add a progress callback to the pipeline so that we can
        // track model loading.
        self.postMessage(x);
    });

    // Actually perform the generation
    let output = await generator(event.data.text);

    // Send the output back to the main thread
    self.postMessage({ status: 'complete', output: output });
});

In short: what is the best way to implement this in WeWeb, i.e. what do I need to put where, in order for it to work? Ideally both in the editor and in prod.

I will be immensely grateful to anyone who can provide any kind of direction on this!

Thanks :slight_smile:

SO we need to respect the same-origin policy for a service worker, but arbitrary assets in Weweb are challenging. My approach would be to take your worker source code, base64 encode it (I would use cyberchef fo this job) and serve it as a data URI to your Worker constructor.

1 Like

:exploding_head:

Wow, that is friggin brilliant - thank you!!!