@livekit/agents 0.7.6 → 0.7.8

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.
Files changed (101) hide show
  1. package/dist/audio.d.cts +9 -0
  2. package/dist/cli.d.cts +14 -0
  3. package/dist/constants.d.cts +5 -0
  4. package/dist/generator.d.cts +23 -0
  5. package/dist/http_server.cjs.map +1 -1
  6. package/dist/http_server.d.cts +19 -0
  7. package/dist/http_server.d.ts +1 -0
  8. package/dist/http_server.d.ts.map +1 -1
  9. package/dist/http_server.js.map +1 -1
  10. package/dist/index.d.cts +29 -0
  11. package/dist/inference_runner.d.cts +12 -0
  12. package/dist/ipc/index.d.cts +2 -0
  13. package/dist/ipc/inference_executor.d.cts +4 -0
  14. package/dist/ipc/inference_proc_executor.cjs +3 -2
  15. package/dist/ipc/inference_proc_executor.cjs.map +1 -1
  16. package/dist/ipc/inference_proc_executor.d.cts +23 -0
  17. package/dist/ipc/inference_proc_executor.js +1 -1
  18. package/dist/ipc/inference_proc_executor.js.map +1 -1
  19. package/dist/ipc/inference_proc_lazy_main.d.cts +2 -0
  20. package/dist/ipc/job_executor.d.cts +18 -0
  21. package/dist/ipc/job_proc_executor.cjs +3 -2
  22. package/dist/ipc/job_proc_executor.cjs.map +1 -1
  23. package/dist/ipc/job_proc_executor.d.cts +19 -0
  24. package/dist/ipc/job_proc_executor.js +1 -1
  25. package/dist/ipc/job_proc_executor.js.map +1 -1
  26. package/dist/ipc/job_proc_lazy_main.cjs +1 -1
  27. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  28. package/dist/ipc/job_proc_lazy_main.d.cts +2 -0
  29. package/dist/ipc/job_proc_lazy_main.js +1 -1
  30. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  31. package/dist/ipc/message.d.cts +58 -0
  32. package/dist/ipc/proc_pool.d.cts +31 -0
  33. package/dist/ipc/supervised_proc.d.cts +30 -0
  34. package/dist/job.d.cts +113 -0
  35. package/dist/llm/chat_context.d.cts +66 -0
  36. package/dist/llm/function_context.d.cts +47 -0
  37. package/dist/llm/index.d.cts +4 -0
  38. package/dist/llm/llm.cjs +3 -3
  39. package/dist/llm/llm.cjs.map +1 -1
  40. package/dist/llm/llm.d.cts +66 -0
  41. package/dist/llm/llm.js +3 -3
  42. package/dist/llm/llm.js.map +1 -1
  43. package/dist/log.d.cts +13 -0
  44. package/dist/metrics/base.d.cts +96 -0
  45. package/dist/metrics/index.d.cts +5 -0
  46. package/dist/metrics/usage_collector.d.cts +14 -0
  47. package/dist/metrics/utils.d.cts +10 -0
  48. package/dist/multimodal/agent_playout.d.cts +34 -0
  49. package/dist/multimodal/index.d.cts +3 -0
  50. package/dist/multimodal/multimodal_agent.d.cts +48 -0
  51. package/dist/pipeline/agent_output.d.cts +33 -0
  52. package/dist/pipeline/agent_playout.d.cts +40 -0
  53. package/dist/pipeline/human_input.d.cts +30 -0
  54. package/dist/pipeline/index.d.cts +2 -0
  55. package/dist/pipeline/pipeline_agent.cjs +16 -14
  56. package/dist/pipeline/pipeline_agent.cjs.map +1 -1
  57. package/dist/pipeline/pipeline_agent.d.cts +150 -0
  58. package/dist/pipeline/pipeline_agent.d.ts +1 -2
  59. package/dist/pipeline/pipeline_agent.d.ts.map +1 -1
  60. package/dist/pipeline/pipeline_agent.js +6 -4
  61. package/dist/pipeline/pipeline_agent.js.map +1 -1
  62. package/dist/pipeline/speech_handle.d.cts +37 -0
  63. package/dist/plugin.d.cts +10 -0
  64. package/dist/stt/index.d.cts +3 -0
  65. package/dist/stt/stream_adapter.d.cts +18 -0
  66. package/dist/stt/stt.d.cts +124 -0
  67. package/dist/tokenize/basic/basic.d.cts +18 -0
  68. package/dist/tokenize/basic/hyphenator.d.cts +17 -0
  69. package/dist/tokenize/basic/index.d.cts +2 -0
  70. package/dist/tokenize/basic/paragraph.d.cts +5 -0
  71. package/dist/tokenize/basic/sentence.d.cts +5 -0
  72. package/dist/tokenize/basic/word.d.cts +5 -0
  73. package/dist/tokenize/index.d.cts +5 -0
  74. package/dist/tokenize/token_stream.d.cts +39 -0
  75. package/dist/tokenize/tokenizer.d.cts +55 -0
  76. package/dist/transcription.d.cts +31 -0
  77. package/dist/tts/index.d.cts +3 -0
  78. package/dist/tts/stream_adapter.d.cts +17 -0
  79. package/dist/tts/tts.cjs +4 -4
  80. package/dist/tts/tts.cjs.map +1 -1
  81. package/dist/tts/tts.d.cts +127 -0
  82. package/dist/tts/tts.js +4 -4
  83. package/dist/tts/tts.js.map +1 -1
  84. package/dist/utils.d.cts +72 -0
  85. package/dist/vad.d.cts +78 -0
  86. package/dist/version.d.cts +2 -0
  87. package/dist/worker.cjs +2 -1
  88. package/dist/worker.cjs.map +1 -1
  89. package/dist/worker.d.cts +109 -0
  90. package/dist/worker.d.ts.map +1 -1
  91. package/dist/worker.js +2 -1
  92. package/dist/worker.js.map +1 -1
  93. package/package.json +9 -5
  94. package/src/http_server.ts +1 -0
  95. package/src/ipc/inference_proc_executor.ts +1 -1
  96. package/src/ipc/job_proc_executor.ts +1 -1
  97. package/src/ipc/job_proc_lazy_main.ts +1 -1
  98. package/src/llm/llm.ts +3 -3
  99. package/src/pipeline/pipeline_agent.ts +7 -4
  100. package/src/tts/tts.ts +4 -4
  101. package/src/worker.ts +1 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/tts/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { TTSMetrics } from '../metrics/base.js';\nimport { AsyncIterableQueue, mergeFrames } from '../utils.js';\n\n/** SynthesizedAudio is a packet of speech synthesis as returned by the TTS. */\nexport interface SynthesizedAudio {\n /** Request ID (one segment could be made up of multiple requests) */\n requestId: string;\n /** Segment ID, each segment is separated by a flush */\n segmentId: string;\n /** Synthesized audio frame */\n frame: AudioFrame;\n /** Current segment of the synthesized audio */\n deltaText?: string;\n /** Whether this is the last frame of the segment (streaming only) */\n final: boolean;\n}\n\n/**\n * Describes the capabilities of the TTS provider.\n *\n * @remarks\n * At present, only `streaming` is supplied to this interface, and the framework only supports\n * providers that do have a streaming endpoint.\n */\nexport interface TTSCapabilities {\n streaming: boolean;\n}\n\nexport enum TTSEvent {\n METRICS_COLLECTED,\n}\n\nexport type TTSCallbacks = {\n [TTSEvent.METRICS_COLLECTED]: (metrics: TTSMetrics) => void;\n};\n\n/**\n * An instance of a text-to-speech adapter.\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child TTS class, which inherits this class's methods.\n */\nexport abstract class TTS extends (EventEmitter as new () => TypedEmitter<TTSCallbacks>) {\n #capabilities: TTSCapabilities;\n #sampleRate: number;\n #numChannels: number;\n abstract label: string;\n\n constructor(sampleRate: number, numChannels: number, capabilities: TTSCapabilities) {\n super();\n this.#capabilities = capabilities;\n this.#sampleRate = sampleRate;\n this.#numChannels = numChannels;\n }\n\n /** Returns this TTS's capabilities */\n get capabilities(): TTSCapabilities {\n return this.#capabilities;\n }\n\n /** Returns the sample rate of audio frames returned by this TTS */\n get sampleRate(): number {\n return this.#sampleRate;\n }\n\n /** Returns the channel count of audio frames returned by this TTS */\n get numChannels(): number {\n return this.#numChannels;\n }\n\n /**\n * Receives text and returns synthesis in the form of a {@link ChunkedStream}\n */\n abstract synthesize(text: string): ChunkedStream;\n\n /**\n * Returns a {@link SynthesizeStream} that can be used to push text and receive audio data\n */\n abstract stream(): SynthesizeStream;\n}\n\n/**\n * An instance of a text-to-speech stream, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child SynthesizeStream class, which inherits this class's methods.\n */\nexport abstract class SynthesizeStream\n implements AsyncIterableIterator<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>\n{\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n static readonly END_OF_STREAM = Symbol('END_OF_STREAM');\n protected input = new AsyncIterableQueue<string | typeof SynthesizeStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected output = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected closed = false;\n abstract label: string;\n #tts: TTS;\n #metricsPendingTexts: string[] = [];\n #metricsText = '';\n #monitorMetricsTask?: Promise<void>;\n\n constructor(tts: TTS) {\n this.#tts = tts;\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDuration = 0;\n let ttfb: bigint | undefined;\n let requestId = '';\n\n const emit = () => {\n if (this.#metricsPendingTexts.length) {\n const text = this.#metricsPendingTexts.shift()!;\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n timestamp: Date.now(),\n requestId,\n ttfb: Math.trunc(Number(ttfb! / BigInt(1000000))),\n duration: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: text.length,\n audioDuration,\n cancelled: false, // XXX(nbsp)\n label: this.label,\n streamed: false,\n };\n this.#tts.emit(TTSEvent.METRICS_COLLECTED, metrics);\n }\n };\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n if (audio === SynthesizeStream.END_OF_STREAM) continue;\n requestId = audio.requestId;\n if (!ttfb) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDuration += audio.frame.samplesPerChannel / audio.frame.sampleRate;\n if (audio.final) {\n emit();\n }\n }\n\n if (requestId) {\n emit();\n }\n this.output.close();\n }\n\n /** Push a string of text to the TTS */\n pushText(text: string) {\n if (!this.#monitorMetricsTask) {\n this.#monitorMetricsTask = this.monitorMetrics();\n }\n this.#metricsText += text;\n\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(text);\n }\n\n /** Flush the TTS, causing it to process all pending text */\n flush() {\n if (this.#metricsText) {\n this.#metricsPendingTexts.push(this.#metricsText);\n this.#metricsText = '';\n }\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(SynthesizeStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.close();\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.input.close();\n this.output.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): SynthesizeStream {\n return this;\n }\n}\n\n/**\n * An instance of a text-to-speech response, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child ChunkedStream class, which inherits this class's methods.\n */\nexport abstract class ChunkedStream implements AsyncIterableIterator<SynthesizedAudio> {\n protected queue = new AsyncIterableQueue<SynthesizedAudio>();\n protected output = new AsyncIterableQueue<SynthesizedAudio>();\n protected closed = false;\n abstract label: string;\n #text: string;\n #tts: TTS;\n\n constructor(text: string, tts: TTS) {\n this.#text = text;\n this.#tts = tts;\n\n this.monitorMetrics();\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDuration = 0;\n let ttfb: bigint | undefined;\n let requestId = '';\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n requestId = audio.requestId;\n if (!ttfb) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDuration += audio.frame.samplesPerChannel / audio.frame.sampleRate;\n }\n this.output.close();\n\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n timestamp: Date.now(),\n requestId,\n ttfb: Math.trunc(Number(ttfb! / BigInt(1000000))),\n duration: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: this.#text.length,\n audioDuration,\n cancelled: false, // XXX(nbsp)\n label: this.label,\n streamed: false,\n };\n this.#tts.emit(TTSEvent.METRICS_COLLECTED, metrics);\n }\n\n /** Collect every frame into one in a single call */\n async collect(): Promise<AudioFrame> {\n const frames = [];\n for await (const event of this) {\n frames.push(event.frame);\n }\n return mergeFrames(frames);\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.queue.close();\n this.output.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): ChunkedStream {\n return this;\n }\n}\n"],"mappings":"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","names":["TTSEvent"]}
1
+ {"version":3,"sources":["../../src/tts/tts.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';\nimport { EventEmitter } from 'node:events';\nimport type { TTSMetrics } from '../metrics/base.js';\nimport { AsyncIterableQueue, mergeFrames } from '../utils.js';\n\n/** SynthesizedAudio is a packet of speech synthesis as returned by the TTS. */\nexport interface SynthesizedAudio {\n /** Request ID (one segment could be made up of multiple requests) */\n requestId: string;\n /** Segment ID, each segment is separated by a flush */\n segmentId: string;\n /** Synthesized audio frame */\n frame: AudioFrame;\n /** Current segment of the synthesized audio */\n deltaText?: string;\n /** Whether this is the last frame of the segment (streaming only) */\n final: boolean;\n}\n\n/**\n * Describes the capabilities of the TTS provider.\n *\n * @remarks\n * At present, only `streaming` is supplied to this interface, and the framework only supports\n * providers that do have a streaming endpoint.\n */\nexport interface TTSCapabilities {\n streaming: boolean;\n}\n\nexport enum TTSEvent {\n METRICS_COLLECTED,\n}\n\nexport type TTSCallbacks = {\n [TTSEvent.METRICS_COLLECTED]: (metrics: TTSMetrics) => void;\n};\n\n/**\n * An instance of a text-to-speech adapter.\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child TTS class, which inherits this class's methods.\n */\nexport abstract class TTS extends (EventEmitter as new () => TypedEmitter<TTSCallbacks>) {\n #capabilities: TTSCapabilities;\n #sampleRate: number;\n #numChannels: number;\n abstract label: string;\n\n constructor(sampleRate: number, numChannels: number, capabilities: TTSCapabilities) {\n super();\n this.#capabilities = capabilities;\n this.#sampleRate = sampleRate;\n this.#numChannels = numChannels;\n }\n\n /** Returns this TTS's capabilities */\n get capabilities(): TTSCapabilities {\n return this.#capabilities;\n }\n\n /** Returns the sample rate of audio frames returned by this TTS */\n get sampleRate(): number {\n return this.#sampleRate;\n }\n\n /** Returns the channel count of audio frames returned by this TTS */\n get numChannels(): number {\n return this.#numChannels;\n }\n\n /**\n * Receives text and returns synthesis in the form of a {@link ChunkedStream}\n */\n abstract synthesize(text: string): ChunkedStream;\n\n /**\n * Returns a {@link SynthesizeStream} that can be used to push text and receive audio data\n */\n abstract stream(): SynthesizeStream;\n}\n\n/**\n * An instance of a text-to-speech stream, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child SynthesizeStream class, which inherits this class's methods.\n */\nexport abstract class SynthesizeStream\n implements AsyncIterableIterator<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>\n{\n protected static readonly FLUSH_SENTINEL = Symbol('FLUSH_SENTINEL');\n static readonly END_OF_STREAM = Symbol('END_OF_STREAM');\n protected input = new AsyncIterableQueue<string | typeof SynthesizeStream.FLUSH_SENTINEL>();\n protected queue = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected output = new AsyncIterableQueue<\n SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM\n >();\n protected closed = false;\n abstract label: string;\n #tts: TTS;\n #metricsPendingTexts: string[] = [];\n #metricsText = '';\n #monitorMetricsTask?: Promise<void>;\n\n constructor(tts: TTS) {\n this.#tts = tts;\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDuration = 0;\n let ttfb: bigint | undefined;\n let requestId = '';\n\n const emit = () => {\n if (this.#metricsPendingTexts.length) {\n const text = this.#metricsPendingTexts.shift()!;\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n timestamp: Date.now(),\n requestId,\n ttfb: Math.trunc(Number((ttfb || BigInt(0)) / BigInt(1000000))),\n duration: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: text.length,\n audioDuration,\n cancelled: false, // XXX(nbsp)\n label: this.label,\n streamed: false,\n };\n this.#tts.emit(TTSEvent.METRICS_COLLECTED, metrics);\n }\n };\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n if (audio === SynthesizeStream.END_OF_STREAM) continue;\n requestId = audio.requestId;\n if (!ttfb) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDuration += audio.frame.samplesPerChannel / audio.frame.sampleRate;\n if (audio.final) {\n emit();\n }\n }\n\n if (requestId) {\n emit();\n }\n this.output.close();\n }\n\n /** Push a string of text to the TTS */\n pushText(text: string) {\n if (!this.#monitorMetricsTask) {\n this.#monitorMetricsTask = this.monitorMetrics();\n }\n this.#metricsText += text;\n\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(text);\n }\n\n /** Flush the TTS, causing it to process all pending text */\n flush() {\n if (this.#metricsText) {\n this.#metricsPendingTexts.push(this.#metricsText);\n this.#metricsText = '';\n }\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.put(SynthesizeStream.FLUSH_SENTINEL);\n }\n\n /** Mark the input as ended and forbid additional pushes */\n endInput() {\n if (this.input.closed) {\n throw new Error('Input is closed');\n }\n if (this.closed) {\n throw new Error('Stream is closed');\n }\n this.input.close();\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio | typeof SynthesizeStream.END_OF_STREAM>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.input.close();\n this.output.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): SynthesizeStream {\n return this;\n }\n}\n\n/**\n * An instance of a text-to-speech response, as an asynchronous iterable iterator.\n *\n * @example Looping through frames\n * ```ts\n * for await (const event of stream) {\n * await source.captureFrame(event.frame);\n * }\n * ```\n *\n * @remarks\n * This class is abstract, and as such cannot be used directly. Instead, use a provider plugin that\n * exports its own child ChunkedStream class, which inherits this class's methods.\n */\nexport abstract class ChunkedStream implements AsyncIterableIterator<SynthesizedAudio> {\n protected queue = new AsyncIterableQueue<SynthesizedAudio>();\n protected output = new AsyncIterableQueue<SynthesizedAudio>();\n protected closed = false;\n abstract label: string;\n #text: string;\n #tts: TTS;\n\n constructor(text: string, tts: TTS) {\n this.#text = text;\n this.#tts = tts;\n\n this.monitorMetrics();\n }\n\n protected async monitorMetrics() {\n const startTime = process.hrtime.bigint();\n let audioDuration = 0;\n let ttfb: bigint = BigInt(-1);\n let requestId = '';\n\n for await (const audio of this.queue) {\n this.output.put(audio);\n requestId = audio.requestId;\n if (ttfb === BigInt(-1)) {\n ttfb = process.hrtime.bigint() - startTime;\n }\n audioDuration += audio.frame.samplesPerChannel / audio.frame.sampleRate;\n }\n this.output.close();\n\n const duration = process.hrtime.bigint() - startTime;\n const metrics: TTSMetrics = {\n timestamp: Date.now(),\n requestId,\n ttfb: ttfb === BigInt(-1) ? -1 : Math.trunc(Number(ttfb / BigInt(1000000))),\n duration: Math.trunc(Number(duration / BigInt(1000000))),\n charactersCount: this.#text.length,\n audioDuration,\n cancelled: false, // XXX(nbsp)\n label: this.label,\n streamed: false,\n };\n this.#tts.emit(TTSEvent.METRICS_COLLECTED, metrics);\n }\n\n /** Collect every frame into one in a single call */\n async collect(): Promise<AudioFrame> {\n const frames = [];\n for await (const event of this) {\n frames.push(event.frame);\n }\n return mergeFrames(frames);\n }\n\n next(): Promise<IteratorResult<SynthesizedAudio>> {\n return this.output.next();\n }\n\n /** Close both the input and output of the TTS stream */\n close() {\n this.queue.close();\n this.output.close();\n this.closed = true;\n }\n\n [Symbol.asyncIterator](): ChunkedStream {\n return this;\n }\n}\n"],"mappings":"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","names":["TTSEvent"]}
@@ -0,0 +1,72 @@
1
+ import type { Room } from '@livekit/rtc-node';
2
+ import { AudioFrame } from '@livekit/rtc-node';
3
+ /** Union of a single and a list of {@link AudioFrame}s */
4
+ export type AudioBuffer = AudioFrame[] | AudioFrame;
5
+ /**
6
+ * Merge one or more {@link AudioFrame}s into a single one.
7
+ *
8
+ * @param buffer Either an {@link AudioFrame} or a list thereof
9
+ * @throws
10
+ * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
11
+ * | TypeError} if sample rate or channel count are mismatched
12
+ */
13
+ export declare const mergeFrames: (buffer: AudioBuffer) => AudioFrame;
14
+ export declare const findMicroTrackId: (room: Room, identity: string) => string;
15
+ /** @internal */
16
+ export declare class Queue<T> {
17
+ #private;
18
+ /** @internal */
19
+ items: T[];
20
+ constructor(limit?: number);
21
+ get(): Promise<T>;
22
+ put(item: T): Promise<void>;
23
+ }
24
+ /** @internal */
25
+ export declare class Future {
26
+ #private;
27
+ constructor();
28
+ get await(): Promise<void>;
29
+ get done(): boolean;
30
+ resolve(): void;
31
+ reject(error: Error): void;
32
+ }
33
+ /** @internal */
34
+ export declare class CancellablePromise<T> {
35
+ #private;
36
+ constructor(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void, onCancel: (cancelFn: () => void) => void) => void);
37
+ get isCancelled(): boolean;
38
+ get error(): Error | null;
39
+ then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | Promise<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | Promise<TResult2>) | null): Promise<TResult1 | TResult2>;
40
+ catch<TResult = never>(onrejected?: ((reason: any) => TResult | Promise<TResult>) | null): Promise<T | TResult>;
41
+ finally(onfinally?: (() => void) | null): Promise<T>;
42
+ cancel(): void;
43
+ static from<T>(promise: Promise<T>): CancellablePromise<T>;
44
+ }
45
+ /** @internal */
46
+ export declare function gracefullyCancel<T>(promise: CancellablePromise<T>): Promise<void>;
47
+ /** @internal */
48
+ export declare class AsyncIterableQueue<T> implements AsyncIterableIterator<T> {
49
+ #private;
50
+ private static readonly CLOSE_SENTINEL;
51
+ get closed(): boolean;
52
+ put(item: T): void;
53
+ close(): void;
54
+ next(): Promise<IteratorResult<T>>;
55
+ [Symbol.asyncIterator](): AsyncIterableQueue<T>;
56
+ }
57
+ /** @internal */
58
+ export declare class ExpFilter {
59
+ #private;
60
+ constructor(alpha: number, max?: number);
61
+ reset(alpha?: number): void;
62
+ apply(exp: number, sample: number): number;
63
+ get filtered(): number | undefined;
64
+ set alpha(alpha: number);
65
+ }
66
+ /** @internal */
67
+ export declare class AudioEnergyFilter {
68
+ #private;
69
+ constructor(cooldownSeconds?: number);
70
+ pushFrame(frame: AudioFrame): boolean;
71
+ }
72
+ //# sourceMappingURL=utils.d.ts.map
package/dist/vad.d.cts ADDED
@@ -0,0 +1,78 @@
1
+ import type { AudioFrame } from '@livekit/rtc-node';
2
+ import type { TypedEventEmitter as TypedEmitter } from '@livekit/typed-emitter';
3
+ import type { VADMetrics } from './metrics/base.js';
4
+ import { AsyncIterableQueue } from './utils.js';
5
+ export declare enum VADEventType {
6
+ START_OF_SPEECH = 0,
7
+ INFERENCE_DONE = 1,
8
+ END_OF_SPEECH = 2,
9
+ METRICS_COLLECTED = 3
10
+ }
11
+ export interface VADEvent {
12
+ /** Type of the VAD event (e.g., start of speech, end of speech, inference done). */
13
+ type: VADEventType;
14
+ /**
15
+ * Index of the audio sample where the event occurred, relative to the inference sample rate.
16
+ */
17
+ samplesIndex: number;
18
+ /** Timestamp when the event was fired. */
19
+ timestamp: number;
20
+ /** Duration of the speech segment. */
21
+ speechDuration: number;
22
+ /** Duration of the silence segment. */
23
+ silenceDuration: number;
24
+ /**
25
+ * List of audio frames associated with the speech.
26
+ *
27
+ * @remarks
28
+ * - For `start_of_speech` events, this contains the audio chunks that triggered the detection.
29
+ * - For `inference_done` events, this contains the audio chunks that were processed.
30
+ * - For `end_of_speech` events, this contains the complete user speech.
31
+ */
32
+ frames: AudioFrame[];
33
+ /** Probability that speech is present (only for `INFERENCE_DONE` events). */
34
+ probability: number;
35
+ /** Time taken to perform the inference, in seconds (only for `INFERENCE_DONE` events). */
36
+ inferenceDuration: number;
37
+ /** Indicates whether speech was detected in the frames. */
38
+ speaking: boolean;
39
+ /** Threshold used to detect silence. */
40
+ rawAccumulatedSilence: number;
41
+ /** Threshold used to detect speech. */
42
+ rawAccumulatedSpeech: number;
43
+ }
44
+ export interface VADCapabilities {
45
+ updateInterval: number;
46
+ }
47
+ export type VADCallbacks = {
48
+ [VADEventType.METRICS_COLLECTED]: (metrics: VADMetrics) => void;
49
+ };
50
+ declare const VAD_base: new () => TypedEmitter<VADCallbacks>;
51
+ export declare abstract class VAD extends VAD_base {
52
+ #private;
53
+ abstract label: string;
54
+ constructor(capabilities: VADCapabilities);
55
+ get capabilities(): VADCapabilities;
56
+ /**
57
+ * Returns a {@link VADStream} that can be used to push audio frames and receive VAD events.
58
+ */
59
+ abstract stream(): VADStream;
60
+ }
61
+ export declare abstract class VADStream implements AsyncIterableIterator<VADEvent> {
62
+ #private;
63
+ protected static readonly FLUSH_SENTINEL: unique symbol;
64
+ protected input: AsyncIterableQueue<AudioFrame | typeof VADStream.FLUSH_SENTINEL>;
65
+ protected queue: AsyncIterableQueue<VADEvent>;
66
+ protected output: AsyncIterableQueue<VADEvent>;
67
+ protected closed: boolean;
68
+ constructor(vad: VAD);
69
+ protected monitorMetrics(): Promise<void>;
70
+ pushFrame(frame: AudioFrame): void;
71
+ flush(): void;
72
+ endInput(): void;
73
+ next(): Promise<IteratorResult<VADEvent>>;
74
+ close(): void;
75
+ [Symbol.asyncIterator](): VADStream;
76
+ }
77
+ export {};
78
+ //# sourceMappingURL=vad.d.ts.map
@@ -0,0 +1,2 @@
1
+ export declare const version = "0.1.0";
2
+ //# sourceMappingURL=version.d.ts.map
package/dist/worker.cjs CHANGED
@@ -263,7 +263,8 @@ class Worker {
263
263
  this.#httpServer = new import_http_server.HTTPServer(opts.host, opts.port, () => ({
264
264
  agent_name: opts.agentName,
265
265
  worker_type: import_protocol.JobType[opts.workerType],
266
- active_jobs: this.activeJobs.length
266
+ active_jobs: this.activeJobs.length,
267
+ sdk_version: import_version.version
267
268
  }));
