Skip to content

Commit ba8711f

Browse files
committed
Add worker, fill out CLI, + various lints
1 parent 9e47658 commit ba8711f

File tree

15 files changed

+466
-57
lines changed

15 files changed

+466
-57
lines changed

agents/build.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import dts from 'bun-plugin-dts';
66

77
await Bun.build({
8-
entrypoints: ['./src/index.ts', './src/tts/index.ts', './src/stt/index.ts'],
8+
entrypoints: ['./src/index.ts', './src/tts/index.ts', './src/stt/index.ts', './src/cli.ts'],
99
outdir: './dist',
1010
target: 'bun', // https://github.com/oven-sh/bun/blob/main/src/bundler/bundle_v2.zig#L2667
1111
sourcemap: 'external',

agents/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
},
1616
"devDependencies": {
1717
"@types/bun": "latest",
18+
"@types/ws": "^8.5.10",
1819
"bun-plugin-dts": "^0.2.1",
1920
"eslint": "^8.57.0",
2021
"eslint-config-prettier": "^9.1.0",
@@ -25,10 +26,11 @@
2526
"typescript": "^5.0.0"
2627
},
2728
"dependencies": {
28-
"@livekit/protocol": "^1.12.0",
29+
"@livekit/protocol": "^1.13.0",
2930
"commander": "^12.0.0",
3031
"livekit-server-sdk": "^2.1.2",
3132
"pino": "^8.19.0",
32-
"pino-pretty": "^11.0.0"
33+
"pino-pretty": "^11.0.0",
34+
"ws": "^8.16.0"
3335
}
3436
}

agents/src/cli.ts

Lines changed: 70 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,77 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
import { version } from './index';
5+
import { version } from '.';
66
import { Option, Command } from 'commander';
7+
import { WorkerOptions, Worker } from './worker';
8+
import { EventEmitter } from 'events';
9+
import { log } from './log';
710

8-
const program = new Command();
9-
program
10-
.name('agents')
11-
.description('LiveKit Agents CLI')
12-
.version(version)
13-
.addOption(
14-
new Option('--log-level', 'Set the logging level').choices([
15-
'DEBUG',
16-
'INFO',
17-
'WARNING',
18-
'ERROR',
19-
'CRITICAL',
20-
]),
21-
);
22-
23-
program
24-
.command('start')
25-
.description('Start the worker')
26-
.addOption(
27-
new Option('--url <string>', 'LiveKit server or Cloud project websocket URL')
28-
.makeOptionMandatory(true)
29-
.env('LIVEKIT_URL'),
30-
)
31-
.addOption(
32-
new Option('--api-key <string>', "LiveKit server or Cloud project's API key")
33-
.makeOptionMandatory(true)
34-
.env('LIVEKIT_API_KEY'),
35-
)
36-
.addOption(
37-
new Option('--api-secret <string>', "LiveKit server or Cloud project's API secret")
38-
.makeOptionMandatory(true)
39-
.env('LIVEKIT_API_SECRET'),
40-
)
41-
.action(() => {
42-
return;
11+
type CliArgs = {
12+
opts: WorkerOptions;
13+
logLevel: string;
14+
production: boolean;
15+
watch: boolean;
16+
event?: EventEmitter;
17+
};
18+
19+
const runWorker = async (args: CliArgs) => {
20+
log.level = args.logLevel;
21+
const worker = new Worker(args.opts);
22+
23+
process.on('SIGINT', async () => {
24+
await worker.close();
25+
process.exit(130); // SIGINT exit code
4326
});
4427

45-
program.parse();
28+
try {
29+
await worker.run();
30+
} catch {
31+
log.fatal('worker failed');
32+
}
33+
};
34+
35+
export const runApp = (opts: WorkerOptions) => {
36+
const program = new Command()
37+
.name('agents')
38+
.description('LiveKit Agents CLI')
39+
.version(version)
40+
.addOption(
41+
new Option('--log-level <level>', 'Set the logging level')
42+
.choices(['trace', 'debug', 'info', 'warn', 'error', 'fatal'])
43+
.default('trace'),
44+
)
45+
.addOption(
46+
new Option('--url <string>', 'LiveKit server or Cloud project websocket URL')
47+
.makeOptionMandatory(true)
48+
.env('LIVEKIT_URL'),
49+
)
50+
.addOption(
51+
new Option('--api-key <string>', "LiveKit server or Cloud project's API key")
52+
.makeOptionMandatory(true)
53+
.env('LIVEKIT_API_KEY'),
54+
)
55+
.addOption(
56+
new Option('--api-secret <string>', "LiveKit server or Cloud project's API secret")
57+
.makeOptionMandatory(true)
58+
.env('LIVEKIT_API_SECRET'),
59+
)
60+
61+
program
62+
.command('start')
63+
.description('Start the worker in production mode')
64+
.action(() => {
65+
const options = program.optsWithGlobals()
66+
opts.wsURL = options.url || opts.wsURL;
67+
opts.apiKey = options.apiKey || opts.apiKey;
68+
opts.apiSecret = options.apiSecret || opts.apiSecret;
69+
runWorker({
70+
opts,
71+
production: true,
72+
watch: false,
73+
logLevel: options.logLevel,
74+
});
75+
});
76+
77+
program.parse();
78+
};

agents/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,6 @@
55
export * from './vad';
66
export * from './plugin';
77
export * from './version';
8+
export * from './job_context';
9+
export * from './job_request';
10+
export * from './worker';

agents/src/ipc/job_process.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ import { runJob } from './job_main';
2020
import { EventEmitter } from 'events';
2121
import { log } from '../log';
2222

23-
const START_TIMEOUT = 90;
24-
const PING_INTERVAL = 5;
25-
const PING_TIMEOUT = 90;
26-
const HIGH_PING_THRESHOLD = 10; // milliseconds
23+
const START_TIMEOUT = 90 * 1000;
24+
const PING_INTERVAL = 5 * 1000;
25+
const PING_TIMEOUT = 90 * 1000;
26+
const HIGH_PING_THRESHOLD = 10;
2727

2828
export class JobProcess {
2929
#job: Job;
@@ -103,6 +103,7 @@ export class JobProcess {
103103
const delay = Date.now() - msg.timestamp;
104104
if (delay > HIGH_PING_THRESHOLD) {
105105
this.logger.warn(`job is unresponsive (${delay}ms delay)`);
106+
// @ts-expect-error: this actually works fine types/bun doesn't have a typedecl for it yet
106107
pongTimeout.refresh();
107108
}
108109
} else if (msg instanceof UserExit || msg instanceof ShutdownResponse) {

agents/src/ipc/protocol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export class StartJobRequest implements Message {
3131

3232
export class StartJobResponse implements Message {
3333
static MSG_ID = 1;
34-
err: Error | undefined;
34+
err?: Error;
3535

3636
get MSG_ID(): number {
3737
return StartJobResponse.MSG_ID;

agents/src/job_context.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { EventEmitter } from 'events';
99
export class JobContext {
1010
#job: Job;
1111
#room: Room;
12-
#publisher: RemoteParticipant | undefined;
12+
#publisher?: RemoteParticipant;
1313
tx: EventEmitter;
1414

1515
constructor(

agents/src/job_request.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
import { JobContext } from './job_context';
6-
import { VideoGrant } from 'livekit-server-sdk';
76
import { Job, ParticipantInfo, Room } from '@livekit/protocol';
87
import { log } from './log';
98
import { EventEmitter } from 'events';
@@ -35,16 +34,15 @@ export type AcceptData = {
3534
entry: AgentEntry;
3635
autoSubscribe: AutoSubscribe;
3736
autoDisconnect: AutoDisconnect;
38-
grants: VideoGrant;
3937
name: string;
4038
identity: string;
4139
metadata: string;
4240
assign: EventEmitter;
4341
};
4442

45-
type AvailRes = {
43+
export type AvailRes = {
4644
avail: boolean;
47-
data: AcceptData | undefined;
45+
data?: AcceptData;
4846
};
4947

5048
export class JobRequest {
@@ -91,7 +89,6 @@ export class JobRequest {
9189
entry: AgentEntry,
9290
autoSubscribe: AutoSubscribe = AutoSubscribe.SUBSCRIBE_ALL,
9391
autoDisconnect: AutoDisconnect = AutoDisconnect.ROOM_EMPTY,
94-
grants: VideoGrant,
9592
name: string = '',
9693
identity: string = '',
9794
metadata: string = '',
@@ -110,7 +107,6 @@ export class JobRequest {
110107
entry,
111108
autoSubscribe,
112109
autoDisconnect,
113-
grants,
114110
name,
115111
identity,
116112
metadata,

agents/src/stt/stream_adapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export class StreamAdapterWrapper extends SpeechStream {
1212
stt: STT;
1313
vadStream: VADStream;
1414
eventQueue: (SpeechEvent | undefined)[];
15-
language: string | undefined;
15+
language?: string;
1616
task: {
1717
run: Promise<void>;
1818
cancel: () => void;

agents/src/stt/stt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export abstract class STT {
4949
this.#streamingSupported = streamingSupported;
5050
}
5151

52-
abstract recognize(buffer: AudioBuffer, language: string | undefined): Promise<SpeechEvent>;
52+
abstract recognize(buffer: AudioBuffer, language?: string): Promise<SpeechEvent>;
5353

5454
abstract stream(language: string | undefined): SpeechStream;
5555

0 commit comments

Comments
 (0)