@livekit/agents 0.3.5 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +36 -0
- package/dist/audio.js +17 -30
- package/dist/audio.js.map +1 -1
- package/dist/cli.js +3 -14
- package/dist/cli.js.map +1 -1
- package/dist/http_server.d.ts +1 -1
- package/dist/http_server.js +5 -9
- package/dist/http_server.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/dist/ipc/job_executor.js +3 -5
- package/dist/ipc/job_executor.js.map +1 -1
- package/dist/ipc/job_main.d.ts +1 -1
- package/dist/ipc/proc_job_executor.js +66 -80
- package/dist/ipc/proc_job_executor.js.map +1 -1
- package/dist/ipc/proc_pool.d.ts +3 -3
- package/dist/ipc/proc_pool.d.ts.map +1 -1
- package/dist/ipc/proc_pool.js +38 -20
- package/dist/ipc/proc_pool.js.map +1 -1
- package/dist/job.js +56 -73
- package/dist/job.js.map +1 -1
- package/dist/llm/chat_context.d.ts +66 -0
- package/dist/llm/chat_context.d.ts.map +1 -0
- package/dist/llm/chat_context.js +93 -0
- package/dist/llm/chat_context.js.map +1 -0
- package/dist/llm/function_context.d.ts +19 -1
- package/dist/llm/function_context.d.ts.map +1 -1
- package/dist/llm/function_context.js +54 -18
- package/dist/llm/function_context.js.map +1 -1
- package/dist/llm/function_context.test.d.ts +2 -0
- package/dist/llm/function_context.test.d.ts.map +1 -0
- package/dist/llm/function_context.test.js +218 -0
- package/dist/llm/function_context.test.js.map +1 -0
- package/dist/llm/index.d.ts +3 -2
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +3 -2
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm.d.ts +53 -0
- package/dist/llm/llm.d.ts.map +1 -0
- package/dist/llm/llm.js +45 -0
- package/dist/llm/llm.js.map +1 -0
- package/dist/multimodal/agent_playout.d.ts +1 -1
- package/dist/multimodal/agent_playout.js +116 -153
- package/dist/multimodal/agent_playout.js.map +1 -1
- package/dist/multimodal/multimodal_agent.d.ts +4 -3
- package/dist/multimodal/multimodal_agent.d.ts.map +1 -1
- package/dist/multimodal/multimodal_agent.js +207 -234
- package/dist/multimodal/multimodal_agent.js.map +1 -1
- package/dist/pipeline/agent_output.d.ts +30 -0
- package/dist/pipeline/agent_output.d.ts.map +1 -0
- package/dist/pipeline/agent_output.js +155 -0
- package/dist/pipeline/agent_output.js.map +1 -0
- package/dist/pipeline/agent_playout.d.ts +38 -0
- package/dist/pipeline/agent_playout.d.ts.map +1 -0
- package/dist/pipeline/agent_playout.js +142 -0
- package/dist/pipeline/agent_playout.js.map +1 -0
- package/dist/pipeline/human_input.d.ts +28 -0
- package/dist/pipeline/human_input.d.ts.map +1 -0
- package/dist/pipeline/human_input.js +134 -0
- package/dist/pipeline/human_input.js.map +1 -0
- package/dist/pipeline/index.d.ts +2 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +5 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/pipeline_agent.d.ts +134 -0
- package/dist/pipeline/pipeline_agent.d.ts.map +1 -0
- package/dist/pipeline/pipeline_agent.js +661 -0
- package/dist/pipeline/pipeline_agent.js.map +1 -0
- package/dist/pipeline/speech_handle.d.ts +27 -0
- package/dist/pipeline/speech_handle.d.ts.map +1 -0
- package/dist/pipeline/speech_handle.js +102 -0
- package/dist/pipeline/speech_handle.js.map +1 -0
- package/dist/plugin.js +7 -20
- package/dist/plugin.js.map +1 -1
- package/dist/stt/index.d.ts +1 -2
- package/dist/stt/index.d.ts.map +1 -1
- package/dist/stt/index.js +1 -2
- package/dist/stt/index.js.map +1 -1
- package/dist/stt/stt.d.ts +62 -24
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +77 -27
- package/dist/stt/stt.js.map +1 -1
- package/dist/tokenize/basic/basic.d.ts +16 -0
- package/dist/tokenize/basic/basic.d.ts.map +1 -0
- package/dist/tokenize/basic/basic.js +50 -0
- package/dist/tokenize/basic/basic.js.map +1 -0
- package/dist/tokenize/basic/hyphenator.d.ts +17 -0
- package/dist/tokenize/basic/hyphenator.d.ts.map +1 -0
- package/dist/tokenize/basic/hyphenator.js +420 -0
- package/dist/tokenize/basic/hyphenator.js.map +1 -0
- package/dist/tokenize/basic/index.d.ts +2 -0
- package/dist/tokenize/basic/index.d.ts.map +1 -0
- package/dist/tokenize/basic/index.js +5 -0
- package/dist/tokenize/basic/index.js.map +1 -0
- package/dist/tokenize/basic/paragraph.d.ts +5 -0
- package/dist/tokenize/basic/paragraph.d.ts.map +1 -0
- package/dist/tokenize/basic/paragraph.js +38 -0
- package/dist/tokenize/basic/paragraph.js.map +1 -0
- package/dist/tokenize/basic/sentence.d.ts +5 -0
- package/dist/tokenize/basic/sentence.d.ts.map +1 -0
- package/dist/tokenize/basic/sentence.js +60 -0
- package/dist/tokenize/basic/sentence.js.map +1 -0
- package/dist/tokenize/basic/word.d.ts +5 -0
- package/dist/tokenize/basic/word.d.ts.map +1 -0
- package/dist/tokenize/basic/word.js +23 -0
- package/dist/tokenize/basic/word.js.map +1 -0
- package/dist/tokenize/index.d.ts +5 -0
- package/dist/tokenize/index.d.ts.map +1 -0
- package/dist/tokenize/index.js +8 -0
- package/dist/tokenize/index.js.map +1 -0
- package/dist/tokenize/token_stream.d.ts +36 -0
- package/dist/tokenize/token_stream.d.ts.map +1 -0
- package/dist/tokenize/token_stream.js +136 -0
- package/dist/tokenize/token_stream.js.map +1 -0
- package/dist/tokenize/tokenizer.d.ts +55 -0
- package/dist/tokenize/tokenizer.d.ts.map +1 -0
- package/dist/tokenize/tokenizer.js +117 -0
- package/dist/tokenize/tokenizer.js.map +1 -0
- package/dist/transcription.js +78 -89
- package/dist/transcription.js.map +1 -1
- package/dist/tts/index.d.ts +1 -3
- package/dist/tts/index.d.ts.map +1 -1
- package/dist/tts/index.js +1 -3
- package/dist/tts/index.js.map +1 -1
- package/dist/tts/tts.d.ts +66 -37
- package/dist/tts/tts.d.ts.map +1 -1
- package/dist/tts/tts.js +79 -74
- package/dist/tts/tts.js.map +1 -1
- package/dist/utils.d.ts +21 -6
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +120 -76
- package/dist/utils.js.map +1 -1
- package/dist/vad.d.ts +43 -39
- package/dist/vad.d.ts.map +1 -1
- package/dist/vad.js +51 -4
- package/dist/vad.js.map +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +257 -247
- package/dist/worker.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +16 -2
- package/src/ipc/proc_pool.ts +25 -13
- package/src/llm/chat_context.ts +147 -0
- package/src/llm/function_context.test.ts +248 -0
- package/src/llm/function_context.ts +77 -18
- package/src/llm/index.ts +21 -2
- package/src/llm/llm.ts +102 -0
- package/src/multimodal/multimodal_agent.ts +6 -2
- package/src/pipeline/agent_output.ts +185 -0
- package/src/pipeline/agent_playout.ts +187 -0
- package/src/pipeline/human_input.ts +166 -0
- package/src/pipeline/index.ts +15 -0
- package/src/pipeline/pipeline_agent.ts +917 -0
- package/src/pipeline/speech_handle.ts +136 -0
- package/src/stt/index.ts +8 -2
- package/src/stt/stt.ts +98 -31
- package/src/tokenize/basic/basic.ts +73 -0
- package/src/tokenize/basic/hyphenator.ts +436 -0
- package/src/tokenize/basic/index.ts +5 -0
- package/src/tokenize/basic/paragraph.ts +43 -0
- package/src/tokenize/basic/sentence.ts +69 -0
- package/src/tokenize/basic/word.ts +27 -0
- package/src/tokenize/index.ts +16 -0
- package/src/tokenize/token_stream.ts +163 -0
- package/src/tokenize/tokenizer.ts +152 -0
- package/src/tts/index.ts +1 -20
- package/src/tts/tts.ts +110 -57
- package/src/utils.ts +95 -25
- package/src/vad.ts +86 -45
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/stt/stream_adapter.d.ts +0 -19
- package/dist/stt/stream_adapter.d.ts.map +0 -1
- package/dist/stt/stream_adapter.js +0 -96
- package/dist/stt/stream_adapter.js.map +0 -1
- package/dist/tokenize.d.ts +0 -15
- package/dist/tokenize.d.ts.map +0 -1
- package/dist/tokenize.js +0 -12
- package/dist/tokenize.js.map +0 -1
- package/dist/tts/stream_adapter.d.ts +0 -19
- package/dist/tts/stream_adapter.d.ts.map +0 -1
- package/dist/tts/stream_adapter.js +0 -111
- package/dist/tts/stream_adapter.js.map +0 -1
- package/src/stt/stream_adapter.ts +0 -104
- package/src/tokenize.ts +0 -22
- package/src/tts/stream_adapter.ts +0 -93
package/dist/worker.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _Worker_instances, _Worker_opts, _Worker_procPool, _Worker_id, _Worker_closed, _Worker_draining, _Worker_connecting, _Worker_tasks, _Worker_pending, _Worker_close, _Worker_session, _Worker_httpServer, _Worker_logger, _Worker_runWS, _Worker_availability, _Worker_termination;
|
|
13
1
|
import { JobType, ParticipantPermission, ServerMessage, WorkerMessage, WorkerStatus, } from '@livekit/protocol';
|
|
14
2
|
import { AccessToken, RoomServiceClient } from 'livekit-server-sdk';
|
|
15
3
|
import { EventEmitter } from 'node:events';
|
|
@@ -90,6 +78,12 @@ const defaultCpuLoad = async () => {
|
|
|
90
78
|
};
|
|
91
79
|
/** Participant permissions to pass to every agent spun up by this worker. */
|
|
92
80
|
export class WorkerPermissions {
|
|
81
|
+
canPublish;
|
|
82
|
+
canSubscribe;
|
|
83
|
+
canPublishData;
|
|
84
|
+
canUpdateMetadata;
|
|
85
|
+
canPublishSources;
|
|
86
|
+
hidden;
|
|
93
87
|
constructor(canPublish = true, canSubscribe = true, canPublishData = true, canUpdateMetadata = true, canPublishSources = [], hidden = false) {
|
|
94
88
|
this.canPublish = canPublish;
|
|
95
89
|
this.canSubscribe = canSubscribe;
|
|
@@ -109,6 +103,24 @@ export class WorkerPermissions {
|
|
|
109
103
|
* This class is mostly useful in conjunction with {@link cli.runApp}.
|
|
110
104
|
*/
|
|
111
105
|
export class WorkerOptions {
|
|
106
|
+
agent;
|
|
107
|
+
requestFunc;
|
|
108
|
+
loadFunc;
|
|
109
|
+
loadThreshold;
|
|
110
|
+
numIdleProcesses;
|
|
111
|
+
shutdownProcessTimeout;
|
|
112
|
+
initializeProcessTimeout;
|
|
113
|
+
permissions;
|
|
114
|
+
agentName;
|
|
115
|
+
workerType;
|
|
116
|
+
maxRetry;
|
|
117
|
+
wsURL;
|
|
118
|
+
apiKey;
|
|
119
|
+
apiSecret;
|
|
120
|
+
host;
|
|
121
|
+
port;
|
|
122
|
+
logLevel;
|
|
123
|
+
production;
|
|
112
124
|
/** @param options */
|
|
113
125
|
constructor({ agent, requestFunc = defaultRequestFunc, loadFunc = defaultCpuLoad, loadThreshold = undefined, numIdleProcesses = undefined, shutdownProcessTimeout = 60 * 1000, initializeProcessTimeout = 10 * 1000, permissions = new WorkerPermissions(), agentName = '', workerType = JobType.JT_ROOM, maxRetry = MAX_RECONNECT_ATTEMPTS, wsURL = 'ws://localhost:7880', apiKey = undefined, apiSecret = undefined, host = 'localhost', port = undefined, logLevel = 'info', production = false, }) {
|
|
114
126
|
this.agent = agent;
|
|
@@ -135,11 +147,9 @@ export class WorkerOptions {
|
|
|
135
147
|
}
|
|
136
148
|
}
|
|
137
149
|
class PendingAssignment {
|
|
138
|
-
|
|
139
|
-
this.
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
}
|
|
150
|
+
promise = new Promise((resolve) => {
|
|
151
|
+
this.resolve = resolve; // this is how JavaScript lets you resolve promises externally
|
|
152
|
+
});
|
|
143
153
|
resolve(arg) {
|
|
144
154
|
arg; // useless call to counteract TypeScript E6133
|
|
145
155
|
}
|
|
@@ -154,22 +164,21 @@ class PendingAssignment {
|
|
|
154
164
|
* behind a wrapper.
|
|
155
165
|
*/
|
|
156
166
|
export class Worker {
|
|
167
|
+
#opts;
|
|
168
|
+
#procPool;
|
|
169
|
+
#id = 'unregistered';
|
|
170
|
+
#closed = true;
|
|
171
|
+
#draining = false;
|
|
172
|
+
#connecting = false;
|
|
173
|
+
#tasks = [];
|
|
174
|
+
#pending = {};
|
|
175
|
+
#close = new Future();
|
|
176
|
+
event = new EventEmitter();
|
|
177
|
+
#session = undefined;
|
|
178
|
+
#httpServer;
|
|
179
|
+
#logger = log().child({ version });
|
|
157
180
|
/* @throws {@link MissingCredentialsError} if URL, API key or API secret are missing */
|
|
158
181
|
constructor(opts) {
|
|
159
|
-
_Worker_instances.add(this);
|
|
160
|
-
_Worker_opts.set(this, void 0);
|
|
161
|
-
_Worker_procPool.set(this, void 0);
|
|
162
|
-
_Worker_id.set(this, 'unregistered');
|
|
163
|
-
_Worker_closed.set(this, true);
|
|
164
|
-
_Worker_draining.set(this, false);
|
|
165
|
-
_Worker_connecting.set(this, false);
|
|
166
|
-
_Worker_tasks.set(this, []);
|
|
167
|
-
_Worker_pending.set(this, {});
|
|
168
|
-
_Worker_close.set(this, new Future());
|
|
169
|
-
this.event = new EventEmitter();
|
|
170
|
-
_Worker_session.set(this, undefined);
|
|
171
|
-
_Worker_httpServer.set(this, void 0);
|
|
172
|
-
_Worker_logger.set(this, log().child({ version }));
|
|
173
182
|
opts.wsURL = opts.wsURL || process.env.LIVEKIT_URL || '';
|
|
174
183
|
opts.apiKey = opts.apiKey || process.env.LIVEKIT_API_KEY || '';
|
|
175
184
|
opts.apiSecret = opts.apiSecret || process.env.LIVEKIT_API_SECRET || '';
|
|
@@ -179,72 +188,72 @@ export class Worker {
|
|
|
179
188
|
throw new MissingCredentialsError('API Key is required: Set LIVEKIT_API_KEY, run with --api-key, or pass apiKey in WorkerOptions');
|
|
180
189
|
if (opts.apiSecret === '')
|
|
181
190
|
throw new MissingCredentialsError('API Secret is required: Set LIVEKIT_API_SECRET, run with --api-secret, or pass apiSecret in WorkerOptions');
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
191
|
+
this.#procPool = new ProcPool(opts.agent, opts.numIdleProcesses, opts.initializeProcessTimeout, opts.shutdownProcessTimeout);
|
|
192
|
+
this.#opts = opts;
|
|
193
|
+
this.#httpServer = new HTTPServer(opts.host, opts.port);
|
|
185
194
|
}
|
|
186
195
|
/* @throws {@link WorkerError} if worker failed to connect or already running */
|
|
187
196
|
async run() {
|
|
188
|
-
if (!
|
|
197
|
+
if (!this.#closed) {
|
|
189
198
|
throw new WorkerError('worker is already running');
|
|
190
199
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
200
|
+
this.#logger.info('starting worker');
|
|
201
|
+
this.#closed = false;
|
|
202
|
+
this.#procPool.start();
|
|
194
203
|
const workerWS = async () => {
|
|
195
204
|
let retries = 0;
|
|
196
|
-
|
|
197
|
-
while (!
|
|
198
|
-
const url = new URL(
|
|
205
|
+
this.#connecting = true;
|
|
206
|
+
while (!this.#closed) {
|
|
207
|
+
const url = new URL(this.#opts.wsURL);
|
|
199
208
|
url.protocol = url.protocol.replace('http', 'ws');
|
|
200
|
-
const token = new AccessToken(
|
|
209
|
+
const token = new AccessToken(this.#opts.apiKey, this.#opts.apiSecret);
|
|
201
210
|
token.addGrant({ agent: true });
|
|
202
211
|
const jwt = await token.toJwt();
|
|
203
|
-
|
|
212
|
+
this.#session = new WebSocket(url + 'agent', {
|
|
204
213
|
headers: { authorization: 'Bearer ' + jwt },
|
|
205
|
-
})
|
|
214
|
+
});
|
|
206
215
|
try {
|
|
207
216
|
await new Promise((resolve, reject) => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
this.#session.on('open', resolve);
|
|
218
|
+
this.#session.on('error', (error) => reject(error));
|
|
219
|
+
this.#session.on('close', (code) => reject(new Error(`WebSocket returned ${code}`)));
|
|
211
220
|
});
|
|
212
221
|
retries = 0;
|
|
213
|
-
|
|
214
|
-
|
|
222
|
+
this.#logger.debug('connected to LiveKit server');
|
|
223
|
+
this.#runWS(this.#session);
|
|
215
224
|
return;
|
|
216
225
|
}
|
|
217
226
|
catch (e) {
|
|
218
|
-
if (
|
|
227
|
+
if (this.#closed)
|
|
219
228
|
return;
|
|
220
|
-
if (retries >=
|
|
229
|
+
if (retries >= this.#opts.maxRetry) {
|
|
221
230
|
throw new WorkerError(`failed to connect to LiveKit server after ${retries} attempts: ${e}`);
|
|
222
231
|
}
|
|
223
232
|
retries++;
|
|
224
233
|
const delay = Math.min(retries * 2, 10);
|
|
225
|
-
|
|
234
|
+
this.#logger.warn(`failed to connect to LiveKit server, retrying in ${delay} seconds: ${e} (${retries}/${this.#opts.maxRetry})`);
|
|
226
235
|
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
|
|
227
236
|
}
|
|
228
237
|
}
|
|
229
238
|
};
|
|
230
|
-
await Promise.all([workerWS(),
|
|
231
|
-
|
|
239
|
+
await Promise.all([workerWS(), this.#httpServer.run()]);
|
|
240
|
+
this.#close.resolve();
|
|
232
241
|
}
|
|
233
242
|
get id() {
|
|
234
|
-
return
|
|
243
|
+
return this.#id;
|
|
235
244
|
}
|
|
236
245
|
get activeJobs() {
|
|
237
|
-
return
|
|
246
|
+
return this.#procPool.processes
|
|
238
247
|
.filter((proc) => proc.runningJob)
|
|
239
248
|
.map((proc) => proc.runningJob);
|
|
240
249
|
}
|
|
241
250
|
/* @throws {@link WorkerError} if worker did not drain in time */
|
|
242
251
|
async drain(timeout) {
|
|
243
|
-
if (
|
|
252
|
+
if (this.#draining) {
|
|
244
253
|
return;
|
|
245
254
|
}
|
|
246
|
-
|
|
247
|
-
|
|
255
|
+
this.#logger.info('draining worker');
|
|
256
|
+
this.#draining = true;
|
|
248
257
|
this.event.emit('worker_msg', new WorkerMessage({
|
|
249
258
|
message: {
|
|
250
259
|
case: 'updateWorker',
|
|
@@ -254,7 +263,7 @@ export class Worker {
|
|
|
254
263
|
},
|
|
255
264
|
}));
|
|
256
265
|
const joinJobs = async () => {
|
|
257
|
-
return Promise.all(
|
|
266
|
+
return Promise.all(this.#procPool.processes.map((proc) => {
|
|
258
267
|
if (!proc.runningJob) {
|
|
259
268
|
proc.close();
|
|
260
269
|
}
|
|
@@ -271,7 +280,7 @@ export class Worker {
|
|
|
271
280
|
});
|
|
272
281
|
}
|
|
273
282
|
async simulateJob(roomName, participantIdentity) {
|
|
274
|
-
const client = new RoomServiceClient(
|
|
283
|
+
const client = new RoomServiceClient(this.#opts.wsURL, this.#opts.apiKey, this.#opts.apiSecret);
|
|
275
284
|
const room = await client.createRoom({ name: roomName });
|
|
276
285
|
let participant = undefined;
|
|
277
286
|
if (participantIdentity) {
|
|
@@ -279,7 +288,7 @@ export class Worker {
|
|
|
279
288
|
participant = await client.getParticipant(roomName, participantIdentity);
|
|
280
289
|
}
|
|
281
290
|
catch (e) {
|
|
282
|
-
|
|
291
|
+
this.#logger.fatal(`participant with identity ${participantIdentity} not found in room ${roomName}`);
|
|
283
292
|
throw e;
|
|
284
293
|
}
|
|
285
294
|
}
|
|
@@ -294,208 +303,209 @@ export class Worker {
|
|
|
294
303
|
},
|
|
295
304
|
}));
|
|
296
305
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
__classPrivateFieldGet(this, _Worker_logger, "f").info('shutting down worker');
|
|
304
|
-
__classPrivateFieldSet(this, _Worker_closed, true, "f");
|
|
305
|
-
await __classPrivateFieldGet(this, _Worker_procPool, "f").close();
|
|
306
|
-
await __classPrivateFieldGet(this, _Worker_httpServer, "f").close();
|
|
307
|
-
await Promise.allSettled(__classPrivateFieldGet(this, _Worker_tasks, "f"));
|
|
308
|
-
(_a = __classPrivateFieldGet(this, _Worker_session, "f")) === null || _a === void 0 ? void 0 : _a.close();
|
|
309
|
-
await __classPrivateFieldGet(this, _Worker_close, "f").await;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
_Worker_opts = new WeakMap(), _Worker_procPool = new WeakMap(), _Worker_id = new WeakMap(), _Worker_closed = new WeakMap(), _Worker_draining = new WeakMap(), _Worker_connecting = new WeakMap(), _Worker_tasks = new WeakMap(), _Worker_pending = new WeakMap(), _Worker_close = new WeakMap(), _Worker_session = new WeakMap(), _Worker_httpServer = new WeakMap(), _Worker_logger = new WeakMap(), _Worker_instances = new WeakSet(), _Worker_runWS = function _Worker_runWS(ws) {
|
|
313
|
-
let closingWS = false;
|
|
314
|
-
const send = (msg) => {
|
|
315
|
-
if (closingWS) {
|
|
316
|
-
this.event.off('worker_msg', send);
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
ws.send(msg.toBinary());
|
|
320
|
-
};
|
|
321
|
-
this.event.on('worker_msg', send);
|
|
322
|
-
ws.addEventListener('close', () => {
|
|
323
|
-
closingWS = true;
|
|
324
|
-
__classPrivateFieldGet(this, _Worker_logger, "f").error('worker connection closed unexpectedly');
|
|
325
|
-
this.close();
|
|
326
|
-
});
|
|
327
|
-
ws.addEventListener('message', (event) => {
|
|
328
|
-
if (event.type !== 'message') {
|
|
329
|
-
__classPrivateFieldGet(this, _Worker_logger, "f").warn('unexpected message type: ' + event.type);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
const msg = new ServerMessage();
|
|
333
|
-
msg.fromBinary(event.data);
|
|
334
|
-
// register is the only valid first message, and it is only valid as the
|
|
335
|
-
// first message
|
|
336
|
-
if (__classPrivateFieldGet(this, _Worker_connecting, "f") && msg.message.case !== 'register') {
|
|
337
|
-
throw new WorkerError('expected register response as first message');
|
|
338
|
-
}
|
|
339
|
-
switch (msg.message.case) {
|
|
340
|
-
case 'register': {
|
|
341
|
-
__classPrivateFieldSet(this, _Worker_id, msg.message.value.workerId, "f");
|
|
342
|
-
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
343
|
-
.child({ id: this.id, server_info: msg.message.value.serverInfo })
|
|
344
|
-
.info('registered worker');
|
|
345
|
-
this.event.emit('worker_registered', msg.message.value.workerId, msg.message.value.serverInfo);
|
|
346
|
-
__classPrivateFieldSet(this, _Worker_connecting, false, "f");
|
|
347
|
-
break;
|
|
306
|
+
#runWS(ws) {
|
|
307
|
+
let closingWS = false;
|
|
308
|
+
const send = (msg) => {
|
|
309
|
+
if (closingWS) {
|
|
310
|
+
this.event.off('worker_msg', send);
|
|
311
|
+
return;
|
|
348
312
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
313
|
+
ws.send(msg.toBinary());
|
|
314
|
+
};
|
|
315
|
+
this.event.on('worker_msg', send);
|
|
316
|
+
ws.addEventListener('close', () => {
|
|
317
|
+
closingWS = true;
|
|
318
|
+
this.#logger.error('worker connection closed unexpectedly');
|
|
319
|
+
this.close();
|
|
320
|
+
});
|
|
321
|
+
ws.addEventListener('message', (event) => {
|
|
322
|
+
if (event.type !== 'message') {
|
|
323
|
+
this.#logger.warn('unexpected message type: ' + event.type);
|
|
324
|
+
return;
|
|
356
325
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
326
|
+
const msg = new ServerMessage();
|
|
327
|
+
msg.fromBinary(event.data);
|
|
328
|
+
// register is the only valid first message, and it is only valid as the
|
|
329
|
+
// first message
|
|
330
|
+
if (this.#connecting && msg.message.case !== 'register') {
|
|
331
|
+
throw new WorkerError('expected register response as first message');
|
|
332
|
+
}
|
|
333
|
+
switch (msg.message.case) {
|
|
334
|
+
case 'register': {
|
|
335
|
+
this.#id = msg.message.value.workerId;
|
|
336
|
+
this.#logger
|
|
337
|
+
.child({ id: this.id, server_info: msg.message.value.serverInfo })
|
|
338
|
+
.info('registered worker');
|
|
339
|
+
this.event.emit('worker_registered', msg.message.value.workerId, msg.message.value.serverInfo);
|
|
340
|
+
this.#connecting = false;
|
|
341
|
+
break;
|
|
365
342
|
}
|
|
366
|
-
|
|
367
|
-
|
|
343
|
+
case 'availability': {
|
|
344
|
+
if (!msg.message.value.job)
|
|
345
|
+
return;
|
|
346
|
+
const task = this.#availability(msg.message.value);
|
|
347
|
+
this.#tasks.push(task);
|
|
348
|
+
task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));
|
|
349
|
+
break;
|
|
368
350
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
value: {
|
|
383
|
-
type: __classPrivateFieldGet(this, _Worker_opts, "f").workerType,
|
|
384
|
-
agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName,
|
|
385
|
-
allowedPermissions: new ParticipantPermission({
|
|
386
|
-
canPublish: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canPublish,
|
|
387
|
-
canSubscribe: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canSubscribe,
|
|
388
|
-
canPublishData: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canPublishData,
|
|
389
|
-
canUpdateMetadata: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canUpdateMetadata,
|
|
390
|
-
hidden: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.hidden,
|
|
391
|
-
agent: true,
|
|
392
|
-
}),
|
|
393
|
-
version,
|
|
394
|
-
},
|
|
395
|
-
},
|
|
396
|
-
}));
|
|
397
|
-
let currentStatus = WorkerStatus.WS_AVAILABLE;
|
|
398
|
-
const loadMonitor = setInterval(() => {
|
|
399
|
-
if (closingWS)
|
|
400
|
-
clearInterval(loadMonitor);
|
|
401
|
-
const oldStatus = currentStatus;
|
|
402
|
-
__classPrivateFieldGet(this, _Worker_opts, "f").loadFunc().then((currentLoad) => {
|
|
403
|
-
const isFull = currentLoad >= __classPrivateFieldGet(this, _Worker_opts, "f").loadThreshold;
|
|
404
|
-
const currentlyAvailable = !isFull;
|
|
405
|
-
currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;
|
|
406
|
-
if (oldStatus != currentStatus) {
|
|
407
|
-
const extra = { load: currentLoad, loadThreshold: __classPrivateFieldGet(this, _Worker_opts, "f").loadThreshold };
|
|
408
|
-
if (isFull) {
|
|
409
|
-
__classPrivateFieldGet(this, _Worker_logger, "f").child(extra).info('worker is at full capacity, marking as unavailable');
|
|
351
|
+
case 'assignment': {
|
|
352
|
+
if (!msg.message.value.job)
|
|
353
|
+
return;
|
|
354
|
+
const job = msg.message.value.job;
|
|
355
|
+
if (job.id in this.#pending) {
|
|
356
|
+
const task = this.#pending[job.id];
|
|
357
|
+
delete this.#pending[job.id];
|
|
358
|
+
task.resolve(msg.message.value);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
this.#logger.child({ job }).warn('received assignment for unknown job ' + job.id);
|
|
362
|
+
}
|
|
363
|
+
break;
|
|
410
364
|
}
|
|
411
|
-
|
|
412
|
-
|
|
365
|
+
case 'termination': {
|
|
366
|
+
const task = this.#termination(msg.message.value);
|
|
367
|
+
this.#tasks.push(task);
|
|
368
|
+
task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));
|
|
369
|
+
break;
|
|
413
370
|
}
|
|
414
371
|
}
|
|
415
|
-
this.event.emit('worker_msg', new WorkerMessage({
|
|
416
|
-
message: {
|
|
417
|
-
case: 'updateWorker',
|
|
418
|
-
value: {
|
|
419
|
-
load: currentLoad,
|
|
420
|
-
status: currentStatus,
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
}));
|
|
424
372
|
});
|
|
425
|
-
}, UPDATE_LOAD_INTERVAL);
|
|
426
|
-
}, _Worker_availability = async function _Worker_availability(msg) {
|
|
427
|
-
let answered = false;
|
|
428
|
-
const onReject = async () => {
|
|
429
|
-
answered = true;
|
|
430
373
|
this.event.emit('worker_msg', new WorkerMessage({
|
|
431
374
|
message: {
|
|
432
|
-
case: '
|
|
375
|
+
case: 'register',
|
|
433
376
|
value: {
|
|
434
|
-
|
|
435
|
-
|
|
377
|
+
type: this.#opts.workerType,
|
|
378
|
+
agentName: this.#opts.agentName,
|
|
379
|
+
allowedPermissions: new ParticipantPermission({
|
|
380
|
+
canPublish: this.#opts.permissions.canPublish,
|
|
381
|
+
canSubscribe: this.#opts.permissions.canSubscribe,
|
|
382
|
+
canPublishData: this.#opts.permissions.canPublishData,
|
|
383
|
+
canUpdateMetadata: this.#opts.permissions.canUpdateMetadata,
|
|
384
|
+
hidden: this.#opts.permissions.hidden,
|
|
385
|
+
agent: true,
|
|
386
|
+
}),
|
|
387
|
+
version,
|
|
436
388
|
},
|
|
437
389
|
},
|
|
438
390
|
}));
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
391
|
+
let currentStatus = WorkerStatus.WS_AVAILABLE;
|
|
392
|
+
const loadMonitor = setInterval(() => {
|
|
393
|
+
if (closingWS)
|
|
394
|
+
clearInterval(loadMonitor);
|
|
395
|
+
const oldStatus = currentStatus;
|
|
396
|
+
this.#opts.loadFunc().then((currentLoad) => {
|
|
397
|
+
const isFull = currentLoad >= this.#opts.loadThreshold;
|
|
398
|
+
const currentlyAvailable = !isFull;
|
|
399
|
+
currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;
|
|
400
|
+
if (oldStatus != currentStatus) {
|
|
401
|
+
const extra = { load: currentLoad, loadThreshold: this.#opts.loadThreshold };
|
|
402
|
+
if (isFull) {
|
|
403
|
+
this.#logger.child(extra).info('worker is at full capacity, marking as unavailable');
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
this.#logger.child(extra).info('worker is below capacity, marking as available');
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
410
|
+
message: {
|
|
411
|
+
case: 'updateWorker',
|
|
412
|
+
value: {
|
|
413
|
+
load: currentLoad,
|
|
414
|
+
status: currentStatus,
|
|
415
|
+
},
|
|
416
|
+
},
|
|
417
|
+
}));
|
|
418
|
+
});
|
|
419
|
+
}, UPDATE_LOAD_INTERVAL);
|
|
420
|
+
}
|
|
421
|
+
async #availability(msg) {
|
|
422
|
+
let answered = false;
|
|
423
|
+
const onReject = async () => {
|
|
424
|
+
answered = true;
|
|
425
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
426
|
+
message: {
|
|
427
|
+
case: 'availability',
|
|
428
|
+
value: {
|
|
429
|
+
jobId: msg.job.id,
|
|
430
|
+
available: false,
|
|
431
|
+
},
|
|
451
432
|
},
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
433
|
+
}));
|
|
434
|
+
};
|
|
435
|
+
const onAccept = async (args) => {
|
|
436
|
+
answered = true;
|
|
437
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
438
|
+
message: {
|
|
439
|
+
case: 'availability',
|
|
440
|
+
value: {
|
|
441
|
+
jobId: msg.job.id,
|
|
442
|
+
available: true,
|
|
443
|
+
participantIdentity: args.identity,
|
|
444
|
+
participantName: args.name,
|
|
445
|
+
participantMetadata: args.metadata,
|
|
446
|
+
},
|
|
447
|
+
},
|
|
448
|
+
}));
|
|
449
|
+
this.#pending[req.id] = new PendingAssignment();
|
|
450
|
+
const timer = setTimeout(() => {
|
|
451
|
+
this.#logger.child({ req }).warn(`assignment for job ${req.id} timed out`);
|
|
452
|
+
return;
|
|
453
|
+
}, ASSIGNMENT_TIMEOUT);
|
|
454
|
+
const asgn = await this.#pending[req.id].promise.then(async (asgn) => {
|
|
455
|
+
clearTimeout(timer);
|
|
456
|
+
return asgn;
|
|
457
|
+
});
|
|
458
|
+
await this.#procPool.launchJob({
|
|
459
|
+
acceptArguments: args,
|
|
460
|
+
job: msg.job,
|
|
461
|
+
url: asgn.url || this.#opts.wsURL,
|
|
462
|
+
token: asgn.token,
|
|
463
|
+
});
|
|
464
|
+
};
|
|
465
|
+
const req = new JobRequest(msg.job, onReject, onAccept);
|
|
466
|
+
this.#logger
|
|
467
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })
|
|
468
|
+
.info('received job request');
|
|
469
|
+
const jobRequestTask = async () => {
|
|
470
|
+
try {
|
|
471
|
+
await this.#opts.requestFunc(req);
|
|
472
|
+
}
|
|
473
|
+
catch (e) {
|
|
474
|
+
this.#logger
|
|
475
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })
|
|
476
|
+
.info('jobRequestFunc failed');
|
|
477
|
+
await onReject();
|
|
478
|
+
}
|
|
479
|
+
if (!answered) {
|
|
480
|
+
this.#logger
|
|
481
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })
|
|
482
|
+
.info('no answer was given inside the jobRequestFunc, automatically rejecting the job');
|
|
483
|
+
}
|
|
484
|
+
};
|
|
485
|
+
const task = jobRequestTask();
|
|
486
|
+
this.#tasks.push(task);
|
|
487
|
+
task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));
|
|
488
|
+
}
|
|
489
|
+
async #termination(msg) {
|
|
490
|
+
const proc = this.#procPool.getByJobId(msg.jobId);
|
|
491
|
+
if (proc === null) {
|
|
492
|
+
// safe to ignore
|
|
457
493
|
return;
|
|
458
|
-
}, ASSIGNMENT_TIMEOUT);
|
|
459
|
-
const asgn = await __classPrivateFieldGet(this, _Worker_pending, "f")[req.id].promise.then(async (asgn) => {
|
|
460
|
-
clearTimeout(timer);
|
|
461
|
-
return asgn;
|
|
462
|
-
});
|
|
463
|
-
await __classPrivateFieldGet(this, _Worker_procPool, "f").launchJob({
|
|
464
|
-
acceptArguments: args,
|
|
465
|
-
job: msg.job,
|
|
466
|
-
url: asgn.url || __classPrivateFieldGet(this, _Worker_opts, "f").wsURL,
|
|
467
|
-
token: asgn.token,
|
|
468
|
-
});
|
|
469
|
-
};
|
|
470
|
-
const req = new JobRequest(msg.job, onReject, onAccept);
|
|
471
|
-
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
472
|
-
.child({ job: msg.job, resuming: msg.resuming, agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName })
|
|
473
|
-
.info('received job request');
|
|
474
|
-
const jobRequestTask = async () => {
|
|
475
|
-
try {
|
|
476
|
-
await __classPrivateFieldGet(this, _Worker_opts, "f").requestFunc(req);
|
|
477
494
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
await
|
|
483
|
-
|
|
484
|
-
if (!answered) {
|
|
485
|
-
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
486
|
-
.child({ job: msg.job, resuming: msg.resuming, agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName })
|
|
487
|
-
.info('no answer was given inside the jobRequestFunc, automatically rejecting the job');
|
|
495
|
+
await proc.close();
|
|
496
|
+
}
|
|
497
|
+
async close() {
|
|
498
|
+
if (this.#closed) {
|
|
499
|
+
await this.#close.await;
|
|
500
|
+
return;
|
|
488
501
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
// safe to ignore
|
|
497
|
-
return;
|
|
502
|
+
this.#logger.info('shutting down worker');
|
|
503
|
+
this.#closed = true;
|
|
504
|
+
await this.#procPool.close();
|
|
505
|
+
await this.#httpServer.close();
|
|
506
|
+
await Promise.allSettled(this.#tasks);
|
|
507
|
+
this.#session?.close();
|
|
508
|
+
await this.#close.await;
|
|
498
509
|
}
|
|
499
|
-
|
|
500
|
-
};
|
|
510
|
+
}
|
|
501
511
|
//# sourceMappingURL=worker.js.map
|