268
269
  }
269
270
  /* @throws {@link WorkerError} if worker failed to connect or already running */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n JobAssignment,\n JobTermination,\n ParticipantInfo,\n TrackSource,\n} from '@livekit/protocol';\nimport {\n type AvailabilityRequest,\n JobType,\n ParticipantPermission,\n ServerMessage,\n WorkerMessage,\n WorkerStatus,\n} from '@livekit/protocol';\nimport { AccessToken, RoomServiceClient } from 'livekit-server-sdk';\nimport { EventEmitter } from 'node:events';\nimport os from 'node:os';\nimport { WebSocket } from 'ws';\nimport { HTTPServer } from './http_server.js';\nimport { InferenceRunner } from './inference_runner.js';\nimport { InferenceProcExecutor } from './ipc/inference_proc_executor.js';\nimport { ProcPool } from './ipc/proc_pool.js';\nimport type { JobAcceptArguments, JobProcess, RunningJobInfo } from './job.js';\nimport { JobRequest } from './job.js';\nimport { log } from './log.js';\nimport { Future } from './utils.js';\nimport { version } from './version.js';\n\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst ASSIGNMENT_TIMEOUT = 7.5 * 1000;\nconst UPDATE_LOAD_INTERVAL = 2.5 * 1000;\n\nclass Default {\n static loadThreshold(production: boolean): number {\n if (production) {\n return 0.65;\n } else {\n return Infinity;\n }\n }\n\n static numIdleProcesses(production: boolean): number {\n if (production) {\n return 3;\n } else {\n return 0;\n }\n }\n\n static port(production: boolean): number {\n if (production) {\n return 8081;\n } else {\n return 0;\n }\n }\n}\n\n/** Necessary credentials not provided and not found in an appropriate environmental variable. */\nexport class MissingCredentialsError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Worker did not run as expected. */\nexport class WorkerError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** @internal */\nexport const defaultInitializeProcessFunc = (_: JobProcess) => _;\nconst defaultRequestFunc = async (ctx: JobRequest) => {\n await ctx.accept();\n};\nconst defaultCpuLoad = async (): Promise<number> => {\n return new Promise((resolve) => {\n const cpus1 = os.cpus();\n\n setTimeout(() => {\n const cpus2 = os.cpus();\n\n let idle = 0;\n let total = 0;\n\n for (let i = 0; i < cpus1.length; i++) {\n const cpu1 = cpus1[i]!.times;\n const cpu2 = cpus2[i]!.times;\n\n idle += cpu2.idle - cpu1.idle;\n\n const total1 = Object.values(cpu1).reduce((acc, i) => acc + i, 0);\n const total2 = Object.values(cpu2).reduce((acc, i) => acc + i, 0);\n\n total += total2 - total1;\n }\n\n resolve(+(1 - idle / total).toFixed(2));\n }, UPDATE_LOAD_INTERVAL);\n });\n};\n\n/** Participant permissions to pass to every agent spun up by this worker. */\nexport class WorkerPermissions {\n canPublish: boolean;\n canSubscribe: boolean;\n canPublishData: boolean;\n canUpdateMetadata: boolean;\n canPublishSources: TrackSource[];\n hidden: boolean;\n\n constructor(\n canPublish = true,\n canSubscribe = true,\n canPublishData = true,\n canUpdateMetadata = true,\n canPublishSources: TrackSource[] = [],\n hidden = false,\n ) {\n this.canPublish = canPublish;\n this.canSubscribe = canSubscribe;\n this.canPublishData = canPublishData;\n this.canUpdateMetadata = canUpdateMetadata;\n this.canPublishSources = canPublishSources;\n this.hidden = hidden;\n }\n}\n\n/**\n * Data class describing worker behaviour.\n *\n * @remarks\n * The Agents framework provides sane worker defaults, and works out-of-the-box with no tweaking\n * necessary. The only mandatory parameter is `agent`, which points to the entry function.\n *\n * This class is mostly useful in conjunction with {@link cli.runApp}.\n */\nexport class WorkerOptions {\n agent: string;\n requestFunc: (job: JobRequest) => Promise<void>;\n loadFunc: () => Promise<number>;\n loadThreshold: number;\n numIdleProcesses: number;\n shutdownProcessTimeout: number;\n initializeProcessTimeout: number;\n permissions: WorkerPermissions;\n agentName: string;\n workerType: JobType;\n maxRetry: number;\n wsURL: string;\n apiKey?: string;\n apiSecret?: string;\n workerToken?: string;\n host: string;\n port: number;\n logLevel: string;\n production: boolean;\n jobMemoryWarnMB: number;\n jobMemoryLimitMB: number;\n\n /** @param options */\n constructor({\n agent,\n requestFunc = defaultRequestFunc,\n loadFunc = defaultCpuLoad,\n loadThreshold = undefined,\n numIdleProcesses = undefined,\n shutdownProcessTimeout = 60 * 1000,\n initializeProcessTimeout = 10 * 1000,\n permissions = new WorkerPermissions(),\n agentName = '',\n workerType = JobType.JT_ROOM,\n maxRetry = MAX_RECONNECT_ATTEMPTS,\n wsURL = 'ws://localhost:7880',\n apiKey = undefined,\n apiSecret = undefined,\n workerToken = undefined,\n host = '0.0.0.0',\n port = undefined,\n logLevel = 'info',\n production = false,\n jobMemoryWarnMB = 300,\n jobMemoryLimitMB = 0,\n }: {\n /**\n * Path to a file that has {@link Agent} as a default export, dynamically imported later for\n * entrypoint and prewarm functions\n */\n agent: string;\n requestFunc?: (job: JobRequest) => Promise<void>;\n /** Called to determine the current load of the worker. Should return a value between 0 and 1. */\n loadFunc?: () => Promise<number>;\n /** When the load exceeds this threshold, the worker will be marked as unavailable. */\n loadThreshold?: number;\n numIdleProcesses?: number;\n shutdownProcessTimeout?: number;\n initializeProcessTimeout?: number;\n permissions?: WorkerPermissions;\n agentName?: string;\n workerType?: JobType;\n maxRetry?: number;\n wsURL?: string;\n apiKey?: string;\n apiSecret?: string;\n workerToken?: string;\n host?: string;\n port?: number;\n logLevel?: string;\n production?: boolean;\n jobMemoryWarnMB?: number;\n jobMemoryLimitMB?: number;\n }) {\n this.agent = agent;\n if (!this.agent) {\n throw new Error('No Agent file was passed to the worker');\n }\n this.requestFunc = requestFunc;\n this.loadFunc = loadFunc;\n this.loadThreshold = loadThreshold || Default.loadThreshold(production);\n this.numIdleProcesses = numIdleProcesses || Default.numIdleProcesses(production);\n this.shutdownProcessTimeout = shutdownProcessTimeout;\n this.initializeProcessTimeout = initializeProcessTimeout;\n this.permissions = permissions;\n this.agentName = agentName;\n this.workerType = workerType;\n this.maxRetry = maxRetry;\n this.wsURL = wsURL;\n this.apiKey = apiKey;\n this.apiSecret = apiSecret;\n this.workerToken = workerToken;\n this.host = host;\n this.port = port || Default.port(production);\n this.logLevel = logLevel;\n this.production = production;\n this.jobMemoryWarnMB = jobMemoryWarnMB;\n this.jobMemoryLimitMB = jobMemoryLimitMB;\n }\n}\n\nclass PendingAssignment {\n promise = new Promise<JobAssignment>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: JobAssignment) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\n/**\n * Central orchestrator for all processes and job requests.\n *\n * @remarks\n * For most usecases, Worker should not be initialized or handled directly; you should instead call\n * for its creation through {@link cli.runApp}. This could, however, be useful in situations where\n * you don't have access to a command line, such as a headless program, or one that uses Agents\n * behind a wrapper.\n */\nexport class Worker {\n #opts: WorkerOptions;\n #procPool: ProcPool;\n\n #id = 'unregistered';\n #closed = true;\n #draining = false;\n #connecting = false;\n #tasks: Promise<void>[] = [];\n #pending: { [id: string]: PendingAssignment } = {};\n #close = new Future();\n\n event = new EventEmitter();\n #session: WebSocket | undefined = undefined;\n #httpServer: HTTPServer;\n #logger = log().child({ version });\n #inferenceExecutor?: InferenceProcExecutor;\n\n /* @throws {@link MissingCredentialsError} if URL, API key or API secret are missing */\n constructor(opts: WorkerOptions) {\n opts.wsURL = opts.wsURL || process.env.LIVEKIT_URL || '';\n opts.apiKey = opts.apiKey || process.env.LIVEKIT_API_KEY || '';\n opts.apiSecret = opts.apiSecret || process.env.LIVEKIT_API_SECRET || '';\n\n if (opts.wsURL === '')\n throw new MissingCredentialsError(\n 'URL is required: Set LIVEKIT_URL, run with --url, or pass wsURL in WorkerOptions',\n );\n if (opts.apiKey === '')\n throw new MissingCredentialsError(\n 'API Key is required: Set LIVEKIT_API_KEY, run with --api-key, or pass apiKey in WorkerOptions',\n );\n if (opts.apiSecret === '')\n throw new MissingCredentialsError(\n 'API Secret is required: Set LIVEKIT_API_SECRET, run with --api-secret, or pass apiSecret in WorkerOptions',\n );\n\n if (Object.entries(InferenceRunner.registeredRunners).length) {\n this.#inferenceExecutor = new InferenceProcExecutor({\n runners: InferenceRunner.registeredRunners,\n initializeTimeout: 30000,\n closeTimeout: 5000,\n memoryWarnMB: 2000,\n memoryLimitMB: 0,\n pingInterval: 5000,\n pingTimeout: 60000,\n highPingThreshold: 2500,\n });\n }\n\n this.#procPool = new ProcPool(\n opts.agent,\n opts.numIdleProcesses,\n opts.initializeProcessTimeout,\n opts.shutdownProcessTimeout,\n this.#inferenceExecutor,\n opts.jobMemoryWarnMB,\n opts.jobMemoryLimitMB,\n );\n\n this.#opts = opts;\n this.#httpServer = new HTTPServer(opts.host, opts.port, () => ({\n agent_name: opts.agentName,\n worker_type: JobType[opts.workerType],\n active_jobs: this.activeJobs.length,\n }));\n }\n\n /* @throws {@link WorkerError} if worker failed to connect or already running */\n async run() {\n if (!this.#closed) {\n throw new WorkerError('worker is already running');\n }\n\n if (this.#inferenceExecutor) {\n await this.#inferenceExecutor.start();\n await this.#inferenceExecutor.initialize();\n }\n\n this.#logger.info('starting worker');\n this.#closed = false;\n this.#procPool.start();\n\n const workerWS = async () => {\n let retries = 0;\n this.#connecting = true;\n\n while (!this.#closed) {\n const url = new URL(this.#opts.wsURL);\n url.protocol = url.protocol.replace('http', 'ws');\n const token = new AccessToken(this.#opts.apiKey, this.#opts.apiSecret);\n token.addGrant({ agent: true });\n const jwt = await token.toJwt();\n const wsUrl = new URL(url + 'agent');\n if (this.#opts.workerToken) {\n wsUrl.searchParams.append('worker_token', this.#opts.workerToken);\n }\n this.#session = new WebSocket(wsUrl, {\n headers: { authorization: 'Bearer ' + jwt },\n });\n\n try {\n await new Promise((resolve, reject) => {\n this.#session!.on('open', resolve);\n this.#session!.on('error', (error) => reject(error.message));\n this.#session!.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n retries = 0;\n this.#logger.debug('connected to LiveKit server');\n await this.#runWS(this.#session);\n } catch (e: unknown) {\n if (e instanceof Error || e instanceof ErrorEvent) {\n e = e.message;\n }\n\n if (this.#closed) return;\n if (retries >= this.#opts.maxRetry) {\n throw new WorkerError(\n `failed to connect to LiveKit server after ${retries} attempts: ${e}`,\n );\n }\n\n retries++;\n const delay = Math.min(retries * 2, 10);\n\n this.#logger.warn(\n `failed to connect to LiveKit server, retrying in ${delay} seconds: ${e} (${retries}/${this.#opts.maxRetry})`,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n }\n }\n };\n\n await Promise.all([workerWS(), this.#httpServer.run()]);\n this.#close.resolve();\n }\n\n get id(): string {\n return this.#id;\n }\n\n get activeJobs(): RunningJobInfo[] {\n return this.#procPool.processes\n .filter((proc) => proc.runningJob)\n .map((proc) => proc.runningJob!);\n }\n\n /* @throws {@link WorkerError} if worker did not drain in time */\n async drain(timeout?: number) {\n if (this.#draining) {\n return;\n }\n\n this.#logger.info('draining worker');\n this.#draining = true;\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'updateWorker',\n value: {\n status: WorkerStatus.WS_FULL,\n },\n },\n }),\n );\n\n const joinJobs = async () => {\n return Promise.all(\n this.#procPool.processes.map((proc) => {\n if (!proc.runningJob) {\n proc.close();\n }\n return proc.join();\n }),\n );\n };\n\n let timer: NodeJS.Timeout | undefined;\n if (timeout) {\n timer = setTimeout(() => {\n throw new WorkerError('timed out draining');\n }, timeout);\n }\n await joinJobs().then(() => {\n if (timeout) {\n clearTimeout(timer);\n }\n });\n }\n\n async simulateJob(roomName: string, participantIdentity?: string) {\n const client = new RoomServiceClient(this.#opts.wsURL, this.#opts.apiKey, this.#opts.apiSecret);\n const room = await client.createRoom({ name: roomName });\n let participant: ParticipantInfo | undefined = undefined;\n if (participantIdentity) {\n try {\n participant = await client.getParticipant(roomName, participantIdentity);\n } catch (e) {\n this.#logger.fatal(\n `participant with identity ${participantIdentity} not found in room ${roomName}`,\n );\n throw e;\n }\n }\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'simulateJob',\n value: {\n type: JobType.JT_PUBLISHER,\n room,\n participant,\n },\n },\n }),\n );\n }\n\n async #runWS(ws: WebSocket) {\n let closingWS = false;\n\n const send = (msg: WorkerMessage) => {\n if (closingWS) {\n this.event.off('worker_msg', send);\n return;\n }\n ws.send(msg.toBinary());\n };\n this.event.on('worker_msg', send);\n\n const close = new Promise<void>((resolve) => {\n ws.addEventListener('close', () => {\n closingWS = true;\n if (!this.#closed) {\n this.#logger.error('worker connection closed unexpectedly');\n }\n resolve();\n });\n });\n\n ws.addEventListener('error', (event) => {\n this.#logger.error('worker error:', event.message);\n });\n\n ws.addEventListener('message', (event) => {\n if (event.type !== 'message') {\n this.#logger.warn('unexpected message type: ' + event.type);\n return;\n }\n\n const msg = new ServerMessage();\n msg.fromBinary(event.data as Uint8Array);\n\n // register is the only valid first message, and it is only valid as the\n // first message\n if (this.#connecting && msg.message.case !== 'register') {\n throw new WorkerError('expected register response as first message');\n }\n\n switch (msg.message.case) {\n case 'register': {\n this.#id = msg.message.value.workerId;\n this.#logger\n .child({ id: this.id, server_info: msg.message.value.serverInfo })\n .info('registered worker');\n this.event.emit(\n 'worker_registered',\n msg.message.value.workerId,\n msg.message.value.serverInfo,\n );\n this.#connecting = false;\n break;\n }\n case 'availability': {\n if (!msg.message.value.job) return;\n const task = this.#availability(msg.message.value);\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n break;\n }\n case 'assignment': {\n if (!msg.message.value.job) return;\n const job = msg.message.value.job;\n if (job.id in this.#pending) {\n const task = this.#pending[job.id];\n delete this.#pending[job.id];\n task?.resolve(msg.message.value);\n } else {\n this.#logger.child({ job }).warn('received assignment for unknown job ' + job.id);\n }\n break;\n }\n case 'termination': {\n const task = this.#termination(msg.message.value);\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n break;\n }\n }\n });\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'register',\n value: {\n type: this.#opts.workerType,\n agentName: this.#opts.agentName,\n allowedPermissions: new ParticipantPermission({\n canPublish: this.#opts.permissions.canPublish,\n canSubscribe: this.#opts.permissions.canSubscribe,\n canPublishData: this.#opts.permissions.canPublishData,\n canUpdateMetadata: this.#opts.permissions.canUpdateMetadata,\n hidden: this.#opts.permissions.hidden,\n agent: true,\n }),\n version,\n },\n },\n }),\n );\n\n let currentStatus = WorkerStatus.WS_AVAILABLE;\n const loadMonitor = setInterval(() => {\n if (closingWS) clearInterval(loadMonitor);\n\n const oldStatus = currentStatus;\n this.#opts.loadFunc().then((currentLoad: number) => {\n const isFull = currentLoad >= this.#opts.loadThreshold;\n const currentlyAvailable = !isFull;\n currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;\n\n if (oldStatus != currentStatus) {\n const extra = { load: currentLoad, loadThreshold: this.#opts.loadThreshold };\n if (isFull) {\n this.#logger.child(extra).info('worker is at full capacity, marking as unavailable');\n } else {\n this.#logger.child(extra).info('worker is below capacity, marking as available');\n }\n }\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'updateWorker',\n value: {\n load: currentLoad,\n status: currentStatus,\n },\n },\n }),\n );\n });\n }, UPDATE_LOAD_INTERVAL);\n\n await close;\n ws.removeAllListeners();\n }\n\n async #availability(msg: AvailabilityRequest) {\n let answered = false;\n\n const onReject = async () => {\n answered = true;\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'availability',\n value: {\n jobId: msg.job!.id,\n available: false,\n },\n },\n }),\n );\n };\n\n const onAccept = async (args: JobAcceptArguments) => {\n answered = true;\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'availability',\n value: {\n jobId: msg.job!.id,\n available: true,\n participantIdentity: args.identity,\n participantName: args.name,\n participantMetadata: args.metadata,\n participantAttributes: args.attributes,\n },\n },\n }),\n );\n\n this.#pending[req.id] = new PendingAssignment();\n const timer = setTimeout(() => {\n this.#logger.child({ req }).warn(`assignment for job ${req.id} timed out`);\n return;\n }, ASSIGNMENT_TIMEOUT);\n const asgn = await this.#pending[req.id]?.promise.then(async (asgn) => {\n clearTimeout(timer);\n return asgn;\n });\n\n if (asgn) {\n await this.#procPool.launchJob({\n acceptArguments: args,\n job: msg.job!,\n url: asgn.url || this.#opts.wsURL,\n token: asgn.token,\n });\n } else {\n this.#logger.child({ requestId: req.id }).warn('pending assignment not found');\n }\n };\n\n const req = new JobRequest(msg.job!, onReject, onAccept);\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('received job request');\n\n const jobRequestTask = async () => {\n try {\n await this.#opts.requestFunc(req);\n } catch (e) {\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('jobRequestFunc failed');\n await onReject();\n }\n\n if (!answered) {\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('no answer was given inside the jobRequestFunc, automatically rejecting the job');\n }\n };\n\n const task = jobRequestTask();\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n }\n\n async #termination(msg: JobTermination) {\n const proc = this.#procPool.getByJobId(msg.jobId);\n if (proc === null) {\n // safe to ignore\n return;\n }\n await proc.close();\n }\n\n async close() {\n if (this.#closed) {\n await this.#close.await;\n return;\n }\n\n this.#logger.info('shutting down worker');\n\n this.#closed = true;\n\n await this.#inferenceExecutor?.close();\n await this.#procPool.close();\n await this.#httpServer.close();\n await Promise.allSettled(this.#tasks);\n\n this.#session?.close();\n await this.#close.await;\n }\n}\n"],"mappings":"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","names":["os","i","asgn"]}
1
+ {"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type {\n JobAssignment,\n JobTermination,\n ParticipantInfo,\n TrackSource,\n} from '@livekit/protocol';\nimport {\n type AvailabilityRequest,\n JobType,\n ParticipantPermission,\n ServerMessage,\n WorkerMessage,\n WorkerStatus,\n} from '@livekit/protocol';\nimport { AccessToken, RoomServiceClient } from 'livekit-server-sdk';\nimport { EventEmitter } from 'node:events';\nimport os from 'node:os';\nimport { WebSocket } from 'ws';\nimport { HTTPServer } from './http_server.js';\nimport { InferenceRunner } from './inference_runner.js';\nimport { InferenceProcExecutor } from './ipc/inference_proc_executor.js';\nimport { ProcPool } from './ipc/proc_pool.js';\nimport type { JobAcceptArguments, JobProcess, RunningJobInfo } from './job.js';\nimport { JobRequest } from './job.js';\nimport { log } from './log.js';\nimport { Future } from './utils.js';\nimport { version } from './version.js';\n\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst ASSIGNMENT_TIMEOUT = 7.5 * 1000;\nconst UPDATE_LOAD_INTERVAL = 2.5 * 1000;\n\nclass Default {\n static loadThreshold(production: boolean): number {\n if (production) {\n return 0.65;\n } else {\n return Infinity;\n }\n }\n\n static numIdleProcesses(production: boolean): number {\n if (production) {\n return 3;\n } else {\n return 0;\n }\n }\n\n static port(production: boolean): number {\n if (production) {\n return 8081;\n } else {\n return 0;\n }\n }\n}\n\n/** Necessary credentials not provided and not found in an appropriate environmental variable. */\nexport class MissingCredentialsError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Worker did not run as expected. */\nexport class WorkerError extends Error {\n constructor(msg?: string) {\n super(msg);\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** @internal */\nexport const defaultInitializeProcessFunc = (_: JobProcess) => _;\nconst defaultRequestFunc = async (ctx: JobRequest) => {\n await ctx.accept();\n};\nconst defaultCpuLoad = async (): Promise<number> => {\n return new Promise((resolve) => {\n const cpus1 = os.cpus();\n\n setTimeout(() => {\n const cpus2 = os.cpus();\n\n let idle = 0;\n let total = 0;\n\n for (let i = 0; i < cpus1.length; i++) {\n const cpu1 = cpus1[i]!.times;\n const cpu2 = cpus2[i]!.times;\n\n idle += cpu2.idle - cpu1.idle;\n\n const total1 = Object.values(cpu1).reduce((acc, i) => acc + i, 0);\n const total2 = Object.values(cpu2).reduce((acc, i) => acc + i, 0);\n\n total += total2 - total1;\n }\n\n resolve(+(1 - idle / total).toFixed(2));\n }, UPDATE_LOAD_INTERVAL);\n });\n};\n\n/** Participant permissions to pass to every agent spun up by this worker. */\nexport class WorkerPermissions {\n canPublish: boolean;\n canSubscribe: boolean;\n canPublishData: boolean;\n canUpdateMetadata: boolean;\n canPublishSources: TrackSource[];\n hidden: boolean;\n\n constructor(\n canPublish = true,\n canSubscribe = true,\n canPublishData = true,\n canUpdateMetadata = true,\n canPublishSources: TrackSource[] = [],\n hidden = false,\n ) {\n this.canPublish = canPublish;\n this.canSubscribe = canSubscribe;\n this.canPublishData = canPublishData;\n this.canUpdateMetadata = canUpdateMetadata;\n this.canPublishSources = canPublishSources;\n this.hidden = hidden;\n }\n}\n\n/**\n * Data class describing worker behaviour.\n *\n * @remarks\n * The Agents framework provides sane worker defaults, and works out-of-the-box with no tweaking\n * necessary. The only mandatory parameter is `agent`, which points to the entry function.\n *\n * This class is mostly useful in conjunction with {@link cli.runApp}.\n */\nexport class WorkerOptions {\n agent: string;\n requestFunc: (job: JobRequest) => Promise<void>;\n loadFunc: () => Promise<number>;\n loadThreshold: number;\n numIdleProcesses: number;\n shutdownProcessTimeout: number;\n initializeProcessTimeout: number;\n permissions: WorkerPermissions;\n agentName: string;\n workerType: JobType;\n maxRetry: number;\n wsURL: string;\n apiKey?: string;\n apiSecret?: string;\n workerToken?: string;\n host: string;\n port: number;\n logLevel: string;\n production: boolean;\n jobMemoryWarnMB: number;\n jobMemoryLimitMB: number;\n\n /** @param options */\n constructor({\n agent,\n requestFunc = defaultRequestFunc,\n loadFunc = defaultCpuLoad,\n loadThreshold = undefined,\n numIdleProcesses = undefined,\n shutdownProcessTimeout = 60 * 1000,\n initializeProcessTimeout = 10 * 1000,\n permissions = new WorkerPermissions(),\n agentName = '',\n workerType = JobType.JT_ROOM,\n maxRetry = MAX_RECONNECT_ATTEMPTS,\n wsURL = 'ws://localhost:7880',\n apiKey = undefined,\n apiSecret = undefined,\n workerToken = undefined,\n host = '0.0.0.0',\n port = undefined,\n logLevel = 'info',\n production = false,\n jobMemoryWarnMB = 300,\n jobMemoryLimitMB = 0,\n }: {\n /**\n * Path to a file that has {@link Agent} as a default export, dynamically imported later for\n * entrypoint and prewarm functions\n */\n agent: string;\n requestFunc?: (job: JobRequest) => Promise<void>;\n /** Called to determine the current load of the worker. Should return a value between 0 and 1. */\n loadFunc?: () => Promise<number>;\n /** When the load exceeds this threshold, the worker will be marked as unavailable. */\n loadThreshold?: number;\n numIdleProcesses?: number;\n shutdownProcessTimeout?: number;\n initializeProcessTimeout?: number;\n permissions?: WorkerPermissions;\n agentName?: string;\n workerType?: JobType;\n maxRetry?: number;\n wsURL?: string;\n apiKey?: string;\n apiSecret?: string;\n workerToken?: string;\n host?: string;\n port?: number;\n logLevel?: string;\n production?: boolean;\n jobMemoryWarnMB?: number;\n jobMemoryLimitMB?: number;\n }) {\n this.agent = agent;\n if (!this.agent) {\n throw new Error('No Agent file was passed to the worker');\n }\n this.requestFunc = requestFunc;\n this.loadFunc = loadFunc;\n this.loadThreshold = loadThreshold || Default.loadThreshold(production);\n this.numIdleProcesses = numIdleProcesses || Default.numIdleProcesses(production);\n this.shutdownProcessTimeout = shutdownProcessTimeout;\n this.initializeProcessTimeout = initializeProcessTimeout;\n this.permissions = permissions;\n this.agentName = agentName;\n this.workerType = workerType;\n this.maxRetry = maxRetry;\n this.wsURL = wsURL;\n this.apiKey = apiKey;\n this.apiSecret = apiSecret;\n this.workerToken = workerToken;\n this.host = host;\n this.port = port || Default.port(production);\n this.logLevel = logLevel;\n this.production = production;\n this.jobMemoryWarnMB = jobMemoryWarnMB;\n this.jobMemoryLimitMB = jobMemoryLimitMB;\n }\n}\n\nclass PendingAssignment {\n promise = new Promise<JobAssignment>((resolve) => {\n this.resolve = resolve; // this is how JavaScript lets you resolve promises externally\n });\n resolve(arg: JobAssignment) {\n arg; // useless call to counteract TypeScript E6133\n }\n}\n\n/**\n * Central orchestrator for all processes and job requests.\n *\n * @remarks\n * For most usecases, Worker should not be initialized or handled directly; you should instead call\n * for its creation through {@link cli.runApp}. This could, however, be useful in situations where\n * you don't have access to a command line, such as a headless program, or one that uses Agents\n * behind a wrapper.\n */\nexport class Worker {\n #opts: WorkerOptions;\n #procPool: ProcPool;\n\n #id = 'unregistered';\n #closed = true;\n #draining = false;\n #connecting = false;\n #tasks: Promise<void>[] = [];\n #pending: { [id: string]: PendingAssignment } = {};\n #close = new Future();\n\n event = new EventEmitter();\n #session: WebSocket | undefined = undefined;\n #httpServer: HTTPServer;\n #logger = log().child({ version });\n #inferenceExecutor?: InferenceProcExecutor;\n\n /* @throws {@link MissingCredentialsError} if URL, API key or API secret are missing */\n constructor(opts: WorkerOptions) {\n opts.wsURL = opts.wsURL || process.env.LIVEKIT_URL || '';\n opts.apiKey = opts.apiKey || process.env.LIVEKIT_API_KEY || '';\n opts.apiSecret = opts.apiSecret || process.env.LIVEKIT_API_SECRET || '';\n\n if (opts.wsURL === '')\n throw new MissingCredentialsError(\n 'URL is required: Set LIVEKIT_URL, run with --url, or pass wsURL in WorkerOptions',\n );\n if (opts.apiKey === '')\n throw new MissingCredentialsError(\n 'API Key is required: Set LIVEKIT_API_KEY, run with --api-key, or pass apiKey in WorkerOptions',\n );\n if (opts.apiSecret === '')\n throw new MissingCredentialsError(\n 'API Secret is required: Set LIVEKIT_API_SECRET, run with --api-secret, or pass apiSecret in WorkerOptions',\n );\n\n if (Object.entries(InferenceRunner.registeredRunners).length) {\n this.#inferenceExecutor = new InferenceProcExecutor({\n runners: InferenceRunner.registeredRunners,\n initializeTimeout: 30000,\n closeTimeout: 5000,\n memoryWarnMB: 2000,\n memoryLimitMB: 0,\n pingInterval: 5000,\n pingTimeout: 60000,\n highPingThreshold: 2500,\n });\n }\n\n this.#procPool = new ProcPool(\n opts.agent,\n opts.numIdleProcesses,\n opts.initializeProcessTimeout,\n opts.shutdownProcessTimeout,\n this.#inferenceExecutor,\n opts.jobMemoryWarnMB,\n opts.jobMemoryLimitMB,\n );\n\n this.#opts = opts;\n this.#httpServer = new HTTPServer(opts.host, opts.port, () => ({\n agent_name: opts.agentName,\n worker_type: JobType[opts.workerType],\n active_jobs: this.activeJobs.length,\n sdk_version: version,\n }));\n }\n\n /* @throws {@link WorkerError} if worker failed to connect or already running */\n async run() {\n if (!this.#closed) {\n throw new WorkerError('worker is already running');\n }\n\n if (this.#inferenceExecutor) {\n await this.#inferenceExecutor.start();\n await this.#inferenceExecutor.initialize();\n }\n\n this.#logger.info('starting worker');\n this.#closed = false;\n this.#procPool.start();\n\n const workerWS = async () => {\n let retries = 0;\n this.#connecting = true;\n\n while (!this.#closed) {\n const url = new URL(this.#opts.wsURL);\n url.protocol = url.protocol.replace('http', 'ws');\n const token = new AccessToken(this.#opts.apiKey, this.#opts.apiSecret);\n token.addGrant({ agent: true });\n const jwt = await token.toJwt();\n const wsUrl = new URL(url + 'agent');\n if (this.#opts.workerToken) {\n wsUrl.searchParams.append('worker_token', this.#opts.workerToken);\n }\n this.#session = new WebSocket(wsUrl, {\n headers: { authorization: 'Bearer ' + jwt },\n });\n\n try {\n await new Promise((resolve, reject) => {\n this.#session!.on('open', resolve);\n this.#session!.on('error', (error) => reject(error.message));\n this.#session!.on('close', (code) => reject(`WebSocket returned ${code}`));\n });\n\n retries = 0;\n this.#logger.debug('connected to LiveKit server');\n await this.#runWS(this.#session);\n } catch (e: unknown) {\n if (e instanceof Error || e instanceof ErrorEvent) {\n e = e.message;\n }\n\n if (this.#closed) return;\n if (retries >= this.#opts.maxRetry) {\n throw new WorkerError(\n `failed to connect to LiveKit server after ${retries} attempts: ${e}`,\n );\n }\n\n retries++;\n const delay = Math.min(retries * 2, 10);\n\n this.#logger.warn(\n `failed to connect to LiveKit server, retrying in ${delay} seconds: ${e} (${retries}/${this.#opts.maxRetry})`,\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay * 1000));\n }\n }\n };\n\n await Promise.all([workerWS(), this.#httpServer.run()]);\n this.#close.resolve();\n }\n\n get id(): string {\n return this.#id;\n }\n\n get activeJobs(): RunningJobInfo[] {\n return this.#procPool.processes\n .filter((proc) => proc.runningJob)\n .map((proc) => proc.runningJob!);\n }\n\n /* @throws {@link WorkerError} if worker did not drain in time */\n async drain(timeout?: number) {\n if (this.#draining) {\n return;\n }\n\n this.#logger.info('draining worker');\n this.#draining = true;\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'updateWorker',\n value: {\n status: WorkerStatus.WS_FULL,\n },\n },\n }),\n );\n\n const joinJobs = async () => {\n return Promise.all(\n this.#procPool.processes.map((proc) => {\n if (!proc.runningJob) {\n proc.close();\n }\n return proc.join();\n }),\n );\n };\n\n let timer: NodeJS.Timeout | undefined;\n if (timeout) {\n timer = setTimeout(() => {\n throw new WorkerError('timed out draining');\n }, timeout);\n }\n await joinJobs().then(() => {\n if (timeout) {\n clearTimeout(timer);\n }\n });\n }\n\n async simulateJob(roomName: string, participantIdentity?: string) {\n const client = new RoomServiceClient(this.#opts.wsURL, this.#opts.apiKey, this.#opts.apiSecret);\n const room = await client.createRoom({ name: roomName });\n let participant: ParticipantInfo | undefined = undefined;\n if (participantIdentity) {\n try {\n participant = await client.getParticipant(roomName, participantIdentity);\n } catch (e) {\n this.#logger.fatal(\n `participant with identity ${participantIdentity} not found in room ${roomName}`,\n );\n throw e;\n }\n }\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'simulateJob',\n value: {\n type: JobType.JT_PUBLISHER,\n room,\n participant,\n },\n },\n }),\n );\n }\n\n async #runWS(ws: WebSocket) {\n let closingWS = false;\n\n const send = (msg: WorkerMessage) => {\n if (closingWS) {\n this.event.off('worker_msg', send);\n return;\n }\n ws.send(msg.toBinary());\n };\n this.event.on('worker_msg', send);\n\n const close = new Promise<void>((resolve) => {\n ws.addEventListener('close', () => {\n closingWS = true;\n if (!this.#closed) {\n this.#logger.error('worker connection closed unexpectedly');\n }\n resolve();\n });\n });\n\n ws.addEventListener('error', (event) => {\n this.#logger.error('worker error:', event.message);\n });\n\n ws.addEventListener('message', (event) => {\n if (event.type !== 'message') {\n this.#logger.warn('unexpected message type: ' + event.type);\n return;\n }\n\n const msg = new ServerMessage();\n msg.fromBinary(event.data as Uint8Array);\n\n // register is the only valid first message, and it is only valid as the\n // first message\n if (this.#connecting && msg.message.case !== 'register') {\n throw new WorkerError('expected register response as first message');\n }\n\n switch (msg.message.case) {\n case 'register': {\n this.#id = msg.message.value.workerId;\n this.#logger\n .child({ id: this.id, server_info: msg.message.value.serverInfo })\n .info('registered worker');\n this.event.emit(\n 'worker_registered',\n msg.message.value.workerId,\n msg.message.value.serverInfo,\n );\n this.#connecting = false;\n break;\n }\n case 'availability': {\n if (!msg.message.value.job) return;\n const task = this.#availability(msg.message.value);\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n break;\n }\n case 'assignment': {\n if (!msg.message.value.job) return;\n const job = msg.message.value.job;\n if (job.id in this.#pending) {\n const task = this.#pending[job.id];\n delete this.#pending[job.id];\n task?.resolve(msg.message.value);\n } else {\n this.#logger.child({ job }).warn('received assignment for unknown job ' + job.id);\n }\n break;\n }\n case 'termination': {\n const task = this.#termination(msg.message.value);\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n break;\n }\n }\n });\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'register',\n value: {\n type: this.#opts.workerType,\n agentName: this.#opts.agentName,\n allowedPermissions: new ParticipantPermission({\n canPublish: this.#opts.permissions.canPublish,\n canSubscribe: this.#opts.permissions.canSubscribe,\n canPublishData: this.#opts.permissions.canPublishData,\n canUpdateMetadata: this.#opts.permissions.canUpdateMetadata,\n hidden: this.#opts.permissions.hidden,\n agent: true,\n }),\n version,\n },\n },\n }),\n );\n\n let currentStatus = WorkerStatus.WS_AVAILABLE;\n const loadMonitor = setInterval(() => {\n if (closingWS) clearInterval(loadMonitor);\n\n const oldStatus = currentStatus;\n this.#opts.loadFunc().then((currentLoad: number) => {\n const isFull = currentLoad >= this.#opts.loadThreshold;\n const currentlyAvailable = !isFull;\n currentStatus = currentlyAvailable ? WorkerStatus.WS_AVAILABLE : WorkerStatus.WS_FULL;\n\n if (oldStatus != currentStatus) {\n const extra = { load: currentLoad, loadThreshold: this.#opts.loadThreshold };\n if (isFull) {\n this.#logger.child(extra).info('worker is at full capacity, marking as unavailable');\n } else {\n this.#logger.child(extra).info('worker is below capacity, marking as available');\n }\n }\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'updateWorker',\n value: {\n load: currentLoad,\n status: currentStatus,\n },\n },\n }),\n );\n });\n }, UPDATE_LOAD_INTERVAL);\n\n await close;\n ws.removeAllListeners();\n }\n\n async #availability(msg: AvailabilityRequest) {\n let answered = false;\n\n const onReject = async () => {\n answered = true;\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'availability',\n value: {\n jobId: msg.job!.id,\n available: false,\n },\n },\n }),\n );\n };\n\n const onAccept = async (args: JobAcceptArguments) => {\n answered = true;\n\n this.event.emit(\n 'worker_msg',\n new WorkerMessage({\n message: {\n case: 'availability',\n value: {\n jobId: msg.job!.id,\n available: true,\n participantIdentity: args.identity,\n participantName: args.name,\n participantMetadata: args.metadata,\n participantAttributes: args.attributes,\n },\n },\n }),\n );\n\n this.#pending[req.id] = new PendingAssignment();\n const timer = setTimeout(() => {\n this.#logger.child({ req }).warn(`assignment for job ${req.id} timed out`);\n return;\n }, ASSIGNMENT_TIMEOUT);\n const asgn = await this.#pending[req.id]?.promise.then(async (asgn) => {\n clearTimeout(timer);\n return asgn;\n });\n\n if (asgn) {\n await this.#procPool.launchJob({\n acceptArguments: args,\n job: msg.job!,\n url: asgn.url || this.#opts.wsURL,\n token: asgn.token,\n });\n } else {\n this.#logger.child({ requestId: req.id }).warn('pending assignment not found');\n }\n };\n\n const req = new JobRequest(msg.job!, onReject, onAccept);\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('received job request');\n\n const jobRequestTask = async () => {\n try {\n await this.#opts.requestFunc(req);\n } catch (e) {\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('jobRequestFunc failed');\n await onReject();\n }\n\n if (!answered) {\n this.#logger\n .child({ job: msg.job, resuming: msg.resuming, agentName: this.#opts.agentName })\n .info('no answer was given inside the jobRequestFunc, automatically rejecting the job');\n }\n };\n\n const task = jobRequestTask();\n this.#tasks.push(task);\n task.finally(() => this.#tasks.splice(this.#tasks.indexOf(task)));\n }\n\n async #termination(msg: JobTermination) {\n const proc = this.#procPool.getByJobId(msg.jobId);\n if (proc === null) {\n // safe to ignore\n return;\n }\n await proc.close();\n }\n\n async close() {\n if (this.#closed) {\n await this.#close.await;\n return;\n }\n\n this.#logger.info('shutting down worker');\n\n this.#closed = true;\n\n await this.#inferenceExecutor?.close();\n await this.#procPool.close();\n await this.#httpServer.close();\n await Promise.allSettled(this.#tasks);\n\n this.#session?.close();\n await this.#close.await;\n }\n}\n"],"mappings":"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","names":["os","i","asgn"]}
@@ -0,0 +1,109 @@
1
+ /// <reference types="node" resolution-mode="require"/>
2
+ import type { TrackSource } from '@livekit/protocol';
3
+ import { JobType } from '@livekit/protocol';
4
+ import { EventEmitter } from 'node:events';
5
+ import type { JobProcess, RunningJobInfo } from './job.js';
6
+ import { JobRequest } from './job.js';
7
+ /** Necessary credentials not provided and not found in an appropriate environmental variable. */
8
+ export declare class MissingCredentialsError extends Error {
9
+ constructor(msg?: string);
10
+ }
11
+ /** Worker did not run as expected. */
12
+ export declare class WorkerError extends Error {
13
+ constructor(msg?: string);
14
+ }
15
+ /** @internal */
16
+ export declare const defaultInitializeProcessFunc: (_: JobProcess) => JobProcess;
17
+ /** Participant permissions to pass to every agent spun up by this worker. */
18
+ export declare class WorkerPermissions {
19
+ canPublish: boolean;
20
+ canSubscribe: boolean;
21
+ canPublishData: boolean;
22
+ canUpdateMetadata: boolean;
23
+ canPublishSources: TrackSource[];
24
+ hidden: boolean;
25
+ constructor(canPublish?: boolean, canSubscribe?: boolean, canPublishData?: boolean, canUpdateMetadata?: boolean, canPublishSources?: TrackSource[], hidden?: boolean);
26
+ }
27
+ /**
28
+ * Data class describing worker behaviour.
29
+ *
30
+ * @remarks
31
+ * The Agents framework provides sane worker defaults, and works out-of-the-box with no tweaking
32
+ * necessary. The only mandatory parameter is `agent`, which points to the entry function.
33
+ *
34
+ * This class is mostly useful in conjunction with {@link cli.runApp}.
35
+ */
36
+ export declare class WorkerOptions {
37
+ agent: string;
38
+ requestFunc: (job: JobRequest) => Promise<void>;
39
+ loadFunc: () => Promise<number>;
40
+ loadThreshold: number;
41
+ numIdleProcesses: number;
42
+ shutdownProcessTimeout: number;
43
+ initializeProcessTimeout: number;
44
+ permissions: WorkerPermissions;
45
+ agentName: string;
46
+ workerType: JobType;
47
+ maxRetry: number;
48
+ wsURL: string;
49
+ apiKey?: string;
50
+ apiSecret?: string;
51
+ workerToken?: string;
52
+ host: string;
53
+ port: number;
54
+ logLevel: string;
55
+ production: boolean;
56
+ jobMemoryWarnMB: number;
57
+ jobMemoryLimitMB: number;
58
+ /** @param options */
59
+ constructor({ agent, requestFunc, loadFunc, loadThreshold, numIdleProcesses, shutdownProcessTimeout, initializeProcessTimeout, permissions, agentName, workerType, maxRetry, wsURL, apiKey, apiSecret, workerToken, host, port, logLevel, production, jobMemoryWarnMB, jobMemoryLimitMB, }: {
60
+ /**
61
+ * Path to a file that has {@link Agent} as a default export, dynamically imported later for
62
+ * entrypoint and prewarm functions
63
+ */
64
+ agent: string;
65
+ requestFunc?: (job: JobRequest) => Promise<void>;
66
+ /** Called to determine the current load of the worker. Should return a value between 0 and 1. */
67
+ loadFunc?: () => Promise<number>;
68
+ /** When the load exceeds this threshold, the worker will be marked as unavailable. */
69
+ loadThreshold?: number;
70
+ numIdleProcesses?: number;
71
+ shutdownProcessTimeout?: number;
72
+ initializeProcessTimeout?: number;
73
+ permissions?: WorkerPermissions;
74
+ agentName?: string;
75
+ workerType?: JobType;
76
+ maxRetry?: number;
77
+ wsURL?: string;
78
+ apiKey?: string;
79
+ apiSecret?: string;
80
+ workerToken?: string;
81
+ host?: string;
82
+ port?: number;
83
+ logLevel?: string;
84
+ production?: boolean;
85
+ jobMemoryWarnMB?: number;
86
+ jobMemoryLimitMB?: number;
87
+ });
88
+ }
89
+ /**
90
+ * Central orchestrator for all processes and job requests.
91
+ *
92
+ * @remarks
93
+ * For most usecases, Worker should not be initialized or handled directly; you should instead call
94
+ * for its creation through {@link cli.runApp}. This could, however, be useful in situations where
95
+ * you don't have access to a command line, such as a headless program, or one that uses Agents
96
+ * behind a wrapper.
97
+ */
98
+ export declare class Worker {
99
+ #private;
100
+ event: EventEmitter<[never]>;
101
+ constructor(opts: WorkerOptions);
102
+ run(): Promise<void>;
103
+ get id(): string;
104
+ get activeJobs(): RunningJobInfo[];
105
+ drain(timeout?: number): Promise<void>;
106
+ simulateJob(roomName: string, participantIdentity?: string): Promise<void>;
107
+ close(): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAIV,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,OAAO,EAKR,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAO3C,OAAO,KAAK,EAAsB,UAAU,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAmCtC,iGAAiG;AACjG,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,GAAG,CAAC,EAAE,MAAM;CAIzB;AAED,sCAAsC;AACtC,qBAAa,WAAY,SAAQ,KAAK;gBACxB,GAAG,CAAC,EAAE,MAAM;CAIzB;AAED,gBAAgB;AAChB,eAAO,MAAM,4BAA4B,MAAO,UAAU,eAAM,CAAC;AA+BjE,6EAA6E;AAC7E,qBAAa,iBAAiB;IAC5B,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,WAAW,EAAE,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;gBAGd,UAAU,UAAO,EACjB,YAAY,UAAO,EACnB,cAAc,UAAO,EACrB,iBAAiB,UAAO,EACxB,iBAAiB,GAAE,WAAW,EAAO,EACrC,MAAM,UAAQ;CASjB;AAED;;;;;;;;GAQG;AACH,qBAAa,aAAa;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,wBAAwB,EAAE,MAAM,CAAC;IACjC,WAAW,EAAE,iBAAiB,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IAEzB,qBAAqB;gBACT,EACV,KAAK,EACL,WAAgC,EAChC,QAAyB,EACzB,aAAyB,EACzB,gBAA4B,EAC5B,sBAAkC,EAClC,wBAAoC,EACpC,WAAqC,EACrC,SAAc,EACd,UAA4B,EAC5B,QAAiC,EACjC,KAA6B,EAC7B,MAAkB,EAClB,SAAqB,EACrB,WAAuB,EACvB,IAAgB,EAChB,IAAgB,EAChB,QAAiB,EACjB,UAAkB,EAClB,eAAqB,EACrB,gBAAoB,GACrB,EAAE;QACD;;;WAGG;QACH,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,iGAAiG;QACjG,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;QACjC,sFAAsF;QACtF,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;QAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;QAChC,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,WAAW,CAAC,EAAE,iBAAiB,CAAC;QAChC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,OAAO,CAAC;QACrB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B;CA0BF;AAWD;;;;;;;;GAQG;AACH,qBAAa,MAAM;;IAYjB,KAAK,wBAAsB;gBAOf,IAAI,EAAE,aAAa;IAkDzB,GAAG;IAsET,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,UAAU,IAAI,cAAc,EAAE,CAIjC;IAGK,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM;IA4CtB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM;IA8Q1D,KAAK;CAkBZ"}
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
package/dist/worker.js CHANGED
@@ -231,7 +231,8 @@ class Worker {
231
231
  this.#httpServer = new HTTPServer(opts.host, opts.port, () => ({
232
232
  agent_name: opts.agentName,
233
233
  worker_type: JobType[opts.workerType],
234
- active_jobs: this.activeJobs.length
234
+ active_jobs: this.activeJobs.length,
235
+ sdk_version: version
235
236
  }));
236
237
  }
237
238
  /* @throws {@link WorkerError} if worker failed to connect or already running */