File size: 4,102 Bytes
f059dc2
 
 
 
 
 
 
 
0d0f934
4dc14dc
 
 
f059dc2
 
 
 
 
 
 
 
 
 
f332e70
f059dc2
 
 
 
 
 
 
 
 
 
 
 
0d0f934
f059dc2
 
 
 
 
 
1bb1792
 
 
 
 
 
774048c
 
 
 
 
 
 
1bb1792
f059dc2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
774048c
 
 
 
 
 
 
4dc14dc
 
 
 
 
774048c
4dc14dc
774048c
 
4dc14dc
774048c
 
4dc14dc
 
 
 
 
 
 
 
 
774048c
 
f059dc2
 
 
0d0f934
 
f059dc2
 
 
 
1bb1792
f059dc2
 
 
d78ffd0
1bb1792
 
f059dc2
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// @ts-check

export function workerConnection() {
  // TODO: load worker and expose an API for the rest of app to use

  const workerLoaded = loadWorker();

  const connection = {
    loaded: workerLoaded.then(worker => ({ env: worker.env })),
    loadModel,
    runPrompt,
    listChatModels
  };

  return connection;

  function loadWorker() {
    // Create a worker from the same URL as the currently executing script.
    // We locate the current script URL via document.currentScript when in the window context.
    // Build a blob that imports this script and calls bootWorker when executed inside a worker.
    return new Promise((resolve, reject) => {
      try {
        const scriptUrl = (typeof document !== 'undefined' && (document.currentScript && /** @type {HTMLScriptElement} */(document.currentScript).src)) ||
          (typeof document !== 'undefined' && document.scripts && document.scripts[document.scripts.length - 1] && document.scripts[document.scripts.length - 1].src) ||
          (typeof window !== 'undefined' && window.location && window.location.href) || '';

        const worker = new Worker(scriptUrl, { type: 'module' });

        const pending = new Map();
        let ready = false;

        worker.addEventListener('message', (ev) => {
          const msg = ev.data || {};
          if (msg && msg.type === 'ready') {
            ready = true;
            resolve({ worker, pending, send, env: msg.env });
            return;
          }
          if (msg && msg.id) {
            const id = msg.id;
            const entry = pending.get(id);
            if (!entry) return;
            if (msg.type === 'response') {
              pending.delete(id);
              entry.resolve(msg.result);
            } else if (msg.type === 'error') {
              pending.delete(id);
              entry.reject(new Error(msg.error));
            } else if (msg.type === 'progress') {
              // progress message for long-running operations
              try {
                if (entry.onProgress) entry.onProgress(msg);
              } catch (e) {
                // swallow progress handler errors
              }
            }
          }
        });

        worker.addEventListener('error', (err) => {
          if (!ready) reject(err);
        });

        function send(message) {
          return new Promise((res, rej) => {
            const id = String(Math.random()).slice(2);
            pending.set(id, { resolve: res, reject: rej });
            worker.postMessage(Object.assign({}, message, { id }));
          });
        }
      } catch (err) {
        reject(err);
      }
    });
  }

  /**
   * List and classify chat-capable models via worker. Returns a promise and accepts an onProgress callback.
   * @param {object} params
   * @param {(msg: any)=>void} [onProgress]
   */
  async function listChatModels(params = {}, onProgress) {
    await workerLoaded;
    const { worker, pending } = await workerLoaded;
    const id = String(Math.random()).slice(2);
    let resolved = false;
    const promise = new Promise((resolve, reject) => {
      pending.set(id, { resolve: (res) => { resolved = true; resolve(res); }, reject, onProgress });
      try {
        worker.postMessage(Object.assign({}, params, { type: 'listChatModels', id }));
      } catch (err) {
        pending.delete(id);
        reject(err);
      }
    });

    const cancel = () => {
      try {
        if (!resolved) worker.postMessage({ type: 'cancelListChatModels', id });
      } catch (e) {}
      pending.delete(id);
    };

    return { id, promise, cancel };
  }

  /** @param {string} modelName */
  async function loadModel(modelName) {
    await workerLoaded;
    const { send } = await workerLoaded;
    return send({ type: 'loadModel', modelName });
  }

  /**
   * @param {string} promptText
   * @param {string} [modelName]
   */
  async function runPrompt(promptText, modelName) {
    await workerLoaded;
    const { send } = await workerLoaded;
    const sendPromise = send({ type: 'runPrompt', prompt: promptText, modelName });
    return sendPromise;
  }
}