@livekit/agents 1.0.46 → 1.0.47

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 (151) hide show
  1. package/dist/cli.cjs +14 -20
  2. package/dist/cli.cjs.map +1 -1
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +14 -20
  5. package/dist/cli.js.map +1 -1
  6. package/dist/ipc/job_proc_lazy_main.cjs +14 -5
  7. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  8. package/dist/ipc/job_proc_lazy_main.js +14 -5
  9. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  10. package/dist/llm/chat_context.cjs +19 -0
  11. package/dist/llm/chat_context.cjs.map +1 -1
  12. package/dist/llm/chat_context.d.cts +4 -0
  13. package/dist/llm/chat_context.d.ts +4 -0
  14. package/dist/llm/chat_context.d.ts.map +1 -1
  15. package/dist/llm/chat_context.js +19 -0
  16. package/dist/llm/chat_context.js.map +1 -1
  17. package/dist/llm/provider_format/index.cjs +2 -0
  18. package/dist/llm/provider_format/index.cjs.map +1 -1
  19. package/dist/llm/provider_format/index.d.cts +1 -1
  20. package/dist/llm/provider_format/index.d.ts +1 -1
  21. package/dist/llm/provider_format/index.d.ts.map +1 -1
  22. package/dist/llm/provider_format/index.js +6 -1
  23. package/dist/llm/provider_format/index.js.map +1 -1
  24. package/dist/llm/provider_format/openai.cjs +82 -2
  25. package/dist/llm/provider_format/openai.cjs.map +1 -1
  26. package/dist/llm/provider_format/openai.d.cts +1 -0
  27. package/dist/llm/provider_format/openai.d.ts +1 -0
  28. package/dist/llm/provider_format/openai.d.ts.map +1 -1
  29. package/dist/llm/provider_format/openai.js +80 -1
  30. package/dist/llm/provider_format/openai.js.map +1 -1
  31. package/dist/llm/provider_format/openai.test.cjs +326 -0
  32. package/dist/llm/provider_format/openai.test.cjs.map +1 -1
  33. package/dist/llm/provider_format/openai.test.js +327 -1
  34. package/dist/llm/provider_format/openai.test.js.map +1 -1
  35. package/dist/llm/provider_format/utils.cjs +4 -3
  36. package/dist/llm/provider_format/utils.cjs.map +1 -1
  37. package/dist/llm/provider_format/utils.d.ts.map +1 -1
  38. package/dist/llm/provider_format/utils.js +4 -3
  39. package/dist/llm/provider_format/utils.js.map +1 -1
  40. package/dist/llm/realtime.cjs.map +1 -1
  41. package/dist/llm/realtime.d.cts +1 -0
  42. package/dist/llm/realtime.d.ts +1 -0
  43. package/dist/llm/realtime.d.ts.map +1 -1
  44. package/dist/llm/realtime.js.map +1 -1
  45. package/dist/log.cjs +5 -2
  46. package/dist/log.cjs.map +1 -1
  47. package/dist/log.d.ts.map +1 -1
  48. package/dist/log.js +5 -2
  49. package/dist/log.js.map +1 -1
  50. package/dist/stream/deferred_stream.cjs +15 -6
  51. package/dist/stream/deferred_stream.cjs.map +1 -1
  52. package/dist/stream/deferred_stream.d.ts.map +1 -1
  53. package/dist/stream/deferred_stream.js +15 -6
  54. package/dist/stream/deferred_stream.js.map +1 -1
  55. package/dist/utils.cjs +31 -2
  56. package/dist/utils.cjs.map +1 -1
  57. package/dist/utils.d.cts +7 -0
  58. package/dist/utils.d.ts +7 -0
  59. package/dist/utils.d.ts.map +1 -1
  60. package/dist/utils.js +31 -2
  61. package/dist/utils.js.map +1 -1
  62. package/dist/utils.test.cjs +71 -0
  63. package/dist/utils.test.cjs.map +1 -1
  64. package/dist/utils.test.js +71 -0
  65. package/dist/utils.test.js.map +1 -1
  66. package/dist/version.cjs +1 -1
  67. package/dist/version.cjs.map +1 -1
  68. package/dist/version.d.cts +1 -1
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.d.ts.map +1 -1
  71. package/dist/version.js +1 -1
  72. package/dist/version.js.map +1 -1
  73. package/dist/voice/agent.cjs +144 -12
  74. package/dist/voice/agent.cjs.map +1 -1
  75. package/dist/voice/agent.d.cts +29 -4
  76. package/dist/voice/agent.d.ts +29 -4
  77. package/dist/voice/agent.d.ts.map +1 -1
  78. package/dist/voice/agent.js +140 -11
  79. package/dist/voice/agent.js.map +1 -1
  80. package/dist/voice/agent.test.cjs +120 -0
  81. package/dist/voice/agent.test.cjs.map +1 -1
  82. package/dist/voice/agent.test.js +122 -2
  83. package/dist/voice/agent.test.js.map +1 -1
  84. package/dist/voice/agent_activity.cjs +383 -298
  85. package/dist/voice/agent_activity.cjs.map +1 -1
  86. package/dist/voice/agent_activity.d.cts +34 -7
  87. package/dist/voice/agent_activity.d.ts +34 -7
  88. package/dist/voice/agent_activity.d.ts.map +1 -1
  89. package/dist/voice/agent_activity.js +383 -293
  90. package/dist/voice/agent_activity.js.map +1 -1
  91. package/dist/voice/agent_session.cjs +140 -40
  92. package/dist/voice/agent_session.cjs.map +1 -1
  93. package/dist/voice/agent_session.d.cts +19 -7
  94. package/dist/voice/agent_session.d.ts +19 -7
  95. package/dist/voice/agent_session.d.ts.map +1 -1
  96. package/dist/voice/agent_session.js +137 -37
  97. package/dist/voice/agent_session.js.map +1 -1
  98. package/dist/voice/audio_recognition.cjs +4 -0
  99. package/dist/voice/audio_recognition.cjs.map +1 -1
  100. package/dist/voice/audio_recognition.d.ts.map +1 -1
  101. package/dist/voice/audio_recognition.js +4 -0
  102. package/dist/voice/audio_recognition.js.map +1 -1
  103. package/dist/voice/generation.cjs +39 -19
  104. package/dist/voice/generation.cjs.map +1 -1
  105. package/dist/voice/generation.d.ts.map +1 -1
  106. package/dist/voice/generation.js +44 -20
  107. package/dist/voice/generation.js.map +1 -1
  108. package/dist/voice/index.cjs +2 -0
  109. package/dist/voice/index.cjs.map +1 -1
  110. package/dist/voice/index.d.cts +1 -1
  111. package/dist/voice/index.d.ts +1 -1
  112. package/dist/voice/index.d.ts.map +1 -1
  113. package/dist/voice/index.js +2 -1
  114. package/dist/voice/index.js.map +1 -1
  115. package/dist/voice/speech_handle.cjs +7 -1
  116. package/dist/voice/speech_handle.cjs.map +1 -1
  117. package/dist/voice/speech_handle.d.cts +2 -0
  118. package/dist/voice/speech_handle.d.ts +2 -0
  119. package/dist/voice/speech_handle.d.ts.map +1 -1
  120. package/dist/voice/speech_handle.js +8 -2
  121. package/dist/voice/speech_handle.js.map +1 -1
  122. package/dist/voice/testing/run_result.cjs +66 -15
  123. package/dist/voice/testing/run_result.cjs.map +1 -1
  124. package/dist/voice/testing/run_result.d.cts +14 -3
  125. package/dist/voice/testing/run_result.d.ts +14 -3
  126. package/dist/voice/testing/run_result.d.ts.map +1 -1
  127. package/dist/voice/testing/run_result.js +66 -15
  128. package/dist/voice/testing/run_result.js.map +1 -1
  129. package/package.json +1 -1
  130. package/src/cli.ts +20 -33
  131. package/src/ipc/job_proc_lazy_main.ts +16 -5
  132. package/src/llm/chat_context.ts +35 -0
  133. package/src/llm/provider_format/index.ts +7 -2
  134. package/src/llm/provider_format/openai.test.ts +385 -1
  135. package/src/llm/provider_format/openai.ts +103 -0
  136. package/src/llm/provider_format/utils.ts +6 -4
  137. package/src/llm/realtime.ts +1 -0
  138. package/src/log.ts +5 -2
  139. package/src/stream/deferred_stream.ts +17 -6
  140. package/src/utils.test.ts +87 -0
  141. package/src/utils.ts +36 -2
  142. package/src/version.ts +1 -1
  143. package/src/voice/agent.test.ts +140 -2
  144. package/src/voice/agent.ts +189 -10
  145. package/src/voice/agent_activity.ts +427 -289
  146. package/src/voice/agent_session.ts +178 -40
  147. package/src/voice/audio_recognition.ts +4 -0
  148. package/src/voice/generation.ts +52 -23
  149. package/src/voice/index.ts +1 -1
  150. package/src/voice/speech_handle.ts +9 -2
  151. package/src/voice/testing/run_result.ts +81 -23
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/llm/provider_format/openai.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\nimport { initializeLogger } from '../../log.js';\nimport {\n AgentHandoffItem,\n ChatContext,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\nimport { serializeImage } from '../utils.js';\nimport { toChatCtx } from './openai.js';\n\n// Mock the serializeImage function\nvi.mock('../utils.js', () => ({\n serializeImage: vi.fn(),\n}));\n\ndescribe('toChatCtx', () => {\n const serializeImageMock = vi.mocked(serializeImage);\n\n // initialize logger at start of test\n initializeLogger({ level: 'silent', pretty: false });\n\n beforeEach(async () => {\n vi.clearAllMocks();\n });\n\n it('should convert simple text messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello' });\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, order may vary due to ID-based sorting\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'user', content: 'Hello' });\n expect(result[1]).toEqual({ role: 'assistant', content: 'Hi there!' });\n });\n\n it('should handle system messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'system', content: 'You are a helpful assistant' });\n ctx.addMessage({ role: 'user', content: 'Hello' });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, order may vary due to ID-based sorting\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'system', content: 'You are a helpful assistant' });\n expect(result[1]).toEqual({ role: 'user', content: 'Hello' });\n });\n\n it('should handle multi-line text content', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: ['Line 1', 'Line 2', 'Line 3'] });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({ role: 'user', content: 'Line 1\\nLine 2\\nLine 3' });\n });\n\n it('should handle messages with external URL images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n 'Check out this image:',\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image.jpg',\n detail: 'high',\n },\n },\n { type: 'text', text: 'Check out this image:' },\n ],\n },\n ]);\n });\n\n it('should handle messages with base64 images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'auto',\n mimeType: 'image/png',\n base64Data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'assistant',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n inferenceDetail: 'auto',\n _cache: {},\n },\n 'Here is the image you requested',\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n detail: 'auto',\n },\n },\n { type: 'text', text: 'Here is the image you requested' },\n ],\n },\n ]);\n });\n\n it('should handle VideoFrame images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'low',\n mimeType: 'image/jpeg',\n base64Data: '/9j/4AAQSkZJRg==',\n });\n\n const frameData = new Uint8Array(4 * 4 * 4); // 4x4 RGBA\n const videoFrame = new VideoFrame(frameData, 4, 4, VideoBufferType.RGBA);\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'frame1',\n type: 'image_content',\n image: videoFrame,\n inferenceDetail: 'low',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/jpeg;base64,/9j/4AAQSkZJRg==',\n detail: 'low',\n },\n },\n ],\n },\n ]);\n });\n\n it('should cache serialized images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n mimeType: 'image/png',\n base64Data: 'cached-data',\n });\n\n const imageContent = {\n id: 'img1',\n type: 'image_content' as const,\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high' as const,\n _cache: {},\n };\n\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: [imageContent] });\n\n // Call twice to test caching\n await toChatCtx(ctx);\n await toChatCtx(ctx);\n\n // serializeImage should only be called once due to caching\n expect(serializeImageMock).toHaveBeenCalledTimes(1);\n expect(imageContent._cache).toHaveProperty('serialized_image');\n });\n\n it('should handle tool calls and outputs', async () => {\n const ctx = ChatContext.empty();\n\n // Add an assistant message with tool calls\n const msg = ctx.addMessage({ role: 'assistant', content: 'Let me help you with that.' });\n const toolCall = FunctionCall.create({\n id: msg.id + '/tool_1',\n callId: 'call_123',\n name: 'get_weather',\n args: '{\"location\": \"San Francisco\"}',\n });\n const toolOutput = FunctionCallOutput.create({\n callId: 'call_123',\n output: '{\"temperature\": 72, \"condition\": \"sunny\"}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: 'Let me help you with that.',\n tool_calls: [\n {\n type: 'function',\n id: 'call_123',\n function: {\n name: 'get_weather',\n arguments: '{\"location\": \"San Francisco\"}',\n },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_123',\n content: '{\"temperature\": 72, \"condition\": \"sunny\"}',\n },\n ]);\n });\n\n it('should include provider-specific extra content on tool calls', async () => {\n const ctx = ChatContext.empty();\n const msg = ctx.addMessage({ role: 'assistant', content: 'Running tool' });\n\n const toolCall = FunctionCall.create({\n id: `${msg.id}/tool_1`,\n callId: 'call_789',\n name: 'google_call',\n args: '{}',\n extra: { google: { thoughtSignature: 'sig-123' } },\n });\n const toolOutput = FunctionCallOutput.create({\n callId: 'call_789',\n output: '{\"result\": \"ok\"}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result[0]).toEqual({\n role: 'assistant',\n content: 'Running tool',\n tool_calls: [\n {\n type: 'function',\n id: 'call_789',\n function: { name: 'google_call', arguments: '{}' },\n extra_content: { google: { thoughtSignature: 'sig-123' } },\n },\n ],\n });\n expect(result[1]).toEqual({\n role: 'tool',\n tool_call_id: 'call_789',\n content: '{\"result\": \"ok\"}',\n });\n });\n\n it('should handle multiple tool calls in one message', async () => {\n const ctx = ChatContext.empty();\n\n const msg = ctx.addMessage({ role: 'assistant', content: \"I'll check both locations.\" });\n const toolCall1 = new FunctionCall({\n id: msg.id + '/tool_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"NYC\"}',\n });\n const toolCall2 = new FunctionCall({\n id: msg.id + '/tool_2',\n callId: 'call_2',\n name: 'get_weather',\n args: '{\"location\": \"LA\"}',\n });\n const toolOutput1 = new FunctionCallOutput({\n callId: 'call_1',\n output: '{\"temperature\": 65}',\n isError: false,\n });\n const toolOutput2 = new FunctionCallOutput({\n callId: 'call_2',\n output: '{\"temperature\": 78}',\n isError: false,\n });\n\n ctx.insert([toolCall1, toolCall2, toolOutput1, toolOutput2]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: \"I'll check both locations.\",\n tool_calls: [\n {\n type: 'function',\n id: 'call_1',\n function: { name: 'get_weather', arguments: '{\"location\": \"NYC\"}' },\n },\n {\n type: 'function',\n id: 'call_2',\n function: { name: 'get_weather', arguments: '{\"location\": \"LA\"}' },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_1',\n content: '{\"temperature\": 65}',\n },\n {\n role: 'tool',\n tool_call_id: 'call_2',\n content: '{\"temperature\": 78}',\n },\n ]);\n });\n\n it('should handle tool calls without accompanying message', async () => {\n const ctx = ChatContext.empty();\n\n const toolCall = new FunctionCall({\n id: 'func_123',\n callId: 'call_456',\n name: 'calculate',\n args: '{\"a\": 5, \"b\": 3}',\n });\n const toolOutput = new FunctionCallOutput({\n callId: 'call_456',\n output: '{\"result\": 8}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n tool_calls: [\n {\n type: 'function',\n id: 'call_456',\n function: { name: 'calculate', arguments: '{\"a\": 5, \"b\": 3}' },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_456',\n content: '{\"result\": 8}',\n },\n ]);\n });\n\n it('should skip empty groups', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello', createdAt: 1000 });\n\n // Create an isolated tool output without corresponding call (will be filtered)\n const orphanOutput = new FunctionCallOutput({\n callId: 'orphan_call',\n output: 'This should be ignored',\n isError: false,\n createdAt: 2000,\n });\n ctx.insert(orphanOutput);\n\n ctx.addMessage({ role: 'assistant', content: 'Hi!', createdAt: 3000 });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, orphan output should be filtered out\n expect(result).toHaveLength(2);\n expect(result).toContainEqual({ role: 'user', content: 'Hello' });\n expect(result).toContainEqual({ role: 'assistant', content: 'Hi!' });\n });\n\n it('should handle mixed content with text and multiple images', async () => {\n serializeImageMock\n .mockResolvedValueOnce({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image1.jpg',\n })\n .mockResolvedValueOnce({\n inferenceDetail: 'low',\n mimeType: 'image/png',\n base64Data: 'base64data',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n 'Here are two images:',\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image1.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n 'And the second one:',\n {\n id: 'img2',\n type: 'image_content',\n image: 'data:image/png;base64,base64data',\n inferenceDetail: 'low',\n _cache: {},\n },\n 'What do you think?',\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image1.jpg',\n detail: 'high',\n },\n },\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/png;base64,base64data',\n detail: 'low',\n },\n },\n {\n type: 'text',\n text: 'Here are two images:\\nAnd the second one:\\nWhat do you think?',\n },\n ],\n },\n ]);\n });\n\n it('should handle content with only images and no text', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'auto',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'auto',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image.jpg',\n detail: 'auto',\n },\n },\n ],\n },\n ]);\n });\n\n it('should throw error for unsupported content type', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [],\n },\n ],\n });\n\n await expect(toChatCtx(ctx)).rejects.toThrow('Unsupported content type: audio_content');\n });\n\n it('should throw error when serialized image has no data', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n // No base64Data or externalUrl\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'invalid-image',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n await expect(toChatCtx(ctx)).rejects.toThrow('Serialized image has no data bytes');\n });\n\n it('should filter out standalone function calls without outputs', async () => {\n const ctx = ChatContext.empty();\n\n // Add standalone function call without output\n const funcCall = new FunctionCall({\n id: 'func_standalone',\n callId: 'call_999',\n name: 'standalone_function',\n args: '{}',\n });\n\n ctx.insert(funcCall);\n\n const result = await toChatCtx(ctx);\n\n // Standalone function calls without outputs are filtered out by groupToolCalls\n expect(result).toEqual([]);\n });\n\n it('should handle function call output correctly', async () => {\n const ctx = ChatContext.empty();\n\n // First add a function call\n const funcCall = new FunctionCall({\n id: 'func_1',\n callId: 'call_output_test',\n name: 'test_function',\n args: '{}',\n });\n\n // Then add its output\n const funcOutput = new FunctionCallOutput({\n callId: 'call_output_test',\n output: 'Function executed successfully',\n isError: false,\n });\n\n ctx.insert([funcCall, funcOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n tool_calls: [\n {\n id: 'call_output_test',\n type: 'function',\n function: {\n name: 'test_function',\n arguments: '{}',\n },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_output_test',\n content: 'Function executed successfully',\n },\n ]);\n });\n\n it('should filter out agent handoff items', async () => {\n const ctx = ChatContext.empty();\n\n ctx.addMessage({ role: 'user', content: 'Hello' });\n\n // Insert an agent handoff item\n const handoff = new AgentHandoffItem({\n oldAgentId: 'agent_1',\n newAgentId: 'agent_2',\n });\n ctx.insert(handoff);\n\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toChatCtx(ctx);\n\n // Agent handoff should be filtered out, only messages should remain\n expect(result).toEqual([\n { role: 'user', content: 'Hello' },\n { role: 'assistant', content: 'Hi there!' },\n ]);\n });\n\n it('should handle multiple agent handoffs without errors', async () => {\n const ctx = ChatContext.empty();\n\n ctx.addMessage({ role: 'user', content: 'Start' });\n\n // Multiple handoffs\n ctx.insert(new AgentHandoffItem({ oldAgentId: undefined, newAgentId: 'agent_1' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 1' });\n\n ctx.insert(new AgentHandoffItem({ oldAgentId: 'agent_1', newAgentId: 'agent_2' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 2' });\n\n ctx.insert(new AgentHandoffItem({ oldAgentId: 'agent_2', newAgentId: 'agent_3' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 3' });\n\n const result = await toChatCtx(ctx);\n\n // All handoffs should be filtered out\n expect(result).toEqual([\n { role: 'user', content: 'Start' },\n { role: 'assistant', content: 'Response from agent 1' },\n { role: 'assistant', content: 'Response from agent 2' },\n { role: 'assistant', content: 'Response from agent 3' },\n ]);\n });\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/llm/provider_format/openai.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { VideoBufferType, VideoFrame } from '@livekit/rtc-node';\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\nimport { initializeLogger } from '../../log.js';\nimport {\n AgentHandoffItem,\n ChatContext,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\nimport { serializeImage } from '../utils.js';\nimport { toChatCtx, toResponsesChatCtx } from './openai.js';\n\n// Mock the serializeImage function\nvi.mock('../utils.js', () => ({\n serializeImage: vi.fn(),\n}));\n\ndescribe('toChatCtx', () => {\n const serializeImageMock = vi.mocked(serializeImage);\n\n // initialize logger at start of test\n initializeLogger({ level: 'silent', pretty: false });\n\n beforeEach(async () => {\n vi.clearAllMocks();\n });\n\n it('should convert simple text messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello' });\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, order may vary due to ID-based sorting\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'user', content: 'Hello' });\n expect(result[1]).toEqual({ role: 'assistant', content: 'Hi there!' });\n });\n\n it('should handle system messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'system', content: 'You are a helpful assistant' });\n ctx.addMessage({ role: 'user', content: 'Hello' });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, order may vary due to ID-based sorting\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'system', content: 'You are a helpful assistant' });\n expect(result[1]).toEqual({ role: 'user', content: 'Hello' });\n });\n\n it('should handle multi-line text content', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: ['Line 1', 'Line 2', 'Line 3'] });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({ role: 'user', content: 'Line 1\\nLine 2\\nLine 3' });\n });\n\n it('should handle messages with external URL images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n 'Check out this image:',\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image.jpg',\n detail: 'high',\n },\n },\n { type: 'text', text: 'Check out this image:' },\n ],\n },\n ]);\n });\n\n it('should handle messages with base64 images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'auto',\n mimeType: 'image/png',\n base64Data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'assistant',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n inferenceDetail: 'auto',\n _cache: {},\n },\n 'Here is the image you requested',\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n detail: 'auto',\n },\n },\n { type: 'text', text: 'Here is the image you requested' },\n ],\n },\n ]);\n });\n\n it('should handle VideoFrame images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'low',\n mimeType: 'image/jpeg',\n base64Data: '/9j/4AAQSkZJRg==',\n });\n\n const frameData = new Uint8Array(4 * 4 * 4); // 4x4 RGBA\n const videoFrame = new VideoFrame(frameData, 4, 4, VideoBufferType.RGBA);\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'frame1',\n type: 'image_content',\n image: videoFrame,\n inferenceDetail: 'low',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/jpeg;base64,/9j/4AAQSkZJRg==',\n detail: 'low',\n },\n },\n ],\n },\n ]);\n });\n\n it('should cache serialized images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n mimeType: 'image/png',\n base64Data: 'cached-data',\n });\n\n const imageContent = {\n id: 'img1',\n type: 'image_content' as const,\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high' as const,\n _cache: {},\n };\n\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: [imageContent] });\n\n // Call twice to test caching\n await toChatCtx(ctx);\n await toChatCtx(ctx);\n\n // serializeImage should only be called once due to caching\n expect(serializeImageMock).toHaveBeenCalledTimes(1);\n expect(imageContent._cache).toHaveProperty('serialized_image');\n });\n\n it('should handle tool calls and outputs', async () => {\n const ctx = ChatContext.empty();\n\n // Add an assistant message with tool calls\n const msg = ctx.addMessage({ role: 'assistant', content: 'Let me help you with that.' });\n const toolCall = FunctionCall.create({\n id: msg.id + '/tool_1',\n callId: 'call_123',\n name: 'get_weather',\n args: '{\"location\": \"San Francisco\"}',\n });\n const toolOutput = FunctionCallOutput.create({\n callId: 'call_123',\n output: '{\"temperature\": 72, \"condition\": \"sunny\"}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: 'Let me help you with that.',\n tool_calls: [\n {\n type: 'function',\n id: 'call_123',\n function: {\n name: 'get_weather',\n arguments: '{\"location\": \"San Francisco\"}',\n },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_123',\n content: '{\"temperature\": 72, \"condition\": \"sunny\"}',\n },\n ]);\n });\n\n it('should include provider-specific extra content on tool calls', async () => {\n const ctx = ChatContext.empty();\n const msg = ctx.addMessage({ role: 'assistant', content: 'Running tool' });\n\n const toolCall = FunctionCall.create({\n id: `${msg.id}/tool_1`,\n callId: 'call_789',\n name: 'google_call',\n args: '{}',\n extra: { google: { thoughtSignature: 'sig-123' } },\n });\n const toolOutput = FunctionCallOutput.create({\n callId: 'call_789',\n output: '{\"result\": \"ok\"}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result[0]).toEqual({\n role: 'assistant',\n content: 'Running tool',\n tool_calls: [\n {\n type: 'function',\n id: 'call_789',\n function: { name: 'google_call', arguments: '{}' },\n extra_content: { google: { thoughtSignature: 'sig-123' } },\n },\n ],\n });\n expect(result[1]).toEqual({\n role: 'tool',\n tool_call_id: 'call_789',\n content: '{\"result\": \"ok\"}',\n });\n });\n\n it('should handle multiple tool calls in one message', async () => {\n const ctx = ChatContext.empty();\n\n const msg = ctx.addMessage({ role: 'assistant', content: \"I'll check both locations.\" });\n const toolCall1 = new FunctionCall({\n id: msg.id + '/tool_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"NYC\"}',\n });\n const toolCall2 = new FunctionCall({\n id: msg.id + '/tool_2',\n callId: 'call_2',\n name: 'get_weather',\n args: '{\"location\": \"LA\"}',\n });\n const toolOutput1 = new FunctionCallOutput({\n callId: 'call_1',\n output: '{\"temperature\": 65}',\n isError: false,\n });\n const toolOutput2 = new FunctionCallOutput({\n callId: 'call_2',\n output: '{\"temperature\": 78}',\n isError: false,\n });\n\n ctx.insert([toolCall1, toolCall2, toolOutput1, toolOutput2]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n content: \"I'll check both locations.\",\n tool_calls: [\n {\n type: 'function',\n id: 'call_1',\n function: { name: 'get_weather', arguments: '{\"location\": \"NYC\"}' },\n },\n {\n type: 'function',\n id: 'call_2',\n function: { name: 'get_weather', arguments: '{\"location\": \"LA\"}' },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_1',\n content: '{\"temperature\": 65}',\n },\n {\n role: 'tool',\n tool_call_id: 'call_2',\n content: '{\"temperature\": 78}',\n },\n ]);\n });\n\n it('should handle tool calls without accompanying message', async () => {\n const ctx = ChatContext.empty();\n\n const toolCall = new FunctionCall({\n id: 'func_123',\n callId: 'call_456',\n name: 'calculate',\n args: '{\"a\": 5, \"b\": 3}',\n });\n const toolOutput = new FunctionCallOutput({\n callId: 'call_456',\n output: '{\"result\": 8}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n tool_calls: [\n {\n type: 'function',\n id: 'call_456',\n function: { name: 'calculate', arguments: '{\"a\": 5, \"b\": 3}' },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_456',\n content: '{\"result\": 8}',\n },\n ]);\n });\n\n it('should skip empty groups', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello', createdAt: 1000 });\n\n // Create an isolated tool output without corresponding call (will be filtered)\n const orphanOutput = new FunctionCallOutput({\n callId: 'orphan_call',\n output: 'This should be ignored',\n isError: false,\n createdAt: 2000,\n });\n ctx.insert(orphanOutput);\n\n ctx.addMessage({ role: 'assistant', content: 'Hi!', createdAt: 3000 });\n\n const result = await toChatCtx(ctx);\n\n // Messages should be in the result, orphan output should be filtered out\n expect(result).toHaveLength(2);\n expect(result).toContainEqual({ role: 'user', content: 'Hello' });\n expect(result).toContainEqual({ role: 'assistant', content: 'Hi!' });\n });\n\n it('should handle mixed content with text and multiple images', async () => {\n serializeImageMock\n .mockResolvedValueOnce({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image1.jpg',\n })\n .mockResolvedValueOnce({\n inferenceDetail: 'low',\n mimeType: 'image/png',\n base64Data: 'base64data',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n 'Here are two images:',\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image1.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n 'And the second one:',\n {\n id: 'img2',\n type: 'image_content',\n image: 'data:image/png;base64,base64data',\n inferenceDetail: 'low',\n _cache: {},\n },\n 'What do you think?',\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image1.jpg',\n detail: 'high',\n },\n },\n {\n type: 'image_url',\n image_url: {\n url: 'data:image/png;base64,base64data',\n detail: 'low',\n },\n },\n {\n type: 'text',\n text: 'Here are two images:\\nAnd the second one:\\nWhat do you think?',\n },\n ],\n },\n ]);\n });\n\n it('should handle content with only images and no text', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'auto',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'auto',\n _cache: {},\n },\n ],\n });\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'image_url',\n image_url: {\n url: 'https://example.com/image.jpg',\n detail: 'auto',\n },\n },\n ],\n },\n ]);\n });\n\n it('should throw error for unsupported content type', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [],\n },\n ],\n });\n\n await expect(toChatCtx(ctx)).rejects.toThrow('Unsupported content type: audio_content');\n });\n\n it('should throw error when serialized image has no data', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n // No base64Data or externalUrl\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'invalid-image',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n await expect(toChatCtx(ctx)).rejects.toThrow('Serialized image has no data bytes');\n });\n\n it('should filter out standalone function calls without outputs', async () => {\n const ctx = ChatContext.empty();\n\n // Add standalone function call without output\n const funcCall = new FunctionCall({\n id: 'func_standalone',\n callId: 'call_999',\n name: 'standalone_function',\n args: '{}',\n });\n\n ctx.insert(funcCall);\n\n const result = await toChatCtx(ctx);\n\n // Standalone function calls without outputs are filtered out by groupToolCalls\n expect(result).toEqual([]);\n });\n\n it('should handle function call output correctly', async () => {\n const ctx = ChatContext.empty();\n\n // First add a function call\n const funcCall = new FunctionCall({\n id: 'func_1',\n callId: 'call_output_test',\n name: 'test_function',\n args: '{}',\n });\n\n // Then add its output\n const funcOutput = new FunctionCallOutput({\n callId: 'call_output_test',\n output: 'Function executed successfully',\n isError: false,\n });\n\n ctx.insert([funcCall, funcOutput]);\n\n const result = await toChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'assistant',\n tool_calls: [\n {\n id: 'call_output_test',\n type: 'function',\n function: {\n name: 'test_function',\n arguments: '{}',\n },\n },\n ],\n },\n {\n role: 'tool',\n tool_call_id: 'call_output_test',\n content: 'Function executed successfully',\n },\n ]);\n });\n\n it('should filter out agent handoff items', async () => {\n const ctx = ChatContext.empty();\n\n ctx.addMessage({ role: 'user', content: 'Hello' });\n\n // Insert an agent handoff item\n const handoff = new AgentHandoffItem({\n oldAgentId: 'agent_1',\n newAgentId: 'agent_2',\n });\n ctx.insert(handoff);\n\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toChatCtx(ctx);\n\n // Agent handoff should be filtered out, only messages should remain\n expect(result).toEqual([\n { role: 'user', content: 'Hello' },\n { role: 'assistant', content: 'Hi there!' },\n ]);\n });\n\n it('should handle multiple agent handoffs without errors', async () => {\n const ctx = ChatContext.empty();\n\n ctx.addMessage({ role: 'user', content: 'Start' });\n\n // Multiple handoffs\n ctx.insert(new AgentHandoffItem({ oldAgentId: undefined, newAgentId: 'agent_1' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 1' });\n\n ctx.insert(new AgentHandoffItem({ oldAgentId: 'agent_1', newAgentId: 'agent_2' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 2' });\n\n ctx.insert(new AgentHandoffItem({ oldAgentId: 'agent_2', newAgentId: 'agent_3' }));\n ctx.addMessage({ role: 'assistant', content: 'Response from agent 3' });\n\n const result = await toChatCtx(ctx);\n\n // All handoffs should be filtered out\n expect(result).toEqual([\n { role: 'user', content: 'Start' },\n { role: 'assistant', content: 'Response from agent 1' },\n { role: 'assistant', content: 'Response from agent 2' },\n { role: 'assistant', content: 'Response from agent 3' },\n ]);\n });\n});\n\ndescribe('toResponsesChatCtx', () => {\n const serializeImageMock = vi.mocked(serializeImage);\n\n initializeLogger({ level: 'silent', pretty: false });\n\n beforeEach(async () => {\n vi.clearAllMocks();\n });\n\n it('should convert simple text messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello' });\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'user', content: 'Hello' });\n expect(result[1]).toEqual({ role: 'assistant', content: 'Hi there!' });\n });\n\n it('should handle system messages', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'system', content: 'You are a helpful assistant' });\n ctx.addMessage({ role: 'user', content: 'Hello' });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toHaveLength(2);\n expect(result[0]).toEqual({ role: 'system', content: 'You are a helpful assistant' });\n expect(result[1]).toEqual({ role: 'user', content: 'Hello' });\n });\n\n it('should handle multi-line text content', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: ['Line 1', 'Line 2', 'Line 3'] });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toHaveLength(1);\n expect(result[0]).toEqual({ role: 'user', content: 'Line 1\\nLine 2\\nLine 3' });\n });\n\n it('should convert images to input_image format with external URL', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'input_image',\n image_url: 'https://example.com/image.jpg',\n detail: 'high',\n },\n ],\n },\n ]);\n });\n\n it('should convert images to input_image format with base64 data', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'auto',\n mimeType: 'image/png',\n base64Data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n inferenceDetail: 'auto',\n _cache: {},\n },\n ],\n });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'input_image',\n image_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB',\n detail: 'auto',\n },\n ],\n },\n ]);\n });\n\n it('should handle mixed content with text and image using input_text', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n externalUrl: 'https://example.com/image.jpg',\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n 'Check this out:',\n {\n id: 'img1',\n type: 'image_content',\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n {\n role: 'user',\n content: [\n {\n type: 'input_image',\n image_url: 'https://example.com/image.jpg',\n detail: 'high',\n },\n { type: 'input_text', text: 'Check this out:' },\n ],\n },\n ]);\n });\n\n it('should handle tool calls as top-level function_call items', async () => {\n const ctx = ChatContext.empty();\n\n const msg = ctx.addMessage({ role: 'assistant', content: 'Let me help you.' });\n const toolCall = FunctionCall.create({\n id: msg.id + '/tool_1',\n callId: 'call_123',\n name: 'get_weather',\n args: '{\"location\": \"Paris\"}',\n });\n const toolOutput = FunctionCallOutput.create({\n callId: 'call_123',\n output: '{\"temperature\": 20}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n { role: 'assistant', content: 'Let me help you.' },\n {\n type: 'function_call',\n call_id: 'call_123',\n name: 'get_weather',\n arguments: '{\"location\": \"Paris\"}',\n },\n {\n type: 'function_call_output',\n call_id: 'call_123',\n output: '{\"temperature\": 20}',\n },\n ]);\n });\n\n it('should handle tool calls without an accompanying message', async () => {\n const ctx = ChatContext.empty();\n\n const toolCall = new FunctionCall({\n id: 'func_1',\n callId: 'call_456',\n name: 'calculate',\n args: '{\"a\": 5, \"b\": 3}',\n });\n const toolOutput = new FunctionCallOutput({\n callId: 'call_456',\n output: '{\"result\": 8}',\n isError: false,\n });\n\n ctx.insert([toolCall, toolOutput]);\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n {\n type: 'function_call',\n call_id: 'call_456',\n name: 'calculate',\n arguments: '{\"a\": 5, \"b\": 3}',\n },\n {\n type: 'function_call_output',\n call_id: 'call_456',\n output: '{\"result\": 8}',\n },\n ]);\n });\n\n it('should handle multiple tool calls as separate function_call items', async () => {\n const ctx = ChatContext.empty();\n\n const msg = ctx.addMessage({ role: 'assistant', content: \"I'll check both.\" });\n const toolCall1 = new FunctionCall({\n id: msg.id + '/tool_1',\n callId: 'call_1',\n name: 'get_weather',\n args: '{\"location\": \"NYC\"}',\n });\n const toolCall2 = new FunctionCall({\n id: msg.id + '/tool_2',\n callId: 'call_2',\n name: 'get_weather',\n args: '{\"location\": \"LA\"}',\n });\n const toolOutput1 = new FunctionCallOutput({\n callId: 'call_1',\n output: '{\"temperature\": 65}',\n isError: false,\n });\n const toolOutput2 = new FunctionCallOutput({\n callId: 'call_2',\n output: '{\"temperature\": 78}',\n isError: false,\n });\n\n ctx.insert([toolCall1, toolCall2, toolOutput1, toolOutput2]);\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n { role: 'assistant', content: \"I'll check both.\" },\n {\n type: 'function_call',\n call_id: 'call_1',\n name: 'get_weather',\n arguments: '{\"location\": \"NYC\"}',\n },\n {\n type: 'function_call',\n call_id: 'call_2',\n name: 'get_weather',\n arguments: '{\"location\": \"LA\"}',\n },\n {\n type: 'function_call_output',\n call_id: 'call_1',\n output: '{\"temperature\": 65}',\n },\n {\n type: 'function_call_output',\n call_id: 'call_2',\n output: '{\"temperature\": 78}',\n },\n ]);\n });\n\n it('should skip empty groups', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: 'Hello', createdAt: 1000 });\n\n const orphanOutput = new FunctionCallOutput({\n callId: 'orphan_call',\n output: 'This should be ignored',\n isError: false,\n createdAt: 2000,\n });\n ctx.insert(orphanOutput);\n\n ctx.addMessage({ role: 'assistant', content: 'Hi!', createdAt: 3000 });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toHaveLength(2);\n expect(result).toContainEqual({ role: 'user', content: 'Hello' });\n expect(result).toContainEqual({ role: 'assistant', content: 'Hi!' });\n });\n\n it('should filter out agent handoff items', async () => {\n const ctx = ChatContext.empty();\n\n ctx.addMessage({ role: 'user', content: 'Hello' });\n ctx.insert(new AgentHandoffItem({ oldAgentId: 'agent_1', newAgentId: 'agent_2' }));\n ctx.addMessage({ role: 'assistant', content: 'Hi there!' });\n\n const result = await toResponsesChatCtx(ctx);\n\n expect(result).toEqual([\n { role: 'user', content: 'Hello' },\n { role: 'assistant', content: 'Hi there!' },\n ]);\n });\n\n it('should cache serialized images', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n mimeType: 'image/png',\n base64Data: 'cached-data',\n });\n\n const imageContent = {\n id: 'img1',\n type: 'image_content' as const,\n image: 'https://example.com/image.jpg',\n inferenceDetail: 'high' as const,\n _cache: {},\n };\n\n const ctx = ChatContext.empty();\n ctx.addMessage({ role: 'user', content: [imageContent] });\n\n await toResponsesChatCtx(ctx);\n await toResponsesChatCtx(ctx);\n\n expect(serializeImageMock).toHaveBeenCalledTimes(1);\n expect(imageContent._cache).toHaveProperty('serialized_image');\n });\n\n it('should throw error for unsupported content type', async () => {\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n type: 'audio_content',\n frame: [],\n },\n ],\n });\n\n await expect(toResponsesChatCtx(ctx)).rejects.toThrow(\n 'Unsupported content type: audio_content',\n );\n });\n\n it('should throw error when serialized image has no data', async () => {\n serializeImageMock.mockResolvedValue({\n inferenceDetail: 'high',\n // No base64Data or externalUrl\n });\n\n const ctx = ChatContext.empty();\n ctx.addMessage({\n role: 'user',\n content: [\n {\n id: 'img1',\n type: 'image_content',\n image: 'invalid-image',\n inferenceDetail: 'high',\n _cache: {},\n },\n ],\n });\n\n await expect(toResponsesChatCtx(ctx)).rejects.toThrow('Serialized image has no data bytes');\n });\n});\n"],"mappings":"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","names":[]}
@@ -53,11 +53,12 @@ class ChatItemGroup {
53
53
  return this;
54
54
  }
55
55
  removeInvalidToolCalls() {
56
- if (this.toolCalls.length === this.toolOutputs.length) {
57
- return;
58
- }
59
56
  const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));
