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