@livekit/agents-plugin-openai 1.0.0-next.5 → 1.0.0-next.7

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/dist/tts.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AudioByteStream, shortuuid, tts } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { OpenAI } from 'openai';\nimport type { TTSModels, TTSVoices } from './models.js';\n\nconst OPENAI_TTS_SAMPLE_RATE = 24000;\nconst OPENAI_TTS_CHANNELS = 1;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n voice: TTSVoices;\n speed: number;\n instructions?: string;\n baseURL?: string;\n client?: OpenAI;\n apiKey?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n apiKey: process.env.OPENAI_API_KEY,\n model: 'tts-1',\n voice: 'alloy',\n speed: 1,\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: OpenAI;\n label = 'openai.TTS';\n\n /**\n * Create a new instance of OpenAI TTS.\n *\n * @remarks\n * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the\n * `OPENAI_API_KEY` environmental variable.\n */\n constructor(opts: Partial<TTSOptions> = defaultTTSOptions) {\n super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });\n\n this.#opts = { ...defaultTTSOptions, ...opts };\n if (this.#opts.apiKey === undefined) {\n throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');\n }\n\n this.#client =\n this.#opts.client ||\n new OpenAI({\n baseURL: opts.baseURL,\n apiKey: opts.apiKey,\n });\n }\n\n updateOptions(opts: { model?: TTSModels | string; voice?: TTSVoices; speed?: number }) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(\n this,\n text,\n this.#client.audio.speech.create({\n input: text,\n model: this.#opts.model,\n voice: this.#opts.voice,\n instructions: this.#opts.instructions,\n response_format: 'pcm',\n speed: this.#opts.speed,\n }),\n );\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on OpenAI TTS');\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'openai.ChunkedStream';\n private stream: Promise<any>;\n\n // set Promise<T> to any because OpenAI returns an annoying Response type\n constructor(tts: TTS, text: string, stream: Promise<any>) {\n super(text, tts);\n this.stream = stream;\n }\n\n protected async run() {\n const buffer = await this.stream.then((r) => r.arrayBuffer());\n const requestId = shortuuid();\n const audioByteStream = new AudioByteStream(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS);\n const frames = audioByteStream.write(buffer);\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n for (const frame of frames) {\n sendLastFrame(requestId, false);\n lastFrame = frame;\n }\n sendLastFrame(requestId, true);\n\n this.queue.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAgD;AAEhD,oBAAuB;AAGvB,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAY5B,MAAM,oBAAgC;AAAA,EACpC,QAAQ,QAAQ,IAAI;AAAA,EACpB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEO,MAAM,YAAY,kBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASR,YAAY,OAA4B,mBAAmB;AACzD,UAAM,wBAAwB,qBAAqB,EAAE,WAAW,MAAM,CAAC;AAEvE,SAAK,QAAQ,EAAE,GAAG,mBAAmB,GAAG,KAAK;AAC7C,QAAI,KAAK,MAAM,WAAW,QAAW;AACnC,YAAM,IAAI,MAAM,0EAA0E;AAAA,IAC5F;AAEA,SAAK,UACH,KAAK,MAAM,UACX,IAAI,qBAAO;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,MAAyE;AACrF,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,KAAK;AAAA,EACxC;AAAA,EAEA,WAAW,MAA6B;AACtC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,QAAQ,MAAM,OAAO,OAAO;AAAA,QAC/B,OAAO;AAAA,QACP,OAAO,KAAK,MAAM;AAAA,QAClB,OAAO,KAAK,MAAM;AAAA,QAClB,cAAc,KAAK,MAAM;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO,KAAK,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAA+B;AAC7B,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACF;AAEO,MAAM,sBAAsB,kBAAI,cAAc;AAAA,EACnD,QAAQ;AAAA,EACA;AAAA;AAAA,EAGR,YAAYA,MAAU,MAAc,QAAsB;AACxD,UAAM,MAAMA,IAAG;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,MAAM;AACpB,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC;AAC5D,UAAM,gBAAY,yBAAU;AAC5B,UAAM,kBAAkB,IAAI,8BAAgB,wBAAwB,mBAAmB;AACvF,UAAM,SAAS,gBAAgB,MAAM,MAAM;AAE3C,QAAI;AACJ,UAAM,gBAAgB,CAAC,WAAmB,UAAmB;AAC3D,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,MAAM,CAAC;AAChE,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B,oBAAc,WAAW,KAAK;AAC9B,kBAAY;AAAA,IACd;AACA,kBAAc,WAAW,IAAI;AAE7B,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;","names":["tts"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AudioByteStream, shortuuid, tts } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { OpenAI } from 'openai';\nimport type { TTSModels, TTSVoices } from './models.js';\n\nconst OPENAI_TTS_SAMPLE_RATE = 24000;\nconst OPENAI_TTS_CHANNELS = 1;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n voice: TTSVoices;\n speed: number;\n instructions?: string;\n baseURL?: string;\n client?: OpenAI;\n apiKey?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n apiKey: process.env.OPENAI_API_KEY,\n model: 'tts-1',\n voice: 'alloy',\n speed: 1,\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: OpenAI;\n label = 'openai.TTS';\n\n /**\n * Create a new instance of OpenAI TTS.\n *\n * @remarks\n * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the\n * `OPENAI_API_KEY` environment variable.\n */\n constructor(opts: Partial<TTSOptions> = defaultTTSOptions) {\n super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });\n\n this.#opts = { ...defaultTTSOptions, ...opts };\n if (this.#opts.apiKey === undefined) {\n throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');\n }\n\n this.#client =\n this.#opts.client ||\n new OpenAI({\n baseURL: opts.baseURL,\n apiKey: opts.apiKey,\n });\n }\n\n updateOptions(opts: { model?: TTSModels | string; voice?: TTSVoices; speed?: number }) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(\n this,\n text,\n this.#client.audio.speech.create({\n input: text,\n model: this.#opts.model,\n voice: this.#opts.voice,\n instructions: this.#opts.instructions,\n response_format: 'pcm',\n speed: this.#opts.speed,\n }),\n );\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on OpenAI TTS');\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'openai.ChunkedStream';\n private stream: Promise<any>;\n\n // set Promise<T> to any because OpenAI returns an annoying Response type\n constructor(tts: TTS, text: string, stream: Promise<any>) {\n super(text, tts);\n this.stream = stream;\n }\n\n protected async run() {\n const buffer = await this.stream.then((r) => r.arrayBuffer());\n const requestId = shortuuid();\n const audioByteStream = new AudioByteStream(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS);\n const frames = audioByteStream.write(buffer);\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n for (const frame of frames) {\n sendLastFrame(requestId, false);\n lastFrame = frame;\n }\n sendLastFrame(requestId, true);\n\n this.queue.close();\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAgD;AAEhD,oBAAuB;AAGvB,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAY5B,MAAM,oBAAgC;AAAA,EACpC,QAAQ,QAAQ,IAAI;AAAA,EACpB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEO,MAAM,YAAY,kBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASR,YAAY,OAA4B,mBAAmB;AACzD,UAAM,wBAAwB,qBAAqB,EAAE,WAAW,MAAM,CAAC;AAEvE,SAAK,QAAQ,EAAE,GAAG,mBAAmB,GAAG,KAAK;AAC7C,QAAI,KAAK,MAAM,WAAW,QAAW;AACnC,YAAM,IAAI,MAAM,0EAA0E;AAAA,IAC5F;AAEA,SAAK,UACH,KAAK,MAAM,UACX,IAAI,qBAAO;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,MAAyE;AACrF,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,KAAK;AAAA,EACxC;AAAA,EAEA,WAAW,MAA6B;AACtC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,QAAQ,MAAM,OAAO,OAAO;AAAA,QAC/B,OAAO;AAAA,QACP,OAAO,KAAK,MAAM;AAAA,QAClB,OAAO,KAAK,MAAM;AAAA,QAClB,cAAc,KAAK,MAAM;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO,KAAK,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAA+B;AAC7B,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACF;AAEO,MAAM,sBAAsB,kBAAI,cAAc;AAAA,EACnD,QAAQ;AAAA,EACA;AAAA;AAAA,EAGR,YAAYA,MAAU,MAAc,QAAsB;AACxD,UAAM,MAAMA,IAAG;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,MAAM;AACpB,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC;AAC5D,UAAM,gBAAY,yBAAU;AAC5B,UAAM,kBAAkB,IAAI,8BAAgB,wBAAwB,mBAAmB;AACvF,UAAM,SAAS,gBAAgB,MAAM,MAAM;AAE3C,QAAI;AACJ,UAAM,gBAAgB,CAAC,WAAmB,UAAmB;AAC3D,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,MAAM,CAAC;AAChE,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B,oBAAc,WAAW,KAAK;AAC9B,kBAAY;AAAA,IACd;AACA,kBAAc,WAAW,IAAI;AAE7B,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;","names":["tts"]}
package/dist/tts.d.cts CHANGED
@@ -18,7 +18,7 @@ export declare class TTS extends tts.TTS {
18
18
  *
19
19
  * @remarks
20
20
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
21
- * `OPENAI_API_KEY` environmental variable.
21
+ * `OPENAI_API_KEY` environment variable.
22
22
  */
23
23
  constructor(opts?: Partial<TTSOptions>);
24
24
  updateOptions(opts: {
package/dist/tts.d.ts CHANGED
@@ -18,7 +18,7 @@ export declare class TTS extends tts.TTS {
18
18
  *
19
19
  * @remarks
20
20
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
21
- * `OPENAI_API_KEY` environmental variable.
21
+ * `OPENAI_API_KEY` environment variable.
22
22
  */
23
23
  constructor(opts?: Partial<TTSOptions>);
24
24
  updateOptions(opts: {
package/dist/tts.js CHANGED
@@ -17,7 +17,7 @@ class TTS extends tts.TTS {
17
17
  *
18
18
  * @remarks
19
19
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
20
- * `OPENAI_API_KEY` environmental variable.
20
+ * `OPENAI_API_KEY` environment variable.
21
21
  */
22
22
  constructor(opts = defaultTTSOptions) {
23
23
  super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });
package/dist/tts.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AudioByteStream, shortuuid, tts } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { OpenAI } from 'openai';\nimport type { TTSModels, TTSVoices } from './models.js';\n\nconst OPENAI_TTS_SAMPLE_RATE = 24000;\nconst OPENAI_TTS_CHANNELS = 1;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n voice: TTSVoices;\n speed: number;\n instructions?: string;\n baseURL?: string;\n client?: OpenAI;\n apiKey?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n apiKey: process.env.OPENAI_API_KEY,\n model: 'tts-1',\n voice: 'alloy',\n speed: 1,\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: OpenAI;\n label = 'openai.TTS';\n\n /**\n * Create a new instance of OpenAI TTS.\n *\n * @remarks\n * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the\n * `OPENAI_API_KEY` environmental variable.\n */\n constructor(opts: Partial<TTSOptions> = defaultTTSOptions) {\n super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });\n\n this.#opts = { ...defaultTTSOptions, ...opts };\n if (this.#opts.apiKey === undefined) {\n throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');\n }\n\n this.#client =\n this.#opts.client ||\n new OpenAI({\n baseURL: opts.baseURL,\n apiKey: opts.apiKey,\n });\n }\n\n updateOptions(opts: { model?: TTSModels | string; voice?: TTSVoices; speed?: number }) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(\n this,\n text,\n this.#client.audio.speech.create({\n input: text,\n model: this.#opts.model,\n voice: this.#opts.voice,\n instructions: this.#opts.instructions,\n response_format: 'pcm',\n speed: this.#opts.speed,\n }),\n );\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on OpenAI TTS');\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'openai.ChunkedStream';\n private stream: Promise<any>;\n\n // set Promise<T> to any because OpenAI returns an annoying Response type\n constructor(tts: TTS, text: string, stream: Promise<any>) {\n super(text, tts);\n this.stream = stream;\n }\n\n protected async run() {\n const buffer = await this.stream.then((r) => r.arrayBuffer());\n const requestId = shortuuid();\n const audioByteStream = new AudioByteStream(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS);\n const frames = audioByteStream.write(buffer);\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n for (const frame of frames) {\n sendLastFrame(requestId, false);\n lastFrame = frame;\n }\n sendLastFrame(requestId, true);\n\n this.queue.close();\n }\n}\n"],"mappings":"AAGA,SAAS,iBAAiB,WAAW,WAAW;AAEhD,SAAS,cAAc;AAGvB,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAY5B,MAAM,oBAAgC;AAAA,EACpC,QAAQ,QAAQ,IAAI;AAAA,EACpB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEO,MAAM,YAAY,IAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASR,YAAY,OAA4B,mBAAmB;AACzD,UAAM,wBAAwB,qBAAqB,EAAE,WAAW,MAAM,CAAC;AAEvE,SAAK,QAAQ,EAAE,GAAG,mBAAmB,GAAG,KAAK;AAC7C,QAAI,KAAK,MAAM,WAAW,QAAW;AACnC,YAAM,IAAI,MAAM,0EAA0E;AAAA,IAC5F;AAEA,SAAK,UACH,KAAK,MAAM,UACX,IAAI,OAAO;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,MAAyE;AACrF,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,KAAK;AAAA,EACxC;AAAA,EAEA,WAAW,MAA6B;AACtC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,QAAQ,MAAM,OAAO,OAAO;AAAA,QAC/B,OAAO;AAAA,QACP,OAAO,KAAK,MAAM;AAAA,QAClB,OAAO,KAAK,MAAM;AAAA,QAClB,cAAc,KAAK,MAAM;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO,KAAK,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAA+B;AAC7B,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACF;AAEO,MAAM,sBAAsB,IAAI,cAAc;AAAA,EACnD,QAAQ;AAAA,EACA;AAAA;AAAA,EAGR,YAAYA,MAAU,MAAc,QAAsB;AACxD,UAAM,MAAMA,IAAG;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,MAAM;AACpB,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC;AAC5D,UAAM,YAAY,UAAU;AAC5B,UAAM,kBAAkB,IAAI,gBAAgB,wBAAwB,mBAAmB;AACvF,UAAM,SAAS,gBAAgB,MAAM,MAAM;AAE3C,QAAI;AACJ,UAAM,gBAAgB,CAAC,WAAmB,UAAmB;AAC3D,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,MAAM,CAAC;AAChE,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B,oBAAc,WAAW,KAAK;AAC9B,kBAAY;AAAA,IACd;AACA,kBAAc,WAAW,IAAI;AAE7B,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;","names":["tts"]}
1
+ {"version":3,"sources":["../src/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { AudioByteStream, shortuuid, tts } from '@livekit/agents';\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { OpenAI } from 'openai';\nimport type { TTSModels, TTSVoices } from './models.js';\n\nconst OPENAI_TTS_SAMPLE_RATE = 24000;\nconst OPENAI_TTS_CHANNELS = 1;\n\nexport interface TTSOptions {\n model: TTSModels | string;\n voice: TTSVoices;\n speed: number;\n instructions?: string;\n baseURL?: string;\n client?: OpenAI;\n apiKey?: string;\n}\n\nconst defaultTTSOptions: TTSOptions = {\n apiKey: process.env.OPENAI_API_KEY,\n model: 'tts-1',\n voice: 'alloy',\n speed: 1,\n};\n\nexport class TTS extends tts.TTS {\n #opts: TTSOptions;\n #client: OpenAI;\n label = 'openai.TTS';\n\n /**\n * Create a new instance of OpenAI TTS.\n *\n * @remarks\n * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the\n * `OPENAI_API_KEY` environment variable.\n */\n constructor(opts: Partial<TTSOptions> = defaultTTSOptions) {\n super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });\n\n this.#opts = { ...defaultTTSOptions, ...opts };\n if (this.#opts.apiKey === undefined) {\n throw new Error('OpenAI API key is required, whether as an argument or as $OPENAI_API_KEY');\n }\n\n this.#client =\n this.#opts.client ||\n new OpenAI({\n baseURL: opts.baseURL,\n apiKey: opts.apiKey,\n });\n }\n\n updateOptions(opts: { model?: TTSModels | string; voice?: TTSVoices; speed?: number }) {\n this.#opts = { ...this.#opts, ...opts };\n }\n\n synthesize(text: string): ChunkedStream {\n return new ChunkedStream(\n this,\n text,\n this.#client.audio.speech.create({\n input: text,\n model: this.#opts.model,\n voice: this.#opts.voice,\n instructions: this.#opts.instructions,\n response_format: 'pcm',\n speed: this.#opts.speed,\n }),\n );\n }\n\n stream(): tts.SynthesizeStream {\n throw new Error('Streaming is not supported on OpenAI TTS');\n }\n}\n\nexport class ChunkedStream extends tts.ChunkedStream {\n label = 'openai.ChunkedStream';\n private stream: Promise<any>;\n\n // set Promise<T> to any because OpenAI returns an annoying Response type\n constructor(tts: TTS, text: string, stream: Promise<any>) {\n super(text, tts);\n this.stream = stream;\n }\n\n protected async run() {\n const buffer = await this.stream.then((r) => r.arrayBuffer());\n const requestId = shortuuid();\n const audioByteStream = new AudioByteStream(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS);\n const frames = audioByteStream.write(buffer);\n\n let lastFrame: AudioFrame | undefined;\n const sendLastFrame = (segmentId: string, final: boolean) => {\n if (lastFrame) {\n this.queue.put({ requestId, segmentId, frame: lastFrame, final });\n lastFrame = undefined;\n }\n };\n\n for (const frame of frames) {\n sendLastFrame(requestId, false);\n lastFrame = frame;\n }\n sendLastFrame(requestId, true);\n\n this.queue.close();\n }\n}\n"],"mappings":"AAGA,SAAS,iBAAiB,WAAW,WAAW;AAEhD,SAAS,cAAc;AAGvB,MAAM,yBAAyB;AAC/B,MAAM,sBAAsB;AAY5B,MAAM,oBAAgC;AAAA,EACpC,QAAQ,QAAQ,IAAI;AAAA,EACpB,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AACT;AAEO,MAAM,YAAY,IAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASR,YAAY,OAA4B,mBAAmB;AACzD,UAAM,wBAAwB,qBAAqB,EAAE,WAAW,MAAM,CAAC;AAEvE,SAAK,QAAQ,EAAE,GAAG,mBAAmB,GAAG,KAAK;AAC7C,QAAI,KAAK,MAAM,WAAW,QAAW;AACnC,YAAM,IAAI,MAAM,0EAA0E;AAAA,IAC5F;AAEA,SAAK,UACH,KAAK,MAAM,UACX,IAAI,OAAO;AAAA,MACT,SAAS,KAAK;AAAA,MACd,QAAQ,KAAK;AAAA,IACf,CAAC;AAAA,EACL;AAAA,EAEA,cAAc,MAAyE;AACrF,SAAK,QAAQ,EAAE,GAAG,KAAK,OAAO,GAAG,KAAK;AAAA,EACxC;AAAA,EAEA,WAAW,MAA6B;AACtC,WAAO,IAAI;AAAA,MACT;AAAA,MACA;AAAA,MACA,KAAK,QAAQ,MAAM,OAAO,OAAO;AAAA,QAC/B,OAAO;AAAA,QACP,OAAO,KAAK,MAAM;AAAA,QAClB,OAAO,KAAK,MAAM;AAAA,QAClB,cAAc,KAAK,MAAM;AAAA,QACzB,iBAAiB;AAAA,QACjB,OAAO,KAAK,MAAM;AAAA,MACpB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAA+B;AAC7B,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AACF;AAEO,MAAM,sBAAsB,IAAI,cAAc;AAAA,EACnD,QAAQ;AAAA,EACA;AAAA;AAAA,EAGR,YAAYA,MAAU,MAAc,QAAsB;AACxD,UAAM,MAAMA,IAAG;AACf,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAgB,MAAM;AACpB,UAAM,SAAS,MAAM,KAAK,OAAO,KAAK,CAAC,MAAM,EAAE,YAAY,CAAC;AAC5D,UAAM,YAAY,UAAU;AAC5B,UAAM,kBAAkB,IAAI,gBAAgB,wBAAwB,mBAAmB;AACvF,UAAM,SAAS,gBAAgB,MAAM,MAAM;AAE3C,QAAI;AACJ,UAAM,gBAAgB,CAAC,WAAmB,UAAmB;AAC3D,UAAI,WAAW;AACb,aAAK,MAAM,IAAI,EAAE,WAAW,WAAW,OAAO,WAAW,MAAM,CAAC;AAChE,oBAAY;AAAA,MACd;AAAA,IACF;AAEA,eAAW,SAAS,QAAQ;AAC1B,oBAAc,WAAW,KAAK;AAC9B,kBAAY;AAAA,IACd;AACA,kBAAc,WAAW,IAAI;AAE7B,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;","names":["tts"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents-plugin-openai",
3
- "version": "1.0.0-next.5",
3
+ "version": "1.0.0-next.7",
4
4
  "description": "OpenAI plugin for LiveKit Node Agents",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -30,9 +30,9 @@
30
30
  "@types/ws": "^8.5.10",
31
31
  "tsup": "^8.3.5",
32
32
  "typescript": "^5.0.0",
33
- "@livekit/agents": "1.0.0-next.5",
34
- "@livekit/agents-plugin-silero": "1.0.0-next.6",
35
- "@livekit/agents-plugins-test": "1.0.0-next.5"
33
+ "@livekit/agents": "1.0.0-next.7",
34
+ "@livekit/agents-plugin-silero": "1.0.0-next.8",
35
+ "@livekit/agents-plugins-test": "1.0.0-next.7"
36
36
  },
37
37
  "dependencies": {
38
38
  "@livekit/mutex": "^1.1.1",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@livekit/rtc-node": "^0.13.12",
45
- "@livekit/agents": "1.0.0-next.5"
45
+ "@livekit/agents": "1.0.0-next.7"
46
46
  },
47
47
  "scripts": {
48
48
  "build": "tsup --onSuccess \"pnpm build:types\"",
package/src/llm.ts CHANGED
@@ -60,7 +60,7 @@ export class LLM extends llm.LLM {
60
60
  *
61
61
  * @remarks
62
62
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
63
- * `OPENAI_API_KEY` environmental variable.
63
+ * `OPENAI_API_KEY` environment variable.
64
64
  */
65
65
  constructor(
66
66
  opts: Partial<LLMOptions> = defaultLLMOptions,
@@ -135,7 +135,7 @@ export class LLM extends llm.LLM {
135
135
  *
136
136
  * @remarks
137
137
  * `apiKey` must be set to your Cerebras API key, either using the argument or by setting the
138
- * `CEREBRAS_API_KEY` environmental variable.
138
+ * `CEREBRAS_API_KEY` environment variable.
139
139
  */
140
140
  static withCerebras(
141
141
  opts: Partial<{
@@ -166,7 +166,7 @@ export class LLM extends llm.LLM {
166
166
  *
167
167
  * @remarks
168
168
  * `apiKey` must be set to your Fireworks API key, either using the argument or by setting the
169
- * `FIREWORKS_API_KEY` environmental variable.
169
+ * `FIREWORKS_API_KEY` environment variable.
170
170
  */
171
171
  static withFireworks(opts: Partial<LLMOptions> = {}): LLM {
172
172
  opts.apiKey = opts.apiKey || process.env.FIREWORKS_API_KEY;
@@ -188,7 +188,7 @@ export class LLM extends llm.LLM {
188
188
  *
189
189
  * @remarks
190
190
  * `apiKey` must be set to your xAI API key, either using the argument or by setting the
191
- * `XAI_API_KEY` environmental variable.
191
+ * `XAI_API_KEY` environment variable.
192
192
  */
193
193
  static withXAI(
194
194
  opts: Partial<{
@@ -217,7 +217,7 @@ export class LLM extends llm.LLM {
217
217
  *
218
218
  * @remarks
219
219
  * `apiKey` must be set to your Groq API key, either using the argument or by setting the
220
- * `GROQ_API_KEY` environmental variable.
220
+ * `GROQ_API_KEY` environment variable.
221
221
  */
222
222
  static withGroq(
223
223
  opts: Partial<{
@@ -246,7 +246,7 @@ export class LLM extends llm.LLM {
246
246
  *
247
247
  * @remarks
248
248
  * `apiKey` must be set to your DeepSeek API key, either using the argument or by setting the
249
- * `DEEPSEEK_API_KEY` environmental variable.
249
+ * `DEEPSEEK_API_KEY` environment variable.
250
250
  */
251
251
  static withDeepSeek(
252
252
  opts: Partial<{
@@ -277,7 +277,7 @@ export class LLM extends llm.LLM {
277
277
  *
278
278
  * @remarks
279
279
  * `apiKey` must be set to your OctoAI API key, either using the argument or by setting the
280
- * `OCTOAI_TOKEN` environmental variable.
280
+ * `OCTOAI_TOKEN` environment variable.
281
281
  */
282
282
  static withOcto(
283
283
  opts: Partial<{
@@ -323,7 +323,7 @@ export class LLM extends llm.LLM {
323
323
  *
324
324
  * @remarks
325
325
  * `apiKey` must be set to your PerplexityAI API key, either using the argument or by setting the
326
- * `PERPLEXITY_API_KEY` environmental variable.
326
+ * `PERPLEXITY_API_KEY` environment variable.
327
327
  */
328
328
  static withPerplexity(
329
329
  opts: Partial<{
@@ -354,7 +354,7 @@ export class LLM extends llm.LLM {
354
354
  *
355
355
  * @remarks
356
356
  * `apiKey` must be set to your TogetherAI API key, either using the argument or by setting the
357
- * `TOGETHER_API_KEY` environmental variable.
357
+ * `TOGETHER_API_KEY` environment variable.
358
358
  */
359
359
  static withTogether(
360
360
  opts: Partial<{
@@ -385,7 +385,7 @@ export class LLM extends llm.LLM {
385
385
  *
386
386
  * @remarks
387
387
  * `apiKey` must be set to your Telnyx API key, either using the argument or by setting the
388
- * `TELNYX_API_KEY` environmental variable.
388
+ * `TELNYX_API_KEY` environment variable.
389
389
  */
390
390
  static withTelnyx(
391
391
  opts: Partial<{
@@ -414,7 +414,7 @@ export class LLM extends llm.LLM {
414
414
  *
415
415
  * @remarks
416
416
  * `apiKey` must be set to your Meta Llama API key, either using the argument or by setting the
417
- * `LLAMA_API_KEY` environmental variable.
417
+ * `LLAMA_API_KEY` environment variable.
418
418
  */
419
419
  static withMeta(
420
420
  opts: Partial<{
@@ -432,7 +432,7 @@ export class LLM extends llm.LLM {
432
432
 
433
433
  if (opts.apiKey === undefined) {
434
434
  throw new Error(
435
- 'Meta Llama API key is required, either as argument or set LLAMA_API_KEY environmental variable',
435
+ 'Meta Llama API key is required, either as argument or set LLAMA_API_KEY environment variable',
436
436
  );
437
437
  }
438
438
 
@@ -166,7 +166,7 @@ export class RealtimeModel extends llm.RealtimeModel {
166
166
 
167
167
  if (options.apiKey === '' && !isAzure) {
168
168
  throw new Error(
169
- 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environmental variable',
169
+ 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',
170
170
  );
171
171
  }
172
172
 
@@ -174,7 +174,7 @@ export class RealtimeModel extends llm.RealtimeModel {
174
174
 
175
175
  if (!apiKey && !isAzure) {
176
176
  throw new Error(
177
- 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environmental variable',
177
+ 'OpenAI API key is required, either using the argument or by setting the OPENAI_API_KEY environment variable',
178
178
  );
179
179
  }
180
180
 
@@ -371,7 +371,7 @@ export class RealtimeSession extends llm.RealtimeSession {
371
371
  private pushedDurationMs: number = 0;
372
372
 
373
373
  #logger = log();
374
- #task: Promise<void>;
374
+ #task: Task<void>;
375
375
  #closed = false;
376
376
 
377
377
  constructor(realtimeModel: RealtimeModel) {
@@ -379,7 +379,7 @@ export class RealtimeSession extends llm.RealtimeSession {
379
379
 
380
380
  this.oaiRealtimeModel = realtimeModel;
381
381
 
382
- this.#task = this.#mainTask();
382
+ this.#task = Task.from(({ signal }) => this.#mainTask(signal));
383
383
 
384
384
  this.sendEvent(this.createSessionUpdateEvent());
385
385
  }
@@ -727,7 +727,7 @@ export class RealtimeSession extends llm.RealtimeSession {
727
727
  });
728
728
  }
729
729
 
730
- async #mainTask(): Promise<void> {
730
+ async #mainTask(signal: AbortSignal): Promise<void> {
731
731
  let reconnecting = false;
732
732
  let numRetries = 0;
733
733
  let wsConn: WebSocket | null = null;
@@ -780,16 +780,20 @@ export class RealtimeSession extends llm.RealtimeSession {
780
780
  };
781
781
 
782
782
  reconnecting = false;
783
- while (!this.#closed) {
783
+ while (!this.#closed && !signal.aborted) {
784
784
  this.#logger.debug('Creating WebSocket connection to OpenAI Realtime API');
785
785
  wsConn = await this.createWsConn();
786
+ if (signal.aborted) break;
786
787
 
787
788
  try {
788
789
  if (reconnecting) {
789
790
  await reconnect();
791
+ if (signal.aborted) break;
790
792
  numRetries = 0;
791
793
  }
794
+
792
795
  await this.runWs(wsConn);
796
+ if (signal.aborted) break;
793
797
  } catch (error) {
794
798
  if (!isAPIError(error)) {
795
799
  this.emitError({ error: error as Error, recoverable: false });
@@ -836,10 +840,13 @@ export class RealtimeSession extends llm.RealtimeSession {
836
840
 
837
841
  private async runWs(wsConn: WebSocket): Promise<void> {
838
842
  const forwardEvents = async (signal: AbortSignal): Promise<void> => {
843
+ const abortFuture = new Future<void>();
844
+ signal.addEventListener('abort', () => abortFuture.resolve());
845
+
839
846
  while (!this.#closed && wsConn.readyState === WebSocket.OPEN && !signal.aborted) {
840
847
  try {
841
- const event = await this.messageChannel.get();
842
- if (signal.aborted) {
848
+ const event = await Promise.race([this.messageChannel.get(), abortFuture.await]);
849
+ if (signal.aborted || abortFuture.done || event === undefined) {
843
850
  break;
844
851
  }
845
852
 
package/src/stt.ts CHANGED
@@ -33,7 +33,7 @@ export class STT extends stt.STT {
33
33
  *
34
34
  * @remarks
35
35
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
36
- * `OPENAI_API_KEY` environmental variable.
36
+ * `OPENAI_API_KEY` environment variable.
37
37
  */
38
38
  constructor(opts: Partial<STTOptions> = defaultSTTOptions) {
39
39
  super({ streaming: false, interimResults: false });
@@ -56,7 +56,7 @@ export class STT extends stt.STT {
56
56
  *
57
57
  * @remarks
58
58
  * `apiKey` must be set to your Groq API key, either using the argument or by setting the
59
- * `GROQ_API_KEY` environmental variable.
59
+ * `GROQ_API_KEY` environment variable.
60
60
  */
61
61
  static withGroq(
62
62
  opts: Partial<{
package/src/tts.ts CHANGED
@@ -36,7 +36,7 @@ export class TTS extends tts.TTS {
36
36
  *
37
37
  * @remarks
38
38
  * `apiKey` must be set to your OpenAI API key, either using the argument or by setting the
39
- * `OPENAI_API_KEY` environmental variable.
39
+ * `OPENAI_API_KEY` environment variable.
40
40
  */
41
41
  constructor(opts: Partial<TTSOptions> = defaultTTSOptions) {
42
42
  super(OPENAI_TTS_SAMPLE_RATE, OPENAI_TTS_CHANNELS, { streaming: false });