60
57
  const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));
58
+ const sameIds = toolCallIds.size === toolOutputIds.size && [...toolCallIds].every((id) => toolOutputIds.has(id));
59
+ if (this.toolCalls.length === this.toolOutputs.length && sameIds) {
60
+ return;
61
+ }
61
62
  const validCallIds = intersection(toolCallIds, toolOutputIds);
62
63
  this.toolCalls = this.toolCalls.filter((call) => {
63
64
  if (validCallIds.has(call.callId)) return true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/llm/provider_format/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '../../log.js';\nimport type {\n ChatContext,\n ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\n\nclass ChatItemGroup {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n logger = log();\n\n constructor(params: {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n }) {\n this.message = params.message;\n this.toolCalls = params.toolCalls;\n this.toolOutputs = params.toolOutputs;\n }\n\n static create(params?: {\n message?: ChatMessage;\n toolCalls?: FunctionCall[];\n toolOutputs?: FunctionCallOutput[];\n }) {\n const { message, toolCalls = [], toolOutputs = [] } = params ?? {};\n return new ChatItemGroup({ message, toolCalls, toolOutputs });\n }\n\n get isEmpty() {\n return (\n this.message === undefined && this.toolCalls.length === 0 && this.toolOutputs.length === 0\n );\n }\n\n add(item: ChatItem) {\n if (item.type === 'message') {\n if (this.message) {\n throw new Error('only one message is allowed in a group');\n }\n this.message = item;\n } else if (item.type === 'function_call') {\n this.toolCalls.push(item);\n } else if (item.type === 'function_call_output') {\n this.toolOutputs.push(item);\n }\n return this;\n }\n\n removeInvalidToolCalls() {\n if (this.toolCalls.length === this.toolOutputs.length) {\n return;\n }\n\n const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));\n const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));\n\n // intersection of tool call ids and tool output ids\n const validCallIds = intersection(toolCallIds, toolOutputIds);\n\n // filter out tool calls that don't have a corresponding tool output\n this.toolCalls = this.toolCalls.filter((call) => {\n if (validCallIds.has(call.callId)) return true;\n this.logger.warn(\n {\n callId: call.callId,\n toolName: call.name,\n },\n 'function call missing the corresponding function output, ignoring',\n );\n return false;\n });\n\n // filter out tool outputs that don't have a corresponding tool call\n this.toolOutputs = this.toolOutputs.filter((output) => {\n if (validCallIds.has(output.callId)) return true;\n this.logger.warn(\n {\n callId: output.callId,\n toolName: output.name,\n },\n 'function output missing the corresponding function call, ignoring',\n );\n return false;\n });\n }\n\n flatten() {\n const items: ChatItem[] = [];\n if (this.message) items.push(this.message);\n items.push(...this.toolCalls, ...this.toolOutputs);\n return items;\n }\n}\n\nfunction intersection<T>(set1: Set<T>, set2: Set<T>): Set<T> {\n return new Set([...set1].filter((item) => set2.has(item)));\n}\n\n/**\n * Group chat items (messages, function calls, and function outputs)\n * into coherent groups based on their item IDs and call IDs.\n *\n * Each group will contain:\n * - Zero or one assistant message\n * - Zero or more function/tool calls\n * - The corresponding function/tool outputs matched by call_id\n *\n * User and system messages are placed in their own individual groups.\n *\n * @param chatCtx - The chat context containing all conversation items\n * @returns A list of ChatItemGroup objects representing the grouped conversation\n */\nexport function groupToolCalls(chatCtx: ChatContext) {\n const itemGroups: Record<string, ChatItemGroup> = {};\n const insertionOrder: Record<string, number> = {};\n const toolOutputs: FunctionCallOutput[] = [];\n const logger = log();\n\n let insertionIndex = 0;\n for (const item of chatCtx.items) {\n const isAssistantMessage = item.type === 'message' && item.role === 'assistant';\n const isFunctionCall = item.type === 'function_call';\n const isFunctionCallOutput = item.type === 'function_call_output';\n\n if (isAssistantMessage || isFunctionCall) {\n // only assistant messages and function calls can be grouped\n // For function calls, use group_id if available (for parallel function calls),\n // otherwise fall back to id-based grouping for backwards compatibility\n const groupId =\n item.type === 'function_call' && item.groupId ? item.groupId : item.id.split('/')[0]!;\n\n if (itemGroups[groupId] === undefined) {\n itemGroups[groupId] = ChatItemGroup.create();\n\n // we use insertion order to sort the groups as they are added to the context\n // simulating the OrderedDict in python\n insertionOrder[groupId] = insertionIndex;\n insertionIndex++;\n }\n itemGroups[groupId]!.add(item);\n } else if (isFunctionCallOutput) {\n toolOutputs.push(item);\n } else {\n itemGroups[item.id] = ChatItemGroup.create().add(item);\n }\n }\n\n // add tool outputs to their corresponding groups\n const callIdToGroup: Record<string, ChatItemGroup> = {};\n for (const group of Object.values(itemGroups)) {\n for (const toolCall of group.toolCalls) {\n callIdToGroup[toolCall.callId] = group;\n }\n }\n\n for (const toolOutput of toolOutputs) {\n const group = callIdToGroup[toolOutput.callId];\n if (group === undefined) {\n logger.warn(\n { callId: toolOutput.callId, toolName: toolOutput.name },\n 'function output missing the corresponding function call, ignoring',\n );\n continue;\n }\n group.add(toolOutput);\n }\n\n // validate that each group and remove invalid tool calls and tool outputs\n for (const group of Object.values(itemGroups)) {\n group.removeInvalidToolCalls();\n }\n\n // sort groups by their item id\n const orderedGroups = Object.entries(itemGroups)\n .sort((a, b) => insertionOrder[a[0]]! - insertionOrder[b[0]]!)\n .map(([, group]) => group);\n return orderedGroups;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/llm/provider_format/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '../../log.js';\nimport type {\n ChatContext,\n ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\n\nclass ChatItemGroup {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n logger = log();\n\n constructor(params: {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n }) {\n this.message = params.message;\n this.toolCalls = params.toolCalls;\n this.toolOutputs = params.toolOutputs;\n }\n\n static create(params?: {\n message?: ChatMessage;\n toolCalls?: FunctionCall[];\n toolOutputs?: FunctionCallOutput[];\n }) {\n const { message, toolCalls = [], toolOutputs = [] } = params ?? {};\n return new ChatItemGroup({ message, toolCalls, toolOutputs });\n }\n\n get isEmpty() {\n return (\n this.message === undefined && this.toolCalls.length === 0 && this.toolOutputs.length === 0\n );\n }\n\n add(item: ChatItem) {\n if (item.type === 'message') {\n if (this.message) {\n throw new Error('only one message is allowed in a group');\n }\n this.message = item;\n } else if (item.type === 'function_call') {\n this.toolCalls.push(item);\n } else if (item.type === 'function_call_output') {\n this.toolOutputs.push(item);\n }\n return this;\n }\n\n removeInvalidToolCalls() {\n const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));\n const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));\n const sameIds =\n toolCallIds.size === toolOutputIds.size &&\n [...toolCallIds].every((id) => toolOutputIds.has(id));\n if (this.toolCalls.length === this.toolOutputs.length && sameIds) {\n return;\n }\n\n // intersection of tool call ids and tool output ids\n const validCallIds = intersection(toolCallIds, toolOutputIds);\n\n // filter out tool calls that don't have a corresponding tool output\n this.toolCalls = this.toolCalls.filter((call) => {\n if (validCallIds.has(call.callId)) return true;\n this.logger.warn(\n {\n callId: call.callId,\n toolName: call.name,\n },\n 'function call missing the corresponding function output, ignoring',\n );\n return false;\n });\n\n // filter out tool outputs that don't have a corresponding tool call\n this.toolOutputs = this.toolOutputs.filter((output) => {\n if (validCallIds.has(output.callId)) return true;\n this.logger.warn(\n {\n callId: output.callId,\n toolName: output.name,\n },\n 'function output missing the corresponding function call, ignoring',\n );\n return false;\n });\n }\n\n flatten() {\n const items: ChatItem[] = [];\n if (this.message) items.push(this.message);\n items.push(...this.toolCalls, ...this.toolOutputs);\n return items;\n }\n}\n\nfunction intersection<T>(set1: Set<T>, set2: Set<T>): Set<T> {\n return new Set([...set1].filter((item) => set2.has(item)));\n}\n\n/**\n * Group chat items (messages, function calls, and function outputs)\n * into coherent groups based on their item IDs and call IDs.\n *\n * Each group will contain:\n * - Zero or one assistant message\n * - Zero or more function/tool calls\n * - The corresponding function/tool outputs matched by call_id\n *\n * User and system messages are placed in their own individual groups.\n *\n * @param chatCtx - The chat context containing all conversation items\n * @returns A list of ChatItemGroup objects representing the grouped conversation\n */\nexport function groupToolCalls(chatCtx: ChatContext) {\n const itemGroups: Record<string, ChatItemGroup> = {};\n const insertionOrder: Record<string, number> = {};\n const toolOutputs: FunctionCallOutput[] = [];\n const logger = log();\n\n let insertionIndex = 0;\n for (const item of chatCtx.items) {\n const isAssistantMessage = item.type === 'message' && item.role === 'assistant';\n const isFunctionCall = item.type === 'function_call';\n const isFunctionCallOutput = item.type === 'function_call_output';\n\n if (isAssistantMessage || isFunctionCall) {\n // only assistant messages and function calls can be grouped\n // For function calls, use group_id if available (for parallel function calls),\n // otherwise fall back to id-based grouping for backwards compatibility\n const groupId =\n item.type === 'function_call' && item.groupId ? item.groupId : item.id.split('/')[0]!;\n\n if (itemGroups[groupId] === undefined) {\n itemGroups[groupId] = ChatItemGroup.create();\n\n // we use insertion order to sort the groups as they are added to the context\n // simulating the OrderedDict in python\n insertionOrder[groupId] = insertionIndex;\n insertionIndex++;\n }\n itemGroups[groupId]!.add(item);\n } else if (isFunctionCallOutput) {\n toolOutputs.push(item);\n } else {\n itemGroups[item.id] = ChatItemGroup.create().add(item);\n }\n }\n\n // add tool outputs to their corresponding groups\n const callIdToGroup: Record<string, ChatItemGroup> = {};\n for (const group of Object.values(itemGroups)) {\n for (const toolCall of group.toolCalls) {\n callIdToGroup[toolCall.callId] = group;\n }\n }\n\n for (const toolOutput of toolOutputs) {\n const group = callIdToGroup[toolOutput.callId];\n if (group === undefined) {\n logger.warn(\n { callId: toolOutput.callId, toolName: toolOutput.name },\n 'function output missing the corresponding function call, ignoring',\n );\n continue;\n }\n group.add(toolOutput);\n }\n\n // validate that each group and remove invalid tool calls and tool outputs\n for (const group of Object.values(itemGroups)) {\n group.removeInvalidToolCalls();\n }\n\n // sort groups by their item id\n const orderedGroups = Object.entries(itemGroups)\n .sort((a, b) => insertionOrder[a[0]]! - insertionOrder[b[0]]!)\n .map(([, group]) => group);\n return orderedGroups;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/llm/provider_format/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,cAAM,aAAa;IACjB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,MAAM,wBAAS;gBAEH,MAAM,EAAE;QAClB,OAAO,CAAC,EAAE,WAAW,CAAC;QACtB,SAAS,EAAE,YAAY,EAAE,CAAC;QAC1B,WAAW,EAAE,kBAAkB,EAAE,CAAC;KACnC;IAMD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,WAAW,CAAC;QACtB,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;QAC3B,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;KACpC;IAKD,IAAI,OAAO,YAIV;IAED,GAAG,CAAC,IAAI,EAAE,QAAQ;IAclB,sBAAsB;IAsCtB,OAAO;CAMR;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,mBAiElD"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/llm/provider_format/utils.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,cAAM,aAAa;IACjB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,MAAM,wBAAS;gBAEH,MAAM,EAAE;QAClB,OAAO,CAAC,EAAE,WAAW,CAAC;QACtB,SAAS,EAAE,YAAY,EAAE,CAAC;QAC1B,WAAW,EAAE,kBAAkB,EAAE,CAAC;KACnC;IAMD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;QACrB,OAAO,CAAC,EAAE,WAAW,CAAC;QACtB,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;QAC3B,WAAW,CAAC,EAAE,kBAAkB,EAAE,CAAC;KACpC;IAKD,IAAI,OAAO,YAIV;IAED,GAAG,CAAC,IAAI,EAAE,QAAQ;IAclB,sBAAsB;IAwCtB,OAAO;CAMR;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,mBAiElD"}
@@ -30,11 +30,12 @@ class ChatItemGroup {
30
30
  return this;
31
31
  }
32
32
  removeInvalidToolCalls() {
33
- if (this.toolCalls.length === this.toolOutputs.length) {
34
- return;
35
- }
36
33
  const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));
