@livekit/agents 0.1.0 → 0.3.0
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 +47 -0
- package/LICENSE +201 -0
- package/dist/audio.d.ts +9 -0
- package/dist/audio.d.ts.map +1 -0
- package/dist/audio.js +54 -0
- package/dist/audio.js.map +1 -0
- package/dist/cli.d.ts +12 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +102 -19
- package/dist/cli.js.map +1 -1
- package/dist/generator.d.ts +17 -6
- package/dist/generator.d.ts.map +1 -1
- package/dist/generator.js +20 -3
- package/dist/generator.js.map +1 -1
- package/dist/http_server.d.ts +1 -1
- package/dist/http_server.d.ts.map +1 -1
- package/dist/http_server.js +5 -3
- package/dist/http_server.js.map +1 -1
- package/dist/index.d.ts +14 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -3
- package/dist/index.js.map +1 -1
- package/dist/ipc/job_executor.d.ts +19 -0
- package/dist/ipc/job_executor.d.ts.map +1 -0
- package/dist/ipc/job_executor.js +8 -0
- package/dist/ipc/job_executor.js.map +1 -0
- package/dist/ipc/job_main.d.ts +7 -4
- package/dist/ipc/job_main.d.ts.map +1 -1
- package/dist/ipc/job_main.js +102 -59
- package/dist/ipc/job_main.js.map +1 -1
- package/dist/ipc/message.d.ts +41 -0
- package/dist/ipc/message.d.ts.map +1 -0
- package/dist/ipc/message.js +2 -0
- package/dist/ipc/message.js.map +1 -0
- package/dist/ipc/proc_job_executor.d.ts +15 -0
- package/dist/ipc/proc_job_executor.d.ts.map +1 -0
- package/dist/ipc/proc_job_executor.js +150 -0
- package/dist/ipc/proc_job_executor.js.map +1 -0
- package/dist/ipc/proc_pool.d.ts +26 -0
- package/dist/ipc/proc_pool.d.ts.map +1 -0
- package/dist/ipc/proc_pool.js +83 -0
- package/dist/ipc/proc_pool.js.map +1 -0
- package/dist/job.d.ts +100 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +213 -0
- package/dist/job.js.map +1 -0
- package/dist/llm/function_context.d.ts +20 -0
- package/dist/llm/function_context.d.ts.map +1 -0
- package/dist/llm/function_context.js +37 -0
- package/dist/llm/function_context.js.map +1 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +6 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/log.d.ts +12 -1
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +28 -11
- package/dist/log.js.map +1 -1
- package/dist/multimodal/agent_playout.d.ts +34 -0
- package/dist/multimodal/agent_playout.d.ts.map +1 -0
- package/dist/multimodal/agent_playout.js +221 -0
- package/dist/multimodal/agent_playout.js.map +1 -0
- package/dist/multimodal/index.d.ts +3 -0
- package/dist/multimodal/index.d.ts.map +1 -0
- package/dist/multimodal/index.js +6 -0
- package/dist/multimodal/index.js.map +1 -0
- package/dist/multimodal/multimodal_agent.d.ts +47 -0
- package/dist/multimodal/multimodal_agent.d.ts.map +1 -0
- package/dist/multimodal/multimodal_agent.js +331 -0
- package/dist/multimodal/multimodal_agent.js.map +1 -0
- package/dist/plugin.js +20 -7
- package/dist/plugin.js.map +1 -1
- package/dist/stt/index.d.ts +1 -1
- package/dist/stt/index.d.ts.map +1 -1
- package/dist/stt/index.js.map +1 -1
- package/dist/stt/stream_adapter.d.ts +2 -11
- package/dist/stt/stream_adapter.d.ts.map +1 -1
- package/dist/stt/stream_adapter.js +47 -33
- package/dist/stt/stream_adapter.js.map +1 -1
- package/dist/stt/stt.d.ts +27 -0
- package/dist/stt/stt.d.ts.map +1 -1
- package/dist/stt/stt.js +32 -5
- package/dist/stt/stt.js.map +1 -1
- package/dist/transcription.d.ts +22 -0
- package/dist/transcription.d.ts.map +1 -0
- package/dist/transcription.js +111 -0
- package/dist/transcription.js.map +1 -0
- package/dist/tts/stream_adapter.d.ts +4 -11
- package/dist/tts/stream_adapter.d.ts.map +1 -1
- package/dist/tts/stream_adapter.js +66 -32
- package/dist/tts/stream_adapter.js.map +1 -1
- package/dist/tts/tts.d.ts +10 -0
- package/dist/tts/tts.d.ts.map +1 -1
- package/dist/tts/tts.js +48 -7
- package/dist/tts/tts.js.map +1 -1
- package/dist/utils.d.ts +59 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +212 -6
- package/dist/utils.js.map +1 -1
- package/dist/vad.d.ts +29 -0
- package/dist/vad.d.ts.map +1 -1
- package/dist/vad.js.map +1 -1
- package/dist/worker.d.ts +69 -50
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +414 -213
- package/dist/worker.js.map +1 -1
- package/package.json +12 -10
- package/src/audio.ts +62 -0
- package/src/cli.ts +108 -20
- package/src/generator.ts +27 -7
- package/src/http_server.ts +5 -0
- package/src/index.ts +15 -3
- package/src/ipc/job_executor.ts +25 -0
- package/src/ipc/job_main.ts +141 -61
- package/src/ipc/message.ts +39 -0
- package/src/ipc/proc_job_executor.ts +162 -0
- package/src/ipc/proc_pool.ts +109 -0
- package/src/job.ts +278 -0
- package/src/llm/function_context.ts +61 -0
- package/src/llm/index.ts +11 -0
- package/src/log.ts +40 -8
- package/src/multimodal/agent_playout.ts +254 -0
- package/src/multimodal/index.ts +5 -0
- package/src/multimodal/multimodal_agent.ts +428 -0
- package/src/stt/index.ts +1 -1
- package/src/stt/stream_adapter.ts +32 -32
- package/src/stt/stt.ts +27 -0
- package/src/transcription.ts +128 -0
- package/src/tts/stream_adapter.ts +32 -31
- package/src/tts/tts.ts +10 -0
- package/src/utils.ts +257 -3
- package/src/vad.ts +29 -0
- package/src/worker.ts +465 -172
- package/tsconfig.json +7 -1
- package/dist/ipc/job_process.d.ts +0 -22
- package/dist/ipc/job_process.d.ts.map +0 -1
- package/dist/ipc/job_process.js +0 -73
- package/dist/ipc/job_process.js.map +0 -1
- package/dist/ipc/protocol.d.ts +0 -40
- package/dist/ipc/protocol.d.ts.map +0 -1
- package/dist/ipc/protocol.js +0 -14
- package/dist/ipc/protocol.js.map +0 -1
- package/dist/job_context.d.ts +0 -16
- package/dist/job_context.d.ts.map +0 -1
- package/dist/job_context.js +0 -31
- package/dist/job_context.js.map +0 -1
- package/dist/job_request.d.ts +0 -42
- package/dist/job_request.d.ts.map +0 -1
- package/dist/job_request.js +0 -79
- package/dist/job_request.js.map +0 -1
- package/src/ipc/job_process.ts +0 -96
- package/src/ipc/protocol.ts +0 -51
- package/src/job_context.ts +0 -49
- package/src/job_request.ts +0 -118
package/dist/worker.js
CHANGED
|
@@ -1,296 +1,497 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
import { JobType, ParticipantPermission, ServerMessage, WorkerMessage, WorkerStatus, } from '@livekit/protocol';
|
|
5
14
|
import { EventEmitter } from 'events';
|
|
6
|
-
import { AccessToken } from 'livekit-server-sdk';
|
|
15
|
+
import { AccessToken, RoomServiceClient } from 'livekit-server-sdk';
|
|
7
16
|
import os from 'os';
|
|
8
17
|
import { WebSocket } from 'ws';
|
|
9
18
|
import { HTTPServer } from './http_server.js';
|
|
10
|
-
import {
|
|
11
|
-
import { JobRequest } from './
|
|
19
|
+
import { ProcPool } from './ipc/proc_pool.js';
|
|
20
|
+
import { JobRequest } from './job.js';
|
|
12
21
|
import { log } from './log.js';
|
|
22
|
+
import { Future } from './utils.js';
|
|
13
23
|
import { version } from './version.js';
|
|
14
24
|
const MAX_RECONNECT_ATTEMPTS = 10;
|
|
15
|
-
const ASSIGNMENT_TIMEOUT =
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
const ASSIGNMENT_TIMEOUT = 7.5 * 1000;
|
|
26
|
+
const UPDATE_LOAD_INTERVAL = 2.5 * 1000;
|
|
27
|
+
class Default {
|
|
28
|
+
static loadThreshold(production) {
|
|
29
|
+
if (production) {
|
|
30
|
+
return 0.65;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
return Infinity;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
static numIdleProcesses(production) {
|
|
37
|
+
if (production) {
|
|
38
|
+
return 3;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
return 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
static port(production) {
|
|
45
|
+
if (production) {
|
|
46
|
+
return 8081;
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** Necessary credentials not provided and not found in an appropriate environmental variable. */
|
|
54
|
+
export class MissingCredentialsError extends Error {
|
|
55
|
+
constructor(msg) {
|
|
56
|
+
super(msg);
|
|
57
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** Worker did not run as expected. */
|
|
61
|
+
export class WorkerError extends Error {
|
|
62
|
+
constructor(msg) {
|
|
63
|
+
super(msg);
|
|
64
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** @internal */
|
|
68
|
+
export const defaultInitializeProcessFunc = (_) => _;
|
|
69
|
+
const defaultRequestFunc = async (ctx) => {
|
|
70
|
+
await ctx.accept();
|
|
71
|
+
};
|
|
72
|
+
const defaultCpuLoad = async () => {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const cpus1 = os.cpus();
|
|
75
|
+
setTimeout(() => {
|
|
76
|
+
const cpus2 = os.cpus();
|
|
77
|
+
let idle = 0;
|
|
78
|
+
let total = 0;
|
|
79
|
+
for (let i = 0; i < cpus1.length; i++) {
|
|
80
|
+
const cpu1 = cpus1[i].times;
|
|
81
|
+
const cpu2 = cpus2[i].times;
|
|
82
|
+
idle += cpu2.idle - cpu1.idle;
|
|
83
|
+
const total1 = Object.values(cpu1).reduce((acc, i) => acc + i, 0);
|
|
84
|
+
const total2 = Object.values(cpu2).reduce((acc, i) => acc + i, 0);
|
|
85
|
+
total += total2 - total1;
|
|
86
|
+
}
|
|
87
|
+
resolve(+(1 - idle / total).toFixed(2));
|
|
88
|
+
}, UPDATE_LOAD_INTERVAL);
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
/** Participant permissions to pass to every agent spun up by this worker. */
|
|
92
|
+
export class WorkerPermissions {
|
|
93
|
+
constructor(canPublish = true, canSubscribe = true, canPublishData = true, canUpdateMetadata = true, canPublishSources = [], hidden = false) {
|
|
29
94
|
this.canPublish = canPublish;
|
|
30
95
|
this.canSubscribe = canSubscribe;
|
|
31
96
|
this.canPublishData = canPublishData;
|
|
32
97
|
this.canUpdateMetadata = canUpdateMetadata;
|
|
98
|
+
this.canPublishSources = canPublishSources;
|
|
33
99
|
this.hidden = hidden;
|
|
34
100
|
}
|
|
35
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Data class describing worker behaviour.
|
|
104
|
+
*
|
|
105
|
+
* @remarks
|
|
106
|
+
* The Agents framework provides sane worker defaults, and works out-of-the-box with no tweaking
|
|
107
|
+
* necessary. The only mandatory parameter is `agent`, which points to the entry function.
|
|
108
|
+
*
|
|
109
|
+
* This class is mostly useful in conjunction with {@link cli.runApp}.
|
|
110
|
+
*/
|
|
36
111
|
export class WorkerOptions {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
wsURL;
|
|
44
|
-
apiKey;
|
|
45
|
-
apiSecret;
|
|
46
|
-
host;
|
|
47
|
-
port;
|
|
48
|
-
constructor({ requestFunc, cpuLoadFunc = cpuLoad, namespace = 'default', permissions = new WorkerPermissions(), workerType = JobType.JT_PUBLISHER, maxRetry = MAX_RECONNECT_ATTEMPTS, wsURL = 'ws://localhost:7880', apiKey = undefined, apiSecret = undefined, host = 'localhost', port = 8081, }) {
|
|
112
|
+
/** @param options */
|
|
113
|
+
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
|
+
this.agent = agent;
|
|
115
|
+
if (!this.agent) {
|
|
116
|
+
throw new Error('No Agent file was passed to the worker');
|
|
117
|
+
}
|
|
49
118
|
this.requestFunc = requestFunc;
|
|
50
|
-
this.
|
|
51
|
-
this.
|
|
119
|
+
this.loadFunc = loadFunc;
|
|
120
|
+
this.loadThreshold = loadThreshold || Default.loadThreshold(production);
|
|
121
|
+
this.numIdleProcesses = numIdleProcesses || Default.numIdleProcesses(production);
|
|
122
|
+
this.shutdownProcessTimeout = shutdownProcessTimeout;
|
|
123
|
+
this.initializeProcessTimeout = initializeProcessTimeout;
|
|
52
124
|
this.permissions = permissions;
|
|
125
|
+
this.agentName = agentName;
|
|
53
126
|
this.workerType = workerType;
|
|
54
127
|
this.maxRetry = maxRetry;
|
|
55
128
|
this.wsURL = wsURL;
|
|
56
129
|
this.apiKey = apiKey;
|
|
57
130
|
this.apiSecret = apiSecret;
|
|
58
131
|
this.host = host;
|
|
59
|
-
this.port = port;
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class ActiveJob {
|
|
63
|
-
job;
|
|
64
|
-
acceptData;
|
|
65
|
-
constructor(job, acceptData) {
|
|
66
|
-
this.job = job;
|
|
67
|
-
this.acceptData = acceptData;
|
|
132
|
+
this.port = port || Default.port(production);
|
|
133
|
+
this.logLevel = logLevel;
|
|
134
|
+
this.production = production;
|
|
68
135
|
}
|
|
69
136
|
}
|
|
70
137
|
class PendingAssignment {
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
|
|
138
|
+
constructor() {
|
|
139
|
+
this.promise = new Promise((resolve) => {
|
|
140
|
+
this.resolve = resolve; // this is how JavaScript lets you resolve promises externally
|
|
141
|
+
});
|
|
142
|
+
}
|
|
74
143
|
resolve(arg) {
|
|
75
|
-
arg;
|
|
144
|
+
arg; // useless call to counteract TypeScript E6133
|
|
76
145
|
}
|
|
77
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Central orchestrator for all processes and job requests.
|
|
149
|
+
*
|
|
150
|
+
* @remarks
|
|
151
|
+
* For most usecases, Worker should not be initialized or handled directly; you should instead call
|
|
152
|
+
* for its creation through {@link cli.runApp}. This could, however, be useful in situations where
|
|
153
|
+
* you don't have access to a command line, such as a headless program, or one that uses Agents
|
|
154
|
+
* behind a wrapper.
|
|
155
|
+
*/
|
|
78
156
|
export class Worker {
|
|
79
|
-
|
|
80
|
-
#id = 'unregistered';
|
|
81
|
-
session = undefined;
|
|
82
|
-
closed = false;
|
|
83
|
-
httpServer;
|
|
84
|
-
logger = log.child({ version });
|
|
85
|
-
event = new EventEmitter();
|
|
86
|
-
pending = {};
|
|
87
|
-
processes = {};
|
|
157
|
+
/* @throws {@link MissingCredentialsError} if URL, API key or API secret are missing */
|
|
88
158
|
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 }));
|
|
89
173
|
opts.wsURL = opts.wsURL || process.env.LIVEKIT_URL || '';
|
|
90
174
|
opts.apiKey = opts.apiKey || process.env.LIVEKIT_API_KEY || '';
|
|
91
175
|
opts.apiSecret = opts.apiSecret || process.env.LIVEKIT_API_SECRET || '';
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
176
|
+
if (opts.wsURL === '')
|
|
177
|
+
throw new MissingCredentialsError('URL is required: Set LIVEKIT_URL, run with --url, or pass wsURL in WorkerOptions');
|
|
178
|
+
if (opts.apiKey === '')
|
|
179
|
+
throw new MissingCredentialsError('API Key is required: Set LIVEKIT_API_KEY, run with --api-key, or pass apiKey in WorkerOptions');
|
|
180
|
+
if (opts.apiSecret === '')
|
|
181
|
+
throw new MissingCredentialsError('API Secret is required: Set LIVEKIT_API_SECRET, run with --api-secret, or pass apiSecret in WorkerOptions');
|
|
182
|
+
__classPrivateFieldSet(this, _Worker_procPool, new ProcPool(opts.agent, opts.numIdleProcesses, opts.initializeProcessTimeout, opts.shutdownProcessTimeout), "f");
|
|
183
|
+
__classPrivateFieldSet(this, _Worker_opts, opts, "f");
|
|
184
|
+
__classPrivateFieldSet(this, _Worker_httpServer, new HTTPServer(opts.host, opts.port), "f");
|
|
97
185
|
}
|
|
186
|
+
/* @throws {@link WorkerError} if worker failed to connect or already running */
|
|
98
187
|
async run() {
|
|
99
|
-
this
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
throw new Error('--api-secret is required, or set LIVEKIT_API_SECRET env var');
|
|
188
|
+
if (!__classPrivateFieldGet(this, _Worker_closed, "f")) {
|
|
189
|
+
throw new WorkerError('worker is already running');
|
|
190
|
+
}
|
|
191
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").info('starting worker');
|
|
192
|
+
__classPrivateFieldSet(this, _Worker_closed, false, "f");
|
|
193
|
+
__classPrivateFieldGet(this, _Worker_procPool, "f").start();
|
|
106
194
|
const workerWS = async () => {
|
|
107
195
|
let retries = 0;
|
|
108
|
-
|
|
109
|
-
|
|
196
|
+
__classPrivateFieldSet(this, _Worker_connecting, true, "f");
|
|
197
|
+
while (!__classPrivateFieldGet(this, _Worker_closed, "f")) {
|
|
198
|
+
const url = new URL(__classPrivateFieldGet(this, _Worker_opts, "f").wsURL);
|
|
199
|
+
url.protocol = url.protocol.replace('http', 'ws');
|
|
200
|
+
const token = new AccessToken(__classPrivateFieldGet(this, _Worker_opts, "f").apiKey, __classPrivateFieldGet(this, _Worker_opts, "f").apiSecret);
|
|
110
201
|
token.addGrant({ agent: true });
|
|
111
202
|
const jwt = await token.toJwt();
|
|
112
|
-
|
|
113
|
-
url.protocol = url.protocol.replace('http', 'ws');
|
|
114
|
-
this.session = new WebSocket(url + 'agent', {
|
|
203
|
+
__classPrivateFieldSet(this, _Worker_session, new WebSocket(url + 'agent', {
|
|
115
204
|
headers: { authorization: 'Bearer ' + jwt },
|
|
116
|
-
});
|
|
205
|
+
}), "f");
|
|
117
206
|
try {
|
|
118
207
|
await new Promise((resolve, reject) => {
|
|
119
|
-
this.
|
|
120
|
-
this.
|
|
121
|
-
this.
|
|
208
|
+
__classPrivateFieldGet(this, _Worker_session, "f").on('open', resolve);
|
|
209
|
+
__classPrivateFieldGet(this, _Worker_session, "f").on('error', (error) => reject(error));
|
|
210
|
+
__classPrivateFieldGet(this, _Worker_session, "f").on('close', (code) => reject(`WebSocket returned ${code}`));
|
|
122
211
|
});
|
|
123
|
-
|
|
212
|
+
retries = 0;
|
|
213
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").debug('connected to LiveKit server');
|
|
214
|
+
__classPrivateFieldGet(this, _Worker_instances, "m", _Worker_runWS).call(this, __classPrivateFieldGet(this, _Worker_session, "f"));
|
|
124
215
|
return;
|
|
125
216
|
}
|
|
126
217
|
catch (e) {
|
|
127
|
-
if (this
|
|
218
|
+
if (__classPrivateFieldGet(this, _Worker_closed, "f"))
|
|
128
219
|
return;
|
|
129
|
-
if (retries >= this.
|
|
130
|
-
throw new
|
|
220
|
+
if (retries >= __classPrivateFieldGet(this, _Worker_opts, "f").maxRetry) {
|
|
221
|
+
throw new WorkerError(`failed to connect to LiveKit server after ${retries} attempts: ${e}`);
|
|
131
222
|
}
|
|
132
223
|
retries++;
|
|
133
224
|
const delay = Math.min(retries * 2, 10);
|
|
134
|
-
this.
|
|
225
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").warn(`failed to connect to LiveKit server, retrying in ${delay} seconds: ${e} (${retries}/${__classPrivateFieldGet(this, _Worker_opts, "f").maxRetry})`);
|
|
135
226
|
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
|
|
136
227
|
}
|
|
137
228
|
}
|
|
138
229
|
};
|
|
139
|
-
await Promise.all([workerWS(), this.
|
|
230
|
+
await Promise.all([workerWS(), __classPrivateFieldGet(this, _Worker_httpServer, "f").run()]);
|
|
231
|
+
__classPrivateFieldGet(this, _Worker_close, "f").resolve();
|
|
140
232
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this.processes[job.id] = { proc, activeJob: new ActiveJob(job, acceptData) };
|
|
144
|
-
proc
|
|
145
|
-
.run()
|
|
146
|
-
.catch((e) => {
|
|
147
|
-
proc.logger.error(`error running job process ${proc.job.id}: ${e}`);
|
|
148
|
-
})
|
|
149
|
-
.finally(() => {
|
|
150
|
-
proc.clear();
|
|
151
|
-
delete this.processes[job.id];
|
|
152
|
-
});
|
|
233
|
+
get id() {
|
|
234
|
+
return __classPrivateFieldGet(this, _Worker_id, "f");
|
|
153
235
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
236
|
+
get activeJobs() {
|
|
237
|
+
return __classPrivateFieldGet(this, _Worker_procPool, "f").processes
|
|
238
|
+
.filter((proc) => proc.runningJob)
|
|
239
|
+
.map((proc) => proc.runningJob);
|
|
240
|
+
}
|
|
241
|
+
/* @throws {@link WorkerError} if worker did not drain in time */
|
|
242
|
+
async drain(timeout) {
|
|
243
|
+
if (__classPrivateFieldGet(this, _Worker_draining, "f")) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").info('draining worker');
|
|
247
|
+
__classPrivateFieldSet(this, _Worker_draining, true, "f");
|
|
248
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
249
|
+
message: {
|
|
250
|
+
case: 'updateWorker',
|
|
251
|
+
value: {
|
|
252
|
+
status: WorkerStatus.WS_FULL,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
}));
|
|
256
|
+
const joinJobs = async () => {
|
|
257
|
+
return Promise.all(__classPrivateFieldGet(this, _Worker_procPool, "f").processes.map((proc) => {
|
|
258
|
+
if (!proc.runningJob) {
|
|
259
|
+
proc.close();
|
|
260
|
+
}
|
|
261
|
+
return proc.join();
|
|
262
|
+
}));
|
|
162
263
|
};
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
264
|
+
const timer = setTimeout(() => {
|
|
265
|
+
throw new WorkerError('timed out draining');
|
|
266
|
+
}, timeout);
|
|
267
|
+
if (timeout === undefined)
|
|
268
|
+
clearTimeout(timer);
|
|
269
|
+
await joinJobs().then(() => {
|
|
270
|
+
clearTimeout(timer);
|
|
168
271
|
});
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
272
|
+
}
|
|
273
|
+
async simulateJob(roomName, participantIdentity) {
|
|
274
|
+
const client = new RoomServiceClient(__classPrivateFieldGet(this, _Worker_opts, "f").wsURL, __classPrivateFieldGet(this, _Worker_opts, "f").apiKey, __classPrivateFieldGet(this, _Worker_opts, "f").apiSecret);
|
|
275
|
+
const room = await client.createRoom({ name: roomName });
|
|
276
|
+
let participant = undefined;
|
|
277
|
+
if (participantIdentity) {
|
|
278
|
+
try {
|
|
279
|
+
participant = await client.getParticipant(roomName, participantIdentity);
|
|
173
280
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
case 'register': {
|
|
178
|
-
this.#id = msg.message.value.workerId;
|
|
179
|
-
log
|
|
180
|
-
.child({ id: this.id, server_info: msg.message.value.serverInfo })
|
|
181
|
-
.info('registered worker');
|
|
182
|
-
break;
|
|
183
|
-
}
|
|
184
|
-
case 'availability': {
|
|
185
|
-
this.availability(msg.message.value);
|
|
186
|
-
break;
|
|
187
|
-
}
|
|
188
|
-
case 'assignment': {
|
|
189
|
-
const job = msg.message.value.job;
|
|
190
|
-
if (job.id in this.pending) {
|
|
191
|
-
const task = this.pending[job.id];
|
|
192
|
-
delete this.pending[job.id];
|
|
193
|
-
task.value.resolve({
|
|
194
|
-
asgn: msg.message.value,
|
|
195
|
-
raw: msg.toJsonString(),
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
log.child({ job }).warn('received assignment for unknown job ' + job.id);
|
|
200
|
-
}
|
|
201
|
-
break;
|
|
202
|
-
}
|
|
281
|
+
catch (e) {
|
|
282
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").fatal(`participant with identity ${participantIdentity} not found in room ${roomName}`);
|
|
283
|
+
throw e;
|
|
203
284
|
}
|
|
204
|
-
}
|
|
285
|
+
}
|
|
205
286
|
this.event.emit('worker_msg', new WorkerMessage({
|
|
206
287
|
message: {
|
|
207
|
-
case: '
|
|
288
|
+
case: 'simulateJob',
|
|
208
289
|
value: {
|
|
209
|
-
type:
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
canPublish: this.opts.permissions.canPublish,
|
|
213
|
-
canSubscribe: this.opts.permissions.canSubscribe,
|
|
214
|
-
canPublishData: this.opts.permissions.canPublishData,
|
|
215
|
-
hidden: this.opts.permissions.hidden,
|
|
216
|
-
agent: true,
|
|
217
|
-
}),
|
|
218
|
-
version,
|
|
290
|
+
type: JobType.JT_PUBLISHER,
|
|
291
|
+
room,
|
|
292
|
+
participant,
|
|
219
293
|
},
|
|
220
294
|
},
|
|
221
295
|
}));
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
296
|
+
}
|
|
297
|
+
async close() {
|
|
298
|
+
var _a;
|
|
299
|
+
if (__classPrivateFieldGet(this, _Worker_closed, "f")) {
|
|
300
|
+
await __classPrivateFieldGet(this, _Worker_close, "f").await;
|
|
301
|
+
return;
|
|
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;
|
|
348
|
+
}
|
|
349
|
+
case 'availability': {
|
|
350
|
+
const task = __classPrivateFieldGet(this, _Worker_instances, "m", _Worker_availability).call(this, msg.message.value);
|
|
351
|
+
__classPrivateFieldGet(this, _Worker_tasks, "f").push(task);
|
|
352
|
+
task.finally(() => __classPrivateFieldGet(this, _Worker_tasks, "f").splice(__classPrivateFieldGet(this, _Worker_tasks, "f").indexOf(task)));
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
case 'assignment': {
|
|
356
|
+
const job = msg.message.value.job;
|
|
357
|
+
if (job.id in __classPrivateFieldGet(this, _Worker_pending, "f")) {
|
|
358
|
+
const task = __classPrivateFieldGet(this, _Worker_pending, "f")[job.id];
|
|
359
|
+
delete __classPrivateFieldGet(this, _Worker_pending, "f")[job.id];
|
|
360
|
+
task.resolve(msg.message.value);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").child({ job }).warn('received assignment for unknown job ' + job.id);
|
|
364
|
+
}
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
case 'termination': {
|
|
368
|
+
const task = __classPrivateFieldGet(this, _Worker_instances, "m", _Worker_termination).call(this, msg.message.value);
|
|
369
|
+
__classPrivateFieldGet(this, _Worker_tasks, "f").push(task);
|
|
370
|
+
task.finally(() => __classPrivateFieldGet(this, _Worker_tasks, "f").splice(__classPrivateFieldGet(this, _Worker_tasks, "f").indexOf(task)));
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
376
|
+
message: {
|
|
377
|
+
case: 'register',
|
|
378
|
+
value: {
|
|
379
|
+
type: __classPrivateFieldGet(this, _Worker_opts, "f").workerType,
|
|
380
|
+
agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName,
|
|
381
|
+
allowedPermissions: new ParticipantPermission({
|
|
382
|
+
canPublish: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canPublish,
|
|
383
|
+
canSubscribe: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canSubscribe,
|
|
384
|
+
canPublishData: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canPublishData,
|
|
385
|
+
canUpdateMetadata: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.canUpdateMetadata,
|
|
386
|
+
hidden: __classPrivateFieldGet(this, _Worker_opts, "f").permissions.hidden,
|
|
387
|
+
agent: true,
|
|
388
|
+
}),
|
|
389
|
+
version,
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
}));
|
|
393
|
+
let currentStatus = WorkerStatus.WS_AVAILABLE;
|
|
394
|
+
const loadMonitor = setInterval(() => {
|
|
395
|
+
if (closingWS)
|
|
396
|
+
clearInterval(loadMonitor);
|
|
397
|
+
const oldStatus = currentStatus;
|
|
398
|
+
__classPrivateFieldGet(this, _Worker_opts, "f").loadFunc().then((currentLoad) => {
|
|
399
|
+
const isFull = currentLoad >= __classPrivateFieldGet(this, _Worker_opts, "f").loadThreshold;
|
|
400
|
+
const currentlyAvailable = !isFull;
|
|
401
|
+
currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;
|
|
402
|
+
if (oldStatus != currentStatus) {
|
|
403
|
+
const extra = { load: currentLoad, loadThreshold: __classPrivateFieldGet(this, _Worker_opts, "f").loadThreshold };
|
|
404
|
+
if (isFull) {
|
|
405
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").child(extra).info('worker is at full capacity, marking as unavailable');
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").child(extra).info('worker is below capacity, marking as available');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
225
411
|
this.event.emit('worker_msg', new WorkerMessage({
|
|
226
412
|
message: {
|
|
227
413
|
case: 'updateWorker',
|
|
228
414
|
value: {
|
|
229
|
-
load:
|
|
415
|
+
load: currentLoad,
|
|
416
|
+
status: currentStatus,
|
|
230
417
|
},
|
|
231
418
|
},
|
|
232
419
|
}));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
420
|
+
});
|
|
421
|
+
}, UPDATE_LOAD_INTERVAL);
|
|
422
|
+
}, _Worker_availability = async function _Worker_availability(msg) {
|
|
423
|
+
let answered = false;
|
|
424
|
+
const onReject = async () => {
|
|
425
|
+
answered = true;
|
|
426
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
427
|
+
message: {
|
|
428
|
+
case: 'availability',
|
|
429
|
+
value: {
|
|
430
|
+
jobId: msg.job.id,
|
|
431
|
+
available: false,
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
}));
|
|
435
|
+
};
|
|
436
|
+
const onAccept = async (args) => {
|
|
437
|
+
answered = true;
|
|
438
|
+
this.event.emit('worker_msg', new WorkerMessage({
|
|
439
|
+
message: {
|
|
440
|
+
case: 'availability',
|
|
441
|
+
value: {
|
|
442
|
+
jobId: msg.job.id,
|
|
443
|
+
available: true,
|
|
444
|
+
participantIdentity: args.identity,
|
|
445
|
+
participantName: args.name,
|
|
446
|
+
participantMetadata: args.metadata,
|
|
249
447
|
},
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
448
|
+
},
|
|
449
|
+
}));
|
|
450
|
+
__classPrivateFieldGet(this, _Worker_pending, "f")[req.id] = new PendingAssignment();
|
|
451
|
+
const timer = setTimeout(() => {
|
|
452
|
+
__classPrivateFieldGet(this, _Worker_logger, "f").child({ req }).warn(`assignment for job ${req.id} timed out`);
|
|
453
|
+
return;
|
|
454
|
+
}, ASSIGNMENT_TIMEOUT);
|
|
455
|
+
const asgn = await __classPrivateFieldGet(this, _Worker_pending, "f")[req.id].promise.then(async (asgn) => {
|
|
456
|
+
clearTimeout(timer);
|
|
457
|
+
return asgn;
|
|
458
|
+
});
|
|
459
|
+
await __classPrivateFieldGet(this, _Worker_procPool, "f").launchJob({
|
|
460
|
+
acceptArguments: args,
|
|
461
|
+
job: msg.job,
|
|
462
|
+
url: asgn.url || __classPrivateFieldGet(this, _Worker_opts, "f").wsURL,
|
|
463
|
+
token: asgn.token,
|
|
263
464
|
});
|
|
465
|
+
};
|
|
466
|
+
const req = new JobRequest(msg.job, onReject, onAccept);
|
|
467
|
+
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
468
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName })
|
|
469
|
+
.info('received job request');
|
|
470
|
+
const jobRequestTask = async () => {
|
|
264
471
|
try {
|
|
265
|
-
this.
|
|
472
|
+
await __classPrivateFieldGet(this, _Worker_opts, "f").requestFunc(req);
|
|
266
473
|
}
|
|
267
474
|
catch (e) {
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
log.child({ req }).error(`no answer for job ${req.id}, automatically rejecting the job`);
|
|
273
|
-
this.event.emit('worker_msg', new WorkerMessage({
|
|
274
|
-
message: {
|
|
275
|
-
case: 'availability',
|
|
276
|
-
value: {
|
|
277
|
-
available: false,
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
}));
|
|
281
|
-
}
|
|
475
|
+
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
476
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName })
|
|
477
|
+
.info('jobRequestFunc failed');
|
|
478
|
+
await onReject();
|
|
282
479
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
this.closed = true;
|
|
288
|
-
this.logger.debug('shutting down worker');
|
|
289
|
-
await this.httpServer.close();
|
|
290
|
-
for await (const value of Object.values(this.processes)) {
|
|
291
|
-
await value.proc.close();
|
|
480
|
+
if (!answered) {
|
|
481
|
+
__classPrivateFieldGet(this, _Worker_logger, "f")
|
|
482
|
+
.child({ job: msg.job, resuming: msg.resuming, agentName: __classPrivateFieldGet(this, _Worker_opts, "f").agentName })
|
|
483
|
+
.info('no answer was given inside the jobRequestFunc, automatically rejecting the job');
|
|
292
484
|
}
|
|
293
|
-
|
|
485
|
+
};
|
|
486
|
+
const task = jobRequestTask();
|
|
487
|
+
__classPrivateFieldGet(this, _Worker_tasks, "f").push(task);
|
|
488
|
+
task.finally(() => __classPrivateFieldGet(this, _Worker_tasks, "f").splice(__classPrivateFieldGet(this, _Worker_tasks, "f").indexOf(task)));
|
|
489
|
+
}, _Worker_termination = async function _Worker_termination(msg) {
|
|
490
|
+
const proc = __classPrivateFieldGet(this, _Worker_procPool, "f").getByJobId(msg.jobId);
|
|
491
|
+
if (proc === null) {
|
|
492
|
+
// safe to ignore
|
|
493
|
+
return;
|
|
294
494
|
}
|
|
295
|
-
|
|
495
|
+
await proc.close();
|
|
496
|
+
};
|
|
296
497
|
//# sourceMappingURL=worker.js.map
|