Skip to content

Commit 6bff3b0

Browse files
nbspgching
andauthored
chore(tsconfig): enable noUncheckedIndexedAccess (#168)
Co-authored-by: Gavin <[email protected]>
1 parent b719e7d commit 6bff3b0

File tree

16 files changed

+73
-51
lines changed

16 files changed

+73
-51
lines changed

Diff for: .changeset/clean-pugs-lick.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@livekit/agents": patch
3+
"@livekit/agents-plugin-deepgram": patch
4+
"@livekit/agents-plugin-openai": patch
5+
"@livekit/agents-plugin-silero": patch
6+
"livekit-agents-examples": patch
7+
---
8+
9+
chore(tsconfig): enable `noUncheckedIndexedAccess`

Diff for: agents/src/ipc/job_main.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ if (process.send) {
9494
// [1] import.meta.filename
9595
// [2] import.meta.filename of function containing entry file
9696
const moduleFile = process.argv[2];
97-
const agent: Agent = await import(pathToFileURL(moduleFile).href).then((module) => {
97+
const agent: Agent = await import(pathToFileURL(moduleFile!).href).then((module) => {
9898
const agent = module.default;
9999
if (agent === undefined || !isAgent(agent)) {
100100
throw new Error(`Unable to load agent: Missing or invalid default export in ${moduleFile}`);
@@ -110,6 +110,7 @@ if (process.send) {
110110
process.on('SIGINT', () => {});
111111

112112
await once(process, 'message').then(([msg]: IPCMessage[]) => {
113+
msg = msg!;
113114
if (msg.case !== 'initializeRequest') {
114115
throw new Error('first message must be InitializeRequest');
115116
}

Diff for: agents/src/ipc/proc_job_executor.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export class ProcJobExecutor extends JobExecutor {
122122
this.#proc!.send({ case: 'initializeRequest', value: { loggerOptions } });
123123
await once(this.#proc!, 'message').then(([msg]: IPCMessage[]) => {
124124
clearTimeout(timer);
125-
if (msg.case !== 'initializeResponse') {
125+
if (msg!.case !== 'initializeResponse') {
126126
throw new Error('first message must be InitializeResponse');
127127
}
128128
});

Diff for: agents/src/job.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -189,10 +189,7 @@ export class JobContext {
189189
/** @internal */
190190
onParticipantConnected(p: RemoteParticipant) {
191191
for (const callback of this.#participantEntrypoints) {
192-
if (
193-
p.identity in this.#participantTasks &&
194-
this.#participantTasks[p.identity].callback == callback
195-
) {
192+
if (this.#participantTasks[p.identity]?.callback == callback) {
196193
this.#logger.warn(
197194
'a participant has joined before a prior prticipant task matching the same identity has finished:',
198195
p.identity,

Diff for: agents/src/llm/function_context.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ export const oaiBuildFunctionInfo = (
127127
fncName: string,
128128
rawArgs: string,
129129
): FunctionCallInfo => {
130-
if (!fncCtx[fncName]) {
130+
const func = fncCtx[fncName];
131+
if (!func) {
131132
throw new Error(`AI function ${fncName} not found`);
132133
}
133134

134135
return {
135136
name: fncName,
136-
func: fncCtx[fncName],
137+
func,
137138
toolCallId,
138139
rawParams: rawArgs,
139140
params: JSON.parse(rawArgs),

Diff for: agents/src/pipeline/pipeline_agent.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,10 @@ export class VoicePipelineAgent extends (EventEmitter as new () => TypedEmitter<
416416
this.#lastEndOfSpeechTime = Date.now();
417417
});
418418
this.#humanInput.on(HumanInputEvent.INTERIM_TRANSCRIPT, (event) => {
419-
this.#transcribedInterimText = event.alternatives[0].text;
419+
this.#transcribedInterimText = event.alternatives![0].text;
420420
});
421421
this.#humanInput.on(HumanInputEvent.FINAL_TRANSCRIPT, (event) => {
422-
const newTranscript = event.alternatives[0].text;
422+
const newTranscript = event.alternatives![0].text;
423423
if (!newTranscript) return;
424424

425425
this.#logger.child({ userTranscript: newTranscript }).debug('received user transcript');
@@ -905,7 +905,7 @@ class DeferredReplyValidation {
905905
#endWithPunctuation(): boolean {
906906
return (
907907
this.#lastFinalTranscript.length > 0 &&
908-
this.PUNCTUATION.includes(this.#lastFinalTranscript[this.#lastFinalTranscript.length - 1])
908+
this.PUNCTUATION.includes(this.#lastFinalTranscript[this.#lastFinalTranscript.length - 1]!)
909909
);
910910
}
911911

Diff for: agents/src/stt/stream_adapter.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ export class StreamAdapterWrapper extends SpeechStream {
5454
for await (const ev of this.#vadStream) {
5555
switch (ev.type) {
5656
case VADEventType.START_OF_SPEECH:
57-
this.queue.put({ type: SpeechEventType.START_OF_SPEECH, alternatives: [] });
57+
this.queue.put({ type: SpeechEventType.START_OF_SPEECH });
5858
break;
5959
case VADEventType.END_OF_SPEECH:
60-
this.queue.put({ type: SpeechEventType.END_OF_SPEECH, alternatives: [] });
60+
this.queue.put({ type: SpeechEventType.END_OF_SPEECH });
6161

6262
const event = await this.#stt.recognize(ev.frames);
63-
if (!event.alternatives.length || !event.alternatives[0].text) {
63+
if (!event.alternatives![0].text) {
6464
continue;
6565
}
6666

Diff for: agents/src/stt/stt.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export interface SpeechData {
4141
/** SpeechEvent is a packet of speech-to-text data. */
4242
export interface SpeechEvent {
4343
type: SpeechEventType;
44-
alternatives: SpeechData[];
44+
alternatives?: [SpeechData, ...SpeechData[]];
4545
}
4646

4747
/**

Diff for: agents/src/tokenize/basic/hyphenator.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,8 @@ class Hyphenator {
4949
}
5050

5151
// if the word is an exception, get the stored points
52-
let points: number[];
53-
if (this.exceptions[word.toLowerCase()]) {
54-
points = this.exceptions[word.toLowerCase()];
55-
} else {
52+
let points = this.exceptions[word.toLowerCase()];
53+
if (!points) {
5654
const work = `.${word.toLowerCase()}.`;
5755
points = new Array(work.length + 1).fill(0);
5856

@@ -64,7 +62,7 @@ class Hyphenator {
6462
if (node[END]) {
6563
const end = node[END];
6664
end.forEach((x, j) => {
67-
points[i + j] = Math.max(points[i + j], x);
65+
points![i + j] = Math.max(points![i + j]!, x);
6866
});
6967
}
7068
} else {
@@ -80,7 +78,7 @@ class Hyphenator {
8078
const pieces = [''];
8179
for (let i = 0; i < word.length; i++) {
8280
pieces[pieces.length - 1] += word[i];
83-
if (points[i + 2] % 2) {
81+
if (points[i + 2]! % 2) {
8482
pieces.push('');
8583
}
8684
}

Diff for: agents/src/utils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ export const mergeFrames = (buffer: AudioBuffer): AudioFrame => {
2828
throw new TypeError('buffer is empty');
2929
}
3030

31-
const sampleRate = buffer[0].sampleRate;
32-
const channels = buffer[0].channels;
31+
const sampleRate = buffer[0]!.sampleRate;
32+
const channels = buffer[0]!.channels;
3333
let samplesPerChannel = 0;
3434
let data = new Int16Array();
3535

Diff for: agents/src/worker.ts

+14-10
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,8 @@ const defaultCpuLoad = async (): Promise<number> => {
8989
let total = 0;
9090

9191
for (let i = 0; i < cpus1.length; i++) {
92-
const cpu1 = cpus1[i].times;
93-
const cpu2 = cpus2[i].times;
92+
const cpu1 = cpus1[i]!.times;
93+
const cpu2 = cpus2[i]!.times;
9494

9595
idle += cpu2.idle - cpu1.idle;
9696

@@ -495,7 +495,7 @@ export class Worker {
495495
if (job.id in this.#pending) {
496496
const task = this.#pending[job.id];
497497
delete this.#pending[job.id];
498-
task.resolve(msg.message.value);
498+
task?.resolve(msg.message.value);
499499
} else {
500500
this.#logger.child({ job }).warn('received assignment for unknown job ' + job.id);
501501
}
@@ -610,17 +610,21 @@ export class Worker {
610610
this.#logger.child({ req }).warn(`assignment for job ${req.id} timed out`);
611611
return;
612612
}, ASSIGNMENT_TIMEOUT);
613-
const asgn = await this.#pending[req.id].promise.then(async (asgn) => {
613+
const asgn = await this.#pending[req.id]?.promise.then(async (asgn) => {
614614
clearTimeout(timer);
615615
return asgn;
616616
});
617617

618-
await this.#procPool.launchJob({
619-
acceptArguments: args,
620-
job: msg.job!,
621-
url: asgn.url || this.#opts.wsURL,
622-
token: asgn.token,
623-
});
618+
if (asgn) {
619+
await this.#procPool.launchJob({
620+
acceptArguments: args,
621+
job: msg.job!,
622+
url: asgn.url || this.#opts.wsURL,
623+
token: asgn.token,
624+
});
625+
} else {
626+
this.#logger.child({ requestId: req.id }).warn('pending assignment not found');
627+
}
624628
};
625629

626630
const req = new JobRequest(msg.job!, onReject, onAccept);

Diff for: examples/src/stt.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default defineAgent({
2525
const recvTask = async () => {
2626
for await (const event of sttStream) {
2727
if (event.type === stt.SpeechEventType.FINAL_TRANSCRIPT) {
28-
console.log(event.alternatives[0].text);
28+
console.log(event.alternatives![0].text);
2929
}
3030
}
3131
};

Diff for: plugins/deepgram/src/stt.ts

+12-6
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ export class SpeechStream extends stt.SpeechStream {
237237
// It's also possible we receive a transcript without a SpeechStarted event.
238238
if (this.#speaking) return;
239239
this.#speaking = true;
240-
this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH, alternatives: [] });
240+
this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });
241241
break;
242242
}
243243
// see this page:
@@ -252,16 +252,22 @@ export class SpeechStream extends stt.SpeechStream {
252252
// If, for some reason, we didn't get a SpeechStarted event but we got
253253
// a transcript with text, we should start speaking. It's rare but has
254254
// been observed.
255-
if (alternatives.length > 0 && alternatives[0].text) {
255+
if (alternatives[0] && alternatives[0].text) {
256256
if (!this.#speaking) {
257257
this.#speaking = true;
258-
this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH, alternatives: [] });
258+
this.queue.put({ type: stt.SpeechEventType.START_OF_SPEECH });
259259
}
260260

261261
if (isFinal) {
262-
this.queue.put({ type: stt.SpeechEventType.FINAL_TRANSCRIPT, alternatives });
262+
this.queue.put({
263+
type: stt.SpeechEventType.FINAL_TRANSCRIPT,
264+
alternatives: [alternatives[0], ...alternatives.splice(0)],
265+
});
263266
} else {
264-
this.queue.put({ type: stt.SpeechEventType.INTERIM_TRANSCRIPT, alternatives });
267+
this.queue.put({
268+
type: stt.SpeechEventType.INTERIM_TRANSCRIPT,
269+
alternatives: [alternatives[0], ...alternatives.splice(0)],
270+
});
265271
}
266272
}
267273

@@ -270,7 +276,7 @@ export class SpeechStream extends stt.SpeechStream {
270276
// a non-empty transcript (deepgram doesn't have a SpeechEnded event)
271277
if (isEndpoint && this.#speaking) {
272278
this.#speaking = false;
273-
this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH, alternatives: [] });
279+
this.queue.put({ type: stt.SpeechEventType.END_OF_SPEECH });
274280
}
275281

276282
break;

Diff for: plugins/openai/src/realtime/realtime_model.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -846,8 +846,8 @@ export class RealtimeSession extends multimodal.RealtimeSession {
846846

847847
#getContent(ptr: ContentPtr): RealtimeContent {
848848
const response = this.#pendingResponses[ptr.response_id];
849-
const output = response.output[ptr.output_index];
850-
const content = output.content[ptr.content_index];
849+
const output = response!.output[ptr.output_index];
850+
const content = output!.content[ptr.content_index]!;
851851
return content;
852852
}
853853

@@ -940,7 +940,7 @@ export class RealtimeSession extends multimodal.RealtimeSession {
940940
#handleResponseDone(event: api_proto.ResponseDoneEvent): void {
941941
const responseData = event.response;
942942
const responseId = responseData.id;
943-
const response = this.#pendingResponses[responseId];
943+
const response = this.#pendingResponses[responseId]!;
944944
response.status = responseData.status;
945945
response.statusDetails = responseData.status_details;
946946
response.usage = responseData.usage ?? null;
@@ -974,17 +974,17 @@ export class RealtimeSession extends multimodal.RealtimeSession {
974974
content: [],
975975
doneFut: new Future(),
976976
};
977-
response.output.push(newOutput);
977+
response?.output.push(newOutput);
978978
this.emit('response_output_added', newOutput);
979979
}
980980

981981
#handleResponseOutputItemDone(event: api_proto.ResponseOutputItemDoneEvent): void {
982982
const responseId = event.response_id;
983983
const response = this.#pendingResponses[responseId];
984984
const outputIndex = event.output_index;
985-
const output = response.output[outputIndex];
985+
const output = response!.output[outputIndex];
986986

987-
if (output.type === 'function_call') {
987+
if (output?.type === 'function_call') {
988988
if (!this.#fncCtx) {
989989
this.#logger.error('function call received but no fncCtx is available');
990990
return;
@@ -995,6 +995,11 @@ export class RealtimeSession extends multimodal.RealtimeSession {
995995
if (item.type !== 'function_call') {
996996
throw new Error('Expected function_call item');
997997
}
998+
const func = this.#fncCtx[item.name];
999+
if (!func) {
1000+
this.#logger.error(`no function with name ${item.name} in fncCtx`);
1001+
return;
1002+
}
9981003

9991004
this.emit('function_call_started', {
10001005
callId: item.call_id,
@@ -1006,7 +1011,7 @@ export class RealtimeSession extends multimodal.RealtimeSession {
10061011
`[Function Call ${item.call_id}] Executing ${item.name} with arguments ${parsedArgs}`,
10071012
);
10081013

1009-
this.#fncCtx[item.name].execute(parsedArgs).then(
1014+
func.execute(parsedArgs).then(
10101015
(content) => {
10111016
this.#logger.debug(`[Function Call ${item.call_id}] ${item.name} returned ${content}`);
10121017
this.emit('function_call_completed', {
@@ -1032,15 +1037,15 @@ export class RealtimeSession extends multimodal.RealtimeSession {
10321037
);
10331038
}
10341039

1035-
output.doneFut.resolve();
1040+
output?.doneFut.resolve();
10361041
this.emit('response_output_done', output);
10371042
}
10381043

10391044
#handleResponseContentPartAdded(event: api_proto.ResponseContentPartAddedEvent): void {
10401045
const responseId = event.response_id;
10411046
const response = this.#pendingResponses[responseId];
10421047
const outputIndex = event.output_index;
1043-
const output = response.output[outputIndex];
1048+
const output = response!.output[outputIndex];
10441049

10451050
const textStream = new AsyncIterableQueue<string>();
10461051
const audioStream = new AsyncIterableQueue<AudioFrame>();
@@ -1056,7 +1061,7 @@ export class RealtimeSession extends multimodal.RealtimeSession {
10561061
audioStream: audioStream,
10571062
toolCalls: [],
10581063
};
1059-
output.content.push(newContent);
1064+
output?.content.push(newContent);
10601065
this.emit('response_content_added', newContent);
10611066
}
10621067

Diff for: plugins/silero/src/onnx_model.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class OnnxModel {
7474
.then((result) => {
7575
// this.#state = result.output.data as Float32Array,
7676
this.#context = this.#inputBuffer.subarray(0, this.#contextSize);
77-
return (result.output.data as Float32Array).at(0)!;
77+
return (result.output!.data as Float32Array).at(0)!;
7878
});
7979
}
8080
}

Diff for: tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"declarationMap": true,
88
"allowJs": false,
99
"strict": true,
10+
"noUncheckedIndexedAccess": true,
1011
"skipLibCheck": true,
1112
"esModuleInterop": true,
1213
"sourceMap": true,

0 commit comments

Comments
 (0)