37
34
  const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));
35
+ const sameIds = toolCallIds.size === toolOutputIds.size && [...toolCallIds].every((id) => toolOutputIds.has(id));
36
+ if (this.toolCalls.length === this.toolOutputs.length && sameIds) {
37
+ return;
38
+ }
38
39
  const validCallIds = intersection(toolCallIds, toolOutputIds);
39
40
  this.toolCalls = this.toolCalls.filter((call) => {
40
41
  if (validCallIds.has(call.callId)) return true;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/llm/provider_format/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '../../log.js';\nimport type {\n ChatContext,\n ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\n\nclass ChatItemGroup {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n logger = log();\n\n constructor(params: {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n }) {\n this.message = params.message;\n this.toolCalls = params.toolCalls;\n this.toolOutputs = params.toolOutputs;\n }\n\n static create(params?: {\n message?: ChatMessage;\n toolCalls?: FunctionCall[];\n toolOutputs?: FunctionCallOutput[];\n }) {\n const { message, toolCalls = [], toolOutputs = [] } = params ?? {};\n return new ChatItemGroup({ message, toolCalls, toolOutputs });\n }\n\n get isEmpty() {\n return (\n this.message === undefined && this.toolCalls.length === 0 && this.toolOutputs.length === 0\n );\n }\n\n add(item: ChatItem) {\n if (item.type === 'message') {\n if (this.message) {\n throw new Error('only one message is allowed in a group');\n }\n this.message = item;\n } else if (item.type === 'function_call') {\n this.toolCalls.push(item);\n } else if (item.type === 'function_call_output') {\n this.toolOutputs.push(item);\n }\n return this;\n }\n\n removeInvalidToolCalls() {\n if (this.toolCalls.length === this.toolOutputs.length) {\n return;\n }\n\n const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));\n const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));\n\n // intersection of tool call ids and tool output ids\n const validCallIds = intersection(toolCallIds, toolOutputIds);\n\n // filter out tool calls that don't have a corresponding tool output\n this.toolCalls = this.toolCalls.filter((call) => {\n if (validCallIds.has(call.callId)) return true;\n this.logger.warn(\n {\n callId: call.callId,\n toolName: call.name,\n },\n 'function call missing the corresponding function output, ignoring',\n );\n return false;\n });\n\n // filter out tool outputs that don't have a corresponding tool call\n this.toolOutputs = this.toolOutputs.filter((output) => {\n if (validCallIds.has(output.callId)) return true;\n this.logger.warn(\n {\n callId: output.callId,\n toolName: output.name,\n },\n 'function output missing the corresponding function call, ignoring',\n );\n return false;\n });\n }\n\n flatten() {\n const items: ChatItem[] = [];\n if (this.message) items.push(this.message);\n items.push(...this.toolCalls, ...this.toolOutputs);\n return items;\n }\n}\n\nfunction intersection<T>(set1: Set<T>, set2: Set<T>): Set<T> {\n return new Set([...set1].filter((item) => set2.has(item)));\n}\n\n/**\n * Group chat items (messages, function calls, and function outputs)\n * into coherent groups based on their item IDs and call IDs.\n *\n * Each group will contain:\n * - Zero or one assistant message\n * - Zero or more function/tool calls\n * - The corresponding function/tool outputs matched by call_id\n *\n * User and system messages are placed in their own individual groups.\n *\n * @param chatCtx - The chat context containing all conversation items\n * @returns A list of ChatItemGroup objects representing the grouped conversation\n */\nexport function groupToolCalls(chatCtx: ChatContext) {\n const itemGroups: Record<string, ChatItemGroup> = {};\n const insertionOrder: Record<string, number> = {};\n const toolOutputs: FunctionCallOutput[] = [];\n const logger = log();\n\n let insertionIndex = 0;\n for (const item of chatCtx.items) {\n const isAssistantMessage = item.type === 'message' && item.role === 'assistant';\n const isFunctionCall = item.type === 'function_call';\n const isFunctionCallOutput = item.type === 'function_call_output';\n\n if (isAssistantMessage || isFunctionCall) {\n // only assistant messages and function calls can be grouped\n // For function calls, use group_id if available (for parallel function calls),\n // otherwise fall back to id-based grouping for backwards compatibility\n const groupId =\n item.type === 'function_call' && item.groupId ? item.groupId : item.id.split('/')[0]!;\n\n if (itemGroups[groupId] === undefined) {\n itemGroups[groupId] = ChatItemGroup.create();\n\n // we use insertion order to sort the groups as they are added to the context\n // simulating the OrderedDict in python\n insertionOrder[groupId] = insertionIndex;\n insertionIndex++;\n }\n itemGroups[groupId]!.add(item);\n } else if (isFunctionCallOutput) {\n toolOutputs.push(item);\n } else {\n itemGroups[item.id] = ChatItemGroup.create().add(item);\n }\n }\n\n // add tool outputs to their corresponding groups\n const callIdToGroup: Record<string, ChatItemGroup> = {};\n for (const group of Object.values(itemGroups)) {\n for (const toolCall of group.toolCalls) {\n callIdToGroup[toolCall.callId] = group;\n }\n }\n\n for (const toolOutput of toolOutputs) {\n const group = callIdToGroup[toolOutput.callId];\n if (group === undefined) {\n logger.warn(\n { callId: toolOutput.callId, toolName: toolOutput.name },\n 'function output missing the corresponding function call, ignoring',\n );\n continue;\n }\n group.add(toolOutput);\n }\n\n // validate that each group and remove invalid tool calls and tool outputs\n for (const group of Object.values(itemGroups)) {\n group.removeInvalidToolCalls();\n }\n\n // sort groups by their item id\n const orderedGroups = Object.entries(itemGroups)\n .sort((a, b) => insertionOrder[a[0]]! - insertionOrder[b[0]]!)\n .map(([, group]) => group);\n return orderedGroups;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/llm/provider_format/utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { log } from '../../log.js';\nimport type {\n ChatContext,\n ChatItem,\n ChatMessage,\n FunctionCall,\n FunctionCallOutput,\n} from '../chat_context.js';\n\nclass ChatItemGroup {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n logger = log();\n\n constructor(params: {\n message?: ChatMessage;\n toolCalls: FunctionCall[];\n toolOutputs: FunctionCallOutput[];\n }) {\n this.message = params.message;\n this.toolCalls = params.toolCalls;\n this.toolOutputs = params.toolOutputs;\n }\n\n static create(params?: {\n message?: ChatMessage;\n toolCalls?: FunctionCall[];\n toolOutputs?: FunctionCallOutput[];\n }) {\n const { message, toolCalls = [], toolOutputs = [] } = params ?? {};\n return new ChatItemGroup({ message, toolCalls, toolOutputs });\n }\n\n get isEmpty() {\n return (\n this.message === undefined && this.toolCalls.length === 0 && this.toolOutputs.length === 0\n );\n }\n\n add(item: ChatItem) {\n if (item.type === 'message') {\n if (this.message) {\n throw new Error('only one message is allowed in a group');\n }\n this.message = item;\n } else if (item.type === 'function_call') {\n this.toolCalls.push(item);\n } else if (item.type === 'function_call_output') {\n this.toolOutputs.push(item);\n }\n return this;\n }\n\n removeInvalidToolCalls() {\n const toolCallIds = new Set(this.toolCalls.map((call) => call.callId));\n const toolOutputIds = new Set(this.toolOutputs.map((output) => output.callId));\n const sameIds =\n toolCallIds.size === toolOutputIds.size &&\n [...toolCallIds].every((id) => toolOutputIds.has(id));\n if (this.toolCalls.length === this.toolOutputs.length && sameIds) {\n return;\n }\n\n // intersection of tool call ids and tool output ids\n const validCallIds = intersection(toolCallIds, toolOutputIds);\n\n // filter out tool calls that don't have a corresponding tool output\n this.toolCalls = this.toolCalls.filter((call) => {\n if (validCallIds.has(call.callId)) return true;\n this.logger.warn(\n {\n callId: call.callId,\n toolName: call.name,\n },\n 'function call missing the corresponding function output, ignoring',\n );\n return false;\n });\n\n // filter out tool outputs that don't have a corresponding tool call\n this.toolOutputs = this.toolOutputs.filter((output) => {\n if (validCallIds.has(output.callId)) return true;\n this.logger.warn(\n {\n callId: output.callId,\n toolName: output.name,\n },\n 'function output missing the corresponding function call, ignoring',\n );\n return false;\n });\n }\n\n flatten() {\n const items: ChatItem[] = [];\n if (this.message) items.push(this.message);\n items.push(...this.toolCalls, ...this.toolOutputs);\n return items;\n }\n}\n\nfunction intersection<T>(set1: Set<T>, set2: Set<T>): Set<T> {\n return new Set([...set1].filter((item) => set2.has(item)));\n}\n\n/**\n * Group chat items (messages, function calls, and function outputs)\n * into coherent groups based on their item IDs and call IDs.\n *\n * Each group will contain:\n * - Zero or one assistant message\n * - Zero or more function/tool calls\n * - The corresponding function/tool outputs matched by call_id\n *\n * User and system messages are placed in their own individual groups.\n *\n * @param chatCtx - The chat context containing all conversation items\n * @returns A list of ChatItemGroup objects representing the grouped conversation\n */\nexport function groupToolCalls(chatCtx: ChatContext) {\n const itemGroups: Record<string, ChatItemGroup> = {};\n const insertionOrder: Record<string, number> = {};\n const toolOutputs: FunctionCallOutput[] = [];\n const logger = log();\n\n let insertionIndex = 0;\n for (const item of chatCtx.items) {\n const isAssistantMessage = item.type === 'message' && item.role === 'assistant';\n const isFunctionCall = item.type === 'function_call';\n const isFunctionCallOutput = item.type === 'function_call_output';\n\n if (isAssistantMessage || isFunctionCall) {\n // only assistant messages and function calls can be grouped\n // For function calls, use group_id if available (for parallel function calls),\n // otherwise fall back to id-based grouping for backwards compatibility\n const groupId =\n item.type === 'function_call' && item.groupId ? item.groupId : item.id.split('/')[0]!;\n\n if (itemGroups[groupId] === undefined) {\n itemGroups[groupId] = ChatItemGroup.create();\n\n // we use insertion order to sort the groups as they are added to the context\n // simulating the OrderedDict in python\n insertionOrder[groupId] = insertionIndex;\n insertionIndex++;\n }\n itemGroups[groupId]!.add(item);\n } else if (isFunctionCallOutput) {\n toolOutputs.push(item);\n } else {\n itemGroups[item.id] = ChatItemGroup.create().add(item);\n }\n }\n\n // add tool outputs to their corresponding groups\n const callIdToGroup: Record<string, ChatItemGroup> = {};\n for (const group of Object.values(itemGroups)) {\n for (const toolCall of group.toolCalls) {\n callIdToGroup[toolCall.callId] = group;\n }\n }\n\n for (const toolOutput of toolOutputs) {\n const group = callIdToGroup[toolOutput.callId];\n if (group === undefined) {\n logger.warn(\n { callId: toolOutput.callId, toolName: toolOutput.name },\n 'function output missing the corresponding function call, ignoring',\n );\n continue;\n }\n group.add(toolOutput);\n }\n\n // validate that each group and remove invalid tool calls and tool outputs\n for (const group of Object.values(itemGroups)) {\n group.removeInvalidToolCalls();\n }\n\n // sort groups by their item id\n const orderedGroups = Object.entries(itemGroups)\n .sort((a, b) => insertionOrder[a[0]]! - insertionOrder[b[0]]!)\n .map(([, group]) => group);\n return orderedGroups;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/llm/realtime.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { EventEmitter } from 'events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { Task } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\nimport type { ChatContext, FunctionCall } from './chat_context.js';\nimport type { ToolChoice, ToolContext } from './tool_context.js';\n\nexport type InputSpeechStartedEvent = object;\n\nexport interface InputSpeechStoppedEvent {\n userTranscriptionEnabled: boolean;\n}\n\nexport interface MessageGeneration {\n messageId: string;\n /**\n * Text stream that may contain plain strings or TimedString objects with timestamps.\n */\n textStream: ReadableStream<string | TimedString>;\n audioStream: ReadableStream<AudioFrame>;\n modalities?: Promise<('text' | 'audio')[]>;\n}\n\nexport interface GenerationCreatedEvent {\n messageStream: ReadableStream<MessageGeneration>;\n functionStream: ReadableStream<FunctionCall>;\n userInitiated: boolean;\n /** Response ID for correlating metrics with spans */\n responseId?: string;\n}\n\nexport interface RealtimeModelError {\n type: 'realtime_model_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport interface RealtimeCapabilities {\n messageTruncation: boolean;\n turnDetection: boolean;\n userTranscription: boolean;\n autoToolReplyGeneration: boolean;\n audioOutput: boolean;\n}\n\nexport interface InputTranscriptionCompleted {\n itemId: string;\n transcript: string;\n isFinal: boolean;\n}\n\nexport interface RealtimeSessionReconnectedEvent {}\n\nexport abstract class RealtimeModel {\n private _capabilities: RealtimeCapabilities;\n\n constructor(capabilities: RealtimeCapabilities) {\n this._capabilities = capabilities;\n }\n\n get capabilities() {\n return this._capabilities;\n }\n\n /** The model name/identifier used by this realtime model */\n abstract get model(): string;\n\n abstract session(): RealtimeSession;\n\n abstract close(): Promise<void>;\n}\n\nexport abstract class RealtimeSession extends EventEmitter {\n protected _realtimeModel: RealtimeModel;\n private deferredInputStream = new DeferredReadableStream<AudioFrame>();\n private _mainTask: Task<void>;\n\n constructor(realtimeModel: RealtimeModel) {\n super();\n this._realtimeModel = realtimeModel;\n this._mainTask = Task.from((controller) => this._mainTaskImpl(controller.signal));\n }\n\n get realtimeModel() {\n return this._realtimeModel;\n }\n\n abstract get chatCtx(): ChatContext;\n\n abstract get tools(): ToolContext;\n\n abstract updateInstructions(instructions: string): Promise<void>;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract updateChatCtx(chatCtx: ChatContext): Promise<void>;\n\n abstract updateTools(tools: ToolContext): Promise<void>;\n\n abstract updateOptions(options: { toolChoice?: ToolChoice | null }): void;\n\n abstract pushAudio(frame: AudioFrame): void;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract generateReply(instructions?: string): Promise<GenerationCreatedEvent>;\n\n /**\n * Commit the input audio buffer to the server\n */\n abstract commitAudio(): Promise<void>;\n\n /**\n * Clear the input audio buffer to the server\n */\n abstract clearAudio(): Promise<void>;\n\n /**\n * Cancel the current generation (do nothing if no generation is in progress)\n */\n abstract interrupt(): Promise<void>;\n\n /**\n * Truncate the message at the given audio end time\n */\n abstract truncate(options: {\n messageId: string;\n audioEndMs: number;\n modalities?: ('text' | 'audio')[];\n audioTranscript?: string;\n }): Promise<void>;\n\n async close(): Promise<void> {\n this._mainTask.cancel();\n }\n\n /**\n * Notifies the model that user activity has started\n */\n startUserActivity(): void {\n return;\n }\n\n private async _mainTaskImpl(signal: AbortSignal): Promise<void> {\n const reader = this.deferredInputStream.stream.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done || signal.aborted) {\n break;\n }\n this.pushAudio(value);\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>): void {\n this.deferredInputStream.setSource(audioStream);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBAA6B;AAE7B,6BAAuC;AACvC,mBAAqB;AAqDd,MAAe,cAAc;AAAA,EAC1B;AAAA,EAER,YAAY,cAAoC;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAQF;AAEO,MAAe,wBAAwB,2BAAa;AAAA,EAC/C;AAAA,EACF,sBAAsB,IAAI,8CAAmC;AAAA,EAC7D;AAAA,EAER,YAAY,eAA8B;AACxC,UAAM;AACN,SAAK,iBAAiB;AACtB,SAAK,YAAY,kBAAK,KAAK,CAAC,eAAe,KAAK,cAAc,WAAW,MAAM,CAAC;AAAA,EAClF;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAiDA,MAAM,QAAuB;AAC3B,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,QAAoC;AAC9D,UAAM,SAAS,KAAK,oBAAoB,OAAO,UAAU;AACzD,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,QAAQ,OAAO,SAAS;AAC1B;AAAA,MACF;AACA,WAAK,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,oBAAoB,aAA+C;AACjE,SAAK,oBAAoB,UAAU,WAAW;AAAA,EAChD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/llm/realtime.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { EventEmitter } from 'events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { Task } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\nimport type { ChatContext, FunctionCall } from './chat_context.js';\nimport type { ToolChoice, ToolContext } from './tool_context.js';\n\nexport type InputSpeechStartedEvent = object;\n\nexport interface InputSpeechStoppedEvent {\n userTranscriptionEnabled: boolean;\n}\n\nexport interface MessageGeneration {\n messageId: string;\n /**\n * Text stream that may contain plain strings or TimedString objects with timestamps.\n */\n textStream: ReadableStream<string | TimedString>;\n audioStream: ReadableStream<AudioFrame>;\n modalities?: Promise<('text' | 'audio')[]>;\n}\n\nexport interface GenerationCreatedEvent {\n messageStream: ReadableStream<MessageGeneration>;\n functionStream: ReadableStream<FunctionCall>;\n userInitiated: boolean;\n /** Response ID for correlating metrics with spans */\n responseId?: string;\n}\n\nexport interface RealtimeModelError {\n type: 'realtime_model_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport interface RealtimeCapabilities {\n messageTruncation: boolean;\n turnDetection: boolean;\n userTranscription: boolean;\n autoToolReplyGeneration: boolean;\n audioOutput: boolean;\n manualFunctionCalls: boolean;\n}\n\nexport interface InputTranscriptionCompleted {\n itemId: string;\n transcript: string;\n isFinal: boolean;\n}\n\nexport interface RealtimeSessionReconnectedEvent {}\n\nexport abstract class RealtimeModel {\n private _capabilities: RealtimeCapabilities;\n\n constructor(capabilities: RealtimeCapabilities) {\n this._capabilities = capabilities;\n }\n\n get capabilities() {\n return this._capabilities;\n }\n\n /** The model name/identifier used by this realtime model */\n abstract get model(): string;\n\n abstract session(): RealtimeSession;\n\n abstract close(): Promise<void>;\n}\n\nexport abstract class RealtimeSession extends EventEmitter {\n protected _realtimeModel: RealtimeModel;\n private deferredInputStream = new DeferredReadableStream<AudioFrame>();\n private _mainTask: Task<void>;\n\n constructor(realtimeModel: RealtimeModel) {\n super();\n this._realtimeModel = realtimeModel;\n this._mainTask = Task.from((controller) => this._mainTaskImpl(controller.signal));\n }\n\n get realtimeModel() {\n return this._realtimeModel;\n }\n\n abstract get chatCtx(): ChatContext;\n\n abstract get tools(): ToolContext;\n\n abstract updateInstructions(instructions: string): Promise<void>;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract updateChatCtx(chatCtx: ChatContext): Promise<void>;\n\n abstract updateTools(tools: ToolContext): Promise<void>;\n\n abstract updateOptions(options: { toolChoice?: ToolChoice | null }): void;\n\n abstract pushAudio(frame: AudioFrame): void;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract generateReply(instructions?: string): Promise<GenerationCreatedEvent>;\n\n /**\n * Commit the input audio buffer to the server\n */\n abstract commitAudio(): Promise<void>;\n\n /**\n * Clear the input audio buffer to the server\n */\n abstract clearAudio(): Promise<void>;\n\n /**\n * Cancel the current generation (do nothing if no generation is in progress)\n */\n abstract interrupt(): Promise<void>;\n\n /**\n * Truncate the message at the given audio end time\n */\n abstract truncate(options: {\n messageId: string;\n audioEndMs: number;\n modalities?: ('text' | 'audio')[];\n audioTranscript?: string;\n }): Promise<void>;\n\n async close(): Promise<void> {\n this._mainTask.cancel();\n }\n\n /**\n * Notifies the model that user activity has started\n */\n startUserActivity(): void {\n return;\n }\n\n private async _mainTaskImpl(signal: AbortSignal): Promise<void> {\n const reader = this.deferredInputStream.stream.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done || signal.aborted) {\n break;\n }\n this.pushAudio(value);\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>): void {\n this.deferredInputStream.setSource(audioStream);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBAA6B;AAE7B,6BAAuC;AACvC,mBAAqB;AAsDd,MAAe,cAAc;AAAA,EAC1B;AAAA,EAER,YAAY,cAAoC;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAQF;AAEO,MAAe,wBAAwB,2BAAa;AAAA,EAC/C;AAAA,EACF,sBAAsB,IAAI,8CAAmC;AAAA,EAC7D;AAAA,EAER,YAAY,eAA8B;AACxC,UAAM;AACN,SAAK,iBAAiB;AACtB,SAAK,YAAY,kBAAK,KAAK,CAAC,eAAe,KAAK,cAAc,WAAW,MAAM,CAAC;AAAA,EAClF;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAiDA,MAAM,QAAuB;AAC3B,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,QAAoC;AAC9D,UAAM,SAAS,KAAK,oBAAoB,OAAO,UAAU;AACzD,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,QAAQ,OAAO,SAAS;AAC1B;AAAA,MACF;AACA,WAAK,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,oBAAoB,aAA+C;AACjE,SAAK,oBAAoB,UAAU,WAAW;AAAA,EAChD;AACF;","names":[]}
@@ -39,6 +39,7 @@ export interface RealtimeCapabilities {
39
39
  userTranscription: boolean;
40
40
  autoToolReplyGeneration: boolean;
41
41
  audioOutput: boolean;
42
+ manualFunctionCalls: boolean;
42
43
  }
43
44
  export interface InputTranscriptionCompleted {
44
45
  itemId: string;
@@ -39,6 +39,7 @@ export interface RealtimeCapabilities {
39
39
  userTranscription: boolean;
40
40
  autoToolReplyGeneration: boolean;
41
41
  audioOutput: boolean;
42
+ manualFunctionCalls: boolean;
42
43
  }
43
44
  export interface InputTranscriptionCompleted {
44
45
  itemId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/llm/realtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/llm/realtime.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/llm/realtime.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { EventEmitter } from 'events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { Task } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\nimport type { ChatContext, FunctionCall } from './chat_context.js';\nimport type { ToolChoice, ToolContext } from './tool_context.js';\n\nexport type InputSpeechStartedEvent = object;\n\nexport interface InputSpeechStoppedEvent {\n userTranscriptionEnabled: boolean;\n}\n\nexport interface MessageGeneration {\n messageId: string;\n /**\n * Text stream that may contain plain strings or TimedString objects with timestamps.\n */\n textStream: ReadableStream<string | TimedString>;\n audioStream: ReadableStream<AudioFrame>;\n modalities?: Promise<('text' | 'audio')[]>;\n}\n\nexport interface GenerationCreatedEvent {\n messageStream: ReadableStream<MessageGeneration>;\n functionStream: ReadableStream<FunctionCall>;\n userInitiated: boolean;\n /** Response ID for correlating metrics with spans */\n responseId?: string;\n}\n\nexport interface RealtimeModelError {\n type: 'realtime_model_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport interface RealtimeCapabilities {\n messageTruncation: boolean;\n turnDetection: boolean;\n userTranscription: boolean;\n autoToolReplyGeneration: boolean;\n audioOutput: boolean;\n}\n\nexport interface InputTranscriptionCompleted {\n itemId: string;\n transcript: string;\n isFinal: boolean;\n}\n\nexport interface RealtimeSessionReconnectedEvent {}\n\nexport abstract class RealtimeModel {\n private _capabilities: RealtimeCapabilities;\n\n constructor(capabilities: RealtimeCapabilities) {\n this._capabilities = capabilities;\n }\n\n get capabilities() {\n return this._capabilities;\n }\n\n /** The model name/identifier used by this realtime model */\n abstract get model(): string;\n\n abstract session(): RealtimeSession;\n\n abstract close(): Promise<void>;\n}\n\nexport abstract class RealtimeSession extends EventEmitter {\n protected _realtimeModel: RealtimeModel;\n private deferredInputStream = new DeferredReadableStream<AudioFrame>();\n private _mainTask: Task<void>;\n\n constructor(realtimeModel: RealtimeModel) {\n super();\n this._realtimeModel = realtimeModel;\n this._mainTask = Task.from((controller) => this._mainTaskImpl(controller.signal));\n }\n\n get realtimeModel() {\n return this._realtimeModel;\n }\n\n abstract get chatCtx(): ChatContext;\n\n abstract get tools(): ToolContext;\n\n abstract updateInstructions(instructions: string): Promise<void>;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract updateChatCtx(chatCtx: ChatContext): Promise<void>;\n\n abstract updateTools(tools: ToolContext): Promise<void>;\n\n abstract updateOptions(options: { toolChoice?: ToolChoice | null }): void;\n\n abstract pushAudio(frame: AudioFrame): void;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract generateReply(instructions?: string): Promise<GenerationCreatedEvent>;\n\n /**\n * Commit the input audio buffer to the server\n */\n abstract commitAudio(): Promise<void>;\n\n /**\n * Clear the input audio buffer to the server\n */\n abstract clearAudio(): Promise<void>;\n\n /**\n * Cancel the current generation (do nothing if no generation is in progress)\n */\n abstract interrupt(): Promise<void>;\n\n /**\n * Truncate the message at the given audio end time\n */\n abstract truncate(options: {\n messageId: string;\n audioEndMs: number;\n modalities?: ('text' | 'audio')[];\n audioTranscript?: string;\n }): Promise<void>;\n\n async close(): Promise<void> {\n this._mainTask.cancel();\n }\n\n /**\n * Notifies the model that user activity has started\n */\n startUserActivity(): void {\n return;\n }\n\n private async _mainTaskImpl(signal: AbortSignal): Promise<void> {\n const reader = this.deferredInputStream.stream.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done || signal.aborted) {\n break;\n }\n this.pushAudio(value);\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>): void {\n this.deferredInputStream.setSource(audioStream);\n }\n}\n"],"mappings":"AAIA,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AACvC,SAAS,YAAY;AAqDd,MAAe,cAAc;AAAA,EAC1B;AAAA,EAER,YAAY,cAAoC;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAQF;AAEO,MAAe,wBAAwB,aAAa;AAAA,EAC/C;AAAA,EACF,sBAAsB,IAAI,uBAAmC;AAAA,EAC7D;AAAA,EAER,YAAY,eAA8B;AACxC,UAAM;AACN,SAAK,iBAAiB;AACtB,SAAK,YAAY,KAAK,KAAK,CAAC,eAAe,KAAK,cAAc,WAAW,MAAM,CAAC;AAAA,EAClF;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAiDA,MAAM,QAAuB;AAC3B,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,QAAoC;AAC9D,UAAM,SAAS,KAAK,oBAAoB,OAAO,UAAU;AACzD,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,QAAQ,OAAO,SAAS;AAC1B;AAAA,MACF;AACA,WAAK,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,oBAAoB,aAA+C;AACjE,SAAK,oBAAoB,UAAU,WAAW;AAAA,EAChD;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/llm/realtime.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport { EventEmitter } from 'events';\nimport type { ReadableStream } from 'node:stream/web';\nimport { DeferredReadableStream } from '../stream/deferred_stream.js';\nimport { Task } from '../utils.js';\nimport type { TimedString } from '../voice/io.js';\nimport type { ChatContext, FunctionCall } from './chat_context.js';\nimport type { ToolChoice, ToolContext } from './tool_context.js';\n\nexport type InputSpeechStartedEvent = object;\n\nexport interface InputSpeechStoppedEvent {\n userTranscriptionEnabled: boolean;\n}\n\nexport interface MessageGeneration {\n messageId: string;\n /**\n * Text stream that may contain plain strings or TimedString objects with timestamps.\n */\n textStream: ReadableStream<string | TimedString>;\n audioStream: ReadableStream<AudioFrame>;\n modalities?: Promise<('text' | 'audio')[]>;\n}\n\nexport interface GenerationCreatedEvent {\n messageStream: ReadableStream<MessageGeneration>;\n functionStream: ReadableStream<FunctionCall>;\n userInitiated: boolean;\n /** Response ID for correlating metrics with spans */\n responseId?: string;\n}\n\nexport interface RealtimeModelError {\n type: 'realtime_model_error';\n timestamp: number;\n label: string;\n error: Error;\n recoverable: boolean;\n}\n\nexport interface RealtimeCapabilities {\n messageTruncation: boolean;\n turnDetection: boolean;\n userTranscription: boolean;\n autoToolReplyGeneration: boolean;\n audioOutput: boolean;\n manualFunctionCalls: boolean;\n}\n\nexport interface InputTranscriptionCompleted {\n itemId: string;\n transcript: string;\n isFinal: boolean;\n}\n\nexport interface RealtimeSessionReconnectedEvent {}\n\nexport abstract class RealtimeModel {\n private _capabilities: RealtimeCapabilities;\n\n constructor(capabilities: RealtimeCapabilities) {\n this._capabilities = capabilities;\n }\n\n get capabilities() {\n return this._capabilities;\n }\n\n /** The model name/identifier used by this realtime model */\n abstract get model(): string;\n\n abstract session(): RealtimeSession;\n\n abstract close(): Promise<void>;\n}\n\nexport abstract class RealtimeSession extends EventEmitter {\n protected _realtimeModel: RealtimeModel;\n private deferredInputStream = new DeferredReadableStream<AudioFrame>();\n private _mainTask: Task<void>;\n\n constructor(realtimeModel: RealtimeModel) {\n super();\n this._realtimeModel = realtimeModel;\n this._mainTask = Task.from((controller) => this._mainTaskImpl(controller.signal));\n }\n\n get realtimeModel() {\n return this._realtimeModel;\n }\n\n abstract get chatCtx(): ChatContext;\n\n abstract get tools(): ToolContext;\n\n abstract updateInstructions(instructions: string): Promise<void>;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract updateChatCtx(chatCtx: ChatContext): Promise<void>;\n\n abstract updateTools(tools: ToolContext): Promise<void>;\n\n abstract updateOptions(options: { toolChoice?: ToolChoice | null }): void;\n\n abstract pushAudio(frame: AudioFrame): void;\n\n /**\n * @throws RealtimeError on Timeout\n */\n abstract generateReply(instructions?: string): Promise<GenerationCreatedEvent>;\n\n /**\n * Commit the input audio buffer to the server\n */\n abstract commitAudio(): Promise<void>;\n\n /**\n * Clear the input audio buffer to the server\n */\n abstract clearAudio(): Promise<void>;\n\n /**\n * Cancel the current generation (do nothing if no generation is in progress)\n */\n abstract interrupt(): Promise<void>;\n\n /**\n * Truncate the message at the given audio end time\n */\n abstract truncate(options: {\n messageId: string;\n audioEndMs: number;\n modalities?: ('text' | 'audio')[];\n audioTranscript?: string;\n }): Promise<void>;\n\n async close(): Promise<void> {\n this._mainTask.cancel();\n }\n\n /**\n * Notifies the model that user activity has started\n */\n startUserActivity(): void {\n return;\n }\n\n private async _mainTaskImpl(signal: AbortSignal): Promise<void> {\n const reader = this.deferredInputStream.stream.getReader();\n while (true) {\n const { done, value } = await reader.read();\n if (done || signal.aborted) {\n break;\n }\n this.pushAudio(value);\n }\n }\n\n setInputAudioStream(audioStream: ReadableStream<AudioFrame>): void {\n this.deferredInputStream.setSource(audioStream);\n }\n}\n"],"mappings":"AAIA,SAAS,oBAAoB;AAE7B,SAAS,8BAA8B;AACvC,SAAS,YAAY;AAsDd,MAAe,cAAc;AAAA,EAC1B;AAAA,EAER,YAAY,cAAoC;AAC9C,SAAK,gBAAgB;AAAA,EACvB;AAAA,EAEA,IAAI,eAAe;AACjB,WAAO,KAAK;AAAA,EACd;AAQF;AAEO,MAAe,wBAAwB,aAAa;AAAA,EAC/C;AAAA,EACF,sBAAsB,IAAI,uBAAmC;AAAA,EAC7D;AAAA,EAER,YAAY,eAA8B;AACxC,UAAM;AACN,SAAK,iBAAiB;AACtB,SAAK,YAAY,KAAK,KAAK,CAAC,eAAe,KAAK,cAAc,WAAW,MAAM,CAAC;AAAA,EAClF;AAAA,EAEA,IAAI,gBAAgB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA,EAiDA,MAAM,QAAuB;AAC3B,SAAK,UAAU,OAAO;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKA,oBAA0B;AACxB;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,QAAoC;AAC9D,UAAM,SAAS,KAAK,oBAAoB,OAAO,UAAU;AACzD,WAAO,MAAM;AACX,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,UAAI,QAAQ,OAAO,SAAS;AAC1B;AAAA,MACF;AACA,WAAK,UAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,oBAAoB,aAA+C;AACjE,SAAK,oBAAoB,UAAU,WAAW;AAAA,EAChD;AACF;","names":[]}
package/dist/log.cjs CHANGED
@@ -43,7 +43,7 @@ const log = () => {
43
43
  const initializeLogger = ({ pretty, level }) => {
44
44
  globals[LOGGER_OPTIONS_KEY] = { pretty, level };
45
45
  globals[LOGGER_KEY] = (0, import_pino.pino)(
46
- { level: level || "info" },
46
+ { level: level || "info", serializers: { error: import_pino.pino.stdSerializers.err } },
47
47
  pretty ? (0, import_pino_pretty.build)({ colorize: true }) : process.stdout
48
48
  );
49
49
  };
@@ -73,7 +73,10 @@ const enableOtelLogging = () => {
73
73
  { stream: pretty ? (0, import_pino_pretty.build)({ colorize: true }) : process.stdout, level: logLevel },
74
74
  { stream: new OtelDestination(), level: "debug" }
75
75
  ];
76
- globals[LOGGER_KEY] = (0, import_pino.pino)({ level: logLevel }, (0, import_pino.multistream)(streams));
76
+ globals[LOGGER_KEY] = (0, import_pino.pino)(
77
+ { level: logLevel, serializers: { error: import_pino.pino.stdSerializers.err } },
78
+ (0, import_pino.multistream)(streams)
79
+ );
77
80
  };
78
81
  // Annotate the CommonJS export names for ESM import in node:
79
82
  0 && (module.exports = {
package/dist/log.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Writable } from 'node:stream';\nimport type { DestinationStream, Logger } from 'pino';\nimport { multistream, pino } from 'pino';\nimport { build as pinoPretty } from 'pino-pretty';\nimport { type PinoLogObject, emitToOtel } from './telemetry/pino_otel_transport.js';\n\n/** @internal */\nexport type LoggerOptions = {\n pretty: boolean;\n level?: string;\n};\n\n// Use Symbol.for() + globalThis to create process-wide singletons.\n// This avoids the \"dual package hazard\". Symbol.for() returns the same Symbol\n// across all module instances, and globalThis is shared process-wide.\nconst LOGGER_KEY = Symbol.for('@livekit/agents:logger');\nconst LOGGER_OPTIONS_KEY = Symbol.for('@livekit/agents:loggerOptions');\nconst OTEL_ENABLED_KEY = Symbol.for('@livekit/agents:otelEnabled');\n\ntype GlobalState = {\n [LOGGER_KEY]?: Logger;\n [LOGGER_OPTIONS_KEY]?: LoggerOptions;\n [OTEL_ENABLED_KEY]?: boolean;\n};\n\nconst globals = globalThis as typeof globalThis & GlobalState;\n\n/** @internal */\nexport const loggerOptions = (): LoggerOptions | undefined => globals[LOGGER_OPTIONS_KEY];\n\n/** @internal */\nexport const log = () => {\n const logger = globals[LOGGER_KEY];\n if (!logger) {\n throw new TypeError('logger not initialized. did you forget to run initializeLogger()?');\n }\n return logger;\n};\n\n/** @internal */\nexport const initializeLogger = ({ pretty, level }: LoggerOptions) => {\n globals[LOGGER_OPTIONS_KEY] = { pretty, level };\n globals[LOGGER_KEY] = pino(\n { level: level || 'info' },\n pretty ? pinoPretty({ colorize: true }) : process.stdout,\n );\n};\n\n/**\n * Custom Pino destination that parses JSON logs and emits to OTEL.\n * This receives the FULL serialized log including msg, level, time, etc.\n */\nclass OtelDestination extends Writable {\n _write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const logObj = JSON.parse(line) as PinoLogObject;\n emitToOtel(logObj);\n }\n } catch {\n // Ignore parse errors (e.g., non-JSON lines)\n }\n callback();\n }\n}\n\n/**\n * Enable OTEL logging by reconfiguring the logger with multistream.\n * Uses a custom destination that receives full JSON logs (with msg, level, time).\n *\n * @internal\n */\nexport const enableOtelLogging = () => {\n if (globals[OTEL_ENABLED_KEY] || !globals[LOGGER_KEY]) {\n console.warn('OTEL logging already enabled or logger not initialized');\n return;\n }\n globals[OTEL_ENABLED_KEY] = true;\n\n const opts = globals[LOGGER_OPTIONS_KEY]!;\n const { pretty, level } = opts;\n\n const logLevel = level || 'info';\n const streams: { stream: DestinationStream; level: string }[] = [\n { stream: pretty ? pinoPretty({ colorize: true }) : process.stdout, level: logLevel },\n { stream: new OtelDestination(), level: 'debug' },\n ];\n\n globals[LOGGER_KEY] = pino({ level: logLevel }, multistream(streams));\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,yBAAyB;AAEzB,kBAAkC;AAClC,yBAAoC;AACpC,iCAA+C;AAW/C,MAAM,aAAa,OAAO,IAAI,wBAAwB;AACtD,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;AACrE,MAAM,mBAAmB,OAAO,IAAI,6BAA6B;AAQjE,MAAM,UAAU;AAGT,MAAM,gBAAgB,MAAiC,QAAQ,kBAAkB;AAGjF,MAAM,MAAM,MAAM;AACvB,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,mEAAmE;AAAA,EACzF;AACA,SAAO;AACT;AAGO,MAAM,mBAAmB,CAAC,EAAE,QAAQ,MAAM,MAAqB;AACpE,UAAQ,kBAAkB,IAAI,EAAE,QAAQ,MAAM;AAC9C,UAAQ,UAAU,QAAI;AAAA,IACpB,EAAE,OAAO,SAAS,OAAO;AAAA,IACzB,aAAS,mBAAAA,OAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ;AAAA,EACpD;AACF;AAMA,MAAM,wBAAwB,4BAAS;AAAA,EACrC,OAAO,OAAe,WAAmB,UAAgD;AACvF,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,EAAE,KAAK;AACnC,UAAI,MAAM;AACR,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,mDAAW,MAAM;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,EACX;AACF;AAQO,MAAM,oBAAoB,MAAM;AACrC,MAAI,QAAQ,gBAAgB,KAAK,CAAC,QAAQ,UAAU,GAAG;AACrD,YAAQ,KAAK,wDAAwD;AACrE;AAAA,EACF;AACA,UAAQ,gBAAgB,IAAI;AAE5B,QAAM,OAAO,QAAQ,kBAAkB;AACvC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAE1B,QAAM,WAAW,SAAS;AAC1B,QAAM,UAA0D;AAAA,IAC9D,EAAE,QAAQ,aAAS,mBAAAA,OAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ,QAAQ,OAAO,SAAS;AAAA,IACpF,EAAE,QAAQ,IAAI,gBAAgB,GAAG,OAAO,QAAQ;AAAA,EAClD;AAEA,UAAQ,UAAU,QAAI,kBAAK,EAAE,OAAO,SAAS,OAAG,yBAAY,OAAO,CAAC;AACtE;","names":["pinoPretty"]}
1
+ {"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Writable } from 'node:stream';\nimport type { DestinationStream, Logger } from 'pino';\nimport { multistream, pino } from 'pino';\nimport { build as pinoPretty } from 'pino-pretty';\nimport { type PinoLogObject, emitToOtel } from './telemetry/pino_otel_transport.js';\n\n/** @internal */\nexport type LoggerOptions = {\n pretty: boolean;\n level?: string;\n};\n\n// Use Symbol.for() + globalThis to create process-wide singletons.\n// This avoids the \"dual package hazard\". Symbol.for() returns the same Symbol\n// across all module instances, and globalThis is shared process-wide.\nconst LOGGER_KEY = Symbol.for('@livekit/agents:logger');\nconst LOGGER_OPTIONS_KEY = Symbol.for('@livekit/agents:loggerOptions');\nconst OTEL_ENABLED_KEY = Symbol.for('@livekit/agents:otelEnabled');\n\ntype GlobalState = {\n [LOGGER_KEY]?: Logger;\n [LOGGER_OPTIONS_KEY]?: LoggerOptions;\n [OTEL_ENABLED_KEY]?: boolean;\n};\n\nconst globals = globalThis as typeof globalThis & GlobalState;\n\n/** @internal */\nexport const loggerOptions = (): LoggerOptions | undefined => globals[LOGGER_OPTIONS_KEY];\n\n/** @internal */\nexport const log = () => {\n const logger = globals[LOGGER_KEY];\n if (!logger) {\n throw new TypeError('logger not initialized. did you forget to run initializeLogger()?');\n }\n return logger;\n};\n\n/** @internal */\nexport const initializeLogger = ({ pretty, level }: LoggerOptions) => {\n globals[LOGGER_OPTIONS_KEY] = { pretty, level };\n globals[LOGGER_KEY] = pino(\n { level: level || 'info', serializers: { error: pino.stdSerializers.err } },\n pretty ? pinoPretty({ colorize: true }) : process.stdout,\n );\n};\n\n/**\n * Custom Pino destination that parses JSON logs and emits to OTEL.\n * This receives the FULL serialized log including msg, level, time, etc.\n */\nclass OtelDestination extends Writable {\n _write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const logObj = JSON.parse(line) as PinoLogObject;\n emitToOtel(logObj);\n }\n } catch {\n // Ignore parse errors (e.g., non-JSON lines)\n }\n callback();\n }\n}\n\n/**\n * Enable OTEL logging by reconfiguring the logger with multistream.\n * Uses a custom destination that receives full JSON logs (with msg, level, time).\n *\n * @internal\n */\nexport const enableOtelLogging = () => {\n if (globals[OTEL_ENABLED_KEY] || !globals[LOGGER_KEY]) {\n console.warn('OTEL logging already enabled or logger not initialized');\n return;\n }\n globals[OTEL_ENABLED_KEY] = true;\n\n const opts = globals[LOGGER_OPTIONS_KEY]!;\n const { pretty, level } = opts;\n\n const logLevel = level || 'info';\n const streams: { stream: DestinationStream; level: string }[] = [\n { stream: pretty ? pinoPretty({ colorize: true }) : process.stdout, level: logLevel },\n { stream: new OtelDestination(), level: 'debug' },\n ];\n\n globals[LOGGER_KEY] = pino(\n { level: logLevel, serializers: { error: pino.stdSerializers.err } },\n multistream(streams),\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,yBAAyB;AAEzB,kBAAkC;AAClC,yBAAoC;AACpC,iCAA+C;AAW/C,MAAM,aAAa,OAAO,IAAI,wBAAwB;AACtD,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;AACrE,MAAM,mBAAmB,OAAO,IAAI,6BAA6B;AAQjE,MAAM,UAAU;AAGT,MAAM,gBAAgB,MAAiC,QAAQ,kBAAkB;AAGjF,MAAM,MAAM,MAAM;AACvB,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,mEAAmE;AAAA,EACzF;AACA,SAAO;AACT;AAGO,MAAM,mBAAmB,CAAC,EAAE,QAAQ,MAAM,MAAqB;AACpE,UAAQ,kBAAkB,IAAI,EAAE,QAAQ,MAAM;AAC9C,UAAQ,UAAU,QAAI;AAAA,IACpB,EAAE,OAAO,SAAS,QAAQ,aAAa,EAAE,OAAO,iBAAK,eAAe,IAAI,EAAE;AAAA,IAC1E,aAAS,mBAAAA,OAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ;AAAA,EACpD;AACF;AAMA,MAAM,wBAAwB,4BAAS;AAAA,EACrC,OAAO,OAAe,WAAmB,UAAgD;AACvF,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,EAAE,KAAK;AACnC,UAAI,MAAM;AACR,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,mDAAW,MAAM;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,EACX;AACF;AAQO,MAAM,oBAAoB,MAAM;AACrC,MAAI,QAAQ,gBAAgB,KAAK,CAAC,QAAQ,UAAU,GAAG;AACrD,YAAQ,KAAK,wDAAwD;AACrE;AAAA,EACF;AACA,UAAQ,gBAAgB,IAAI;AAE5B,QAAM,OAAO,QAAQ,kBAAkB;AACvC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAE1B,QAAM,WAAW,SAAS;AAC1B,QAAM,UAA0D;AAAA,IAC9D,EAAE,QAAQ,aAAS,mBAAAA,OAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ,QAAQ,OAAO,SAAS;AAAA,IACpF,EAAE,QAAQ,IAAI,gBAAgB,GAAG,OAAO,QAAQ;AAAA,EAClD;AAEA,UAAQ,UAAU,QAAI;AAAA,IACpB,EAAE,OAAO,UAAU,aAAa,EAAE,OAAO,iBAAK,eAAe,IAAI,EAAE;AAAA,QACnE,yBAAY,OAAO;AAAA,EACrB;AACF;","names":["pinoPretty"]}
package/dist/log.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAqB,MAAM,EAAE,MAAM,MAAM,CAAC;AAKtD,gBAAgB;AAChB,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiBF,gBAAgB;AAChB,eAAO,MAAM,aAAa,QAAO,aAAa,GAAG,SAAwC,CAAC;AAE1F,gBAAgB;AAChB,eAAO,MAAM,GAAG,cAMf,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,sBAAuB,aAAa,SAMhE,CAAC;AAqBF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,YAiB7B,CAAC"}
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAqB,MAAM,EAAE,MAAM,MAAM,CAAC;AAKtD,gBAAgB;AAChB,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAiBF,gBAAgB;AAChB,eAAO,MAAM,aAAa,QAAO,aAAa,GAAG,SAAwC,CAAC;AAE1F,gBAAgB;AAChB,eAAO,MAAM,GAAG,cAMf,CAAC;AAEF,gBAAgB;AAChB,eAAO,MAAM,gBAAgB,sBAAuB,aAAa,SAMhE,CAAC;AAqBF;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,YAoB7B,CAAC"}
package/dist/log.js CHANGED
@@ -17,7 +17,7 @@ const log = () => {
17
17
  const initializeLogger = ({ pretty, level }) => {
18
18
  globals[LOGGER_OPTIONS_KEY] = { pretty, level };
19
19
  globals[LOGGER_KEY] = pino(
20
- { level: level || "info" },
20
+ { level: level || "info", serializers: { error: pino.stdSerializers.err } },
21
21
  pretty ? pinoPretty({ colorize: true }) : process.stdout
22
22
  );
23
23
  };
@@ -47,7 +47,10 @@ const enableOtelLogging = () => {
47
47
  { stream: pretty ? pinoPretty({ colorize: true }) : process.stdout, level: logLevel },
48
48
  { stream: new OtelDestination(), level: "debug" }
49
49
  ];
50
- globals[LOGGER_KEY] = pino({ level: logLevel }, multistream(streams));
50
+ globals[LOGGER_KEY] = pino(
51
+ { level: logLevel, serializers: { error: pino.stdSerializers.err } },
52
+ multistream(streams)
53
+ );
51
54
  };
52
55
  export {
53
56
  enableOtelLogging,
package/dist/log.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Writable } from 'node:stream';\nimport type { DestinationStream, Logger } from 'pino';\nimport { multistream, pino } from 'pino';\nimport { build as pinoPretty } from 'pino-pretty';\nimport { type PinoLogObject, emitToOtel } from './telemetry/pino_otel_transport.js';\n\n/** @internal */\nexport type LoggerOptions = {\n pretty: boolean;\n level?: string;\n};\n\n// Use Symbol.for() + globalThis to create process-wide singletons.\n// This avoids the \"dual package hazard\". Symbol.for() returns the same Symbol\n// across all module instances, and globalThis is shared process-wide.\nconst LOGGER_KEY = Symbol.for('@livekit/agents:logger');\nconst LOGGER_OPTIONS_KEY = Symbol.for('@livekit/agents:loggerOptions');\nconst OTEL_ENABLED_KEY = Symbol.for('@livekit/agents:otelEnabled');\n\ntype GlobalState = {\n [LOGGER_KEY]?: Logger;\n [LOGGER_OPTIONS_KEY]?: LoggerOptions;\n [OTEL_ENABLED_KEY]?: boolean;\n};\n\nconst globals = globalThis as typeof globalThis & GlobalState;\n\n/** @internal */\nexport const loggerOptions = (): LoggerOptions | undefined => globals[LOGGER_OPTIONS_KEY];\n\n/** @internal */\nexport const log = () => {\n const logger = globals[LOGGER_KEY];\n if (!logger) {\n throw new TypeError('logger not initialized. did you forget to run initializeLogger()?');\n }\n return logger;\n};\n\n/** @internal */\nexport const initializeLogger = ({ pretty, level }: LoggerOptions) => {\n globals[LOGGER_OPTIONS_KEY] = { pretty, level };\n globals[LOGGER_KEY] = pino(\n { level: level || 'info' },\n pretty ? pinoPretty({ colorize: true }) : process.stdout,\n );\n};\n\n/**\n * Custom Pino destination that parses JSON logs and emits to OTEL.\n * This receives the FULL serialized log including msg, level, time, etc.\n */\nclass OtelDestination extends Writable {\n _write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const logObj = JSON.parse(line) as PinoLogObject;\n emitToOtel(logObj);\n }\n } catch {\n // Ignore parse errors (e.g., non-JSON lines)\n }\n callback();\n }\n}\n\n/**\n * Enable OTEL logging by reconfiguring the logger with multistream.\n * Uses a custom destination that receives full JSON logs (with msg, level, time).\n *\n * @internal\n */\nexport const enableOtelLogging = () => {\n if (globals[OTEL_ENABLED_KEY] || !globals[LOGGER_KEY]) {\n console.warn('OTEL logging already enabled or logger not initialized');\n return;\n }\n globals[OTEL_ENABLED_KEY] = true;\n\n const opts = globals[LOGGER_OPTIONS_KEY]!;\n const { pretty, level } = opts;\n\n const logLevel = level || 'info';\n const streams: { stream: DestinationStream; level: string }[] = [\n { stream: pretty ? pinoPretty({ colorize: true }) : process.stdout, level: logLevel },\n { stream: new OtelDestination(), level: 'debug' },\n ];\n\n globals[LOGGER_KEY] = pino({ level: logLevel }, multistream(streams));\n};\n"],"mappings":"AAGA,SAAS,gBAAgB;AAEzB,SAAS,aAAa,YAAY;AAClC,SAAS,SAAS,kBAAkB;AACpC,SAA6B,kBAAkB;AAW/C,MAAM,aAAa,OAAO,IAAI,wBAAwB;AACtD,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;AACrE,MAAM,mBAAmB,OAAO,IAAI,6BAA6B;AAQjE,MAAM,UAAU;AAGT,MAAM,gBAAgB,MAAiC,QAAQ,kBAAkB;AAGjF,MAAM,MAAM,MAAM;AACvB,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,mEAAmE;AAAA,EACzF;AACA,SAAO;AACT;AAGO,MAAM,mBAAmB,CAAC,EAAE,QAAQ,MAAM,MAAqB;AACpE,UAAQ,kBAAkB,IAAI,EAAE,QAAQ,MAAM;AAC9C,UAAQ,UAAU,IAAI;AAAA,IACpB,EAAE,OAAO,SAAS,OAAO;AAAA,IACzB,SAAS,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ;AAAA,EACpD;AACF;AAMA,MAAM,wBAAwB,SAAS;AAAA,EACrC,OAAO,OAAe,WAAmB,UAAgD;AACvF,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,EAAE,KAAK;AACnC,UAAI,MAAM;AACR,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,EACX;AACF;AAQO,MAAM,oBAAoB,MAAM;AACrC,MAAI,QAAQ,gBAAgB,KAAK,CAAC,QAAQ,UAAU,GAAG;AACrD,YAAQ,KAAK,wDAAwD;AACrE;AAAA,EACF;AACA,UAAQ,gBAAgB,IAAI;AAE5B,QAAM,OAAO,QAAQ,kBAAkB;AACvC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAE1B,QAAM,WAAW,SAAS;AAC1B,QAAM,UAA0D;AAAA,IAC9D,EAAE,QAAQ,SAAS,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ,QAAQ,OAAO,SAAS;AAAA,IACpF,EAAE,QAAQ,IAAI,gBAAgB,GAAG,OAAO,QAAQ;AAAA,EAClD;AAEA,UAAQ,UAAU,IAAI,KAAK,EAAE,OAAO,SAAS,GAAG,YAAY,OAAO,CAAC;AACtE;","names":[]}
1
+ {"version":3,"sources":["../src/log.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { Writable } from 'node:stream';\nimport type { DestinationStream, Logger } from 'pino';\nimport { multistream, pino } from 'pino';\nimport { build as pinoPretty } from 'pino-pretty';\nimport { type PinoLogObject, emitToOtel } from './telemetry/pino_otel_transport.js';\n\n/** @internal */\nexport type LoggerOptions = {\n pretty: boolean;\n level?: string;\n};\n\n// Use Symbol.for() + globalThis to create process-wide singletons.\n// This avoids the \"dual package hazard\". Symbol.for() returns the same Symbol\n// across all module instances, and globalThis is shared process-wide.\nconst LOGGER_KEY = Symbol.for('@livekit/agents:logger');\nconst LOGGER_OPTIONS_KEY = Symbol.for('@livekit/agents:loggerOptions');\nconst OTEL_ENABLED_KEY = Symbol.for('@livekit/agents:otelEnabled');\n\ntype GlobalState = {\n [LOGGER_KEY]?: Logger;\n [LOGGER_OPTIONS_KEY]?: LoggerOptions;\n [OTEL_ENABLED_KEY]?: boolean;\n};\n\nconst globals = globalThis as typeof globalThis & GlobalState;\n\n/** @internal */\nexport const loggerOptions = (): LoggerOptions | undefined => globals[LOGGER_OPTIONS_KEY];\n\n/** @internal */\nexport const log = () => {\n const logger = globals[LOGGER_KEY];\n if (!logger) {\n throw new TypeError('logger not initialized. did you forget to run initializeLogger()?');\n }\n return logger;\n};\n\n/** @internal */\nexport const initializeLogger = ({ pretty, level }: LoggerOptions) => {\n globals[LOGGER_OPTIONS_KEY] = { pretty, level };\n globals[LOGGER_KEY] = pino(\n { level: level || 'info', serializers: { error: pino.stdSerializers.err } },\n pretty ? pinoPretty({ colorize: true }) : process.stdout,\n );\n};\n\n/**\n * Custom Pino destination that parses JSON logs and emits to OTEL.\n * This receives the FULL serialized log including msg, level, time, etc.\n */\nclass OtelDestination extends Writable {\n _write(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void): void {\n try {\n const line = chunk.toString().trim();\n if (line) {\n const logObj = JSON.parse(line) as PinoLogObject;\n emitToOtel(logObj);\n }\n } catch {\n // Ignore parse errors (e.g., non-JSON lines)\n }\n callback();\n }\n}\n\n/**\n * Enable OTEL logging by reconfiguring the logger with multistream.\n * Uses a custom destination that receives full JSON logs (with msg, level, time).\n *\n * @internal\n */\nexport const enableOtelLogging = () => {\n if (globals[OTEL_ENABLED_KEY] || !globals[LOGGER_KEY]) {\n console.warn('OTEL logging already enabled or logger not initialized');\n return;\n }\n globals[OTEL_ENABLED_KEY] = true;\n\n const opts = globals[LOGGER_OPTIONS_KEY]!;\n const { pretty, level } = opts;\n\n const logLevel = level || 'info';\n const streams: { stream: DestinationStream; level: string }[] = [\n { stream: pretty ? pinoPretty({ colorize: true }) : process.stdout, level: logLevel },\n { stream: new OtelDestination(), level: 'debug' },\n ];\n\n globals[LOGGER_KEY] = pino(\n { level: logLevel, serializers: { error: pino.stdSerializers.err } },\n multistream(streams),\n );\n};\n"],"mappings":"AAGA,SAAS,gBAAgB;AAEzB,SAAS,aAAa,YAAY;AAClC,SAAS,SAAS,kBAAkB;AACpC,SAA6B,kBAAkB;AAW/C,MAAM,aAAa,OAAO,IAAI,wBAAwB;AACtD,MAAM,qBAAqB,OAAO,IAAI,+BAA+B;AACrE,MAAM,mBAAmB,OAAO,IAAI,6BAA6B;AAQjE,MAAM,UAAU;AAGT,MAAM,gBAAgB,MAAiC,QAAQ,kBAAkB;AAGjF,MAAM,MAAM,MAAM;AACvB,QAAM,SAAS,QAAQ,UAAU;AACjC,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,UAAU,mEAAmE;AAAA,EACzF;AACA,SAAO;AACT;AAGO,MAAM,mBAAmB,CAAC,EAAE,QAAQ,MAAM,MAAqB;AACpE,UAAQ,kBAAkB,IAAI,EAAE,QAAQ,MAAM;AAC9C,UAAQ,UAAU,IAAI;AAAA,IACpB,EAAE,OAAO,SAAS,QAAQ,aAAa,EAAE,OAAO,KAAK,eAAe,IAAI,EAAE;AAAA,IAC1E,SAAS,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ;AAAA,EACpD;AACF;AAMA,MAAM,wBAAwB,SAAS;AAAA,EACrC,OAAO,OAAe,WAAmB,UAAgD;AACvF,QAAI;AACF,YAAM,OAAO,MAAM,SAAS,EAAE,KAAK;AACnC,UAAI,MAAM;AACR,cAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,aAAS;AAAA,EACX;AACF;AAQO,MAAM,oBAAoB,MAAM;AACrC,MAAI,QAAQ,gBAAgB,KAAK,CAAC,QAAQ,UAAU,GAAG;AACrD,YAAQ,KAAK,wDAAwD;AACrE;AAAA,EACF;AACA,UAAQ,gBAAgB,IAAI;AAE5B,QAAM,OAAO,QAAQ,kBAAkB;AACvC,QAAM,EAAE,QAAQ,MAAM,IAAI;AAE1B,QAAM,WAAW,SAAS;AAC1B,QAAM,UAA0D;AAAA,IAC9D,EAAE,QAAQ,SAAS,WAAW,EAAE,UAAU,KAAK,CAAC,IAAI,QAAQ,QAAQ,OAAO,SAAS;AAAA,IACpF,EAAE,QAAQ,IAAI,gBAAgB,GAAG,OAAO,QAAQ;AAAA,EAClD;AAEA,UAAQ,UAAU,IAAI;AAAA,IACpB,EAAE,OAAO,UAAU,aAAa,EAAE,OAAO,KAAK,eAAe,IAAI,EAAE;AAAA,IACnE,YAAY,OAAO;AAAA,EACrB;AACF;","names":[]}
@@ -56,14 +56,15 @@ class DeferredReadableStream {
56
56
  if (this.isSourceSet) {
57
57
  throw new Error("Stream source already set");
58
58
  }
59
- this.sourceReader = source.getReader();
60
- this.pump();
59
+ const sourceReader = source.getReader();
60
+ this.sourceReader = sourceReader;
61
+ void this.pump(sourceReader);
61
62
  }
62
- async pump() {
63
+ async pump(sourceReader) {
63
64
  let sourceError;
64
65
  try {
65
66
  while (true) {
66
- const { done, value } = await this.sourceReader.read();
67
+ const { done, value } = await sourceReader.read();
67
68
  if (done) break;
68
69
  await this.writer.write(value);
69
70
  }
@@ -73,7 +74,7 @@ class DeferredReadableStream {
73
74
  } finally {
74
75
  if (sourceError) {
75
76
  try {
76
- this.writer.abort(sourceError);
77
+ await this.writer.abort(sourceError);
77
78
  } catch (e) {
78
79
  }
79
80
  return;
@@ -95,7 +96,15 @@ class DeferredReadableStream {
95
96
  if (!this.isSourceSet) {
96
97
  return;
97
98
  }
98
- this.sourceReader.releaseLock();
99
+ const sourceReader = this.sourceReader;
100
+ this.sourceReader = void 0;
101
+ try {
102
+ sourceReader.releaseLock();
103
+ } catch (e) {
104
+ if (!isStreamReaderReleaseError(e)) {
105
+ throw e;
106
+ }
107
+ }
99
108
  }
100
109
  }
101
110
  // Annotate the CommonJS export names for ESM import in node: