@livekit/agents 1.0.21 → 1.0.23

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 (149) hide show
  1. package/dist/inference/api_protos.cjs +2 -2
  2. package/dist/inference/api_protos.cjs.map +1 -1
  3. package/dist/inference/api_protos.d.cts +16 -16
  4. package/dist/inference/api_protos.d.ts +16 -16
  5. package/dist/inference/api_protos.js +2 -2
  6. package/dist/inference/api_protos.js.map +1 -1
  7. package/dist/inference/stt.cjs +42 -30
  8. package/dist/inference/stt.cjs.map +1 -1
  9. package/dist/inference/stt.d.ts.map +1 -1
  10. package/dist/inference/stt.js +42 -30
  11. package/dist/inference/stt.js.map +1 -1
  12. package/dist/inference/tts.cjs +2 -3
  13. package/dist/inference/tts.cjs.map +1 -1
  14. package/dist/inference/tts.d.ts.map +1 -1
  15. package/dist/inference/tts.js +2 -3
  16. package/dist/inference/tts.js.map +1 -1
  17. package/dist/ipc/job_proc_lazy_main.cjs +35 -1
  18. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  19. package/dist/ipc/job_proc_lazy_main.js +13 -1
  20. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  21. package/dist/job.cjs +52 -6
  22. package/dist/job.cjs.map +1 -1
  23. package/dist/job.d.cts +2 -0
  24. package/dist/job.d.ts +2 -0
  25. package/dist/job.d.ts.map +1 -1
  26. package/dist/job.js +52 -6
  27. package/dist/job.js.map +1 -1
  28. package/dist/llm/llm.cjs +38 -3
  29. package/dist/llm/llm.cjs.map +1 -1
  30. package/dist/llm/llm.d.cts +1 -0
  31. package/dist/llm/llm.d.ts +1 -0
  32. package/dist/llm/llm.d.ts.map +1 -1
  33. package/dist/llm/llm.js +38 -3
  34. package/dist/llm/llm.js.map +1 -1
  35. package/dist/log.cjs +34 -10
  36. package/dist/log.cjs.map +1 -1
  37. package/dist/log.d.cts +7 -0
  38. package/dist/log.d.ts +7 -0
  39. package/dist/log.d.ts.map +1 -1
  40. package/dist/log.js +34 -11
  41. package/dist/log.js.map +1 -1
  42. package/dist/stt/stt.cjs +18 -5
  43. package/dist/stt/stt.cjs.map +1 -1
  44. package/dist/stt/stt.d.ts.map +1 -1
  45. package/dist/stt/stt.js +18 -5
  46. package/dist/stt/stt.js.map +1 -1
  47. package/dist/telemetry/index.cjs +23 -2
  48. package/dist/telemetry/index.cjs.map +1 -1
  49. package/dist/telemetry/index.d.cts +4 -1
  50. package/dist/telemetry/index.d.ts +4 -1
  51. package/dist/telemetry/index.d.ts.map +1 -1
  52. package/dist/telemetry/index.js +27 -2
  53. package/dist/telemetry/index.js.map +1 -1
  54. package/dist/telemetry/logging.cjs +65 -0
  55. package/dist/telemetry/logging.cjs.map +1 -0
  56. package/dist/telemetry/logging.d.cts +21 -0
  57. package/dist/telemetry/logging.d.ts +21 -0
  58. package/dist/telemetry/logging.d.ts.map +1 -0
  59. package/dist/telemetry/logging.js +40 -0
  60. package/dist/telemetry/logging.js.map +1 -0
  61. package/dist/telemetry/otel_http_exporter.cjs +144 -0
  62. package/dist/telemetry/otel_http_exporter.cjs.map +1 -0
  63. package/dist/telemetry/otel_http_exporter.d.cts +62 -0
  64. package/dist/telemetry/otel_http_exporter.d.ts +62 -0
  65. package/dist/telemetry/otel_http_exporter.d.ts.map +1 -0
  66. package/dist/telemetry/otel_http_exporter.js +120 -0
  67. package/dist/telemetry/otel_http_exporter.js.map +1 -0
  68. package/dist/telemetry/pino_otel_transport.cjs +217 -0
  69. package/dist/telemetry/pino_otel_transport.cjs.map +1 -0
  70. package/dist/telemetry/pino_otel_transport.d.cts +58 -0
  71. package/dist/telemetry/pino_otel_transport.d.ts +58 -0
  72. package/dist/telemetry/pino_otel_transport.d.ts.map +1 -0
  73. package/dist/telemetry/pino_otel_transport.js +189 -0
  74. package/dist/telemetry/pino_otel_transport.js.map +1 -0
  75. package/dist/telemetry/traces.cjs +225 -16
  76. package/dist/telemetry/traces.cjs.map +1 -1
  77. package/dist/telemetry/traces.d.cts +17 -0
  78. package/dist/telemetry/traces.d.ts +17 -0
  79. package/dist/telemetry/traces.d.ts.map +1 -1
  80. package/dist/telemetry/traces.js +211 -14
  81. package/dist/telemetry/traces.js.map +1 -1
  82. package/dist/tts/tts.cjs +68 -20
  83. package/dist/tts/tts.cjs.map +1 -1
  84. package/dist/tts/tts.d.cts +2 -0
  85. package/dist/tts/tts.d.ts +2 -0
  86. package/dist/tts/tts.d.ts.map +1 -1
  87. package/dist/tts/tts.js +68 -20
  88. package/dist/tts/tts.js.map +1 -1
  89. package/dist/utils.cjs +6 -0
  90. package/dist/utils.cjs.map +1 -1
  91. package/dist/utils.d.cts +1 -0
  92. package/dist/utils.d.ts +1 -0
  93. package/dist/utils.d.ts.map +1 -1
  94. package/dist/utils.js +5 -0
  95. package/dist/utils.js.map +1 -1
  96. package/dist/voice/agent_activity.cjs +93 -7
  97. package/dist/voice/agent_activity.cjs.map +1 -1
  98. package/dist/voice/agent_activity.d.cts +3 -0
  99. package/dist/voice/agent_activity.d.ts +3 -0
  100. package/dist/voice/agent_activity.d.ts.map +1 -1
  101. package/dist/voice/agent_activity.js +93 -7
  102. package/dist/voice/agent_activity.js.map +1 -1
  103. package/dist/voice/agent_session.cjs +122 -27
  104. package/dist/voice/agent_session.cjs.map +1 -1
  105. package/dist/voice/agent_session.d.cts +15 -0
  106. package/dist/voice/agent_session.d.ts +15 -0
  107. package/dist/voice/agent_session.d.ts.map +1 -1
  108. package/dist/voice/agent_session.js +122 -27
  109. package/dist/voice/agent_session.js.map +1 -1
  110. package/dist/voice/audio_recognition.cjs +69 -22
  111. package/dist/voice/audio_recognition.cjs.map +1 -1
  112. package/dist/voice/audio_recognition.d.cts +5 -0
  113. package/dist/voice/audio_recognition.d.ts +5 -0
  114. package/dist/voice/audio_recognition.d.ts.map +1 -1
  115. package/dist/voice/audio_recognition.js +69 -22
  116. package/dist/voice/audio_recognition.js.map +1 -1
  117. package/dist/voice/generation.cjs +43 -3
  118. package/dist/voice/generation.cjs.map +1 -1
  119. package/dist/voice/generation.d.ts.map +1 -1
  120. package/dist/voice/generation.js +43 -3
  121. package/dist/voice/generation.js.map +1 -1
  122. package/dist/voice/report.cjs +3 -2
  123. package/dist/voice/report.cjs.map +1 -1
  124. package/dist/voice/report.d.cts +7 -1
  125. package/dist/voice/report.d.ts +7 -1
  126. package/dist/voice/report.d.ts.map +1 -1
  127. package/dist/voice/report.js +3 -2
  128. package/dist/voice/report.js.map +1 -1
  129. package/package.json +8 -2
  130. package/src/inference/api_protos.ts +2 -2
  131. package/src/inference/stt.ts +48 -33
  132. package/src/inference/tts.ts +4 -3
  133. package/src/ipc/job_proc_lazy_main.ts +12 -1
  134. package/src/job.ts +59 -10
  135. package/src/llm/llm.ts +48 -5
  136. package/src/log.ts +52 -15
  137. package/src/stt/stt.ts +18 -5
  138. package/src/telemetry/index.ts +22 -4
  139. package/src/telemetry/logging.ts +55 -0
  140. package/src/telemetry/otel_http_exporter.ts +191 -0
  141. package/src/telemetry/pino_otel_transport.ts +265 -0
  142. package/src/telemetry/traces.ts +320 -20
  143. package/src/tts/tts.ts +85 -24
  144. package/src/utils.ts +5 -0
  145. package/src/voice/agent_activity.ts +140 -22
  146. package/src/voice/agent_session.ts +174 -34
  147. package/src/voice/audio_recognition.ts +85 -26
  148. package/src/voice/generation.ts +59 -7
  149. package/src/voice/report.ts +10 -4
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/voice/generation.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { AudioResampler } from '@livekit/rtc-node';\nimport type { ReadableStream, ReadableStreamDefaultReader } from 'stream/web';\nimport {\n type ChatContext,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../llm/chat_context.js';\nimport type { ChatChunk } from '../llm/llm.js';\nimport {\n type ToolChoice,\n type ToolContext,\n isAgentHandoff,\n isFunctionTool,\n isToolError,\n} from '../llm/tool_context.js';\nimport { isZodSchema, parseZodSchema } from '../llm/zod-utils.js';\nimport { log } from '../log.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { Future, Task, shortuuid, toError } from '../utils.js';\nimport { type Agent, type ModelSettings, asyncLocalStorage, isStopResponse } from './agent.js';\nimport type { AgentSession } from './agent_session.js';\nimport type { AudioOutput, LLMNode, TTSNode, TextOutput } from './io.js';\nimport { RunContext } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\n/** @internal */\nexport class _LLMGenerationData {\n generatedText: string = '';\n generatedToolCalls: FunctionCall[];\n id: string;\n\n constructor(\n public readonly textStream: ReadableStream<string>,\n public readonly toolCallStream: ReadableStream<FunctionCall>,\n ) {\n this.id = shortuuid('item_');\n this.generatedToolCalls = [];\n }\n}\n\n// TODO(brian): remove this class in favor of ToolOutput\nexport class _ToolOutput {\n output: _JsOutput[];\n firstToolFut: Future;\n\n constructor() {\n this.output = [];\n this.firstToolFut = new Future();\n }\n}\n\n// TODO(brian): remove this class in favor of ToolExecutionOutput\nexport class _SanitizedOutput {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n replyRequired: boolean;\n agentTask?: Agent;\n\n constructor(\n toolCall: FunctionCall,\n toolCallOutput: FunctionCallOutput | undefined,\n replyRequired: boolean,\n agentTask: Agent | undefined,\n ) {\n this.toolCall = toolCall;\n this.toolCallOutput = toolCallOutput;\n this.replyRequired = replyRequired;\n this.agentTask = agentTask;\n }\n\n static create(params: {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n replyRequired?: boolean;\n agentTask?: Agent;\n }) {\n const { toolCall, toolCallOutput, replyRequired = true, agentTask } = params;\n return new _SanitizedOutput(toolCall, toolCallOutput, replyRequired, agentTask);\n }\n}\n\nfunction isValidToolOutput(toolOutput: unknown): boolean {\n const validTypes = ['string', 'number', 'boolean'];\n\n if (validTypes.includes(typeof toolOutput)) {\n return true;\n }\n\n if (toolOutput === undefined || toolOutput === null) {\n return true;\n }\n\n if (Array.isArray(toolOutput)) {\n return toolOutput.every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Set) {\n return Array.from(toolOutput).every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Map) {\n return Array.from(toolOutput.values()).every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Object) {\n return Object.entries(toolOutput).every(\n ([key, value]) => validTypes.includes(typeof key) && isValidToolOutput(value),\n );\n }\n\n return false;\n}\n\nexport class ToolExecutionOutput {\n constructor(\n public readonly toolCall: FunctionCall,\n public readonly toolCallOutput: FunctionCallOutput | undefined,\n public readonly agentTask: Agent | undefined,\n public readonly rawOutput: unknown,\n public readonly rawException: Error | undefined,\n public readonly replyRequired: boolean,\n ) {}\n\n static create(params: {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n agentTask?: Agent;\n rawOutput: unknown;\n rawException?: Error;\n replyRequired?: boolean;\n }) {\n const {\n toolCall,\n toolCallOutput,\n agentTask,\n rawOutput,\n rawException,\n replyRequired = true,\n } = params;\n return new ToolExecutionOutput(\n toolCall,\n toolCallOutput,\n agentTask,\n rawOutput,\n rawException,\n replyRequired,\n );\n }\n}\n\nexport interface ToolOutput {\n output: ToolExecutionOutput[];\n firstToolStartedFuture: Future<void>;\n}\n\n// TODO(brian): remove this class in favor of ToolExecutionOutput\nexport class _JsOutput {\n toolCall: FunctionCall;\n output: unknown;\n exception?: Error;\n\n #logger = log();\n\n constructor(toolCall: FunctionCall, output: unknown, exception: Error | undefined) {\n this.toolCall = toolCall;\n this.output = output;\n this.exception = exception;\n }\n\n static create(params: { toolCall: FunctionCall; output?: unknown; exception?: Error }) {\n const { toolCall, output = undefined, exception = undefined } = params;\n return new _JsOutput(toolCall, output, exception);\n }\n\n sanitize(): _SanitizedOutput {\n if (isToolError(this.exception)) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: this.exception.message,\n isError: true,\n }),\n });\n }\n\n if (isStopResponse(this.exception)) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n });\n }\n\n if (this.exception !== undefined) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: 'An internal error occurred while executing the tool.', // Don't send the actual error message, as it may contain sensitive information\n isError: true,\n }),\n });\n }\n\n let agentTask: Agent | undefined = undefined;\n let toolOutput: unknown = this.output;\n if (isAgentHandoff(this.output)) {\n agentTask = this.output.agent;\n toolOutput = this.output.returns;\n }\n\n if (!isValidToolOutput(toolOutput)) {\n this.#logger.error(\n {\n callId: this.toolCall.callId,\n function: this.toolCall.name,\n },\n `AI function ${this.toolCall.name} returned an invalid output`,\n );\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: undefined,\n });\n }\n\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: toolOutput !== undefined ? JSON.stringify(toolOutput) : '', // take the string representation of the output\n isError: false,\n }),\n replyRequired: toolOutput !== undefined, // require a reply if the tool returned an output\n agentTask,\n });\n }\n}\n\nexport function createToolOutput(params: {\n toolCall: FunctionCall;\n output?: unknown;\n exception?: Error;\n}): ToolExecutionOutput {\n const { toolCall, output, exception } = params;\n const logger = log();\n\n // support returning Exception instead of raising them (for devex purposes inside evals)\n let finalOutput = output;\n let finalException = exception;\n if (output instanceof Error) {\n finalException = output;\n finalOutput = undefined;\n }\n\n if (isToolError(finalException)) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: finalException.message,\n isError: true,\n }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n if (isStopResponse(finalException)) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n if (finalException !== undefined) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: 'An internal error occurred', // Don't send the actual error message, as it may contain sensitive information\n isError: true,\n }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n let agentTask: Agent | undefined = undefined;\n let toolOutput: unknown = finalOutput;\n if (isAgentHandoff(finalOutput)) {\n agentTask = finalOutput.agent;\n toolOutput = finalOutput.returns;\n }\n\n if (!isValidToolOutput(toolOutput)) {\n logger.error(\n {\n callId: toolCall.callId,\n output: finalOutput,\n },\n `AI function ${toolCall.name} returned an invalid output`,\n );\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: toolOutput !== undefined ? JSON.stringify(toolOutput) : '', // take the string representation of the output\n isError: false,\n }),\n replyRequired: toolOutput !== undefined, // require a reply if the tool returned an output\n agentTask,\n rawOutput: finalOutput,\n rawException: finalException,\n });\n}\n\nconst INSTRUCTIONS_MESSAGE_ID = 'lk.agent_task.instructions';\n\n/**\n * Update the instruction message in the chat context or insert a new one if missing.\n *\n * This function looks for an existing instruction message in the chat context using the identifier\n * 'INSTRUCTIONS_MESSAGE_ID'.\n *\n * @param options - The options for updating the instructions.\n * @param options.chatCtx - The chat context to update.\n * @param options.instructions - The instructions to add.\n * @param options.addIfMissing - Whether to add the instructions if they are missing.\n */\nexport function updateInstructions(options: {\n chatCtx: ChatContext;\n instructions: string;\n addIfMissing: boolean;\n}) {\n const { chatCtx, instructions, addIfMissing } = options;\n\n const idx = chatCtx.indexById(INSTRUCTIONS_MESSAGE_ID);\n if (idx !== undefined) {\n if (chatCtx.items[idx]!.type === 'message') {\n // create a new instance to avoid mutating the original\n chatCtx.items[idx] = ChatMessage.create({\n id: INSTRUCTIONS_MESSAGE_ID,\n role: 'system',\n content: [instructions],\n createdAt: chatCtx.items[idx]!.createdAt,\n });\n } else {\n throw new Error('expected the instructions inside the chatCtx to be of type \"message\"');\n }\n } else if (addIfMissing) {\n // insert the instructions at the beginning of the chat context\n chatCtx.items.unshift(\n ChatMessage.create({\n id: INSTRUCTIONS_MESSAGE_ID,\n role: 'system',\n content: [instructions],\n }),\n );\n }\n}\n\n// TODO(brian): PR3 - Add @tracer.startActiveSpan('llm_node') decorator/wrapper\nexport function performLLMInference(\n node: LLMNode,\n chatCtx: ChatContext,\n toolCtx: ToolContext,\n modelSettings: ModelSettings,\n controller: AbortController,\n): [Task<void>, _LLMGenerationData] {\n const textStream = new IdentityTransform<string>();\n const toolCallStream = new IdentityTransform<FunctionCall>();\n\n const textWriter = textStream.writable.getWriter();\n const toolCallWriter = toolCallStream.writable.getWriter();\n const data = new _LLMGenerationData(textStream.readable, toolCallStream.readable);\n\n const inferenceTask = async (signal: AbortSignal) => {\n let llmStreamReader: ReadableStreamDefaultReader<string | ChatChunk> | null = null;\n let llmStream: ReadableStream<string | ChatChunk> | null = null;\n\n try {\n llmStream = await node(chatCtx, toolCtx, modelSettings);\n if (llmStream === null) {\n await textWriter.close();\n return;\n }\n\n // TODO(brian): add support for dynamic tools\n\n llmStreamReader = llmStream.getReader();\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: chunk } = await llmStreamReader.read();\n if (done) {\n break;\n }\n\n if (typeof chunk === 'string') {\n data.generatedText += chunk;\n await textWriter.write(chunk);\n // TODO(shubhra): better way to check??\n } else {\n if (chunk.delta === undefined) {\n continue;\n }\n\n if (chunk.delta.toolCalls) {\n for (const tool of chunk.delta.toolCalls) {\n if (tool.type !== 'function_call') continue;\n\n const toolCall = FunctionCall.create({\n callId: `${data.id}/fnc_${data.generatedToolCalls.length}`,\n name: tool.name,\n args: tool.args,\n });\n\n data.generatedToolCalls.push(toolCall);\n await toolCallWriter.write(toolCall);\n }\n }\n\n if (chunk.delta.content) {\n data.generatedText += chunk.delta.content;\n await textWriter.write(chunk.delta.content);\n }\n }\n\n // No need to check if chunk is of type other than ChatChunk or string like in\n // Python since chunk is defined in the type ChatChunk | string in TypeScript\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n // Abort signal was triggered, handle gracefully\n return;\n }\n throw error;\n } finally {\n llmStreamReader?.releaseLock();\n await llmStream?.cancel();\n await textWriter.close();\n await toolCallWriter.close();\n }\n };\n\n return [\n Task.from((controller) => inferenceTask(controller.signal), controller, 'performLLMInference'),\n data,\n ];\n}\n\n// TODO(brian): PR3 - Add @tracer.startActiveSpan('tts_node') decorator/wrapper\nexport function performTTSInference(\n node: TTSNode,\n text: ReadableStream<string>,\n modelSettings: ModelSettings,\n controller: AbortController,\n): [Task<void>, ReadableStream<AudioFrame>] {\n const audioStream = new IdentityTransform<AudioFrame>();\n const outputWriter = audioStream.writable.getWriter();\n const audioOutputStream = audioStream.readable;\n\n const inferenceTask = async (signal: AbortSignal) => {\n let ttsStreamReader: ReadableStreamDefaultReader<AudioFrame> | null = null;\n let ttsStream: ReadableStream<AudioFrame> | null = null;\n\n try {\n ttsStream = await node(text, modelSettings);\n if (ttsStream === null) {\n await outputWriter.close();\n return;\n }\n\n ttsStreamReader = ttsStream.getReader();\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: chunk } = await ttsStreamReader.read();\n if (done) {\n break;\n }\n await outputWriter.write(chunk);\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n // Abort signal was triggered, handle gracefully\n return;\n }\n throw error;\n } finally {\n ttsStreamReader?.releaseLock();\n await ttsStream?.cancel();\n await outputWriter.close();\n }\n };\n\n return [\n Task.from((controller) => inferenceTask(controller.signal), controller, 'performTTSInference'),\n audioOutputStream,\n ];\n}\n\nexport interface _TextOut {\n text: string;\n firstTextFut: Future;\n}\n\nasync function forwardText(\n source: ReadableStream<string>,\n out: _TextOut,\n signal: AbortSignal,\n textOutput: TextOutput | null,\n): Promise<void> {\n const reader = source.getReader();\n try {\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: delta } = await reader.read();\n if (done) break;\n out.text += delta;\n if (textOutput !== null) {\n await textOutput.captureText(delta);\n }\n if (!out.firstTextFut.done) {\n out.firstTextFut.resolve();\n }\n }\n } finally {\n if (textOutput !== null) {\n textOutput.flush();\n }\n reader?.releaseLock();\n }\n}\n\nexport function performTextForwarding(\n source: ReadableStream<string>,\n controller: AbortController,\n textOutput: TextOutput | null,\n): [Task<void>, _TextOut] {\n const out = {\n text: '',\n firstTextFut: new Future(),\n };\n return [\n Task.from(\n (controller) => forwardText(source, out, controller.signal, textOutput),\n controller,\n 'performTextForwarding',\n ),\n out,\n ];\n}\n\nexport interface _AudioOut {\n audio: Array<AudioFrame>;\n firstFrameFut: Future;\n}\n\nasync function forwardAudio(\n ttsStream: ReadableStream<AudioFrame>,\n audioOuput: AudioOutput,\n out: _AudioOut,\n signal?: AbortSignal,\n): Promise<void> {\n const reader = ttsStream.getReader();\n let resampler: AudioResampler | null = null;\n\n try {\n while (true) {\n if (signal?.aborted) {\n break;\n }\n\n const { done, value: frame } = await reader.read();\n if (done) break;\n\n out.audio.push(frame);\n\n if (\n !out.firstFrameFut.done &&\n audioOuput.sampleRate &&\n audioOuput.sampleRate !== frame.sampleRate &&\n !resampler\n ) {\n resampler = new AudioResampler(frame.sampleRate, audioOuput.sampleRate, 1);\n }\n\n if (resampler) {\n for (const f of resampler.push(frame)) {\n await audioOuput.captureFrame(f);\n }\n } else {\n await audioOuput.captureFrame(frame);\n }\n\n // set the first frame future if not already set\n // (after completing the first frame)\n if (!out.firstFrameFut.done) {\n out.firstFrameFut.resolve();\n }\n }\n } finally {\n reader?.releaseLock();\n if (resampler) {\n for (const f of resampler.flush()) {\n await audioOuput.captureFrame(f);\n }\n }\n audioOuput.flush();\n }\n}\n\nexport function performAudioForwarding(\n ttsStream: ReadableStream<AudioFrame>,\n audioOutput: AudioOutput,\n controller: AbortController,\n): [Task<void>, _AudioOut] {\n const out = {\n audio: [],\n firstFrameFut: new Future(),\n };\n return [\n Task.from(\n (controller) => forwardAudio(ttsStream, audioOutput, out, controller.signal),\n controller,\n 'performAudioForwarding',\n ),\n out,\n ];\n}\n\n// TODO(brian): PR3 - Add @tracer.startActiveSpan('function_tool') wrapper for each tool execution\nexport function performToolExecutions({\n session,\n speechHandle,\n toolCtx,\n toolChoice,\n toolCallStream,\n onToolExecutionStarted = () => {},\n onToolExecutionCompleted = () => {},\n controller,\n}: {\n session: AgentSession;\n speechHandle: SpeechHandle;\n toolCtx: ToolContext;\n toolChoice?: ToolChoice;\n toolCallStream: ReadableStream<FunctionCall>;\n onToolExecutionStarted?: (toolCall: FunctionCall) => void;\n onToolExecutionCompleted?: (toolExecutionOutput: ToolExecutionOutput) => void;\n controller: AbortController;\n}): [Task<void>, ToolOutput] {\n const logger = log();\n const toolOutput: ToolOutput = {\n output: [],\n firstToolStartedFuture: new Future(),\n };\n\n const toolCompleted = (out: ToolExecutionOutput) => {\n onToolExecutionCompleted(out);\n toolOutput.output.push(out);\n };\n\n const executeToolsTask = async (controller: AbortController) => {\n const signal = controller.signal;\n const reader = toolCallStream.getReader();\n\n const tasks: Promise<any>[] = [];\n while (!signal.aborted) {\n const { done, value: toolCall } = await reader.read();\n if (signal.aborted) break;\n if (done) break;\n\n if (toolChoice === 'none') {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n \"received a tool call with toolChoice set to 'none', ignoring\",\n );\n continue;\n }\n\n // TODO(brian): assert other toolChoice values\n\n const tool = toolCtx[toolCall.name];\n if (!tool) {\n logger.warn(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n `unknown AI function ${toolCall.name}`,\n );\n continue;\n }\n\n if (!isFunctionTool(tool)) {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n `unknown tool type: ${typeof tool}`,\n );\n continue;\n }\n\n let parsedArgs: object | undefined;\n\n // Ensure valid arguments\n try {\n const jsonArgs = JSON.parse(toolCall.args);\n\n if (isZodSchema(tool.parameters)) {\n const result = await parseZodSchema<object>(tool.parameters, jsonArgs);\n if (result.success) {\n parsedArgs = result.data;\n } else {\n throw result.error;\n }\n } else {\n parsedArgs = jsonArgs;\n }\n } catch (rawError) {\n const error = toError(rawError);\n logger.error(\n {\n function: toolCall.name,\n arguments: toolCall.args,\n speech_id: speechHandle.id,\n error: error.message,\n },\n `tried to call AI function ${toolCall.name} with invalid arguments`,\n );\n toolCompleted(\n createToolOutput({\n toolCall,\n exception: error,\n }),\n );\n continue;\n }\n\n if (!toolOutput.firstToolStartedFuture.done) {\n toolOutput.firstToolStartedFuture.resolve();\n }\n\n onToolExecutionStarted(toolCall);\n\n logger.info(\n {\n function: toolCall.name,\n arguments: parsedArgs,\n speech_id: speechHandle.id,\n },\n 'Executing LLM tool call',\n );\n\n const toolExecution = asyncLocalStorage.run({ functionCall: toolCall }, async () => {\n return await tool.execute(parsedArgs, {\n ctx: new RunContext(session, speechHandle, toolCall),\n toolCallId: toolCall.callId,\n abortSignal: signal,\n });\n });\n\n const tracableToolExecution = async (toolExecTask: Promise<unknown>) => {\n // TODO(brian): add tracing\n\n // await for task to complete, if task is aborted, set exception\n let toolOutput: ToolExecutionOutput | undefined;\n try {\n const { result, isAborted } = await waitUntilAborted(toolExecTask, signal);\n toolOutput = createToolOutput({\n toolCall,\n exception: isAborted ? new Error('tool call was aborted') : undefined,\n output: isAborted ? undefined : result,\n });\n } catch (rawError) {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n error: toError(rawError).message,\n },\n 'exception occurred while executing tool',\n );\n toolOutput = createToolOutput({\n toolCall,\n exception: toError(rawError),\n });\n } finally {\n if (!toolOutput) throw new Error('toolOutput is undefined');\n toolCompleted(toolOutput);\n }\n };\n\n // wait, not cancelling all tool calling tasks\n tasks.push(tracableToolExecution(toolExecution));\n }\n\n await Promise.allSettled(tasks);\n if (toolOutput.output.length > 0) {\n logger.debug(\n {\n speech_id: speechHandle.id,\n },\n 'tools execution completed',\n );\n }\n };\n\n return [Task.from(executeToolsTask, controller, 'performToolExecutions'), toolOutput];\n}\n\ntype Aborted<T> =\n | {\n result: T;\n isAborted: false;\n }\n | {\n result: undefined;\n isAborted: true;\n };\n\nasync function waitUntilAborted<T>(promise: Promise<T>, signal: AbortSignal): Promise<Aborted<T>> {\n const abortFut = new Future<Aborted<T>>();\n\n const resolveAbort = () => {\n if (!abortFut.done) {\n abortFut.resolve({ result: undefined, isAborted: true });\n }\n };\n\n signal.addEventListener('abort', resolveAbort);\n\n promise\n .then((r) => {\n if (!abortFut.done) {\n abortFut.resolve({ result: r, isAborted: false });\n }\n })\n .catch((e) => {\n if (!abortFut.done) {\n abortFut.reject(e);\n }\n })\n .finally(() => {\n signal.removeEventListener('abort', resolveAbort);\n });\n\n return await abortFut.await;\n}\n\nexport function removeInstructions(chatCtx: ChatContext) {\n // loop in case there are items with the same id (shouldn't happen!)\n while (true) {\n const idx = chatCtx.indexById(INSTRUCTIONS_MESSAGE_ID);\n if (idx !== undefined) {\n chatCtx.items.splice(idx, 1);\n } else {\n break;\n }\n }\n}\n"],"mappings":"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","names":["controller","toolOutput"]}
1
+ {"version":3,"sources":["../../src/voice/generation.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { AudioResampler } from '@livekit/rtc-node';\nimport type { Span } from '@opentelemetry/api';\nimport { context as otelContext } from '@opentelemetry/api';\nimport type { ReadableStream, ReadableStreamDefaultReader } from 'stream/web';\nimport {\n type ChatContext,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../llm/chat_context.js';\nimport type { ChatChunk } from '../llm/llm.js';\nimport {\n type ToolChoice,\n type ToolContext,\n isAgentHandoff,\n isFunctionTool,\n isToolError,\n} from '../llm/tool_context.js';\nimport { isZodSchema, parseZodSchema } from '../llm/zod-utils.js';\nimport { log } from '../log.js';\nimport { IdentityTransform } from '../stream/identity_transform.js';\nimport { traceTypes, tracer } from '../telemetry/index.js';\nimport { Future, Task, shortuuid, toError } from '../utils.js';\nimport { type Agent, type ModelSettings, asyncLocalStorage, isStopResponse } from './agent.js';\nimport type { AgentSession } from './agent_session.js';\nimport type { AudioOutput, LLMNode, TTSNode, TextOutput } from './io.js';\nimport { RunContext } from './run_context.js';\nimport type { SpeechHandle } from './speech_handle.js';\n\n/** @internal */\nexport class _LLMGenerationData {\n generatedText: string = '';\n generatedToolCalls: FunctionCall[];\n id: string;\n\n constructor(\n public readonly textStream: ReadableStream<string>,\n public readonly toolCallStream: ReadableStream<FunctionCall>,\n ) {\n this.id = shortuuid('item_');\n this.generatedToolCalls = [];\n }\n}\n\n// TODO(brian): remove this class in favor of ToolOutput\nexport class _ToolOutput {\n output: _JsOutput[];\n firstToolFut: Future;\n\n constructor() {\n this.output = [];\n this.firstToolFut = new Future();\n }\n}\n\n// TODO(brian): remove this class in favor of ToolExecutionOutput\nexport class _SanitizedOutput {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n replyRequired: boolean;\n agentTask?: Agent;\n\n constructor(\n toolCall: FunctionCall,\n toolCallOutput: FunctionCallOutput | undefined,\n replyRequired: boolean,\n agentTask: Agent | undefined,\n ) {\n this.toolCall = toolCall;\n this.toolCallOutput = toolCallOutput;\n this.replyRequired = replyRequired;\n this.agentTask = agentTask;\n }\n\n static create(params: {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n replyRequired?: boolean;\n agentTask?: Agent;\n }) {\n const { toolCall, toolCallOutput, replyRequired = true, agentTask } = params;\n return new _SanitizedOutput(toolCall, toolCallOutput, replyRequired, agentTask);\n }\n}\n\nfunction isValidToolOutput(toolOutput: unknown): boolean {\n const validTypes = ['string', 'number', 'boolean'];\n\n if (validTypes.includes(typeof toolOutput)) {\n return true;\n }\n\n if (toolOutput === undefined || toolOutput === null) {\n return true;\n }\n\n if (Array.isArray(toolOutput)) {\n return toolOutput.every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Set) {\n return Array.from(toolOutput).every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Map) {\n return Array.from(toolOutput.values()).every(isValidToolOutput);\n }\n\n if (toolOutput instanceof Object) {\n return Object.entries(toolOutput).every(\n ([key, value]) => validTypes.includes(typeof key) && isValidToolOutput(value),\n );\n }\n\n return false;\n}\n\nexport class ToolExecutionOutput {\n constructor(\n public readonly toolCall: FunctionCall,\n public readonly toolCallOutput: FunctionCallOutput | undefined,\n public readonly agentTask: Agent | undefined,\n public readonly rawOutput: unknown,\n public readonly rawException: Error | undefined,\n public readonly replyRequired: boolean,\n ) {}\n\n static create(params: {\n toolCall: FunctionCall;\n toolCallOutput?: FunctionCallOutput;\n agentTask?: Agent;\n rawOutput: unknown;\n rawException?: Error;\n replyRequired?: boolean;\n }) {\n const {\n toolCall,\n toolCallOutput,\n agentTask,\n rawOutput,\n rawException,\n replyRequired = true,\n } = params;\n return new ToolExecutionOutput(\n toolCall,\n toolCallOutput,\n agentTask,\n rawOutput,\n rawException,\n replyRequired,\n );\n }\n}\n\nexport interface ToolOutput {\n output: ToolExecutionOutput[];\n firstToolStartedFuture: Future<void>;\n}\n\n// TODO(brian): remove this class in favor of ToolExecutionOutput\nexport class _JsOutput {\n toolCall: FunctionCall;\n output: unknown;\n exception?: Error;\n\n #logger = log();\n\n constructor(toolCall: FunctionCall, output: unknown, exception: Error | undefined) {\n this.toolCall = toolCall;\n this.output = output;\n this.exception = exception;\n }\n\n static create(params: { toolCall: FunctionCall; output?: unknown; exception?: Error }) {\n const { toolCall, output = undefined, exception = undefined } = params;\n return new _JsOutput(toolCall, output, exception);\n }\n\n sanitize(): _SanitizedOutput {\n if (isToolError(this.exception)) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: this.exception.message,\n isError: true,\n }),\n });\n }\n\n if (isStopResponse(this.exception)) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n });\n }\n\n if (this.exception !== undefined) {\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: 'An internal error occurred while executing the tool.', // Don't send the actual error message, as it may contain sensitive information\n isError: true,\n }),\n });\n }\n\n let agentTask: Agent | undefined = undefined;\n let toolOutput: unknown = this.output;\n if (isAgentHandoff(this.output)) {\n agentTask = this.output.agent;\n toolOutput = this.output.returns;\n }\n\n if (!isValidToolOutput(toolOutput)) {\n this.#logger.error(\n {\n callId: this.toolCall.callId,\n function: this.toolCall.name,\n },\n `AI function ${this.toolCall.name} returned an invalid output`,\n );\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: undefined,\n });\n }\n\n return _SanitizedOutput.create({\n toolCall: FunctionCall.create({ ...this.toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: this.toolCall.name,\n callId: this.toolCall.callId,\n output: toolOutput !== undefined ? JSON.stringify(toolOutput) : '', // take the string representation of the output\n isError: false,\n }),\n replyRequired: toolOutput !== undefined, // require a reply if the tool returned an output\n agentTask,\n });\n }\n}\n\nexport function createToolOutput(params: {\n toolCall: FunctionCall;\n output?: unknown;\n exception?: Error;\n}): ToolExecutionOutput {\n const { toolCall, output, exception } = params;\n const logger = log();\n\n // support returning Exception instead of raising them (for devex purposes inside evals)\n let finalOutput = output;\n let finalException = exception;\n if (output instanceof Error) {\n finalException = output;\n finalOutput = undefined;\n }\n\n if (isToolError(finalException)) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: finalException.message,\n isError: true,\n }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n if (isStopResponse(finalException)) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n if (finalException !== undefined) {\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: 'An internal error occurred', // Don't send the actual error message, as it may contain sensitive information\n isError: true,\n }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n let agentTask: Agent | undefined = undefined;\n let toolOutput: unknown = finalOutput;\n if (isAgentHandoff(finalOutput)) {\n agentTask = finalOutput.agent;\n toolOutput = finalOutput.returns;\n }\n\n if (!isValidToolOutput(toolOutput)) {\n logger.error(\n {\n callId: toolCall.callId,\n output: finalOutput,\n },\n `AI function ${toolCall.name} returned an invalid output`,\n );\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n rawOutput: finalOutput,\n rawException: finalException,\n });\n }\n\n return ToolExecutionOutput.create({\n toolCall: FunctionCall.create({ ...toolCall }),\n toolCallOutput: FunctionCallOutput.create({\n name: toolCall.name,\n callId: toolCall.callId,\n output: toolOutput !== undefined ? JSON.stringify(toolOutput) : '', // take the string representation of the output\n isError: false,\n }),\n replyRequired: toolOutput !== undefined, // require a reply if the tool returned an output\n agentTask,\n rawOutput: finalOutput,\n rawException: finalException,\n });\n}\n\nconst INSTRUCTIONS_MESSAGE_ID = 'lk.agent_task.instructions';\n\n/**\n * Update the instruction message in the chat context or insert a new one if missing.\n *\n * This function looks for an existing instruction message in the chat context using the identifier\n * 'INSTRUCTIONS_MESSAGE_ID'.\n *\n * @param options - The options for updating the instructions.\n * @param options.chatCtx - The chat context to update.\n * @param options.instructions - The instructions to add.\n * @param options.addIfMissing - Whether to add the instructions if they are missing.\n */\nexport function updateInstructions(options: {\n chatCtx: ChatContext;\n instructions: string;\n addIfMissing: boolean;\n}) {\n const { chatCtx, instructions, addIfMissing } = options;\n\n const idx = chatCtx.indexById(INSTRUCTIONS_MESSAGE_ID);\n if (idx !== undefined) {\n if (chatCtx.items[idx]!.type === 'message') {\n // create a new instance to avoid mutating the original\n chatCtx.items[idx] = ChatMessage.create({\n id: INSTRUCTIONS_MESSAGE_ID,\n role: 'system',\n content: [instructions],\n createdAt: chatCtx.items[idx]!.createdAt,\n });\n } else {\n throw new Error('expected the instructions inside the chatCtx to be of type \"message\"');\n }\n } else if (addIfMissing) {\n // insert the instructions at the beginning of the chat context\n chatCtx.items.unshift(\n ChatMessage.create({\n id: INSTRUCTIONS_MESSAGE_ID,\n role: 'system',\n content: [instructions],\n }),\n );\n }\n}\n\nexport function performLLMInference(\n node: LLMNode,\n chatCtx: ChatContext,\n toolCtx: ToolContext,\n modelSettings: ModelSettings,\n controller: AbortController,\n): [Task<void>, _LLMGenerationData] {\n const textStream = new IdentityTransform<string>();\n const toolCallStream = new IdentityTransform<FunctionCall>();\n\n const textWriter = textStream.writable.getWriter();\n const toolCallWriter = toolCallStream.writable.getWriter();\n const data = new _LLMGenerationData(textStream.readable, toolCallStream.readable);\n\n const _performLLMInferenceImpl = async (signal: AbortSignal, span: Span) => {\n span.setAttribute(\n traceTypes.ATTR_CHAT_CTX,\n JSON.stringify(chatCtx.toJSON({ excludeTimestamp: false })),\n );\n span.setAttribute(traceTypes.ATTR_FUNCTION_TOOLS, JSON.stringify(Object.keys(toolCtx)));\n\n let llmStreamReader: ReadableStreamDefaultReader<string | ChatChunk> | null = null;\n let llmStream: ReadableStream<string | ChatChunk> | null = null;\n\n try {\n llmStream = await node(chatCtx, toolCtx, modelSettings);\n if (llmStream === null) {\n await textWriter.close();\n return;\n }\n\n // TODO(brian): add support for dynamic tools\n\n llmStreamReader = llmStream.getReader();\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: chunk } = await llmStreamReader.read();\n if (done) {\n break;\n }\n\n if (typeof chunk === 'string') {\n data.generatedText += chunk;\n await textWriter.write(chunk);\n // TODO(shubhra): better way to check??\n } else {\n if (chunk.delta === undefined) {\n continue;\n }\n\n if (chunk.delta.toolCalls) {\n for (const tool of chunk.delta.toolCalls) {\n if (tool.type !== 'function_call') continue;\n\n const toolCall = FunctionCall.create({\n callId: `${data.id}/fnc_${data.generatedToolCalls.length}`,\n name: tool.name,\n args: tool.args,\n });\n\n data.generatedToolCalls.push(toolCall);\n await toolCallWriter.write(toolCall);\n }\n }\n\n if (chunk.delta.content) {\n data.generatedText += chunk.delta.content;\n await textWriter.write(chunk.delta.content);\n }\n }\n\n // No need to check if chunk is of type other than ChatChunk or string like in\n // Python since chunk is defined in the type ChatChunk | string in TypeScript\n }\n\n span.setAttribute(traceTypes.ATTR_RESPONSE_TEXT, data.generatedText);\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n // Abort signal was triggered, handle gracefully\n return;\n }\n throw error;\n } finally {\n llmStreamReader?.releaseLock();\n await llmStream?.cancel();\n await textWriter.close();\n await toolCallWriter.close();\n }\n };\n\n // Capture the current context (agent_turn) to ensure llm_node is properly parented\n const currentContext = otelContext.active();\n\n const inferenceTask = async (signal: AbortSignal) =>\n tracer.startActiveSpan(async (span) => _performLLMInferenceImpl(signal, span), {\n name: 'llm_node',\n context: currentContext,\n });\n\n return [\n Task.from((controller) => inferenceTask(controller.signal), controller, 'performLLMInference'),\n data,\n ];\n}\n\nexport function performTTSInference(\n node: TTSNode,\n text: ReadableStream<string>,\n modelSettings: ModelSettings,\n controller: AbortController,\n): [Task<void>, ReadableStream<AudioFrame>] {\n const audioStream = new IdentityTransform<AudioFrame>();\n const outputWriter = audioStream.writable.getWriter();\n const audioOutputStream = audioStream.readable;\n\n const _performTTSInferenceImpl = async (signal: AbortSignal) => {\n let ttsStreamReader: ReadableStreamDefaultReader<AudioFrame> | null = null;\n let ttsStream: ReadableStream<AudioFrame> | null = null;\n\n try {\n ttsStream = await node(text, modelSettings);\n if (ttsStream === null) {\n await outputWriter.close();\n return;\n }\n\n ttsStreamReader = ttsStream.getReader();\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: chunk } = await ttsStreamReader.read();\n if (done) {\n break;\n }\n await outputWriter.write(chunk);\n }\n } catch (error) {\n if (error instanceof DOMException && error.name === 'AbortError') {\n // Abort signal was triggered, handle gracefully\n return;\n }\n throw error;\n } finally {\n ttsStreamReader?.releaseLock();\n await ttsStream?.cancel();\n await outputWriter.close();\n }\n };\n\n // Capture the current context (agent_turn) to ensure tts_node is properly parented\n const currentContext = otelContext.active();\n\n const inferenceTask = async (signal: AbortSignal) =>\n tracer.startActiveSpan(async () => _performTTSInferenceImpl(signal), {\n name: 'tts_node',\n context: currentContext,\n });\n\n return [\n Task.from((controller) => inferenceTask(controller.signal), controller, 'performTTSInference'),\n audioOutputStream,\n ];\n}\n\nexport interface _TextOut {\n text: string;\n firstTextFut: Future;\n}\n\nasync function forwardText(\n source: ReadableStream<string>,\n out: _TextOut,\n signal: AbortSignal,\n textOutput: TextOutput | null,\n): Promise<void> {\n const reader = source.getReader();\n try {\n while (true) {\n if (signal.aborted) {\n break;\n }\n const { done, value: delta } = await reader.read();\n if (done) break;\n out.text += delta;\n if (textOutput !== null) {\n await textOutput.captureText(delta);\n }\n if (!out.firstTextFut.done) {\n out.firstTextFut.resolve();\n }\n }\n } finally {\n if (textOutput !== null) {\n textOutput.flush();\n }\n reader?.releaseLock();\n }\n}\n\nexport function performTextForwarding(\n source: ReadableStream<string>,\n controller: AbortController,\n textOutput: TextOutput | null,\n): [Task<void>, _TextOut] {\n const out = {\n text: '',\n firstTextFut: new Future(),\n };\n return [\n Task.from(\n (controller) => forwardText(source, out, controller.signal, textOutput),\n controller,\n 'performTextForwarding',\n ),\n out,\n ];\n}\n\nexport interface _AudioOut {\n audio: Array<AudioFrame>;\n firstFrameFut: Future;\n}\n\nasync function forwardAudio(\n ttsStream: ReadableStream<AudioFrame>,\n audioOuput: AudioOutput,\n out: _AudioOut,\n signal?: AbortSignal,\n): Promise<void> {\n const reader = ttsStream.getReader();\n let resampler: AudioResampler | null = null;\n\n try {\n while (true) {\n if (signal?.aborted) {\n break;\n }\n\n const { done, value: frame } = await reader.read();\n if (done) break;\n\n out.audio.push(frame);\n\n if (\n !out.firstFrameFut.done &&\n audioOuput.sampleRate &&\n audioOuput.sampleRate !== frame.sampleRate &&\n !resampler\n ) {\n resampler = new AudioResampler(frame.sampleRate, audioOuput.sampleRate, 1);\n }\n\n if (resampler) {\n for (const f of resampler.push(frame)) {\n await audioOuput.captureFrame(f);\n }\n } else {\n await audioOuput.captureFrame(frame);\n }\n\n // set the first frame future if not already set\n // (after completing the first frame)\n if (!out.firstFrameFut.done) {\n out.firstFrameFut.resolve();\n }\n }\n } finally {\n reader?.releaseLock();\n if (resampler) {\n for (const f of resampler.flush()) {\n await audioOuput.captureFrame(f);\n }\n }\n audioOuput.flush();\n }\n}\n\nexport function performAudioForwarding(\n ttsStream: ReadableStream<AudioFrame>,\n audioOutput: AudioOutput,\n controller: AbortController,\n): [Task<void>, _AudioOut] {\n const out = {\n audio: [],\n firstFrameFut: new Future(),\n };\n return [\n Task.from(\n (controller) => forwardAudio(ttsStream, audioOutput, out, controller.signal),\n controller,\n 'performAudioForwarding',\n ),\n out,\n ];\n}\n\n// function_tool span is already implemented in tracableToolExecution below (line ~796)\nexport function performToolExecutions({\n session,\n speechHandle,\n toolCtx,\n toolChoice,\n toolCallStream,\n onToolExecutionStarted = () => {},\n onToolExecutionCompleted = () => {},\n controller,\n}: {\n session: AgentSession;\n speechHandle: SpeechHandle;\n toolCtx: ToolContext;\n toolChoice?: ToolChoice;\n toolCallStream: ReadableStream<FunctionCall>;\n onToolExecutionStarted?: (toolCall: FunctionCall) => void;\n onToolExecutionCompleted?: (toolExecutionOutput: ToolExecutionOutput) => void;\n controller: AbortController;\n}): [Task<void>, ToolOutput] {\n const logger = log();\n const toolOutput: ToolOutput = {\n output: [],\n firstToolStartedFuture: new Future(),\n };\n\n const toolCompleted = (out: ToolExecutionOutput) => {\n onToolExecutionCompleted(out);\n toolOutput.output.push(out);\n };\n\n const executeToolsTask = async (controller: AbortController) => {\n const signal = controller.signal;\n const reader = toolCallStream.getReader();\n\n const tasks: Promise<any>[] = [];\n while (!signal.aborted) {\n const { done, value: toolCall } = await reader.read();\n if (signal.aborted) break;\n if (done) break;\n\n if (toolChoice === 'none') {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n \"received a tool call with toolChoice set to 'none', ignoring\",\n );\n continue;\n }\n\n // TODO(brian): assert other toolChoice values\n\n const tool = toolCtx[toolCall.name];\n if (!tool) {\n logger.warn(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n `unknown AI function ${toolCall.name}`,\n );\n continue;\n }\n\n if (!isFunctionTool(tool)) {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n },\n `unknown tool type: ${typeof tool}`,\n );\n continue;\n }\n\n let parsedArgs: object | undefined;\n\n // Ensure valid arguments\n try {\n const jsonArgs = JSON.parse(toolCall.args);\n\n if (isZodSchema(tool.parameters)) {\n const result = await parseZodSchema<object>(tool.parameters, jsonArgs);\n if (result.success) {\n parsedArgs = result.data;\n } else {\n throw result.error;\n }\n } else {\n parsedArgs = jsonArgs;\n }\n } catch (rawError) {\n const error = toError(rawError);\n logger.error(\n {\n function: toolCall.name,\n arguments: toolCall.args,\n speech_id: speechHandle.id,\n error: error.message,\n },\n `tried to call AI function ${toolCall.name} with invalid arguments`,\n );\n toolCompleted(\n createToolOutput({\n toolCall,\n exception: error,\n }),\n );\n continue;\n }\n\n if (!toolOutput.firstToolStartedFuture.done) {\n toolOutput.firstToolStartedFuture.resolve();\n }\n\n onToolExecutionStarted(toolCall);\n\n logger.info(\n {\n function: toolCall.name,\n arguments: parsedArgs,\n speech_id: speechHandle.id,\n },\n 'Executing LLM tool call',\n );\n\n const toolExecution = asyncLocalStorage.run({ functionCall: toolCall }, async () => {\n return await tool.execute(parsedArgs, {\n ctx: new RunContext(session, speechHandle, toolCall),\n toolCallId: toolCall.callId,\n abortSignal: signal,\n });\n });\n\n const _tracableToolExecutionImpl = async (toolExecTask: Promise<unknown>, span: Span) => {\n span.setAttribute(traceTypes.ATTR_FUNCTION_TOOL_NAME, toolCall.name);\n span.setAttribute(traceTypes.ATTR_FUNCTION_TOOL_ARGS, toolCall.args);\n\n // await for task to complete, if task is aborted, set exception\n let toolOutput: ToolExecutionOutput | undefined;\n try {\n const { result, isAborted } = await waitUntilAborted(toolExecTask, signal);\n toolOutput = createToolOutput({\n toolCall,\n exception: isAborted ? new Error('tool call was aborted') : undefined,\n output: isAborted ? undefined : result,\n });\n\n if (toolOutput.toolCallOutput) {\n span.setAttribute(\n traceTypes.ATTR_FUNCTION_TOOL_OUTPUT,\n toolOutput.toolCallOutput.output,\n );\n span.setAttribute(\n traceTypes.ATTR_FUNCTION_TOOL_IS_ERROR,\n toolOutput.toolCallOutput.isError,\n );\n }\n } catch (rawError) {\n logger.error(\n {\n function: toolCall.name,\n speech_id: speechHandle.id,\n error: toError(rawError).message,\n },\n 'exception occurred while executing tool',\n );\n toolOutput = createToolOutput({\n toolCall,\n exception: toError(rawError),\n });\n\n if (toolOutput.toolCallOutput) {\n span.setAttribute(\n traceTypes.ATTR_FUNCTION_TOOL_OUTPUT,\n toolOutput.toolCallOutput.output,\n );\n span.setAttribute(traceTypes.ATTR_FUNCTION_TOOL_IS_ERROR, true);\n }\n } finally {\n if (!toolOutput) throw new Error('toolOutput is undefined');\n toolCompleted(toolOutput);\n }\n };\n\n const tracableToolExecution = (toolExecTask: Promise<unknown>) =>\n tracer.startActiveSpan(async (span) => _tracableToolExecutionImpl(toolExecTask, span), {\n name: 'function_tool',\n });\n\n // wait, not cancelling all tool calling tasks\n tasks.push(tracableToolExecution(toolExecution));\n }\n\n await Promise.allSettled(tasks);\n if (toolOutput.output.length > 0) {\n logger.debug(\n {\n speech_id: speechHandle.id,\n },\n 'tools execution completed',\n );\n }\n };\n\n return [Task.from(executeToolsTask, controller, 'performToolExecutions'), toolOutput];\n}\n\ntype Aborted<T> =\n | {\n result: T;\n isAborted: false;\n }\n | {\n result: undefined;\n isAborted: true;\n };\n\nasync function waitUntilAborted<T>(promise: Promise<T>, signal: AbortSignal): Promise<Aborted<T>> {\n const abortFut = new Future<Aborted<T>>();\n\n const resolveAbort = () => {\n if (!abortFut.done) {\n abortFut.resolve({ result: undefined, isAborted: true });\n }\n };\n\n signal.addEventListener('abort', resolveAbort);\n\n promise\n .then((r) => {\n if (!abortFut.done) {\n abortFut.resolve({ result: r, isAborted: false });\n }\n })\n .catch((e) => {\n if (!abortFut.done) {\n abortFut.reject(e);\n }\n })\n .finally(() => {\n signal.removeEventListener('abort', resolveAbort);\n });\n\n return await abortFut.await;\n}\n\nexport function removeInstructions(chatCtx: ChatContext) {\n // loop in case there are items with the same id (shouldn't happen!)\n while (true) {\n const idx = chatCtx.indexById(INSTRUCTIONS_MESSAGE_ID);\n if (idx !== undefined) {\n chatCtx.items.splice(idx, 1);\n } else {\n break;\n }\n }\n}\n"],"mappings":"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","names":["controller","toolOutput"]}
@@ -30,7 +30,8 @@ function createSessionReport(opts) {
30
30
  options: opts.options,
31
31
  events: opts.events,
32
32
  chatHistory: opts.chatHistory,
33
- enableUserDataTraining: opts.enableUserDataTraining ?? false,
33
+ enableRecording: opts.enableUserDataTraining ?? false,
34
+ startedAt: opts.startedAt ?? Date.now(),
34
35
  timestamp: opts.timestamp ?? Date.now()
35
36
  };
36
37
  }
