Skip to content

Commit ad04a7d

Browse files
authored
[Function] Add heartbeat to service worker (#400)
## Overview This PR adds heartbeat event in web service worker so that the client can monitor its status and respond correspondingly. ## Primary Changes - Update `{ type: "keepAlive" }` event to `{ kind: "keepAlive" }` to keep all event format consistent - Add heartbeat event in web service worker handler to report back its status ## Testing <img width="616" alt="Screenshot 2024-05-15 at 2 01 51 AM" src="https://github.com/mlc-ai/web-llm/assets/23090573/42fcd7b0-f8f2-4b23-80c3-664426853161">
1 parent 4d1915e commit ad04a7d

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

src/message.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ type RequestKind = (
1616
"interruptGenerate" | "unload" | "resetChat" | "init" |
1717
"initProgressCallback" | "generateProgressCallback" | "getMaxStorageBufferBindingSize" |
1818
"getGPUVendor" | "forwardTokensAndSample" | "chatCompletionNonStreaming" | "getMessage" |
19-
"chatCompletionStreamInit" | "chatCompletionStreamNextChunk" | "customRequest");
19+
"chatCompletionStreamInit" | "chatCompletionStreamNextChunk" | "customRequest" | 'keepAlive' | 'heartbeat');
20+
2021
export interface ReloadParams {
2122
modelId: string;
2223
chatOpts?: ChatOptions;

src/service_worker.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ export async function CreateServiceWorkerEngine(
139139
engineConfig?: EngineConfig,
140140
keepAliveMs: number = 10000
141141
): Promise<ServiceWorkerEngine> {
142-
const serviceWorkerEngine = new ServiceWorkerEngine();
142+
const serviceWorkerEngine = new ServiceWorkerEngine(keepAliveMs);
143143
serviceWorkerEngine.setInitProgressCallback(
144144
engineConfig?.initProgressCallback
145145
);
@@ -148,9 +148,6 @@ export async function CreateServiceWorkerEngine(
148148
engineConfig?.chatOpts,
149149
engineConfig?.appConfig
150150
);
151-
setInterval(() => {
152-
serviceWorkerEngine.keepAlive();
153-
}, keepAliveMs);
154151
return serviceWorkerEngine;
155152
}
156153

@@ -191,15 +188,18 @@ class PortAdapter implements ChatWorker {
191188
export class ServiceWorkerEngine extends WebWorkerEngine {
192189
port: chrome.runtime.Port;
193190

194-
constructor() {
191+
constructor(keepAliveMs: number = 10000) {
195192
let port = chrome.runtime.connect({ name: "web_llm_service_worker" });
196193
let chatWorker = new PortAdapter(port);
197194
super(chatWorker);
198195
this.port = port;
196+
setInterval(() => {
197+
this.keepAlive();
198+
}, keepAliveMs);
199199
}
200200

201201
keepAlive() {
202-
this.port.postMessage({ type: "keepAlive" });
202+
this.worker.postMessage({ kind: "keepAlive" });
203203
}
204204

205205
/**

src/web_service_worker.ts

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,16 @@ export class ServiceWorkerEngineHandler extends EngineWorkerHandler {
7474
const msgEvent = event as MessageEvent;
7575
const msg = msgEvent.data as WorkerMessage;
7676

77+
if (msg.kind === "keepAlive") {
78+
const msg: WorkerMessage = {
79+
kind: "heartbeat",
80+
uuid: msgEvent.data.uuid,
81+
content: "",
82+
};
83+
this.postMessageInternal(msg);
84+
return;
85+
}
86+
7787
if (msg.kind === "init") {
7888
this.handleTask(msg.uuid, async () => {
7989
const params = msg.content as ReloadParams;
@@ -114,7 +124,7 @@ export class ServiceWorkerEngineHandler extends EngineWorkerHandler {
114124
});
115125
return;
116126
}
117-
super.onmessage(event);
127+
super.onmessage(msg);
118128
}
119129
}
120130

@@ -147,13 +157,36 @@ export async function CreateServiceWorkerEngine(
147157
* A client of Engine that exposes the same interface
148158
*/
149159
export class ServiceWorkerEngine extends WebWorkerEngine {
150-
constructor(worker: ChatWorker) {
160+
missedHeatbeat = 0
161+
162+
constructor(worker: ChatWorker, keepAliveMs=10000) {
151163
super(worker);
152-
clientBroadcastChannel.onmessage = this.onmessage.bind(this);
164+
clientBroadcastChannel.onmessage = (event) => {
165+
try {
166+
this.onevent.bind(this)(event)
167+
} catch (err: any) {
168+
// This is expected to throw if user has multiple windows open
169+
if (!err.message.startsWith("return from a unknown uuid")) {
170+
console.error("CreateWebServiceWorkerEngine.onmessage", err);
171+
}
172+
}
173+
};
174+
setInterval(() => {
175+
this.keepAlive();
176+
}, keepAliveMs);
153177
}
154178

155179
keepAlive() {
156-
this.worker.postMessage({ type: "keepAlive" });
180+
this.worker.postMessage({ kind: "keepAlive" });
181+
}
182+
183+
onevent(event: MessageEvent): void {
184+
const msg = event.data as WorkerMessage;
185+
if (msg.kind === "heartbeat") {
186+
this.missedHeatbeat = 0
187+
return;
188+
}
189+
this.onmessage(msg);
157190
}
158191

159192
/**

0 commit comments

Comments
 (0)