@@ -57,7 +58,7 @@ function sessionReportToJSON(report) {
57
58
  max_tool_steps: report.options.maxToolSteps
58
59
  },
59
60
  chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),
60
- enable_user_data_training: report.enableUserDataTraining,
61
+ enable_user_data_training: report.enableRecording,
61
62
  timestamp: report.timestamp
62
63
  };
63
64
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/voice/report.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ChatContext } from '../llm/chat_context.js';\nimport type { VoiceOptions } from './agent_session.js';\nimport type { AgentEvent } from './events.js';\n\nexport interface SessionReport {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining: boolean;\n timestamp: number;\n}\n\nexport interface SessionReportOptions {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining?: boolean;\n timestamp?: number;\n}\n\nexport function createSessionReport(opts: SessionReportOptions): SessionReport {\n return {\n jobId: opts.jobId,\n roomId: opts.roomId,\n room: opts.room,\n options: opts.options,\n events: opts.events,\n chatHistory: opts.chatHistory,\n enableUserDataTraining: opts.enableUserDataTraining ?? false,\n timestamp: opts.timestamp ?? Date.now(),\n };\n}\n\n// TODO(brian): PR5 - Add uploadSessionReport() function that creates multipart form with:\n// - header: protobuf MetricsRecordingHeader (room_id, duration, start_time)\n// - chat_history: JSON serialized chat history (use sessionReportToJSON)\n// - audio: audio recording file if available (ogg format)\n// - Uploads to LiveKit Cloud observability endpoint with JWT auth\nexport function sessionReportToJSON(report: SessionReport): Record<string, unknown> {\n const events: Record<string, unknown>[] = [];\n\n for (const event of report.events) {\n if (event.type === 'metrics_collected') {\n continue; // metrics are too noisy, Cloud is using the chat_history as the source of truth\n }\n\n events.push({ ...event });\n }\n\n return {\n job_id: report.jobId,\n room_id: report.roomId,\n room: report.room,\n events,\n options: {\n allow_interruptions: report.options.allowInterruptions,\n discard_audio_if_uninterruptible: report.options.discardAudioIfUninterruptible,\n min_interruption_duration: report.options.minInterruptionDuration,\n min_interruption_words: report.options.minInterruptionWords,\n min_endpointing_delay: report.options.minEndpointingDelay,\n max_endpointing_delay: report.options.maxEndpointingDelay,\n max_tool_steps: report.options.maxToolSteps,\n },\n chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),\n enable_user_data_training: report.enableUserDataTraining,\n timestamp: report.timestamp,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6BO,SAAS,oBAAoB,MAA2C;AAC7E,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,wBAAwB,KAAK,0BAA0B;AAAA,IACvD,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,EACxC;AACF;AAOO,SAAS,oBAAoB,QAAgD;AAClF,QAAM,SAAoC,CAAC;AAE3C,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,MAAM,SAAS,qBAAqB;AACtC;AAAA,IACF;AAEA,WAAO,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,qBAAqB,OAAO,QAAQ;AAAA,MACpC,kCAAkC,OAAO,QAAQ;AAAA,MACjD,2BAA2B,OAAO,QAAQ;AAAA,MAC1C,wBAAwB,OAAO,QAAQ;AAAA,MACvC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,gBAAgB,OAAO,QAAQ;AAAA,IACjC;AAAA,IACA,cAAc,OAAO,YAAY,OAAO,EAAE,kBAAkB,MAAM,CAAC;AAAA,IACnE,2BAA2B,OAAO;AAAA,IAClC,WAAW,OAAO;AAAA,EACpB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/voice/report.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ChatContext } from '../llm/chat_context.js';\nimport type { VoiceOptions } from './agent_session.js';\nimport type { AgentEvent } from './events.js';\n\nexport interface SessionReport {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableRecording: boolean;\n /** Timestamp when the session started (milliseconds) */\n startedAt: number;\n /** Timestamp when the session report was created (milliseconds), typically at the end of the session */\n timestamp: number;\n}\n\nexport interface SessionReportOptions {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining?: boolean;\n /** Timestamp when the session started (milliseconds) */\n startedAt?: number;\n /** Timestamp when the session report was created (milliseconds) */\n timestamp?: number;\n}\n\nexport function createSessionReport(opts: SessionReportOptions): SessionReport {\n return {\n jobId: opts.jobId,\n roomId: opts.roomId,\n room: opts.room,\n options: opts.options,\n events: opts.events,\n chatHistory: opts.chatHistory,\n enableRecording: opts.enableUserDataTraining ?? false,\n startedAt: opts.startedAt ?? Date.now(),\n timestamp: opts.timestamp ?? Date.now(),\n };\n}\n\n// - header: protobuf MetricsRecordingHeader (room_id, duration, start_time)\n// - chat_history: JSON serialized chat history (use sessionReportToJSON)\n// - audio: audio recording file if available (ogg format)\n// - Uploads to LiveKit Cloud observability endpoint with JWT auth\nexport function sessionReportToJSON(report: SessionReport): Record<string, unknown> {\n const events: Record<string, unknown>[] = [];\n\n for (const event of report.events) {\n if (event.type === 'metrics_collected') {\n continue; // metrics are too noisy, Cloud is using the chat_history as the source of truth\n }\n\n events.push({ ...event });\n }\n\n return {\n job_id: report.jobId,\n room_id: report.roomId,\n room: report.room,\n events,\n options: {\n allow_interruptions: report.options.allowInterruptions,\n discard_audio_if_uninterruptible: report.options.discardAudioIfUninterruptible,\n min_interruption_duration: report.options.minInterruptionDuration,\n min_interruption_words: report.options.minInterruptionWords,\n min_endpointing_delay: report.options.minEndpointingDelay,\n max_endpointing_delay: report.options.maxEndpointingDelay,\n max_tool_steps: report.options.maxToolSteps,\n },\n chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),\n enable_user_data_training: report.enableRecording,\n timestamp: report.timestamp,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmCO,SAAS,oBAAoB,MAA2C;AAC7E,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,iBAAiB,KAAK,0BAA0B;AAAA,IAChD,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,IACtC,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,EACxC;AACF;AAMO,SAAS,oBAAoB,QAAgD;AAClF,QAAM,SAAoC,CAAC;AAE3C,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,MAAM,SAAS,qBAAqB;AACtC;AAAA,IACF;AAEA,WAAO,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,qBAAqB,OAAO,QAAQ;AAAA,MACpC,kCAAkC,OAAO,QAAQ;AAAA,MACjD,2BAA2B,OAAO,QAAQ;AAAA,MAC1C,wBAAwB,OAAO,QAAQ;AAAA,MACvC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,gBAAgB,OAAO,QAAQ;AAAA,IACjC;AAAA,IACA,cAAc,OAAO,YAAY,OAAO,EAAE,kBAAkB,MAAM,CAAC;AAAA,IACnE,2BAA2B,OAAO;AAAA,IAClC,WAAW,OAAO;AAAA,EACpB;AACF;","names":[]}
@@ -8,7 +8,10 @@ export interface SessionReport {
8
8
  options: VoiceOptions;
9
9
  events: AgentEvent[];
10
10
  chatHistory: ChatContext;
11
- enableUserDataTraining: boolean;
11
+ enableRecording: boolean;
12
+ /** Timestamp when the session started (milliseconds) */
13
+ startedAt: number;
14
+ /** Timestamp when the session report was created (milliseconds), typically at the end of the session */
12
15
  timestamp: number;
13
16
  }
14
17
  export interface SessionReportOptions {
@@ -19,6 +22,9 @@ export interface SessionReportOptions {
19
22
  events: AgentEvent[];
20
23
  chatHistory: ChatContext;
21
24
  enableUserDataTraining?: boolean;
25
+ /** Timestamp when the session started (milliseconds) */
26
+ startedAt?: number;
27
+ /** Timestamp when the session report was created (milliseconds) */
22
28
  timestamp?: number;
23
29
  }
24
30
  export declare function createSessionReport(opts: SessionReportOptions): SessionReport;
@@ -8,7 +8,10 @@ export interface SessionReport {
8
8
  options: VoiceOptions;
9
9
  events: AgentEvent[];
10
10
  chatHistory: ChatContext;
11
- enableUserDataTraining: boolean;
11
+ enableRecording: boolean;
12
+ /** Timestamp when the session started (milliseconds) */
13
+ startedAt: number;
14
+ /** Timestamp when the session report was created (milliseconds), typically at the end of the session */
12
15
  timestamp: number;
13
16
  }
14
17
  export interface SessionReportOptions {
@@ -19,6 +22,9 @@ export interface SessionReportOptions {
19
22
  events: AgentEvent[];
20
23
  chatHistory: ChatContext;
21
24
  enableUserDataTraining?: boolean;
25
+ /** Timestamp when the session started (milliseconds) */
26
+ startedAt?: number;
27
+ /** Timestamp when the session report was created (milliseconds) */
22
28
  timestamp?: number;
23
29
  }
24
30
  export declare function createSessionReport(opts: SessionReportOptions): SessionReport;
@@ -1 +1 @@
1
- {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/voice/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,sBAAsB,EAAE,OAAO,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAW7E;AAOD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BlF"}
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/voice/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;IAClB,wGAAwG;IACxG,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,CAAC;IACtB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,aAAa,CAY7E;AAMD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6BlF"}
@@ -6,7 +6,8 @@ function createSessionReport(opts) {
6
6
  options: opts.options,
7
7
  events: opts.events,
8
8
  chatHistory: opts.chatHistory,
9
- enableUserDataTraining: opts.enableUserDataTraining ?? false,
9
+ enableRecording: opts.enableUserDataTraining ?? false,
10
+ startedAt: opts.startedAt ?? Date.now(),
10
11
  timestamp: opts.timestamp ?? Date.now()
11
12
  };
12
13
  }
@@ -33,7 +34,7 @@ function sessionReportToJSON(report) {
33
34
  max_tool_steps: report.options.maxToolSteps
34
35
  },
35
36
  chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),
36
- enable_user_data_training: report.enableUserDataTraining,
37
+ enable_user_data_training: report.enableRecording,
37
38
  timestamp: report.timestamp
38
39
  };
39
40
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/voice/report.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ChatContext } from '../llm/chat_context.js';\nimport type { VoiceOptions } from './agent_session.js';\nimport type { AgentEvent } from './events.js';\n\nexport interface SessionReport {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining: boolean;\n timestamp: number;\n}\n\nexport interface SessionReportOptions {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining?: boolean;\n timestamp?: number;\n}\n\nexport function createSessionReport(opts: SessionReportOptions): SessionReport {\n return {\n jobId: opts.jobId,\n roomId: opts.roomId,\n room: opts.room,\n options: opts.options,\n events: opts.events,\n chatHistory: opts.chatHistory,\n enableUserDataTraining: opts.enableUserDataTraining ?? false,\n timestamp: opts.timestamp ?? Date.now(),\n };\n}\n\n// TODO(brian): PR5 - Add uploadSessionReport() function that creates multipart form with:\n// - header: protobuf MetricsRecordingHeader (room_id, duration, start_time)\n// - chat_history: JSON serialized chat history (use sessionReportToJSON)\n// - audio: audio recording file if available (ogg format)\n// - Uploads to LiveKit Cloud observability endpoint with JWT auth\nexport function sessionReportToJSON(report: SessionReport): Record<string, unknown> {\n const events: Record<string, unknown>[] = [];\n\n for (const event of report.events) {\n if (event.type === 'metrics_collected') {\n continue; // metrics are too noisy, Cloud is using the chat_history as the source of truth\n }\n\n events.push({ ...event });\n }\n\n return {\n job_id: report.jobId,\n room_id: report.roomId,\n room: report.room,\n events,\n options: {\n allow_interruptions: report.options.allowInterruptions,\n discard_audio_if_uninterruptible: report.options.discardAudioIfUninterruptible,\n min_interruption_duration: report.options.minInterruptionDuration,\n min_interruption_words: report.options.minInterruptionWords,\n min_endpointing_delay: report.options.minEndpointingDelay,\n max_endpointing_delay: report.options.maxEndpointingDelay,\n max_tool_steps: report.options.maxToolSteps,\n },\n chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),\n enable_user_data_training: report.enableUserDataTraining,\n timestamp: report.timestamp,\n };\n}\n"],"mappings":"AA6BO,SAAS,oBAAoB,MAA2C;AAC7E,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,wBAAwB,KAAK,0BAA0B;AAAA,IACvD,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,EACxC;AACF;AAOO,SAAS,oBAAoB,QAAgD;AAClF,QAAM,SAAoC,CAAC;AAE3C,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,MAAM,SAAS,qBAAqB;AACtC;AAAA,IACF;AAEA,WAAO,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,qBAAqB,OAAO,QAAQ;AAAA,MACpC,kCAAkC,OAAO,QAAQ;AAAA,MACjD,2BAA2B,OAAO,QAAQ;AAAA,MAC1C,wBAAwB,OAAO,QAAQ;AAAA,MACvC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,gBAAgB,OAAO,QAAQ;AAAA,IACjC;AAAA,IACA,cAAc,OAAO,YAAY,OAAO,EAAE,kBAAkB,MAAM,CAAC;AAAA,IACnE,2BAA2B,OAAO;AAAA,IAClC,WAAW,OAAO;AAAA,EACpB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/voice/report.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { ChatContext } from '../llm/chat_context.js';\nimport type { VoiceOptions } from './agent_session.js';\nimport type { AgentEvent } from './events.js';\n\nexport interface SessionReport {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableRecording: boolean;\n /** Timestamp when the session started (milliseconds) */\n startedAt: number;\n /** Timestamp when the session report was created (milliseconds), typically at the end of the session */\n timestamp: number;\n}\n\nexport interface SessionReportOptions {\n jobId: string;\n roomId: string;\n room: string;\n options: VoiceOptions;\n events: AgentEvent[];\n chatHistory: ChatContext;\n enableUserDataTraining?: boolean;\n /** Timestamp when the session started (milliseconds) */\n startedAt?: number;\n /** Timestamp when the session report was created (milliseconds) */\n timestamp?: number;\n}\n\nexport function createSessionReport(opts: SessionReportOptions): SessionReport {\n return {\n jobId: opts.jobId,\n roomId: opts.roomId,\n room: opts.room,\n options: opts.options,\n events: opts.events,\n chatHistory: opts.chatHistory,\n enableRecording: opts.enableUserDataTraining ?? false,\n startedAt: opts.startedAt ?? Date.now(),\n timestamp: opts.timestamp ?? Date.now(),\n };\n}\n\n// - header: protobuf MetricsRecordingHeader (room_id, duration, start_time)\n// - chat_history: JSON serialized chat history (use sessionReportToJSON)\n// - audio: audio recording file if available (ogg format)\n// - Uploads to LiveKit Cloud observability endpoint with JWT auth\nexport function sessionReportToJSON(report: SessionReport): Record<string, unknown> {\n const events: Record<string, unknown>[] = [];\n\n for (const event of report.events) {\n if (event.type === 'metrics_collected') {\n continue; // metrics are too noisy, Cloud is using the chat_history as the source of truth\n }\n\n events.push({ ...event });\n }\n\n return {\n job_id: report.jobId,\n room_id: report.roomId,\n room: report.room,\n events,\n options: {\n allow_interruptions: report.options.allowInterruptions,\n discard_audio_if_uninterruptible: report.options.discardAudioIfUninterruptible,\n min_interruption_duration: report.options.minInterruptionDuration,\n min_interruption_words: report.options.minInterruptionWords,\n min_endpointing_delay: report.options.minEndpointingDelay,\n max_endpointing_delay: report.options.maxEndpointingDelay,\n max_tool_steps: report.options.maxToolSteps,\n },\n chat_history: report.chatHistory.toJSON({ excludeTimestamp: false }),\n enable_user_data_training: report.enableRecording,\n timestamp: report.timestamp,\n };\n}\n"],"mappings":"AAmCO,SAAS,oBAAoB,MAA2C;AAC7E,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,QAAQ,KAAK;AAAA,IACb,MAAM,KAAK;AAAA,IACX,SAAS,KAAK;AAAA,IACd,QAAQ,KAAK;AAAA,IACb,aAAa,KAAK;AAAA,IAClB,iBAAiB,KAAK,0BAA0B;AAAA,IAChD,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,IACtC,WAAW,KAAK,aAAa,KAAK,IAAI;AAAA,EACxC;AACF;AAMO,SAAS,oBAAoB,QAAgD;AAClF,QAAM,SAAoC,CAAC;AAE3C,aAAW,SAAS,OAAO,QAAQ;AACjC,QAAI,MAAM,SAAS,qBAAqB;AACtC;AAAA,IACF;AAEA,WAAO,KAAK,EAAE,GAAG,MAAM,CAAC;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL,QAAQ,OAAO;AAAA,IACf,SAAS,OAAO;AAAA,IAChB,MAAM,OAAO;AAAA,IACb;AAAA,IACA,SAAS;AAAA,MACP,qBAAqB,OAAO,QAAQ;AAAA,MACpC,kCAAkC,OAAO,QAAQ;AAAA,MACjD,2BAA2B,OAAO,QAAQ;AAAA,MAC1C,wBAAwB,OAAO,QAAQ;AAAA,MACvC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,uBAAuB,OAAO,QAAQ;AAAA,MACtC,gBAAgB,OAAO,QAAQ;AAAA,IACjC;AAAA,IACA,cAAc,OAAO,YAAY,OAAO,EAAE,kBAAkB,MAAM,CAAC;AAAA,IACnE,2BAA2B,OAAO;AAAA,IAClC,WAAW,OAAO;AAAA,EACpB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@livekit/agents",
3
- "version": "1.0.21",
3
+ "version": "1.0.23",
4
4
  "description": "LiveKit Agents - Node.js",
5
5
  "main": "dist/index.js",
6
6
  "require": "dist/index.cjs",
@@ -42,15 +42,21 @@
42
42
  "@livekit/protocol": "^1.43.0",
43
43
  "@livekit/typed-emitter": "^3.0.0",
44
44
  "@opentelemetry/api": "^1.9.0",
45
- "@opentelemetry/exporter-trace-otlp-http": "^0.54.0",
45
+ "@opentelemetry/api-logs": "^0.54.0",
46
+ "@opentelemetry/core": "^2.2.0",
47
+ "@opentelemetry/exporter-logs-otlp-proto": "^0.54.0",
48
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.54.0",
49
+ "@opentelemetry/instrumentation-pino": "^0.43.0",
46
50
  "@opentelemetry/otlp-exporter-base": "^0.208.0",
47
51
  "@opentelemetry/resources": "^1.28.0",
52
+ "@opentelemetry/sdk-logs": "^0.54.0",
48
53
  "@opentelemetry/sdk-trace-base": "^1.28.0",
49
54
  "@opentelemetry/sdk-trace-node": "^1.28.0",
50
55
  "@opentelemetry/semantic-conventions": "^1.28.0",
51
56
  "@types/pidusage": "^2.0.5",
52
57
  "commander": "^12.0.0",
53
58
  "fluent-ffmpeg": "^2.1.3",
59
+ "form-data": "^4.0.5",
54
60
  "heap-js": "^2.6.0",
55
61
  "json-schema": "^0.4.0",
56
62
  "livekit-server-sdk": "^2.14.1",
@@ -50,8 +50,8 @@ export const ttsSessionClosedEventSchema = z.object({
50
50
 
51
51
  export const ttsErrorEventSchema = z.object({
52
52
  type: z.literal('error'),
53
- message: z.string(),
54
- session_id: z.string(),
53
+ message: z.string().optional(),
54
+ session_id: z.string().optional(),
55
55
  });
56
56
 
57
57
  export const ttsClientEventSchema = z.discriminatedUnion('type', [
@@ -459,52 +459,67 @@ export class SpeechStream<TModel extends STTModels> extends BaseSpeechStream {
459
459
  }
460
460
 
461
461
  private processTranscript(data: Record<string, any>, isFinal: boolean) {
462
+ // Check if queue is closed to avoid race condition during disconnect
463
+ if (this.queue.closed) return;
464
+
462
465
  const requestId = data.request_id ?? this.requestId;
463
466
  const text = data.transcript ?? '';
464
467
  const language = data.language ?? this.opts.language ?? 'en';
465
468
 
466
469
  if (!text && !isFinal) return;
467
470
 
468
- // We'll have a more accurate way of detecting when speech started when we have VAD
469
- if (!this.speaking) {
470
- this.speaking = true;
471
- this.queue.put({ type: SpeechEventType.START_OF_SPEECH });
472
- }
471
+ try {
472
+ // We'll have a more accurate way of detecting when speech started when we have VAD
473
+ if (!this.speaking) {
474
+ this.speaking = true;
475
+ this.queue.put({ type: SpeechEventType.START_OF_SPEECH });
476
+ }
473
477
 
474
- const speechData: SpeechData = {
475
- language,
476
- startTime: data.start ?? 0,
477
- endTime: data.duration ?? 0,
478
- confidence: data.confidence ?? 1.0,
479
- text,
480
- };
478
+ const speechData: SpeechData = {
479
+ language,
480
+ startTime: data.start ?? 0,
481
+ endTime: data.duration ?? 0,
482
+ confidence: data.confidence ?? 1.0,
483
+ text,
484
+ };
485
+
486
+ if (isFinal) {
487
+ if (this.speechDuration > 0) {
488
+ this.queue.put({
489
+ type: SpeechEventType.RECOGNITION_USAGE,
490
+ requestId,
491
+ recognitionUsage: { audioDuration: this.speechDuration },
492
+ });
493
+ this.speechDuration = 0;
494
+ }
481
495
 
482
- if (isFinal) {
483
- if (this.speechDuration > 0) {
484
496
  this.queue.put({
485
- type: SpeechEventType.RECOGNITION_USAGE,
497
+ type: SpeechEventType.FINAL_TRANSCRIPT,
486
498
  requestId,
487
- recognitionUsage: { audioDuration: this.speechDuration },
499
+ alternatives: [speechData],
488
500
  });
489
- this.speechDuration = 0;
490
- }
491
-
492
- this.queue.put({
493
- type: SpeechEventType.FINAL_TRANSCRIPT,
494
- requestId,
495
- alternatives: [speechData],
496
- });
497
501
 
498
- if (this.speaking) {
499
- this.speaking = false;
500
- this.queue.put({ type: SpeechEventType.END_OF_SPEECH });
502
+ if (this.speaking) {
503
+ this.speaking = false;
504
+ this.queue.put({ type: SpeechEventType.END_OF_SPEECH });
505
+ }
506
+ } else {
507
+ this.queue.put({
508
+ type: SpeechEventType.INTERIM_TRANSCRIPT,
509
+ requestId,
510
+ alternatives: [speechData],
511
+ });
512
+ }
513
+ } catch (e) {
514
+ if (e instanceof Error && e.message.includes('Queue is closed')) {
515
+ // Expected behavior on disconnect, log as warning
516
+ this.#logger.warn(
517
+ { err: e },
518
+ 'Queue closed during transcript processing (expected during disconnect)',
519
+ );
520
+ } else {
521
+ this.#logger.error({ err: e }, 'Error putting transcript to queue');
501
522
  }
502
- } else {
503
- this.queue.put({
504
- type: SpeechEventType.INTERIM_TRANSCRIPT,
505
- requestId,
506
- alternatives: [speechData],
507
- });
508
523
  }
509
524
  }
510
525
  }
@@ -277,6 +277,9 @@ export class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeSt
277
277
  };
278
278
 
279
279
  const sendClientEvent = async (event: TtsClientEvent) => {
280
+ // Don't send events to a closed WebSocket or aborted controller
281
+ if (this.abortController.signal.aborted || closing) return;
282
+
280
283
  const validatedEvent = await ttsClientEventSchema.parseAsync(event);
281
284
  if (!ws || ws.readyState !== WebSocket.OPEN) {
282
285
  this.#logger.warn('Trying to send client TTS event to a closed WebSocket');
@@ -321,7 +324,7 @@ export class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeSt
321
324
  return new Promise<void>((resolve, reject) => {
322
325
  this.abortController.signal.addEventListener('abort', () => {
323
326
  resourceCleanup();
324
- reject(new Error('WebSocket connection aborted'));
327
+ resolve(); // Abort is triggered by close(), which is a normal shutdown, not an error
325
328
  });
326
329
 
327
330
  ws.on('message', async (data) => {
@@ -420,8 +423,6 @@ export class SynthesizeStream<TModel extends TTSModels> extends BaseSynthesizeSt
420
423
  createWsListenerTask(ws),
421
424
  createRecvTask(),
422
425
  ]);
423
- } catch (e) {
424
- this.#logger.error({ error: e }, 'Error in SynthesizeStream');
425
426
  } finally {
426
427
  resourceCleanup();
427
428
  }
@@ -99,7 +99,18 @@ const startJob = (
99
99
  }, 10000);
100
100
 
101
101
  // Run the job function within the AsyncLocalStorage context
102
- await runWithJobContextAsync(ctx, () => func(ctx)).finally(() => {
102
+ await runWithJobContextAsync(ctx, async () => {
103
+ const { tracer, traceTypes } = await import('../telemetry/index.js');
104
+ return tracer.startActiveSpan(
105
+ async (span) => {
106
+ span.setAttribute(traceTypes.ATTR_JOB_ID, info.job.id);
107
+ span.setAttribute(traceTypes.ATTR_AGENT_NAME, info.job.agentName);
108
+ span.setAttribute(traceTypes.ATTR_ROOM_NAME, info.job.room?.name ?? '');
109
+ return func(ctx);
110
+ },
111
+ { name: 'job_entrypoint' },
112
+ );
113
+ }).finally(() => {
103
114
  clearTimeout(unconnectedTimeout);
104
115
  });
105
116