@livekit/agents 1.1.0 → 1.2.0

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 (942) hide show
  1. package/dist/audio.cjs +89 -3
  2. package/dist/audio.cjs.map +1 -1
  3. package/dist/audio.d.cts +36 -1
  4. package/dist/audio.d.ts +36 -1
  5. package/dist/audio.d.ts.map +1 -1
  6. package/dist/audio.js +76 -2
  7. package/dist/audio.js.map +1 -1
  8. package/dist/beta/index.cjs +29 -0
  9. package/dist/beta/index.cjs.map +1 -0
  10. package/dist/beta/index.d.cts +2 -0
  11. package/dist/beta/index.d.ts +2 -0
  12. package/dist/beta/index.d.ts.map +1 -0
  13. package/dist/beta/index.js +7 -0
  14. package/dist/beta/index.js.map +1 -0
  15. package/dist/beta/workflows/index.cjs +29 -0
  16. package/dist/beta/workflows/index.cjs.map +1 -0
  17. package/dist/beta/workflows/index.d.cts +2 -0
  18. package/dist/beta/workflows/index.d.ts +2 -0
  19. package/dist/beta/workflows/index.d.ts.map +1 -0
  20. package/dist/beta/workflows/index.js +7 -0
  21. package/dist/beta/workflows/index.js.map +1 -0
  22. package/dist/beta/workflows/task_group.cjs +162 -0
  23. package/dist/beta/workflows/task_group.cjs.map +1 -0
  24. package/dist/beta/workflows/task_group.d.cts +32 -0
  25. package/dist/beta/workflows/task_group.d.ts +32 -0
  26. package/dist/beta/workflows/task_group.d.ts.map +1 -0
  27. package/dist/beta/workflows/task_group.js +138 -0
  28. package/dist/beta/workflows/task_group.js.map +1 -0
  29. package/dist/cli.cjs +44 -46
  30. package/dist/cli.cjs.map +1 -1
  31. package/dist/cli.d.cts +3 -3
  32. package/dist/cli.d.ts +3 -3
  33. package/dist/cli.d.ts.map +1 -1
  34. package/dist/cli.js +45 -47
  35. package/dist/cli.js.map +1 -1
  36. package/dist/connection_pool.cjs +242 -0
  37. package/dist/connection_pool.cjs.map +1 -0
  38. package/dist/connection_pool.d.cts +123 -0
  39. package/dist/connection_pool.d.ts +123 -0
  40. package/dist/connection_pool.d.ts.map +1 -0
  41. package/dist/connection_pool.js +218 -0
  42. package/dist/connection_pool.js.map +1 -0
  43. package/dist/connection_pool.test.cjs +256 -0
  44. package/dist/connection_pool.test.cjs.map +1 -0
  45. package/dist/connection_pool.test.js +255 -0
  46. package/dist/connection_pool.test.js.map +1 -0
  47. package/dist/constants.cjs +30 -0
  48. package/dist/constants.cjs.map +1 -1
  49. package/dist/constants.d.cts +10 -0
  50. package/dist/constants.d.ts +10 -0
  51. package/dist/constants.d.ts.map +1 -1
  52. package/dist/constants.js +20 -0
  53. package/dist/constants.js.map +1 -1
  54. package/dist/cpu.cjs +189 -0
  55. package/dist/cpu.cjs.map +1 -0
  56. package/dist/cpu.d.cts +24 -0
  57. package/dist/cpu.d.ts +24 -0
  58. package/dist/cpu.d.ts.map +1 -0
  59. package/dist/cpu.js +152 -0
  60. package/dist/cpu.js.map +1 -0
  61. package/dist/cpu.test.cjs +227 -0
  62. package/dist/cpu.test.cjs.map +1 -0
  63. package/dist/cpu.test.js +204 -0
  64. package/dist/cpu.test.js.map +1 -0
  65. package/dist/http_server.cjs +9 -6
  66. package/dist/http_server.cjs.map +1 -1
  67. package/dist/http_server.d.cts +5 -1
  68. package/dist/http_server.d.ts +5 -1
  69. package/dist/http_server.d.ts.map +1 -1
  70. package/dist/http_server.js +9 -6
  71. package/dist/http_server.js.map +1 -1
  72. package/dist/index.cjs +24 -9
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +15 -11
  75. package/dist/index.d.ts +15 -11
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +18 -9
  78. package/dist/index.js.map +1 -1
  79. package/dist/inference/api_protos.cjs +70 -2
  80. package/dist/inference/api_protos.cjs.map +1 -1
  81. package/dist/inference/api_protos.d.cts +373 -32
  82. package/dist/inference/api_protos.d.ts +373 -32
  83. package/dist/inference/api_protos.d.ts.map +1 -1
  84. package/dist/inference/api_protos.js +62 -2
  85. package/dist/inference/api_protos.js.map +1 -1
  86. package/dist/inference/index.cjs +8 -0
  87. package/dist/inference/index.cjs.map +1 -1
  88. package/dist/inference/index.d.cts +3 -4
  89. package/dist/inference/index.d.ts +3 -4
  90. package/dist/inference/index.d.ts.map +1 -1
  91. package/dist/inference/index.js +18 -3
  92. package/dist/inference/index.js.map +1 -1
  93. package/dist/inference/interruption/defaults.cjs +81 -0
  94. package/dist/inference/interruption/defaults.cjs.map +1 -0
  95. package/dist/inference/interruption/defaults.d.cts +19 -0
  96. package/dist/inference/interruption/defaults.d.ts +19 -0
  97. package/dist/inference/interruption/defaults.d.ts.map +1 -0
  98. package/dist/inference/interruption/defaults.js +46 -0
  99. package/dist/inference/interruption/defaults.js.map +1 -0
  100. package/dist/inference/interruption/errors.cjs +44 -0
  101. package/dist/inference/interruption/errors.cjs.map +1 -0
  102. package/dist/inference/interruption/errors.d.cts +12 -0
  103. package/dist/inference/interruption/errors.d.ts +12 -0
  104. package/dist/inference/interruption/errors.d.ts.map +1 -0
  105. package/dist/inference/interruption/errors.js +20 -0
  106. package/dist/inference/interruption/errors.js.map +1 -0
  107. package/dist/inference/interruption/http_transport.cjs +163 -0
  108. package/dist/inference/interruption/http_transport.cjs.map +1 -0
  109. package/dist/inference/interruption/http_transport.d.cts +63 -0
  110. package/dist/inference/interruption/http_transport.d.ts +63 -0
  111. package/dist/inference/interruption/http_transport.d.ts.map +1 -0
  112. package/dist/inference/interruption/http_transport.js +137 -0
  113. package/dist/inference/interruption/http_transport.js.map +1 -0
  114. package/dist/inference/interruption/interruption_cache_entry.cjs +58 -0
  115. package/dist/inference/interruption/interruption_cache_entry.cjs.map +1 -0
  116. package/dist/inference/interruption/interruption_cache_entry.d.cts +30 -0
  117. package/dist/inference/interruption/interruption_cache_entry.d.ts +30 -0
  118. package/dist/inference/interruption/interruption_cache_entry.d.ts.map +1 -0
  119. package/dist/inference/interruption/interruption_cache_entry.js +34 -0
  120. package/dist/inference/interruption/interruption_cache_entry.js.map +1 -0
  121. package/dist/inference/interruption/interruption_detector.cjs +198 -0
  122. package/dist/inference/interruption/interruption_detector.cjs.map +1 -0
  123. package/dist/inference/interruption/interruption_detector.d.cts +59 -0
  124. package/dist/inference/interruption/interruption_detector.d.ts +59 -0
  125. package/dist/inference/interruption/interruption_detector.d.ts.map +1 -0
  126. package/dist/inference/interruption/interruption_detector.js +164 -0
  127. package/dist/inference/interruption/interruption_detector.js.map +1 -0
  128. package/dist/inference/interruption/interruption_stream.cjs +368 -0
  129. package/dist/inference/interruption/interruption_stream.cjs.map +1 -0
  130. package/dist/inference/interruption/interruption_stream.d.cts +46 -0
  131. package/dist/inference/interruption/interruption_stream.d.ts +46 -0
  132. package/dist/inference/interruption/interruption_stream.d.ts.map +1 -0
  133. package/dist/inference/interruption/interruption_stream.js +344 -0
  134. package/dist/inference/interruption/interruption_stream.js.map +1 -0
  135. package/dist/inference/interruption/types.cjs +17 -0
  136. package/dist/inference/interruption/types.cjs.map +1 -0
  137. package/dist/inference/interruption/types.d.cts +66 -0
  138. package/dist/inference/interruption/types.d.ts +66 -0
  139. package/dist/inference/interruption/types.d.ts.map +1 -0
  140. package/dist/inference/interruption/types.js +1 -0
  141. package/dist/inference/interruption/types.js.map +1 -0
  142. package/dist/inference/interruption/utils.cjs +130 -0
  143. package/dist/inference/interruption/utils.cjs.map +1 -0
  144. package/dist/inference/interruption/utils.d.cts +41 -0
  145. package/dist/inference/interruption/utils.d.ts +41 -0
  146. package/dist/inference/interruption/utils.d.ts.map +1 -0
  147. package/dist/inference/interruption/utils.js +105 -0
  148. package/dist/inference/interruption/utils.js.map +1 -0
  149. package/dist/inference/interruption/utils.test.cjs +105 -0
  150. package/dist/inference/interruption/utils.test.cjs.map +1 -0
  151. package/dist/inference/interruption/utils.test.js +104 -0
  152. package/dist/inference/interruption/utils.test.js.map +1 -0
  153. package/dist/inference/interruption/ws_transport.cjs +342 -0
  154. package/dist/inference/interruption/ws_transport.cjs.map +1 -0
  155. package/dist/inference/interruption/ws_transport.d.cts +33 -0
  156. package/dist/inference/interruption/ws_transport.d.ts +33 -0
  157. package/dist/inference/interruption/ws_transport.d.ts.map +1 -0
  158. package/dist/inference/interruption/ws_transport.js +308 -0
  159. package/dist/inference/interruption/ws_transport.js.map +1 -0
  160. package/dist/inference/llm.cjs +106 -66
  161. package/dist/inference/llm.cjs.map +1 -1
  162. package/dist/inference/llm.d.cts +65 -43
  163. package/dist/inference/llm.d.ts +65 -43
  164. package/dist/inference/llm.d.ts.map +1 -1
  165. package/dist/inference/llm.js +100 -66
  166. package/dist/inference/llm.js.map +1 -1
  167. package/dist/inference/stt.cjs +319 -170
  168. package/dist/inference/stt.cjs.map +1 -1
  169. package/dist/inference/stt.d.cts +64 -15
  170. package/dist/inference/stt.d.ts +64 -15
  171. package/dist/inference/stt.d.ts.map +1 -1
  172. package/dist/inference/stt.js +319 -170
  173. package/dist/inference/stt.js.map +1 -1
  174. package/dist/inference/stt.test.cjs +218 -0
  175. package/dist/inference/stt.test.cjs.map +1 -0
  176. package/dist/inference/stt.test.js +217 -0
  177. package/dist/inference/stt.test.js.map +1 -0
  178. package/dist/inference/tts.cjs +249 -71
  179. package/dist/inference/tts.cjs.map +1 -1
  180. package/dist/inference/tts.d.cts +55 -16
  181. package/dist/inference/tts.d.ts +55 -16
  182. package/dist/inference/tts.d.ts.map +1 -1
  183. package/dist/inference/tts.js +249 -77
  184. package/dist/inference/tts.js.map +1 -1
  185. package/dist/inference/tts.test.cjs +233 -0
  186. package/dist/inference/tts.test.cjs.map +1 -0
  187. package/dist/inference/tts.test.js +232 -0
  188. package/dist/inference/tts.test.js.map +1 -0
  189. package/dist/inference/utils.cjs +26 -7
  190. package/dist/inference/utils.cjs.map +1 -1
  191. package/dist/inference/utils.d.cts +14 -1
  192. package/dist/inference/utils.d.ts +14 -1
  193. package/dist/inference/utils.d.ts.map +1 -1
  194. package/dist/inference/utils.js +18 -2
  195. package/dist/inference/utils.js.map +1 -1
  196. package/dist/ipc/inference_proc_executor.cjs +6 -3
  197. package/dist/ipc/inference_proc_executor.cjs.map +1 -1
  198. package/dist/ipc/inference_proc_executor.d.ts.map +1 -1
  199. package/dist/ipc/inference_proc_executor.js +6 -3
  200. package/dist/ipc/inference_proc_executor.js.map +1 -1
  201. package/dist/ipc/inference_proc_lazy_main.cjs +13 -1
  202. package/dist/ipc/inference_proc_lazy_main.cjs.map +1 -1
  203. package/dist/ipc/inference_proc_lazy_main.js +13 -1
  204. package/dist/ipc/inference_proc_lazy_main.js.map +1 -1
  205. package/dist/ipc/job_proc_executor.cjs +6 -1
  206. package/dist/ipc/job_proc_executor.cjs.map +1 -1
  207. package/dist/ipc/job_proc_executor.d.ts.map +1 -1
  208. package/dist/ipc/job_proc_executor.js +6 -1
  209. package/dist/ipc/job_proc_executor.js.map +1 -1
  210. package/dist/ipc/job_proc_lazy_main.cjs +89 -17
  211. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  212. package/dist/ipc/job_proc_lazy_main.js +68 -18
  213. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  214. package/dist/ipc/supervised_proc.cjs +34 -8
  215. package/dist/ipc/supervised_proc.cjs.map +1 -1
  216. package/dist/ipc/supervised_proc.d.cts +8 -0
  217. package/dist/ipc/supervised_proc.d.ts +8 -0
  218. package/dist/ipc/supervised_proc.d.ts.map +1 -1
  219. package/dist/ipc/supervised_proc.js +34 -8
  220. package/dist/ipc/supervised_proc.js.map +1 -1
  221. package/dist/ipc/supervised_proc.test.cjs +145 -0
  222. package/dist/ipc/supervised_proc.test.cjs.map +1 -0
  223. package/dist/ipc/supervised_proc.test.js +122 -0
  224. package/dist/ipc/supervised_proc.test.js.map +1 -0
  225. package/dist/job.cjs +109 -1
  226. package/dist/job.cjs.map +1 -1
  227. package/dist/job.d.cts +14 -0
  228. package/dist/job.d.ts +14 -0
  229. package/dist/job.d.ts.map +1 -1
  230. package/dist/job.js +99 -1
  231. package/dist/job.js.map +1 -1
  232. package/dist/language.cjs +394 -0
  233. package/dist/language.cjs.map +1 -0
  234. package/dist/language.d.cts +15 -0
  235. package/dist/language.d.ts +15 -0
  236. package/dist/language.d.ts.map +1 -0
  237. package/dist/language.js +363 -0
  238. package/dist/language.js.map +1 -0
  239. package/dist/language.test.cjs +43 -0
  240. package/dist/language.test.cjs.map +1 -0
  241. package/dist/language.test.js +49 -0
  242. package/dist/language.test.js.map +1 -0
  243. package/dist/llm/chat_context.cjs +274 -3
  244. package/dist/llm/chat_context.cjs.map +1 -1
  245. package/dist/llm/chat_context.d.cts +86 -2
  246. package/dist/llm/chat_context.d.ts +86 -2
  247. package/dist/llm/chat_context.d.ts.map +1 -1
  248. package/dist/llm/chat_context.js +273 -3
  249. package/dist/llm/chat_context.js.map +1 -1
  250. package/dist/llm/chat_context.test.cjs +574 -0
  251. package/dist/llm/chat_context.test.cjs.map +1 -1
  252. package/dist/llm/chat_context.test.js +574 -0
  253. package/dist/llm/chat_context.test.js.map +1 -1
  254. package/dist/llm/fallback_adapter.cjs +278 -0
  255. package/dist/llm/fallback_adapter.cjs.map +1 -0
  256. package/dist/llm/fallback_adapter.d.cts +73 -0
  257. package/dist/llm/fallback_adapter.d.ts +73 -0
  258. package/dist/llm/fallback_adapter.d.ts.map +1 -0
  259. package/dist/llm/fallback_adapter.js +254 -0
  260. package/dist/llm/fallback_adapter.js.map +1 -0
  261. package/dist/llm/fallback_adapter.test.cjs +176 -0
  262. package/dist/llm/fallback_adapter.test.cjs.map +1 -0
  263. package/dist/llm/fallback_adapter.test.js +175 -0
  264. package/dist/llm/fallback_adapter.test.js.map +1 -0
  265. package/dist/llm/index.cjs +9 -0
  266. package/dist/llm/index.cjs.map +1 -1
  267. package/dist/llm/index.d.cts +4 -3
  268. package/dist/llm/index.d.ts +4 -3
  269. package/dist/llm/index.d.ts.map +1 -1
  270. package/dist/llm/index.js +11 -1
  271. package/dist/llm/index.js.map +1 -1
  272. package/dist/llm/llm.cjs +65 -11
  273. package/dist/llm/llm.cjs.map +1 -1
  274. package/dist/llm/llm.d.cts +13 -2
  275. package/dist/llm/llm.d.ts +13 -2
  276. package/dist/llm/llm.d.ts.map +1 -1
  277. package/dist/llm/llm.js +65 -11
  278. package/dist/llm/llm.js.map +1 -1
  279. package/dist/llm/provider_format/google.cjs +6 -2
  280. package/dist/llm/provider_format/google.cjs.map +1 -1
  281. package/dist/llm/provider_format/google.d.cts +1 -1
  282. package/dist/llm/provider_format/google.d.ts +1 -1
  283. package/dist/llm/provider_format/google.d.ts.map +1 -1
  284. package/dist/llm/provider_format/google.js +6 -2
  285. package/dist/llm/provider_format/google.js.map +1 -1
  286. package/dist/llm/provider_format/google.test.cjs +48 -0
  287. package/dist/llm/provider_format/google.test.cjs.map +1 -1
  288. package/dist/llm/provider_format/google.test.js +54 -1
  289. package/dist/llm/provider_format/google.test.js.map +1 -1
  290. package/dist/llm/provider_format/index.cjs +2 -0
  291. package/dist/llm/provider_format/index.cjs.map +1 -1
  292. package/dist/llm/provider_format/index.d.cts +2 -2
  293. package/dist/llm/provider_format/index.d.ts +2 -2
  294. package/dist/llm/provider_format/index.d.ts.map +1 -1
  295. package/dist/llm/provider_format/index.js +6 -1
  296. package/dist/llm/provider_format/index.js.map +1 -1
  297. package/dist/llm/provider_format/openai.cjs +126 -24
  298. package/dist/llm/provider_format/openai.cjs.map +1 -1
  299. package/dist/llm/provider_format/openai.d.cts +1 -0
  300. package/dist/llm/provider_format/openai.d.ts +1 -0
  301. package/dist/llm/provider_format/openai.d.ts.map +1 -1
  302. package/dist/llm/provider_format/openai.js +124 -23
  303. package/dist/llm/provider_format/openai.js.map +1 -1
  304. package/dist/llm/provider_format/openai.test.cjs +393 -0
  305. package/dist/llm/provider_format/openai.test.cjs.map +1 -1
  306. package/dist/llm/provider_format/openai.test.js +400 -2
  307. package/dist/llm/provider_format/openai.test.js.map +1 -1
  308. package/dist/llm/provider_format/utils.cjs +5 -4
  309. package/dist/llm/provider_format/utils.cjs.map +1 -1
  310. package/dist/llm/provider_format/utils.d.ts.map +1 -1
  311. package/dist/llm/provider_format/utils.js +5 -4
  312. package/dist/llm/provider_format/utils.js.map +1 -1
  313. package/dist/llm/realtime.cjs +3 -0
  314. package/dist/llm/realtime.cjs.map +1 -1
  315. package/dist/llm/realtime.d.cts +15 -1
  316. package/dist/llm/realtime.d.ts +15 -1
  317. package/dist/llm/realtime.d.ts.map +1 -1
  318. package/dist/llm/realtime.js +3 -0
  319. package/dist/llm/realtime.js.map +1 -1
  320. package/dist/llm/remote_chat_context.cjs.map +1 -1
  321. package/dist/llm/remote_chat_context.d.cts +2 -0
  322. package/dist/llm/remote_chat_context.d.ts +2 -0
  323. package/dist/llm/remote_chat_context.d.ts.map +1 -1
  324. package/dist/llm/remote_chat_context.js.map +1 -1
  325. package/dist/llm/tool_context.cjs +50 -2
  326. package/dist/llm/tool_context.cjs.map +1 -1
  327. package/dist/llm/tool_context.d.cts +47 -11
  328. package/dist/llm/tool_context.d.ts +47 -11
  329. package/dist/llm/tool_context.d.ts.map +1 -1
  330. package/dist/llm/tool_context.js +48 -3
  331. package/dist/llm/tool_context.js.map +1 -1
  332. package/dist/llm/tool_context.test.cjs +197 -0
  333. package/dist/llm/tool_context.test.cjs.map +1 -1
  334. package/dist/llm/tool_context.test.js +175 -0
  335. package/dist/llm/tool_context.test.js.map +1 -1
  336. package/dist/llm/utils.cjs +18 -12
  337. package/dist/llm/utils.cjs.map +1 -1
  338. package/dist/llm/utils.d.cts +2 -3
  339. package/dist/llm/utils.d.ts +2 -3
  340. package/dist/llm/utils.d.ts.map +1 -1
  341. package/dist/llm/utils.js +18 -12
  342. package/dist/llm/utils.js.map +1 -1
  343. package/dist/llm/zod-utils.cjs +102 -0
  344. package/dist/llm/zod-utils.cjs.map +1 -0
  345. package/dist/llm/zod-utils.d.cts +65 -0
  346. package/dist/llm/zod-utils.d.ts +65 -0
  347. package/dist/llm/zod-utils.d.ts.map +1 -0
  348. package/dist/llm/zod-utils.js +64 -0
  349. package/dist/llm/zod-utils.js.map +1 -0
  350. package/dist/llm/zod-utils.test.cjs +472 -0
  351. package/dist/llm/zod-utils.test.cjs.map +1 -0
  352. package/dist/llm/zod-utils.test.js +455 -0
  353. package/dist/llm/zod-utils.test.js.map +1 -0
  354. package/dist/log.cjs +45 -14
  355. package/dist/log.cjs.map +1 -1
  356. package/dist/log.d.cts +8 -1
  357. package/dist/log.d.ts +8 -1
  358. package/dist/log.d.ts.map +1 -1
  359. package/dist/log.js +45 -15
  360. package/dist/log.js.map +1 -1
  361. package/dist/metrics/base.cjs.map +1 -1
  362. package/dist/metrics/base.d.cts +75 -19
  363. package/dist/metrics/base.d.ts +75 -19
  364. package/dist/metrics/base.d.ts.map +1 -1
  365. package/dist/metrics/index.cjs +5 -0
  366. package/dist/metrics/index.cjs.map +1 -1
  367. package/dist/metrics/index.d.cts +2 -1
  368. package/dist/metrics/index.d.ts +2 -1
  369. package/dist/metrics/index.d.ts.map +1 -1
  370. package/dist/metrics/index.js +6 -0
  371. package/dist/metrics/index.js.map +1 -1
  372. package/dist/metrics/model_usage.cjs +189 -0
  373. package/dist/metrics/model_usage.cjs.map +1 -0
  374. package/dist/metrics/model_usage.d.cts +92 -0
  375. package/dist/metrics/model_usage.d.ts +92 -0
  376. package/dist/metrics/model_usage.d.ts.map +1 -0
  377. package/dist/metrics/model_usage.js +164 -0
  378. package/dist/metrics/model_usage.js.map +1 -0
  379. package/dist/metrics/model_usage.test.cjs +474 -0
  380. package/dist/metrics/model_usage.test.cjs.map +1 -0
  381. package/dist/metrics/model_usage.test.js +476 -0
  382. package/dist/metrics/model_usage.test.js.map +1 -0
  383. package/dist/metrics/usage_collector.cjs +5 -2
  384. package/dist/metrics/usage_collector.cjs.map +1 -1
  385. package/dist/metrics/usage_collector.d.cts +10 -1
  386. package/dist/metrics/usage_collector.d.ts +10 -1
  387. package/dist/metrics/usage_collector.d.ts.map +1 -1
  388. package/dist/metrics/usage_collector.js +5 -2
  389. package/dist/metrics/usage_collector.js.map +1 -1
  390. package/dist/metrics/utils.cjs +23 -7
  391. package/dist/metrics/utils.cjs.map +1 -1
  392. package/dist/metrics/utils.d.ts.map +1 -1
  393. package/dist/metrics/utils.js +23 -7
  394. package/dist/metrics/utils.js.map +1 -1
  395. package/dist/stream/deferred_stream.cjs +31 -10
  396. package/dist/stream/deferred_stream.cjs.map +1 -1
  397. package/dist/stream/deferred_stream.d.cts +6 -1
  398. package/dist/stream/deferred_stream.d.ts +6 -1
  399. package/dist/stream/deferred_stream.d.ts.map +1 -1
  400. package/dist/stream/deferred_stream.js +31 -10
  401. package/dist/stream/deferred_stream.js.map +1 -1
  402. package/dist/stream/deferred_stream.test.cjs +2 -2
  403. package/dist/stream/deferred_stream.test.cjs.map +1 -1
  404. package/dist/stream/deferred_stream.test.js +2 -2
  405. package/dist/stream/deferred_stream.test.js.map +1 -1
  406. package/dist/stream/index.cjs +3 -0
  407. package/dist/stream/index.cjs.map +1 -1
  408. package/dist/stream/index.d.cts +1 -0
  409. package/dist/stream/index.d.ts +1 -0
  410. package/dist/stream/index.d.ts.map +1 -1
  411. package/dist/stream/index.js +2 -0
  412. package/dist/stream/index.js.map +1 -1
  413. package/dist/stream/multi_input_stream.cjs +139 -0
  414. package/dist/stream/multi_input_stream.cjs.map +1 -0
  415. package/dist/stream/multi_input_stream.d.cts +55 -0
  416. package/dist/stream/multi_input_stream.d.ts +55 -0
  417. package/dist/stream/multi_input_stream.d.ts.map +1 -0
  418. package/dist/stream/multi_input_stream.js +115 -0
  419. package/dist/stream/multi_input_stream.js.map +1 -0
  420. package/dist/stream/multi_input_stream.test.cjs +344 -0
  421. package/dist/stream/multi_input_stream.test.cjs.map +1 -0
  422. package/dist/stream/multi_input_stream.test.js +343 -0
  423. package/dist/stream/multi_input_stream.test.js.map +1 -0
  424. package/dist/stream/stream_channel.cjs +39 -1
  425. package/dist/stream/stream_channel.cjs.map +1 -1
  426. package/dist/stream/stream_channel.d.cts +5 -2
  427. package/dist/stream/stream_channel.d.ts +5 -2
  428. package/dist/stream/stream_channel.d.ts.map +1 -1
  429. package/dist/stream/stream_channel.js +39 -1
  430. package/dist/stream/stream_channel.js.map +1 -1
  431. package/dist/stream/stream_channel.test.cjs +27 -0
  432. package/dist/stream/stream_channel.test.cjs.map +1 -1
  433. package/dist/stream/stream_channel.test.js +27 -0
  434. package/dist/stream/stream_channel.test.js.map +1 -1
  435. package/dist/stt/stream_adapter.cjs +24 -9
  436. package/dist/stt/stream_adapter.cjs.map +1 -1
  437. package/dist/stt/stream_adapter.d.cts +7 -3
  438. package/dist/stt/stream_adapter.d.ts +7 -3
  439. package/dist/stt/stream_adapter.d.ts.map +1 -1
  440. package/dist/stt/stream_adapter.js +24 -9
  441. package/dist/stt/stream_adapter.js.map +1 -1
  442. package/dist/stt/stt.cjs +86 -19
  443. package/dist/stt/stt.cjs.map +1 -1
  444. package/dist/stt/stt.d.cts +60 -5
  445. package/dist/stt/stt.d.ts +60 -5
  446. package/dist/stt/stt.d.ts.map +1 -1
  447. package/dist/stt/stt.js +88 -21
  448. package/dist/stt/stt.js.map +1 -1
  449. package/dist/telemetry/index.cjs +72 -0
  450. package/dist/telemetry/index.cjs.map +1 -0
  451. package/dist/telemetry/index.d.cts +7 -0
  452. package/dist/telemetry/index.d.ts +7 -0
  453. package/dist/telemetry/index.d.ts.map +1 -0
  454. package/dist/telemetry/index.js +37 -0
  455. package/dist/telemetry/index.js.map +1 -0
  456. package/dist/telemetry/logging.cjs +65 -0
  457. package/dist/telemetry/logging.cjs.map +1 -0
  458. package/dist/telemetry/logging.d.cts +21 -0
  459. package/dist/telemetry/logging.d.ts +21 -0
  460. package/dist/telemetry/logging.d.ts.map +1 -0
  461. package/dist/telemetry/logging.js +40 -0
  462. package/dist/telemetry/logging.js.map +1 -0
  463. package/dist/telemetry/otel_http_exporter.cjs +166 -0
  464. package/dist/telemetry/otel_http_exporter.cjs.map +1 -0
  465. package/dist/telemetry/otel_http_exporter.d.cts +63 -0
  466. package/dist/telemetry/otel_http_exporter.d.ts +63 -0
  467. package/dist/telemetry/otel_http_exporter.d.ts.map +1 -0
  468. package/dist/telemetry/otel_http_exporter.js +142 -0
  469. package/dist/telemetry/otel_http_exporter.js.map +1 -0
  470. package/dist/telemetry/pino_otel_transport.cjs +217 -0
  471. package/dist/telemetry/pino_otel_transport.cjs.map +1 -0
  472. package/dist/telemetry/pino_otel_transport.d.cts +58 -0
  473. package/dist/telemetry/pino_otel_transport.d.ts +58 -0
  474. package/dist/telemetry/pino_otel_transport.d.ts.map +1 -0
  475. package/dist/telemetry/pino_otel_transport.js +189 -0
  476. package/dist/telemetry/pino_otel_transport.js.map +1 -0
  477. package/dist/telemetry/trace_types.cjs +233 -0
  478. package/dist/telemetry/trace_types.cjs.map +1 -0
  479. package/dist/telemetry/trace_types.d.cts +74 -0
  480. package/dist/telemetry/trace_types.d.ts +74 -0
  481. package/dist/telemetry/trace_types.d.ts.map +1 -0
  482. package/dist/telemetry/trace_types.js +141 -0
  483. package/dist/telemetry/trace_types.js.map +1 -0
  484. package/dist/telemetry/traces.cjs +484 -0
  485. package/dist/telemetry/traces.cjs.map +1 -0
  486. package/dist/telemetry/traces.d.cts +116 -0
  487. package/dist/telemetry/traces.d.ts +116 -0
  488. package/dist/telemetry/traces.d.ts.map +1 -0
  489. package/dist/telemetry/traces.js +449 -0
  490. package/dist/telemetry/traces.js.map +1 -0
  491. package/dist/telemetry/utils.cjs +86 -0
  492. package/dist/telemetry/utils.cjs.map +1 -0
  493. package/dist/telemetry/utils.d.cts +5 -0
  494. package/dist/telemetry/utils.d.ts +5 -0
  495. package/dist/telemetry/utils.d.ts.map +1 -0
  496. package/dist/telemetry/utils.js +51 -0
  497. package/dist/telemetry/utils.js.map +1 -0
  498. package/dist/tokenize/basic/sentence.cjs +3 -3
  499. package/dist/tokenize/basic/sentence.cjs.map +1 -1
  500. package/dist/tokenize/basic/sentence.js +3 -3
  501. package/dist/tokenize/basic/sentence.js.map +1 -1
  502. package/dist/tokenize/tokenizer.test.cjs +3 -1
  503. package/dist/tokenize/tokenizer.test.cjs.map +1 -1
  504. package/dist/tokenize/tokenizer.test.js +3 -1
  505. package/dist/tokenize/tokenizer.test.js.map +1 -1
  506. package/dist/transcription.cjs.map +1 -1
  507. package/dist/transcription.d.cts +6 -0
  508. package/dist/transcription.d.ts +6 -0
  509. package/dist/transcription.d.ts.map +1 -1
  510. package/dist/transcription.js.map +1 -1
  511. package/dist/tts/fallback_adapter.cjs +466 -0
  512. package/dist/tts/fallback_adapter.cjs.map +1 -0
  513. package/dist/tts/fallback_adapter.d.cts +110 -0
  514. package/dist/tts/fallback_adapter.d.ts +110 -0
  515. package/dist/tts/fallback_adapter.d.ts.map +1 -0
  516. package/dist/tts/fallback_adapter.js +442 -0
  517. package/dist/tts/fallback_adapter.js.map +1 -0
  518. package/dist/tts/index.cjs +3 -0
  519. package/dist/tts/index.cjs.map +1 -1
  520. package/dist/tts/index.d.cts +1 -0
  521. package/dist/tts/index.d.ts +1 -0
  522. package/dist/tts/index.d.ts.map +1 -1
  523. package/dist/tts/index.js +2 -0
  524. package/dist/tts/index.js.map +1 -1
  525. package/dist/tts/stream_adapter.cjs +25 -8
  526. package/dist/tts/stream_adapter.cjs.map +1 -1
  527. package/dist/tts/stream_adapter.d.cts +6 -3
  528. package/dist/tts/stream_adapter.d.ts +6 -3
  529. package/dist/tts/stream_adapter.d.ts.map +1 -1
  530. package/dist/tts/stream_adapter.js +25 -8
  531. package/dist/tts/stream_adapter.js.map +1 -1
  532. package/dist/tts/tts.cjs +189 -57
  533. package/dist/tts/tts.cjs.map +1 -1
  534. package/dist/tts/tts.d.cts +58 -6
  535. package/dist/tts/tts.d.ts +58 -6
  536. package/dist/tts/tts.d.ts.map +1 -1
  537. package/dist/tts/tts.js +191 -59
  538. package/dist/tts/tts.js.map +1 -1
  539. package/dist/types.cjs +24 -32
  540. package/dist/types.cjs.map +1 -1
  541. package/dist/types.d.cts +45 -10
  542. package/dist/types.d.ts +45 -10
  543. package/dist/types.d.ts.map +1 -1
  544. package/dist/types.js +20 -30
  545. package/dist/types.js.map +1 -1
  546. package/dist/utils.cjs +122 -26
  547. package/dist/utils.cjs.map +1 -1
  548. package/dist/utils.d.cts +41 -1
  549. package/dist/utils.d.ts +41 -1
  550. package/dist/utils.d.ts.map +1 -1
  551. package/dist/utils.js +117 -25
  552. package/dist/utils.js.map +1 -1
  553. package/dist/utils.test.cjs +73 -1
  554. package/dist/utils.test.cjs.map +1 -1
  555. package/dist/utils.test.js +74 -10
  556. package/dist/utils.test.js.map +1 -1
  557. package/dist/vad.cjs +35 -15
  558. package/dist/vad.cjs.map +1 -1
  559. package/dist/vad.d.cts +15 -5
  560. package/dist/vad.d.ts +15 -5
  561. package/dist/vad.d.ts.map +1 -1
  562. package/dist/vad.js +35 -15
  563. package/dist/vad.js.map +1 -1
  564. package/dist/version.cjs +1 -1
  565. package/dist/version.cjs.map +1 -1
  566. package/dist/version.d.cts +1 -1
  567. package/dist/version.d.ts +1 -1
  568. package/dist/version.d.ts.map +1 -1
  569. package/dist/version.js +1 -1
  570. package/dist/version.js.map +1 -1
  571. package/dist/voice/agent.cjs +258 -35
  572. package/dist/voice/agent.cjs.map +1 -1
  573. package/dist/voice/agent.d.cts +54 -13
  574. package/dist/voice/agent.d.ts +54 -13
  575. package/dist/voice/agent.d.ts.map +1 -1
  576. package/dist/voice/agent.js +254 -34
  577. package/dist/voice/agent.js.map +1 -1
  578. package/dist/voice/agent.test.cjs +314 -0
  579. package/dist/voice/agent.test.cjs.map +1 -1
  580. package/dist/voice/agent.test.js +316 -2
  581. package/dist/voice/agent.test.js.map +1 -1
  582. package/dist/voice/agent_activity.cjs +1116 -385
  583. package/dist/voice/agent_activity.cjs.map +1 -1
  584. package/dist/voice/agent_activity.d.cts +72 -11
  585. package/dist/voice/agent_activity.d.ts +72 -11
  586. package/dist/voice/agent_activity.d.ts.map +1 -1
  587. package/dist/voice/agent_activity.js +1119 -383
  588. package/dist/voice/agent_activity.js.map +1 -1
  589. package/dist/voice/agent_activity.test.cjs +135 -0
  590. package/dist/voice/agent_activity.test.cjs.map +1 -0
  591. package/dist/voice/agent_activity.test.js +134 -0
  592. package/dist/voice/agent_activity.test.js.map +1 -0
  593. package/dist/voice/agent_session.cjs +550 -90
  594. package/dist/voice/agent_session.cjs.map +1 -1
  595. package/dist/voice/agent_session.d.cts +185 -25
  596. package/dist/voice/agent_session.d.ts +185 -25
  597. package/dist/voice/agent_session.d.ts.map +1 -1
  598. package/dist/voice/agent_session.js +556 -91
  599. package/dist/voice/agent_session.js.map +1 -1
  600. package/dist/voice/audio_recognition.cjs +605 -46
  601. package/dist/voice/audio_recognition.cjs.map +1 -1
  602. package/dist/voice/audio_recognition.d.cts +96 -4
  603. package/dist/voice/audio_recognition.d.ts +96 -4
  604. package/dist/voice/audio_recognition.d.ts.map +1 -1
  605. package/dist/voice/audio_recognition.js +611 -47
  606. package/dist/voice/audio_recognition.js.map +1 -1
  607. package/dist/voice/audio_recognition_span.test.cjs +295 -0
  608. package/dist/voice/audio_recognition_span.test.cjs.map +1 -0
  609. package/dist/voice/audio_recognition_span.test.js +299 -0
  610. package/dist/voice/audio_recognition_span.test.js.map +1 -0
  611. package/dist/voice/avatar/datastream_io.cjs +7 -1
  612. package/dist/voice/avatar/datastream_io.cjs.map +1 -1
  613. package/dist/voice/avatar/datastream_io.d.cts +1 -0
  614. package/dist/voice/avatar/datastream_io.d.ts +1 -0
  615. package/dist/voice/avatar/datastream_io.d.ts.map +1 -1
  616. package/dist/voice/avatar/datastream_io.js +7 -1
  617. package/dist/voice/avatar/datastream_io.js.map +1 -1
  618. package/dist/voice/background_audio.cjs +367 -0
  619. package/dist/voice/background_audio.cjs.map +1 -0
  620. package/dist/voice/background_audio.d.cts +123 -0
  621. package/dist/voice/background_audio.d.ts +123 -0
  622. package/dist/voice/background_audio.d.ts.map +1 -0
  623. package/dist/voice/background_audio.js +343 -0
  624. package/dist/voice/background_audio.js.map +1 -0
  625. package/dist/voice/events.cjs +3 -0
  626. package/dist/voice/events.cjs.map +1 -1
  627. package/dist/voice/events.d.cts +16 -9
  628. package/dist/voice/events.d.ts +16 -9
  629. package/dist/voice/events.d.ts.map +1 -1
  630. package/dist/voice/events.js +3 -0
  631. package/dist/voice/events.js.map +1 -1
  632. package/dist/voice/generation.cjs +205 -41
  633. package/dist/voice/generation.cjs.map +1 -1
  634. package/dist/voice/generation.d.cts +21 -5
  635. package/dist/voice/generation.d.ts +21 -5
  636. package/dist/voice/generation.d.ts.map +1 -1
  637. package/dist/voice/generation.js +215 -43
  638. package/dist/voice/generation.js.map +1 -1
  639. package/dist/voice/generation_tools.test.cjs +236 -0
  640. package/dist/voice/generation_tools.test.cjs.map +1 -0
  641. package/dist/voice/generation_tools.test.js +235 -0
  642. package/dist/voice/generation_tools.test.js.map +1 -0
  643. package/dist/voice/index.cjs +33 -2
  644. package/dist/voice/index.cjs.map +1 -1
  645. package/dist/voice/index.d.cts +8 -2
  646. package/dist/voice/index.d.ts +8 -2
  647. package/dist/voice/index.d.ts.map +1 -1
  648. package/dist/voice/index.js +19 -2
  649. package/dist/voice/index.js.map +1 -1
  650. package/dist/voice/interruption_detection.test.cjs +114 -0
  651. package/dist/voice/interruption_detection.test.cjs.map +1 -0
  652. package/dist/voice/interruption_detection.test.js +113 -0
  653. package/dist/voice/interruption_detection.test.js.map +1 -0
  654. package/dist/voice/io.cjs +66 -6
  655. package/dist/voice/io.cjs.map +1 -1
  656. package/dist/voice/io.d.cts +67 -7
  657. package/dist/voice/io.d.ts +67 -7
  658. package/dist/voice/io.d.ts.map +1 -1
  659. package/dist/voice/io.js +62 -5
  660. package/dist/voice/io.js.map +1 -1
  661. package/dist/voice/recorder_io/index.cjs +23 -0
  662. package/dist/voice/recorder_io/index.cjs.map +1 -0
  663. package/dist/voice/recorder_io/index.d.cts +2 -0
  664. package/dist/voice/recorder_io/index.d.ts +2 -0
  665. package/dist/voice/recorder_io/index.d.ts.map +1 -0
  666. package/dist/voice/recorder_io/index.js +2 -0
  667. package/dist/voice/recorder_io/index.js.map +1 -0
  668. package/dist/voice/recorder_io/recorder_io.cjs +607 -0
  669. package/dist/voice/recorder_io/recorder_io.cjs.map +1 -0
  670. package/dist/voice/recorder_io/recorder_io.d.cts +106 -0
  671. package/dist/voice/recorder_io/recorder_io.d.ts +106 -0
  672. package/dist/voice/recorder_io/recorder_io.d.ts.map +1 -0
  673. package/dist/voice/recorder_io/recorder_io.js +573 -0
  674. package/dist/voice/recorder_io/recorder_io.js.map +1 -0
  675. package/dist/voice/remote_session.cjs +922 -0
  676. package/dist/voice/remote_session.cjs.map +1 -0
  677. package/dist/voice/remote_session.d.cts +108 -0
  678. package/dist/voice/remote_session.d.ts +108 -0
  679. package/dist/voice/remote_session.d.ts.map +1 -0
  680. package/dist/voice/remote_session.js +887 -0
  681. package/dist/voice/remote_session.js.map +1 -0
  682. package/dist/voice/report.cjs +88 -0
  683. package/dist/voice/report.cjs.map +1 -0
  684. package/dist/voice/report.d.cts +49 -0
  685. package/dist/voice/report.d.ts +49 -0
  686. package/dist/voice/report.d.ts.map +1 -0
  687. package/dist/voice/report.js +63 -0
  688. package/dist/voice/report.js.map +1 -0
  689. package/dist/voice/report.test.cjs +121 -0
  690. package/dist/voice/report.test.cjs.map +1 -0
  691. package/dist/voice/report.test.js +120 -0
  692. package/dist/voice/report.test.js.map +1 -0
  693. package/dist/voice/room_io/_input.cjs +40 -7
  694. package/dist/voice/room_io/_input.cjs.map +1 -1
  695. package/dist/voice/room_io/_input.d.cts +5 -2
  696. package/dist/voice/room_io/_input.d.ts +5 -2
  697. package/dist/voice/room_io/_input.d.ts.map +1 -1
  698. package/dist/voice/room_io/_input.js +41 -8
  699. package/dist/voice/room_io/_input.js.map +1 -1
  700. package/dist/voice/room_io/_output.cjs +19 -11
  701. package/dist/voice/room_io/_output.cjs.map +1 -1
  702. package/dist/voice/room_io/_output.d.cts +7 -4
  703. package/dist/voice/room_io/_output.d.ts +7 -4
  704. package/dist/voice/room_io/_output.d.ts.map +1 -1
  705. package/dist/voice/room_io/_output.js +20 -12
  706. package/dist/voice/room_io/_output.js.map +1 -1
  707. package/dist/voice/room_io/room_io.cjs +33 -6
  708. package/dist/voice/room_io/room_io.cjs.map +1 -1
  709. package/dist/voice/room_io/room_io.d.cts +29 -9
  710. package/dist/voice/room_io/room_io.d.ts +29 -9
  711. package/dist/voice/room_io/room_io.d.ts.map +1 -1
  712. package/dist/voice/room_io/room_io.js +33 -7
  713. package/dist/voice/room_io/room_io.js.map +1 -1
  714. package/dist/voice/speech_handle.cjs +22 -4
  715. package/dist/voice/speech_handle.cjs.map +1 -1
  716. package/dist/voice/speech_handle.d.cts +17 -2
  717. package/dist/voice/speech_handle.d.ts +17 -2
  718. package/dist/voice/speech_handle.d.ts.map +1 -1
  719. package/dist/voice/speech_handle.js +21 -4
  720. package/dist/voice/speech_handle.js.map +1 -1
  721. package/dist/voice/testing/fake_llm.cjs +127 -0
  722. package/dist/voice/testing/fake_llm.cjs.map +1 -0
  723. package/dist/voice/testing/fake_llm.d.cts +30 -0
  724. package/dist/voice/testing/fake_llm.d.ts +30 -0
  725. package/dist/voice/testing/fake_llm.d.ts.map +1 -0
  726. package/dist/voice/testing/fake_llm.js +103 -0
  727. package/dist/voice/testing/fake_llm.js.map +1 -0
  728. package/dist/voice/testing/index.cjs +57 -0
  729. package/dist/voice/testing/index.cjs.map +1 -0
  730. package/dist/voice/testing/index.d.cts +21 -0
  731. package/dist/voice/testing/index.d.ts +21 -0
  732. package/dist/voice/testing/index.d.ts.map +1 -0
  733. package/dist/voice/testing/index.js +35 -0
  734. package/dist/voice/testing/index.js.map +1 -0
  735. package/dist/voice/testing/run_result.cjs +817 -0
  736. package/dist/voice/testing/run_result.cjs.map +1 -0
  737. package/dist/voice/testing/run_result.d.cts +385 -0
  738. package/dist/voice/testing/run_result.d.ts +385 -0
  739. package/dist/voice/testing/run_result.d.ts.map +1 -0
  740. package/dist/voice/testing/run_result.js +790 -0
  741. package/dist/voice/testing/run_result.js.map +1 -0
  742. package/dist/voice/testing/types.cjs +46 -0
  743. package/dist/voice/testing/types.cjs.map +1 -0
  744. package/dist/voice/testing/types.d.cts +83 -0
  745. package/dist/voice/testing/types.d.ts +83 -0
  746. package/dist/voice/testing/types.d.ts.map +1 -0
  747. package/dist/voice/testing/types.js +19 -0
  748. package/dist/voice/testing/types.js.map +1 -0
  749. package/dist/voice/transcription/synchronizer.cjs +139 -15
  750. package/dist/voice/transcription/synchronizer.cjs.map +1 -1
  751. package/dist/voice/transcription/synchronizer.d.cts +35 -4
  752. package/dist/voice/transcription/synchronizer.d.ts +35 -4
  753. package/dist/voice/transcription/synchronizer.d.ts.map +1 -1
  754. package/dist/voice/transcription/synchronizer.js +143 -16
  755. package/dist/voice/transcription/synchronizer.js.map +1 -1
  756. package/dist/voice/transcription/synchronizer.test.cjs +151 -0
  757. package/dist/voice/transcription/synchronizer.test.cjs.map +1 -0
  758. package/dist/voice/transcription/synchronizer.test.js +150 -0
  759. package/dist/voice/transcription/synchronizer.test.js.map +1 -0
  760. package/dist/voice/turn_config/endpointing.cjs +33 -0
  761. package/dist/voice/turn_config/endpointing.cjs.map +1 -0
  762. package/dist/voice/turn_config/endpointing.d.cts +30 -0
  763. package/dist/voice/turn_config/endpointing.d.ts +30 -0
  764. package/dist/voice/turn_config/endpointing.d.ts.map +1 -0
  765. package/dist/voice/turn_config/endpointing.js +9 -0
  766. package/dist/voice/turn_config/endpointing.js.map +1 -0
  767. package/dist/voice/turn_config/interruption.cjs +37 -0
  768. package/dist/voice/turn_config/interruption.cjs.map +1 -0
  769. package/dist/voice/turn_config/interruption.d.cts +53 -0
  770. package/dist/voice/turn_config/interruption.d.ts +53 -0
  771. package/dist/voice/turn_config/interruption.d.ts.map +1 -0
  772. package/dist/voice/turn_config/interruption.js +13 -0
  773. package/dist/voice/turn_config/interruption.js.map +1 -0
  774. package/dist/voice/turn_config/turn_handling.cjs +35 -0
  775. package/dist/voice/turn_config/turn_handling.cjs.map +1 -0
  776. package/dist/voice/turn_config/turn_handling.d.cts +36 -0
  777. package/dist/voice/turn_config/turn_handling.d.ts +36 -0
  778. package/dist/voice/turn_config/turn_handling.d.ts.map +1 -0
  779. package/dist/voice/turn_config/turn_handling.js +11 -0
  780. package/dist/voice/turn_config/turn_handling.js.map +1 -0
  781. package/dist/voice/turn_config/utils.cjs +157 -0
  782. package/dist/voice/turn_config/utils.cjs.map +1 -0
  783. package/dist/voice/turn_config/utils.d.cts +37 -0
  784. package/dist/voice/turn_config/utils.d.ts +37 -0
  785. package/dist/voice/turn_config/utils.d.ts.map +1 -0
  786. package/dist/voice/turn_config/utils.js +131 -0
  787. package/dist/voice/turn_config/utils.js.map +1 -0
  788. package/dist/voice/turn_config/utils.test.cjs +128 -0
  789. package/dist/voice/turn_config/utils.test.cjs.map +1 -0
  790. package/dist/voice/turn_config/utils.test.js +127 -0
  791. package/dist/voice/turn_config/utils.test.js.map +1 -0
  792. package/dist/voice/utils.cjs +47 -0
  793. package/dist/voice/utils.cjs.map +1 -0
  794. package/dist/voice/utils.d.cts +4 -0
  795. package/dist/voice/utils.d.ts +4 -0
  796. package/dist/voice/utils.d.ts.map +1 -0
  797. package/dist/voice/utils.js +23 -0
  798. package/dist/voice/utils.js.map +1 -0
  799. package/dist/worker.cjs +44 -52
  800. package/dist/worker.cjs.map +1 -1
  801. package/dist/worker.d.cts +18 -8
  802. package/dist/worker.d.ts +18 -8
  803. package/dist/worker.d.ts.map +1 -1
  804. package/dist/worker.js +43 -43
  805. package/dist/worker.js.map +1 -1
  806. package/package.json +32 -12
  807. package/resources/NOTICE +2 -0
  808. package/resources/keyboard-typing.ogg +0 -0
  809. package/resources/keyboard-typing2.ogg +0 -0
  810. package/resources/office-ambience.ogg +0 -0
  811. package/src/audio.ts +132 -1
  812. package/src/beta/index.ts +9 -0
  813. package/src/beta/workflows/index.ts +9 -0
  814. package/src/beta/workflows/task_group.ts +194 -0
  815. package/src/cli.ts +57 -66
  816. package/src/connection_pool.test.ts +346 -0
  817. package/src/connection_pool.ts +307 -0
  818. package/src/constants.ts +14 -0
  819. package/src/cpu.test.ts +239 -0
  820. package/src/cpu.ts +173 -0
  821. package/src/http_server.ts +18 -6
  822. package/src/index.ts +15 -13
  823. package/src/inference/api_protos.ts +85 -2
  824. package/src/inference/index.ts +32 -4
  825. package/src/inference/interruption/defaults.ts +51 -0
  826. package/src/inference/interruption/errors.ts +25 -0
  827. package/src/inference/interruption/http_transport.ts +206 -0
  828. package/src/inference/interruption/interruption_cache_entry.ts +50 -0
  829. package/src/inference/interruption/interruption_detector.ts +204 -0
  830. package/src/inference/interruption/interruption_stream.ts +467 -0
  831. package/src/inference/interruption/types.ts +84 -0
  832. package/src/inference/interruption/utils.test.ts +132 -0
  833. package/src/inference/interruption/utils.ts +137 -0
  834. package/src/inference/interruption/ws_transport.ts +406 -0
  835. package/src/inference/llm.ts +214 -163
  836. package/src/inference/stt.test.ts +253 -0
  837. package/src/inference/stt.ts +449 -208
  838. package/src/inference/tts.test.ts +267 -0
  839. package/src/inference/tts.ts +377 -115
  840. package/src/inference/utils.ts +30 -2
  841. package/src/ipc/inference_proc_executor.ts +11 -3
  842. package/src/ipc/inference_proc_lazy_main.ts +13 -1
  843. package/src/ipc/job_proc_executor.ts +11 -1
  844. package/src/ipc/job_proc_lazy_main.ts +86 -20
  845. package/src/ipc/supervised_proc.test.ts +153 -0
  846. package/src/ipc/supervised_proc.ts +39 -10
  847. package/src/job.ts +120 -1
  848. package/src/language.test.ts +62 -0
  849. package/src/language.ts +380 -0
  850. package/src/llm/__snapshots__/zod-utils.test.ts.snap +559 -0
  851. package/src/llm/chat_context.test.ts +655 -0
  852. package/src/llm/chat_context.ts +412 -2
  853. package/src/llm/fallback_adapter.test.ts +238 -0
  854. package/src/llm/fallback_adapter.ts +391 -0
  855. package/src/llm/index.ts +11 -0
  856. package/src/llm/llm.ts +77 -12
  857. package/src/llm/provider_format/google.test.ts +72 -1
  858. package/src/llm/provider_format/google.ts +10 -6
  859. package/src/llm/provider_format/index.ts +7 -2
  860. package/src/llm/provider_format/openai.test.ts +480 -2
  861. package/src/llm/provider_format/openai.ts +152 -21
  862. package/src/llm/provider_format/utils.ts +11 -5
  863. package/src/llm/realtime.ts +23 -2
  864. package/src/llm/remote_chat_context.ts +2 -2
  865. package/src/llm/tool_context.test.ts +210 -1
  866. package/src/llm/tool_context.ts +115 -17
  867. package/src/llm/utils.ts +24 -16
  868. package/src/llm/zod-utils.test.ts +577 -0
  869. package/src/llm/zod-utils.ts +153 -0
  870. package/src/log.ts +71 -19
  871. package/src/metrics/base.ts +78 -19
  872. package/src/metrics/index.ts +12 -0
  873. package/src/metrics/model_usage.test.ts +545 -0
  874. package/src/metrics/model_usage.ts +262 -0
  875. package/src/metrics/usage_collector.ts +14 -3
  876. package/src/metrics/utils.ts +27 -7
  877. package/src/stream/deferred_stream.test.ts +3 -3
  878. package/src/stream/deferred_stream.ts +43 -11
  879. package/src/stream/index.ts +1 -0
  880. package/src/stream/multi_input_stream.test.ts +545 -0
  881. package/src/stream/multi_input_stream.ts +172 -0
  882. package/src/stream/stream_channel.test.ts +37 -0
  883. package/src/stream/stream_channel.ts +43 -3
  884. package/src/stt/stream_adapter.ts +30 -9
  885. package/src/stt/stt.ts +131 -22
  886. package/src/telemetry/index.ts +28 -0
  887. package/src/telemetry/logging.ts +55 -0
  888. package/src/telemetry/otel_http_exporter.ts +218 -0
  889. package/src/telemetry/pino_otel_transport.ts +265 -0
  890. package/src/telemetry/trace_types.ts +109 -0
  891. package/src/telemetry/traces.ts +673 -0
  892. package/src/telemetry/utils.ts +61 -0
  893. package/src/tokenize/basic/sentence.ts +3 -3
  894. package/src/tokenize/tokenizer.test.ts +4 -0
  895. package/src/transcription.ts +6 -0
  896. package/src/tts/fallback_adapter.ts +579 -0
  897. package/src/tts/index.ts +1 -0
  898. package/src/tts/stream_adapter.ts +38 -8
  899. package/src/tts/tts.ts +245 -62
  900. package/src/types.ts +62 -33
  901. package/src/utils.test.ts +90 -10
  902. package/src/utils.ts +176 -31
  903. package/src/vad.ts +42 -18
  904. package/src/version.ts +1 -1
  905. package/src/voice/agent.test.ts +347 -2
  906. package/src/voice/agent.ts +346 -44
  907. package/src/voice/agent_activity.test.ts +194 -0
  908. package/src/voice/agent_activity.ts +1457 -388
  909. package/src/voice/agent_session.ts +817 -112
  910. package/src/voice/audio_recognition.ts +845 -70
  911. package/src/voice/audio_recognition_span.test.ts +341 -0
  912. package/src/voice/avatar/datastream_io.ts +9 -1
  913. package/src/voice/background_audio.ts +494 -0
  914. package/src/voice/events.ts +27 -7
  915. package/src/voice/generation.ts +310 -56
  916. package/src/voice/generation_tools.test.ts +268 -0
  917. package/src/voice/index.ts +17 -3
  918. package/src/voice/interruption_detection.test.ts +151 -0
  919. package/src/voice/io.ts +115 -12
  920. package/src/voice/recorder_io/index.ts +4 -0
  921. package/src/voice/recorder_io/recorder_io.ts +783 -0
  922. package/src/voice/remote_session.ts +1083 -0
  923. package/src/voice/report.test.ts +136 -0
  924. package/src/voice/report.ts +140 -0
  925. package/src/voice/room_io/_input.ts +45 -10
  926. package/src/voice/room_io/_output.ts +26 -14
  927. package/src/voice/room_io/room_io.ts +67 -22
  928. package/src/voice/speech_handle.ts +38 -6
  929. package/src/voice/testing/fake_llm.ts +138 -0
  930. package/src/voice/testing/index.ts +52 -0
  931. package/src/voice/testing/run_result.ts +995 -0
  932. package/src/voice/testing/types.ts +118 -0
  933. package/src/voice/transcription/synchronizer.test.ts +206 -0
  934. package/src/voice/transcription/synchronizer.ts +204 -19
  935. package/src/voice/turn_config/endpointing.ts +33 -0
  936. package/src/voice/turn_config/interruption.ts +56 -0
  937. package/src/voice/turn_config/turn_handling.ts +45 -0
  938. package/src/voice/turn_config/utils.test.ts +148 -0
  939. package/src/voice/turn_config/utils.ts +167 -0
  940. package/src/voice/utils.ts +29 -0
  941. package/src/worker.ts +92 -78
  942. package/src/llm/__snapshots__/utils.test.ts.snap +0 -65
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/voice/transcription/synchronizer.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { ReadableStream, WritableStreamDefaultWriter } from 'node:stream/web';\nimport { log } from '../../log.js';\nimport { IdentityTransform } from '../../stream/identity_transform.js';\nimport type { SentenceStream, SentenceTokenizer } from '../../tokenize/index.js';\nimport { basic } from '../../tokenize/index.js';\nimport { Future, Task, delay } from '../../utils.js';\nimport { AudioOutput, type PlaybackFinishedEvent, TextOutput } from '../io.js';\n\nconst STANDARD_SPEECH_RATE = 3.83; // hyphens (syllables) per second\n\ninterface TextSyncOptions {\n speed: number;\n hyphenateWord: (word: string) => string[];\n splitWords: (words: string) => [string, number, number][];\n sentenceTokenizer: SentenceTokenizer;\n}\n\ninterface TextData {\n sentenceStream: SentenceStream;\n pushedText: string;\n done: boolean;\n forwardedHyphens: number;\n forwardedText: string;\n}\n\ninterface AudioData {\n pushedDuration: number;\n done: boolean;\n}\n\nclass SegmentSynchronizerImpl {\n private textData: TextData;\n private audioData: AudioData;\n private speed: number;\n private outputStream: IdentityTransform<string>;\n private outputStreamWriter: WritableStreamDefaultWriter<string>;\n private captureTask: Promise<void>;\n private startWallTime?: number;\n\n private startFuture: Future = new Future();\n private closedFuture: Future = new Future();\n private playbackCompleted: boolean = false;\n\n private logger = log();\n\n constructor(\n private readonly options: TextSyncOptions,\n private readonly nextInChain: TextOutput,\n ) {\n this.speed = options.speed * STANDARD_SPEECH_RATE; // hyphens per second\n this.textData = {\n sentenceStream: options.sentenceTokenizer.stream(),\n pushedText: '',\n done: false,\n forwardedHyphens: 0,\n forwardedText: '',\n };\n this.audioData = {\n pushedDuration: 0,\n done: false,\n };\n this.outputStream = new IdentityTransform();\n this.outputStreamWriter = this.outputStream.writable.getWriter();\n\n this.mainTask()\n .then(() => {\n this.outputStreamWriter.close();\n })\n .catch((error) => {\n this.logger.error({ error }, 'mainTask SegmentSynchronizerImpl');\n });\n this.captureTask = this.captureTaskImpl();\n }\n\n get closed() {\n return this.closedFuture.done;\n }\n\n get audioInputEnded() {\n return this.audioData.done;\n }\n\n get textInputEnded() {\n return this.textData.done;\n }\n\n get readable(): ReadableStream<string> {\n return this.outputStream.readable;\n }\n\n pushAudio(frame: AudioFrame) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.pushAudio called after close');\n return;\n }\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n\n if (!this.startWallTime && frameDuration > 0) {\n this.startWallTime = Date.now();\n this.startFuture.resolve();\n }\n\n this.audioData.pushedDuration += frameDuration;\n }\n\n endAudioInput() {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.endAudioInput called after close');\n return;\n }\n\n this.audioData.done = true;\n }\n\n pushText(text: string) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.pushText called after close');\n return;\n }\n\n this.textData.sentenceStream.pushText(text);\n this.textData.pushedText += text;\n }\n\n endTextInput() {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.endTextInput called after close');\n return;\n }\n\n this.textData.done = true;\n this.textData.sentenceStream.endInput();\n }\n\n markPlaybackFinished(_playbackPosition: number, interrupted: boolean) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.markPlaybackFinished called after close');\n return;\n }\n\n if (!this.textData.done || !this.audioData.done) {\n this.logger.warn(\n { textDone: this.textData.done, audioDone: this.audioData.done },\n 'SegmentSynchronizerImpl.markPlaybackFinished called before text/audio input is done',\n );\n return;\n }\n\n if (!interrupted) {\n this.playbackCompleted = true;\n }\n }\n\n get synchronizedTranscript(): string {\n if (this.playbackCompleted) {\n return this.textData.pushedText;\n }\n return this.textData.forwardedText;\n }\n\n private async captureTaskImpl() {\n // Don't use a for-await loop here, because exiting the loop will close the writer in the\n // outputStream, which will cause an error in the mainTask.then method.\n const reader = this.outputStream.readable.getReader();\n while (true) {\n const { done, value: text } = await reader.read();\n if (done) {\n break;\n }\n this.textData.forwardedText += text;\n await this.nextInChain.captureText(text);\n }\n reader.releaseLock();\n this.nextInChain.flush();\n }\n\n private async mainTask(): Promise<void> {\n await this.startFuture.await;\n\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n if (!this.startWallTime) {\n throw new Error('startWallTime is not set when starting SegmentSynchronizerImpl.mainTask');\n }\n\n for await (const textSegment of this.textData.sentenceStream) {\n const sentence = textSegment.token;\n\n let textCursor = 0;\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n for (const [word, _, endPos] of this.options.splitWords(sentence)) {\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n if (this.playbackCompleted) {\n this.outputStreamWriter.write(sentence.slice(textCursor, endPos));\n textCursor = endPos;\n continue;\n }\n\n const wordHphens = this.options.hyphenateWord(word).length;\n const elapsedSeconds = (Date.now() - this.startWallTime) / 1000;\n const targetHyphens = elapsedSeconds * this.options.speed;\n const hyphensBehind = Math.max(0, targetHyphens - this.textData.forwardedHyphens);\n let delay = Math.max(0, wordHphens - hyphensBehind) / this.speed;\n\n if (this.playbackCompleted) {\n delay = 0;\n }\n\n await this.sleepIfNotClosed(delay / 2);\n this.outputStreamWriter.write(sentence.slice(textCursor, endPos));\n await this.sleepIfNotClosed(delay / 2);\n\n this.textData.forwardedHyphens += wordHphens;\n textCursor = endPos;\n }\n\n if (textCursor < sentence.length) {\n const remaining = sentence.slice(textCursor);\n this.outputStreamWriter.write(remaining);\n }\n }\n }\n\n private async sleepIfNotClosed(sleepTimeSeconds: number) {\n if (this.closed) {\n return;\n }\n await delay(sleepTimeSeconds * 1000);\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closedFuture.resolve();\n this.startFuture.resolve(); // avoid deadlock of mainTaskImpl in case it never started\n this.textData.sentenceStream.close();\n await this.captureTask;\n }\n}\n\nexport interface TranscriptionSynchronizerOptions {\n speed: number;\n hyphenateWord: (word: string) => string[];\n splitWords: (words: string) => [string, number, number][];\n sentenceTokenizer: SentenceTokenizer;\n}\n\nexport const defaultTextSyncOptions: TranscriptionSynchronizerOptions = {\n speed: 1,\n hyphenateWord: basic.hyphenateWord,\n splitWords: basic.splitWords,\n sentenceTokenizer: new basic.SentenceTokenizer({\n retainFormat: true,\n }),\n};\n\nexport class TranscriptionSynchronizer {\n readonly audioOutput: SyncedAudioOutput;\n readonly textOutput: SyncedTextOutput;\n\n private options: TextSyncOptions;\n private rotateSegmentTask: Task<void>;\n private _enabled: boolean = true;\n private closed: boolean = false;\n\n /* @internal */\n _impl: SegmentSynchronizerImpl;\n\n private logger = log();\n\n constructor(\n nextInChainAudio: AudioOutput,\n nextInChainText: TextOutput,\n options: TranscriptionSynchronizerOptions = defaultTextSyncOptions,\n ) {\n this.audioOutput = new SyncedAudioOutput(this, nextInChainAudio);\n this.textOutput = new SyncedTextOutput(this, nextInChainText);\n this.options = {\n speed: options.speed,\n hyphenateWord: options.hyphenateWord,\n splitWords: options.splitWords,\n sentenceTokenizer: options.sentenceTokenizer,\n };\n\n // initial segment/first segment, recreated for each new segment\n this._impl = new SegmentSynchronizerImpl(this.options, nextInChainText);\n this.rotateSegmentTask = Task.from((controller) =>\n this.rotateSegmentTaskImpl(controller.signal),\n );\n }\n\n get enabled(): boolean {\n return this._enabled;\n }\n\n set enabled(enabled: boolean) {\n if (this._enabled === enabled) {\n return;\n }\n\n this._enabled = enabled;\n this.rotateSegment();\n }\n\n rotateSegment() {\n if (this.closed) {\n return;\n }\n\n if (!this.rotateSegmentTask.done) {\n this.logger.warn('rotateSegment called while previous segment is still being rotated');\n }\n this.rotateSegmentTask = Task.from((controller) =>\n this.rotateSegmentTaskImpl(controller.signal, this.rotateSegmentTask),\n );\n }\n\n async close(): Promise<void> {\n this.closed = true;\n await this.rotateSegmentTask.cancelAndWait();\n await this._impl.close();\n }\n\n async barrier(): Promise<void> {\n if (this.rotateSegmentTask.done) {\n return;\n }\n await this.rotateSegmentTask.result;\n }\n\n private async rotateSegmentTaskImpl(abort: AbortSignal, oldTask?: Task<void>) {\n if (oldTask) {\n await oldTask.result;\n }\n\n if (abort.aborted) {\n return;\n }\n await this._impl.close();\n this._impl = new SegmentSynchronizerImpl(this.options, this.textOutput.nextInChain);\n }\n}\n\nclass SyncedAudioOutput extends AudioOutput {\n private pushedDuration: number = 0.0;\n\n constructor(\n public synchronizer: TranscriptionSynchronizer,\n private nextInChainAudio: AudioOutput,\n ) {\n super(nextInChainAudio.sampleRate, nextInChainAudio);\n }\n\n async captureFrame(frame: AudioFrame): Promise<void> {\n // using barrier() on capture should be sufficient, flush() must not be called if\n // capture_frame isn't completed\n await this.synchronizer.barrier();\n\n await super.captureFrame(frame);\n await this.nextInChainAudio.captureFrame(frame); // passthrough audio\n\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDuration += frame.samplesPerChannel / frame.sampleRate;\n\n if (!this.synchronizer.enabled) {\n return;\n }\n\n if (this.synchronizer._impl.audioInputEnded) {\n this.logger.warn(\n 'SegmentSynchronizerImpl audio marked as ended in capture audio, rotating segment',\n );\n this.synchronizer.rotateSegment();\n await this.synchronizer.barrier();\n }\n this.synchronizer._impl.pushAudio(frame);\n }\n\n flush() {\n super.flush();\n this.nextInChainAudio.flush();\n\n if (!this.synchronizer.enabled) {\n return;\n }\n\n if (!this.pushedDuration) {\n // in case there is no audio after the text was pushed, rotate the segment\n this.synchronizer.rotateSegment();\n return;\n }\n\n this.synchronizer._impl.endAudioInput();\n }\n\n clearBuffer() {\n this.nextInChainAudio.clearBuffer();\n }\n\n // this is going to be automatically called by the next_in_chain\n onPlaybackFinished(ev: PlaybackFinishedEvent) {\n if (!this.synchronizer.enabled) {\n super.onPlaybackFinished(ev);\n return;\n }\n\n this.synchronizer._impl.markPlaybackFinished(ev.playbackPosition, ev.interrupted);\n super.onPlaybackFinished({\n playbackPosition: ev.playbackPosition,\n interrupted: ev.interrupted,\n synchronizedTranscript: this.synchronizer._impl.synchronizedTranscript,\n });\n\n this.synchronizer.rotateSegment();\n this.pushedDuration = 0.0;\n }\n}\n\nclass SyncedTextOutput extends TextOutput {\n private capturing: boolean = false;\n private logger = log();\n\n constructor(\n private readonly synchronizer: TranscriptionSynchronizer,\n public readonly nextInChain: TextOutput,\n ) {\n super(nextInChain);\n }\n\n async captureText(text: string): Promise<void> {\n await this.synchronizer.barrier();\n\n if (!this.synchronizer.enabled) {\n // pass through to the next in chain\n await this.nextInChain.captureText(text);\n return;\n }\n\n this.capturing = true;\n if (this.synchronizer._impl.textInputEnded) {\n this.logger.warn(\n 'SegmentSynchronizerImpl text marked as ended in capture text, rotating segment',\n );\n this.synchronizer.rotateSegment();\n await this.synchronizer.barrier();\n }\n this.synchronizer._impl.pushText(text);\n }\n\n flush() {\n if (!this.synchronizer.enabled) {\n this.nextInChain.flush(); // passthrough text if the synchronizer is disabled\n return;\n }\n\n if (!this.capturing) {\n return;\n }\n\n this.capturing = false;\n this.synchronizer._impl.endTextInput();\n }\n}\n"],"mappings":"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","names":["delay"]}
1
+ {"version":3,"sources":["../../../src/voice/transcription/synchronizer.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport type { AudioFrame } from '@livekit/rtc-node';\nimport type { ReadableStream, WritableStreamDefaultWriter } from 'node:stream/web';\nimport { log } from '../../log.js';\nimport { IdentityTransform } from '../../stream/identity_transform.js';\nimport type { SentenceStream, SentenceTokenizer } from '../../tokenize/index.js';\nimport { basic } from '../../tokenize/index.js';\nimport { Future, Task, delay } from '../../utils.js';\nimport {\n AudioOutput,\n type PlaybackFinishedEvent,\n TextOutput,\n type TimedString,\n isTimedString,\n} from '../io.js';\n\nconst STANDARD_SPEECH_RATE = 3.83; // hyphens (syllables) per second\n\ninterface TextSyncOptions {\n speed: number;\n hyphenateWord: (word: string) => string[];\n splitWords: (words: string) => [string, number, number][];\n sentenceTokenizer: SentenceTokenizer;\n}\n\ninterface TextData {\n sentenceStream: SentenceStream;\n pushedText: string;\n done: boolean;\n forwardedHyphens: number;\n forwardedText: string;\n}\n\n/**\n * Tracks speaking rate data from TTS timing annotations.\n * @internal Exported for testing purposes.\n */\nexport class SpeakingRateData {\n /** Timestamps of the speaking rate. */\n timestamps: number[] = [];\n /** Speed at the timestamp. */\n speakingRate: number[] = [];\n /** Accumulated speaking units up to the timestamp. */\n speakIntegrals: number[] = [];\n /** Buffer for text without timing annotations yet. */\n private textBuffer: string[] = [];\n\n /**\n * Add by speaking rate estimation.\n */\n addByRate(timestamp: number, speakingRate: number): void {\n const integral =\n this.speakIntegrals.length > 0 ? this.speakIntegrals[this.speakIntegrals.length - 1]! : 0;\n const dt = timestamp - this.pushedDuration;\n const newIntegral = integral + speakingRate * dt;\n\n this.timestamps.push(timestamp);\n this.speakingRate.push(speakingRate);\n this.speakIntegrals.push(newIntegral);\n }\n\n /**\n * Add annotation from TimedString with start_time/end_time.\n */\n addByAnnotation(text: string, startTime: number | undefined, endTime: number | undefined): void {\n if (startTime !== undefined) {\n // Calculate the integral of the speaking rate up to the start time\n const integral =\n this.speakIntegrals.length > 0 ? this.speakIntegrals[this.speakIntegrals.length - 1]! : 0;\n\n const dt = startTime - this.pushedDuration;\n // Use the length of the text directly instead of hyphens\n const textLen = this.textBuffer.reduce((sum, t) => sum + t.length, 0);\n const newIntegral = integral + textLen;\n const rate = dt > 0 ? textLen / dt : 0;\n\n this.timestamps.push(startTime);\n this.speakingRate.push(rate);\n this.speakIntegrals.push(newIntegral);\n this.textBuffer = [];\n }\n\n this.textBuffer.push(text);\n\n if (endTime !== undefined) {\n this.addByAnnotation('', endTime, undefined);\n }\n }\n\n /**\n * Get accumulated speaking units up to the given timestamp.\n */\n accumulateTo(timestamp: number): number {\n if (this.timestamps.length === 0) {\n return 0;\n }\n\n // Binary search for the right position (equivalent to np.searchsorted with side=\"right\")\n let idx = 0;\n for (let i = 0; i < this.timestamps.length; i++) {\n if (this.timestamps[i]! <= timestamp) {\n idx = i + 1;\n } else {\n break;\n }\n }\n\n if (idx === 0) {\n return 0;\n }\n\n let integralT = this.speakIntegrals[idx - 1]!;\n\n // Fill the tail assuming the speaking rate is constant\n const dt = timestamp - this.timestamps[idx - 1]!;\n const rate =\n idx < this.speakingRate.length ? this.speakingRate[idx]! : this.speakingRate[idx - 1]!;\n integralT += rate * dt;\n\n // If there is a next timestamp, make sure the integral does not exceed the next\n if (idx < this.timestamps.length) {\n integralT = Math.min(integralT, this.speakIntegrals[idx]!);\n }\n\n return integralT;\n }\n\n /** Get the last pushed timestamp. */\n get pushedDuration(): number {\n return this.timestamps.length > 0 ? this.timestamps[this.timestamps.length - 1]! : 0;\n }\n}\n\ninterface AudioData {\n pushedDuration: number;\n done: boolean;\n annotatedRate: SpeakingRateData | null;\n}\n\nclass SegmentSynchronizerImpl {\n private textData: TextData;\n private audioData: AudioData;\n private speed: number;\n private outputStream: IdentityTransform<string>;\n private outputStreamWriter: WritableStreamDefaultWriter<string>;\n private captureTask: Promise<void>;\n private startWallTime?: number;\n\n private startFuture: Future = new Future();\n private closedFuture: Future = new Future();\n private playbackCompleted: boolean = false;\n\n private logger = log();\n\n constructor(\n private readonly options: TextSyncOptions,\n private readonly nextInChain: TextOutput,\n ) {\n this.speed = options.speed * STANDARD_SPEECH_RATE; // hyphens per second\n this.textData = {\n sentenceStream: options.sentenceTokenizer.stream(),\n pushedText: '',\n done: false,\n forwardedHyphens: 0,\n forwardedText: '',\n };\n this.audioData = {\n pushedDuration: 0,\n done: false,\n annotatedRate: null,\n };\n this.outputStream = new IdentityTransform();\n this.outputStreamWriter = this.outputStream.writable.getWriter();\n\n this.mainTask()\n .then(() => {\n this.outputStreamWriter.close();\n })\n .catch((error) => {\n this.logger.error({ error }, 'mainTask SegmentSynchronizerImpl');\n });\n this.captureTask = this.captureTaskImpl();\n }\n\n get closed() {\n return this.closedFuture.done;\n }\n\n get audioInputEnded() {\n return this.audioData.done;\n }\n\n get textInputEnded() {\n return this.textData.done;\n }\n\n get hasPendingText(): boolean {\n return this.textData.pushedText.length > this.textData.forwardedText.length;\n }\n\n get readable(): ReadableStream<string> {\n return this.outputStream.readable;\n }\n\n pushAudio(frame: AudioFrame) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.pushAudio called after close');\n return;\n }\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n const frameDuration = frame.samplesPerChannel / frame.sampleRate;\n\n if (!this.startWallTime && frameDuration > 0) {\n this.startWallTime = Date.now();\n this.startFuture.resolve();\n }\n\n this.audioData.pushedDuration += frameDuration;\n }\n\n endAudioInput() {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.endAudioInput called after close');\n return;\n }\n\n this.audioData.done = true;\n }\n\n pushText(text: string | TimedString) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.pushText called after close');\n return;\n }\n\n // Check if text is a TimedString (has timing information)\n let textStr: string;\n let startTime: number | undefined;\n let endTime: number | undefined;\n\n if (isTimedString(text)) {\n // This is a TimedString\n textStr = text.text;\n startTime = text.startTime;\n endTime = text.endTime;\n\n // Create annotatedRate if it doesn't exist\n if (!this.audioData.annotatedRate) {\n this.audioData.annotatedRate = new SpeakingRateData();\n }\n\n // Add the timing annotation\n this.audioData.annotatedRate.addByAnnotation(textStr, startTime, endTime);\n } else {\n textStr = text;\n }\n\n this.textData.sentenceStream.pushText(textStr);\n this.textData.pushedText += textStr;\n }\n\n endTextInput() {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.endTextInput called after close');\n return;\n }\n\n this.textData.done = true;\n this.textData.sentenceStream.endInput();\n }\n\n markPlaybackFinished(_playbackPosition: number, interrupted: boolean) {\n if (this.closed) {\n this.logger.warn('SegmentSynchronizerImpl.markPlaybackFinished called after close');\n return;\n }\n\n if (!this.textData.done || !this.audioData.done) {\n this.logger.warn(\n { textDone: this.textData.done, audioDone: this.audioData.done },\n 'SegmentSynchronizerImpl.markPlaybackFinished called before text/audio input is done',\n );\n // This allows mainTask to flush remaining text even if audio wasn't formally ended\n if (!interrupted) {\n this.playbackCompleted = true;\n }\n return;\n }\n\n if (!interrupted) {\n this.playbackCompleted = true;\n }\n }\n\n get synchronizedTranscript(): string {\n if (this.playbackCompleted) {\n return this.textData.pushedText;\n }\n return this.textData.forwardedText;\n }\n\n private async captureTaskImpl() {\n // Don't use a for-await loop here, because exiting the loop will close the writer in the\n // outputStream, which will cause an error in the mainTask.then method.\n // NOTE: forwardedText is updated in mainTask, NOT here\n const reader = this.outputStream.readable.getReader();\n while (true) {\n const { done, value: text } = await reader.read();\n if (done) {\n break;\n }\n await this.nextInChain.captureText(text);\n }\n reader.releaseLock();\n this.nextInChain.flush();\n }\n\n private async mainTask(): Promise<void> {\n await this.startFuture.await;\n\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n if (!this.startWallTime) {\n throw new Error('startWallTime is not set when starting SegmentSynchronizerImpl.mainTask');\n }\n\n for await (const textSegment of this.textData.sentenceStream) {\n const sentence = textSegment.token;\n\n let textCursor = 0;\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n for (const [word, _, endPos] of this.options.splitWords(sentence)) {\n if (this.closed && !this.playbackCompleted) {\n return;\n }\n\n if (this.playbackCompleted) {\n this.outputStreamWriter.write(sentence.slice(textCursor, endPos));\n textCursor = endPos;\n continue;\n }\n\n const wordHphens = this.options.hyphenateWord(word).length;\n const elapsedSeconds = (Date.now() - this.startWallTime) / 1000;\n\n let dHyphens = 0;\n const annotated = this.audioData.annotatedRate;\n\n if (annotated && annotated.pushedDuration >= elapsedSeconds) {\n // Use actual TTS timing annotations for accurate sync\n const targetLen = Math.floor(annotated.accumulateTo(elapsedSeconds));\n const forwardedLen = this.textData.forwardedText.length;\n\n if (targetLen >= forwardedLen) {\n const dText = this.textData.pushedText.slice(forwardedLen, targetLen);\n dHyphens = this.calcHyphens(dText).length;\n } else {\n const dText = this.textData.pushedText.slice(targetLen, forwardedLen);\n dHyphens = -this.calcHyphens(dText).length;\n }\n } else {\n // Fall back to estimated hyphens-per-second calculation\n const targetHyphens = elapsedSeconds * this.options.speed;\n dHyphens = Math.max(0, targetHyphens - this.textData.forwardedHyphens);\n }\n\n let delayTime = Math.max(0, wordHphens - dHyphens) / this.speed;\n\n if (this.playbackCompleted) {\n delayTime = 0;\n }\n\n await this.sleepIfNotClosed(delayTime / 2);\n const forwardedWord = sentence.slice(textCursor, endPos);\n this.outputStreamWriter.write(forwardedWord);\n\n await this.sleepIfNotClosed(delayTime / 2);\n\n this.textData.forwardedHyphens += wordHphens;\n this.textData.forwardedText += forwardedWord;\n textCursor = endPos;\n }\n\n if (textCursor < sentence.length) {\n const remaining = sentence.slice(textCursor);\n this.outputStreamWriter.write(remaining);\n }\n }\n }\n\n private calcHyphens(text: string): string[] {\n const words = this.options.splitWords(text);\n const hyphens: string[] = [];\n for (const [word] of words) {\n hyphens.push(...this.options.hyphenateWord(word));\n }\n return hyphens;\n }\n\n private async sleepIfNotClosed(sleepTimeSeconds: number) {\n if (this.closed) {\n return;\n }\n await delay(sleepTimeSeconds * 1000);\n }\n\n async close(): Promise<void> {\n if (this.closed) {\n return;\n }\n this.closedFuture.resolve();\n this.startFuture.resolve(); // avoid deadlock of mainTaskImpl in case it never started\n this.textData.sentenceStream.close();\n await this.captureTask;\n }\n}\n\nexport interface TranscriptionSynchronizerOptions {\n speed: number;\n hyphenateWord: (word: string) => string[];\n splitWords: (words: string) => [string, number, number][];\n sentenceTokenizer: SentenceTokenizer;\n}\n\nexport const defaultTextSyncOptions: TranscriptionSynchronizerOptions = {\n speed: 1,\n hyphenateWord: basic.hyphenateWord,\n splitWords: basic.splitWords,\n sentenceTokenizer: new basic.SentenceTokenizer({\n retainFormat: true,\n }),\n};\n\nexport class TranscriptionSynchronizer {\n readonly audioOutput: SyncedAudioOutput;\n readonly textOutput: SyncedTextOutput;\n\n private options: TextSyncOptions;\n private rotateSegmentTask: Task<void>;\n private _enabled: boolean = true;\n private closed: boolean = false;\n\n /** @internal */\n _impl: SegmentSynchronizerImpl;\n\n private logger = log();\n\n constructor(\n nextInChainAudio: AudioOutput,\n nextInChainText: TextOutput,\n options: TranscriptionSynchronizerOptions = defaultTextSyncOptions,\n ) {\n this.audioOutput = new SyncedAudioOutput(this, nextInChainAudio);\n this.textOutput = new SyncedTextOutput(this, nextInChainText);\n this.options = {\n speed: options.speed,\n hyphenateWord: options.hyphenateWord,\n splitWords: options.splitWords,\n sentenceTokenizer: options.sentenceTokenizer,\n };\n\n // initial segment/first segment, recreated for each new segment\n this._impl = new SegmentSynchronizerImpl(this.options, nextInChainText);\n this.rotateSegmentTask = Task.from((controller) =>\n this.rotateSegmentTaskImpl(controller.signal),\n );\n }\n\n get enabled(): boolean {\n return this._enabled;\n }\n\n set enabled(enabled: boolean) {\n if (this._enabled === enabled) {\n return;\n }\n\n this._enabled = enabled;\n this.rotateSegment();\n }\n\n rotateSegment() {\n if (this.closed) {\n return;\n }\n\n if (!this.rotateSegmentTask.done) {\n this.logger.warn('rotateSegment called while previous segment is still being rotated');\n }\n this.rotateSegmentTask = Task.from((controller) =>\n this.rotateSegmentTaskImpl(controller.signal, this.rotateSegmentTask),\n );\n }\n\n async close(): Promise<void> {\n this.closed = true;\n await this.rotateSegmentTask.cancelAndWait();\n await this._impl.close();\n }\n\n async barrier(): Promise<void> {\n if (this.rotateSegmentTask.done) {\n return;\n }\n await this.rotateSegmentTask.result;\n }\n\n private async rotateSegmentTaskImpl(abort: AbortSignal, oldTask?: Task<void>) {\n if (oldTask) {\n await oldTask.result;\n }\n\n if (abort.aborted) {\n return;\n }\n\n await this._impl.close();\n this._impl = new SegmentSynchronizerImpl(this.options, this.textOutput.nextInChain);\n }\n}\n\nclass SyncedAudioOutput extends AudioOutput {\n private pushedDuration: number = 0.0;\n\n constructor(\n public synchronizer: TranscriptionSynchronizer,\n private nextInChainAudio: AudioOutput,\n ) {\n super(nextInChainAudio.sampleRate, nextInChainAudio, { pause: true });\n }\n\n async captureFrame(frame: AudioFrame): Promise<void> {\n // using barrier() on capture should be sufficient, flush() must not be called if\n // capture_frame isn't completed\n await this.synchronizer.barrier();\n\n await super.captureFrame(frame);\n await this.nextInChainAudio.captureFrame(frame); // passthrough audio\n\n // TODO(AJS-102): use frame.durationMs once available in rtc-node\n this.pushedDuration += frame.samplesPerChannel / frame.sampleRate;\n\n if (!this.synchronizer.enabled) {\n return;\n }\n\n if (this.synchronizer._impl.audioInputEnded) {\n this.logger.warn(\n 'SegmentSynchronizerImpl audio marked as ended in capture audio, rotating segment',\n );\n this.synchronizer.rotateSegment();\n await this.synchronizer.barrier();\n }\n this.synchronizer._impl.pushAudio(frame);\n }\n\n flush() {\n super.flush();\n this.nextInChainAudio.flush();\n\n if (!this.synchronizer.enabled) {\n return;\n }\n\n if (!this.pushedDuration) {\n // For timed texts, audio goes directly to room without going through synchronizer.\n // If text was pushed but no audio, still end audio input so text can be processed.\n // Only rotate if there's also no text (truly empty segment).\n if (this.synchronizer._impl.hasPendingText) {\n // Text is pending - end audio input to allow text processing\n this.synchronizer._impl.endAudioInput();\n return;\n }\n // No text and no audio - rotate the segment\n this.synchronizer.rotateSegment();\n return;\n }\n\n this.synchronizer._impl.endAudioInput();\n }\n\n clearBuffer() {\n this.nextInChainAudio.clearBuffer();\n }\n\n // this is going to be automatically called by the next_in_chain\n onPlaybackFinished(ev: PlaybackFinishedEvent) {\n if (!this.synchronizer.enabled) {\n super.onPlaybackFinished(ev);\n return;\n }\n\n this.synchronizer._impl.markPlaybackFinished(ev.playbackPosition, ev.interrupted);\n super.onPlaybackFinished({\n playbackPosition: ev.playbackPosition,\n interrupted: ev.interrupted,\n synchronizedTranscript: this.synchronizer._impl.synchronizedTranscript,\n });\n\n this.synchronizer.rotateSegment();\n this.pushedDuration = 0.0;\n }\n}\n\nclass SyncedTextOutput extends TextOutput {\n private capturing: boolean = false;\n private logger = log();\n\n constructor(\n private readonly synchronizer: TranscriptionSynchronizer,\n public readonly nextInChain: TextOutput,\n ) {\n super(nextInChain);\n }\n\n async captureText(text: string | TimedString): Promise<void> {\n await this.synchronizer.barrier();\n\n const textStr = isTimedString(text) ? text.text : text;\n\n if (!this.synchronizer.enabled) {\n // pass through to the next in chain (extract string from TimedString if needed)\n await this.nextInChain.captureText(textStr);\n return;\n }\n\n this.capturing = true;\n if (this.synchronizer._impl.textInputEnded) {\n this.logger.warn(\n 'SegmentSynchronizerImpl text marked as ended in capture text, rotating segment',\n );\n this.synchronizer.rotateSegment();\n await this.synchronizer.barrier();\n }\n // Pass the TimedString to pushText for timing extraction\n this.synchronizer._impl.pushText(text);\n }\n\n async flush() {\n // Wait for any pending rotation to complete before accessing _impl\n await this.synchronizer.barrier();\n\n if (!this.synchronizer.enabled) {\n this.nextInChain.flush(); // passthrough text if the synchronizer is disabled\n return;\n }\n\n if (!this.capturing) {\n return;\n }\n\n this.capturing = false;\n this.synchronizer._impl.endTextInput();\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var import_vitest = require("vitest");
3
+ var import_synchronizer = require("./synchronizer.cjs");
4
+ (0, import_vitest.describe)("SpeakingRateData", () => {
5
+ (0, import_vitest.describe)("constructor", () => {
6
+ (0, import_vitest.it)("should initialize with empty arrays", () => {
7
+ const data = new import_synchronizer.SpeakingRateData();
8
+ (0, import_vitest.expect)(data.timestamps).toEqual([]);
9
+ (0, import_vitest.expect)(data.speakingRate).toEqual([]);
10
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([]);
11
+ (0, import_vitest.expect)(data.pushedDuration).toBe(0);
12
+ });
13
+ });
14
+ (0, import_vitest.describe)("addByRate", () => {
15
+ (0, import_vitest.it)("should add a single rate entry", () => {
16
+ const data = new import_synchronizer.SpeakingRateData();
17
+ data.addByRate(1, 5);
18
+ (0, import_vitest.expect)(data.timestamps).toEqual([1]);
19
+ (0, import_vitest.expect)(data.speakingRate).toEqual([5]);
20
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([5]);
21
+ (0, import_vitest.expect)(data.pushedDuration).toBe(1);
22
+ });
23
+ (0, import_vitest.it)("should accumulate integrals across multiple entries", () => {
24
+ const data = new import_synchronizer.SpeakingRateData();
25
+ data.addByRate(1, 4);
26
+ data.addByRate(2, 6);
27
+ data.addByRate(3.5, 2);
28
+ (0, import_vitest.expect)(data.timestamps).toEqual([1, 2, 3.5]);
29
+ (0, import_vitest.expect)(data.speakingRate).toEqual([4, 6, 2]);
30
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([4, 10, 13]);
31
+ (0, import_vitest.expect)(data.pushedDuration).toBe(3.5);
32
+ });
33
+ (0, import_vitest.it)("should handle zero rate", () => {
34
+ const data = new import_synchronizer.SpeakingRateData();
35
+ data.addByRate(1, 0);
36
+ (0, import_vitest.expect)(data.timestamps).toEqual([1]);
37
+ (0, import_vitest.expect)(data.speakingRate).toEqual([0]);
38
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([0]);
39
+ });
40
+ });
41
+ (0, import_vitest.describe)("addByAnnotation", () => {
42
+ (0, import_vitest.it)("should buffer text without startTime", () => {
43
+ const data = new import_synchronizer.SpeakingRateData();
44
+ data.addByAnnotation("hello", void 0, void 0);
45
+ (0, import_vitest.expect)(data.timestamps).toEqual([]);
46
+ (0, import_vitest.expect)(data.pushedDuration).toBe(0);
47
+ });
48
+ (0, import_vitest.it)("should add entry when startTime is provided", () => {
49
+ const data = new import_synchronizer.SpeakingRateData();
50
+ data.addByAnnotation("hello", void 0, void 0);
51
+ data.addByAnnotation("world", 1, void 0);
52
+ (0, import_vitest.expect)(data.timestamps).toEqual([1]);
53
+ (0, import_vitest.expect)(data.speakingRate).toEqual([5]);
54
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([5]);
55
+ });
56
+ (0, import_vitest.it)("should handle startTime and endTime together", () => {
57
+ const data = new import_synchronizer.SpeakingRateData();
58
+ data.addByAnnotation("hello ", 0, 0.5);
59
+ data.addByAnnotation("world", 0.5, 1);
60
+ (0, import_vitest.expect)(data.timestamps.length).toBeGreaterThanOrEqual(2);
61
+ (0, import_vitest.expect)(data.pushedDuration).toBe(1);
62
+ });
63
+ (0, import_vitest.it)("should calculate rate based on buffered text length", () => {
64
+ const data = new import_synchronizer.SpeakingRateData();
65
+ data.addByAnnotation("ab", void 0, void 0);
66
+ data.addByAnnotation("cde", void 0, void 0);
67
+ data.addByAnnotation("", 2, void 0);
68
+ (0, import_vitest.expect)(data.timestamps).toEqual([2]);
69
+ (0, import_vitest.expect)(data.speakingRate).toEqual([2.5]);
70
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([5]);
71
+ });
72
+ (0, import_vitest.it)("should handle zero time delta gracefully", () => {
73
+ const data = new import_synchronizer.SpeakingRateData();
74
+ data.addByAnnotation("hello", 0, void 0);
75
+ (0, import_vitest.expect)(data.timestamps).toEqual([0]);
76
+ (0, import_vitest.expect)(data.speakingRate).toEqual([0]);
77
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([0]);
78
+ });
79
+ });
80
+ (0, import_vitest.describe)("accumulateTo", () => {
81
+ (0, import_vitest.it)("should return 0 for empty data", () => {
82
+ const data = new import_synchronizer.SpeakingRateData();
83
+ (0, import_vitest.expect)(data.accumulateTo(1)).toBe(0);
84
+ });
85
+ (0, import_vitest.it)("should return 0 for timestamp before first entry", () => {
86
+ const data = new import_synchronizer.SpeakingRateData();
87
+ data.addByRate(1, 5);
88
+ (0, import_vitest.expect)(data.accumulateTo(0.5)).toBe(0);
89
+ });
90
+ (0, import_vitest.it)("should return exact integral at timestamp", () => {
91
+ const data = new import_synchronizer.SpeakingRateData();
92
+ data.addByRate(1, 4);
93
+ data.addByRate(2, 6);
94
+ (0, import_vitest.expect)(data.accumulateTo(1)).toBe(4);
95
+ (0, import_vitest.expect)(data.accumulateTo(2)).toBe(10);
96
+ });
97
+ (0, import_vitest.it)("should interpolate between timestamps", () => {
98
+ const data = new import_synchronizer.SpeakingRateData();
99
+ data.addByRate(1, 4);
100
+ data.addByRate(2, 6);
101
+ (0, import_vitest.expect)(data.accumulateTo(1.5)).toBe(7);
102
+ });
103
+ (0, import_vitest.it)("should extrapolate beyond last timestamp", () => {
104
+ const data = new import_synchronizer.SpeakingRateData();
105
+ data.addByRate(1, 4);
106
+ data.addByRate(2, 6);
107
+ (0, import_vitest.expect)(data.accumulateTo(3)).toBe(16);
108
+ });
109
+ (0, import_vitest.it)("should not exceed next integral when interpolating", () => {
110
+ const data = new import_synchronizer.SpeakingRateData();
111
+ data.addByRate(1, 100);
112
+ data.addByRate(2, 1);
113
+ (0, import_vitest.expect)(data.accumulateTo(1.5)).toBe(100.5);
114
+ });
115
+ });
116
+ (0, import_vitest.describe)("pushedDuration", () => {
117
+ (0, import_vitest.it)("should return 0 when empty", () => {
118
+ const data = new import_synchronizer.SpeakingRateData();
119
+ (0, import_vitest.expect)(data.pushedDuration).toBe(0);
120
+ });
121
+ (0, import_vitest.it)("should return last timestamp", () => {
122
+ const data = new import_synchronizer.SpeakingRateData();
123
+ data.addByRate(1, 5);
124
+ data.addByRate(2.5, 3);
125
+ data.addByRate(4, 7);
126
+ (0, import_vitest.expect)(data.pushedDuration).toBe(4);
127
+ });
128
+ });
129
+ (0, import_vitest.describe)("integration scenarios", () => {
130
+ (0, import_vitest.it)("should handle typical TTS word timing scenario", () => {
131
+ const data = new import_synchronizer.SpeakingRateData();
132
+ data.addByAnnotation("Hello ", 0, 0.3);
133
+ data.addByAnnotation("world", 0.3, 0.6);
134
+ (0, import_vitest.expect)(data.pushedDuration).toBe(0.6);
135
+ const mid1 = data.accumulateTo(0.15);
136
+ (0, import_vitest.expect)(mid1).toBeGreaterThan(0);
137
+ (0, import_vitest.expect)(mid1).toBeLessThan(6);
138
+ const mid2 = data.accumulateTo(0.45);
139
+ (0, import_vitest.expect)(mid2).toBeGreaterThan(6);
140
+ });
141
+ (0, import_vitest.it)("should handle mixed rate and annotation data", () => {
142
+ const data = new import_synchronizer.SpeakingRateData();
143
+ data.addByRate(0.5, 4);
144
+ data.addByAnnotation("test", void 0, void 0);
145
+ data.addByAnnotation("", 1, void 0);
146
+ (0, import_vitest.expect)(data.timestamps).toEqual([0.5, 1]);
147
+ (0, import_vitest.expect)(data.speakIntegrals).toEqual([2, 6]);
148
+ });
149
+ });
150
+ });
151
+ //# sourceMappingURL=synchronizer.test.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/voice/transcription/synchronizer.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { SpeakingRateData } from './synchronizer.js';\n\ndescribe('SpeakingRateData', () => {\n describe('constructor', () => {\n it('should initialize with empty arrays', () => {\n const data = new SpeakingRateData();\n expect(data.timestamps).toEqual([]);\n expect(data.speakingRate).toEqual([]);\n expect(data.speakIntegrals).toEqual([]);\n expect(data.pushedDuration).toBe(0);\n });\n });\n\n describe('addByRate', () => {\n it('should add a single rate entry', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n\n expect(data.timestamps).toEqual([1.0]);\n expect(data.speakingRate).toEqual([5.0]);\n // integral = 0 + 5.0 * (1.0 - 0) = 5.0\n expect(data.speakIntegrals).toEqual([5.0]);\n expect(data.pushedDuration).toBe(1.0);\n });\n\n it('should accumulate integrals across multiple entries', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 0 + 4.0 * 1.0 = 4.0\n data.addByRate(2.0, 6.0); // integral = 4.0 + 6.0 * 1.0 = 10.0\n data.addByRate(3.5, 2.0); // integral = 10.0 + 2.0 * 1.5 = 13.0\n\n expect(data.timestamps).toEqual([1.0, 2.0, 3.5]);\n expect(data.speakingRate).toEqual([4.0, 6.0, 2.0]);\n expect(data.speakIntegrals).toEqual([4.0, 10.0, 13.0]);\n expect(data.pushedDuration).toBe(3.5);\n });\n\n it('should handle zero rate', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 0.0);\n\n expect(data.timestamps).toEqual([1.0]);\n expect(data.speakingRate).toEqual([0.0]);\n expect(data.speakIntegrals).toEqual([0.0]);\n });\n });\n\n describe('addByAnnotation', () => {\n it('should buffer text without startTime', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', undefined, undefined);\n\n // Text is buffered, no timestamp entry yet\n expect(data.timestamps).toEqual([]);\n expect(data.pushedDuration).toBe(0);\n });\n\n it('should add entry when startTime is provided', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', undefined, undefined); // buffer \"hello\"\n data.addByAnnotation('world', 1.0, undefined); // flush with startTime\n\n expect(data.timestamps).toEqual([1.0]);\n // textLen = 5 (hello), dt = 1.0, rate = 5/1 = 5.0\n expect(data.speakingRate).toEqual([5.0]);\n expect(data.speakIntegrals).toEqual([5.0]);\n });\n\n it('should handle startTime and endTime together', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello ', 0.0, 0.5);\n data.addByAnnotation('world', 0.5, 1.0);\n\n // First annotation: startTime=0.0, text=\"hello \", then recursively calls with endTime=0.5\n // Second annotation: startTime=0.5, text=\"world\", then recursively calls with endTime=1.0\n expect(data.timestamps.length).toBeGreaterThanOrEqual(2);\n expect(data.pushedDuration).toBe(1.0);\n });\n\n it('should calculate rate based on buffered text length', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('ab', undefined, undefined); // buffer 2 chars\n data.addByAnnotation('cde', undefined, undefined); // buffer 3 more chars\n data.addByAnnotation('', 2.0, undefined); // flush: textLen=5, dt=2.0, rate=2.5\n\n expect(data.timestamps).toEqual([2.0]);\n expect(data.speakingRate).toEqual([2.5]);\n expect(data.speakIntegrals).toEqual([5.0]);\n });\n\n it('should handle zero time delta gracefully', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', 0.0, undefined); // dt=0, rate should be 0\n\n expect(data.timestamps).toEqual([0.0]);\n expect(data.speakingRate).toEqual([0.0]);\n expect(data.speakIntegrals).toEqual([0.0]);\n });\n });\n\n describe('accumulateTo', () => {\n it('should return 0 for empty data', () => {\n const data = new SpeakingRateData();\n expect(data.accumulateTo(1.0)).toBe(0);\n });\n\n it('should return 0 for timestamp before first entry', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n expect(data.accumulateTo(0.5)).toBe(0);\n });\n\n it('should return exact integral at timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n expect(data.accumulateTo(1.0)).toBe(4.0);\n expect(data.accumulateTo(2.0)).toBe(10.0);\n });\n\n it('should interpolate between timestamps', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n // At 1.5: integral = 4.0 + 6.0 * 0.5 = 7.0\n expect(data.accumulateTo(1.5)).toBe(7.0);\n });\n\n it('should extrapolate beyond last timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n // At 3.0: integral = 10.0 + 6.0 * 1.0 = 16.0\n expect(data.accumulateTo(3.0)).toBe(16.0);\n });\n\n it('should not exceed next integral when interpolating', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 100.0); // integral = 100.0 (very high rate)\n data.addByRate(2.0, 1.0); // integral = 101.0\n\n // At 1.5 with rate 1.0: would be 100.0 + 1.0 * 0.5 = 100.5\n // But capped at next integral 101.0, so result is min(100.5, 101.0) = 100.5\n expect(data.accumulateTo(1.5)).toBe(100.5);\n });\n });\n\n describe('pushedDuration', () => {\n it('should return 0 when empty', () => {\n const data = new SpeakingRateData();\n expect(data.pushedDuration).toBe(0);\n });\n\n it('should return last timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n data.addByRate(2.5, 3.0);\n data.addByRate(4.0, 7.0);\n\n expect(data.pushedDuration).toBe(4.0);\n });\n });\n\n describe('integration scenarios', () => {\n it('should handle typical TTS word timing scenario', () => {\n const data = new SpeakingRateData();\n\n // Simulating words with timing: \"Hello \" at 0-0.3s, \"world\" at 0.3-0.6s\n data.addByAnnotation('Hello ', 0.0, 0.3);\n data.addByAnnotation('world', 0.3, 0.6);\n\n // Should have accumulated text lengths at each timestamp\n expect(data.pushedDuration).toBe(0.6);\n\n // At 0.15s (middle of first word), should be partway through\n const mid1 = data.accumulateTo(0.15);\n expect(mid1).toBeGreaterThan(0);\n expect(mid1).toBeLessThan(6); // \"Hello \" is 6 chars\n\n // At 0.45s (middle of second word), should be past first word\n const mid2 = data.accumulateTo(0.45);\n expect(mid2).toBeGreaterThan(6);\n });\n\n it('should handle mixed rate and annotation data', () => {\n const data = new SpeakingRateData();\n\n // Start with rate-based data\n data.addByRate(0.5, 4.0); // integral = 2.0\n\n // Then add annotation\n data.addByAnnotation('test', undefined, undefined);\n data.addByAnnotation('', 1.0, undefined); // textLen=4, dt=0.5, rate=8.0, integral = 2.0 + 4.0 = 6.0\n\n expect(data.timestamps).toEqual([0.5, 1.0]);\n expect(data.speakIntegrals).toEqual([2.0, 6.0]);\n });\n });\n});\n"],"mappings":"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","names":[]}
@@ -0,0 +1,150 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { SpeakingRateData } from "./synchronizer.js";
3
+ describe("SpeakingRateData", () => {
4
+ describe("constructor", () => {
5
+ it("should initialize with empty arrays", () => {
6
+ const data = new SpeakingRateData();
7
+ expect(data.timestamps).toEqual([]);
8
+ expect(data.speakingRate).toEqual([]);
9
+ expect(data.speakIntegrals).toEqual([]);
10
+ expect(data.pushedDuration).toBe(0);
11
+ });
12
+ });
13
+ describe("addByRate", () => {
14
+ it("should add a single rate entry", () => {
15
+ const data = new SpeakingRateData();
16
+ data.addByRate(1, 5);
17
+ expect(data.timestamps).toEqual([1]);
18
+ expect(data.speakingRate).toEqual([5]);
19
+ expect(data.speakIntegrals).toEqual([5]);
20
+ expect(data.pushedDuration).toBe(1);
21
+ });
22
+ it("should accumulate integrals across multiple entries", () => {
23
+ const data = new SpeakingRateData();
24
+ data.addByRate(1, 4);
25
+ data.addByRate(2, 6);
26
+ data.addByRate(3.5, 2);
27
+ expect(data.timestamps).toEqual([1, 2, 3.5]);
28
+ expect(data.speakingRate).toEqual([4, 6, 2]);
29
+ expect(data.speakIntegrals).toEqual([4, 10, 13]);
30
+ expect(data.pushedDuration).toBe(3.5);
31
+ });
32
+ it("should handle zero rate", () => {
33
+ const data = new SpeakingRateData();
34
+ data.addByRate(1, 0);
35
+ expect(data.timestamps).toEqual([1]);
36
+ expect(data.speakingRate).toEqual([0]);
37
+ expect(data.speakIntegrals).toEqual([0]);
38
+ });
39
+ });
40
+ describe("addByAnnotation", () => {
41
+ it("should buffer text without startTime", () => {
42
+ const data = new SpeakingRateData();
43
+ data.addByAnnotation("hello", void 0, void 0);
44
+ expect(data.timestamps).toEqual([]);
45
+ expect(data.pushedDuration).toBe(0);
46
+ });
47
+ it("should add entry when startTime is provided", () => {
48
+ const data = new SpeakingRateData();
49
+ data.addByAnnotation("hello", void 0, void 0);
50
+ data.addByAnnotation("world", 1, void 0);
51
+ expect(data.timestamps).toEqual([1]);
52
+ expect(data.speakingRate).toEqual([5]);
53
+ expect(data.speakIntegrals).toEqual([5]);
54
+ });
55
+ it("should handle startTime and endTime together", () => {
56
+ const data = new SpeakingRateData();
57
+ data.addByAnnotation("hello ", 0, 0.5);
58
+ data.addByAnnotation("world", 0.5, 1);
59
+ expect(data.timestamps.length).toBeGreaterThanOrEqual(2);
60
+ expect(data.pushedDuration).toBe(1);
61
+ });
62
+ it("should calculate rate based on buffered text length", () => {
63
+ const data = new SpeakingRateData();
64
+ data.addByAnnotation("ab", void 0, void 0);
65
+ data.addByAnnotation("cde", void 0, void 0);
66
+ data.addByAnnotation("", 2, void 0);
67
+ expect(data.timestamps).toEqual([2]);
68
+ expect(data.speakingRate).toEqual([2.5]);
69
+ expect(data.speakIntegrals).toEqual([5]);
70
+ });
71
+ it("should handle zero time delta gracefully", () => {
72
+ const data = new SpeakingRateData();
73
+ data.addByAnnotation("hello", 0, void 0);
74
+ expect(data.timestamps).toEqual([0]);
75
+ expect(data.speakingRate).toEqual([0]);
76
+ expect(data.speakIntegrals).toEqual([0]);
77
+ });
78
+ });
79
+ describe("accumulateTo", () => {
80
+ it("should return 0 for empty data", () => {
81
+ const data = new SpeakingRateData();
82
+ expect(data.accumulateTo(1)).toBe(0);
83
+ });
84
+ it("should return 0 for timestamp before first entry", () => {
85
+ const data = new SpeakingRateData();
86
+ data.addByRate(1, 5);
87
+ expect(data.accumulateTo(0.5)).toBe(0);
88
+ });
89
+ it("should return exact integral at timestamp", () => {
90
+ const data = new SpeakingRateData();
91
+ data.addByRate(1, 4);
92
+ data.addByRate(2, 6);
93
+ expect(data.accumulateTo(1)).toBe(4);
94
+ expect(data.accumulateTo(2)).toBe(10);
95
+ });
96
+ it("should interpolate between timestamps", () => {
97
+ const data = new SpeakingRateData();
98
+ data.addByRate(1, 4);
99
+ data.addByRate(2, 6);
100
+ expect(data.accumulateTo(1.5)).toBe(7);
101
+ });
102
+ it("should extrapolate beyond last timestamp", () => {
103
+ const data = new SpeakingRateData();
104
+ data.addByRate(1, 4);
105
+ data.addByRate(2, 6);
106
+ expect(data.accumulateTo(3)).toBe(16);
107
+ });
108
+ it("should not exceed next integral when interpolating", () => {
109
+ const data = new SpeakingRateData();
110
+ data.addByRate(1, 100);
111
+ data.addByRate(2, 1);
112
+ expect(data.accumulateTo(1.5)).toBe(100.5);
113
+ });
114
+ });
115
+ describe("pushedDuration", () => {
116
+ it("should return 0 when empty", () => {
117
+ const data = new SpeakingRateData();
118
+ expect(data.pushedDuration).toBe(0);
119
+ });
120
+ it("should return last timestamp", () => {
121
+ const data = new SpeakingRateData();
122
+ data.addByRate(1, 5);
123
+ data.addByRate(2.5, 3);
124
+ data.addByRate(4, 7);
125
+ expect(data.pushedDuration).toBe(4);
126
+ });
127
+ });
128
+ describe("integration scenarios", () => {
129
+ it("should handle typical TTS word timing scenario", () => {
130
+ const data = new SpeakingRateData();
131
+ data.addByAnnotation("Hello ", 0, 0.3);
132
+ data.addByAnnotation("world", 0.3, 0.6);
133
+ expect(data.pushedDuration).toBe(0.6);
134
+ const mid1 = data.accumulateTo(0.15);
135
+ expect(mid1).toBeGreaterThan(0);
136
+ expect(mid1).toBeLessThan(6);
137
+ const mid2 = data.accumulateTo(0.45);
138
+ expect(mid2).toBeGreaterThan(6);
139
+ });
140
+ it("should handle mixed rate and annotation data", () => {
141
+ const data = new SpeakingRateData();
142
+ data.addByRate(0.5, 4);
143
+ data.addByAnnotation("test", void 0, void 0);
144
+ data.addByAnnotation("", 1, void 0);
145
+ expect(data.timestamps).toEqual([0.5, 1]);
146
+ expect(data.speakIntegrals).toEqual([2, 6]);
147
+ });
148
+ });
149
+ });
150
+ //# sourceMappingURL=synchronizer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/voice/transcription/synchronizer.test.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2024 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { describe, expect, it } from 'vitest';\nimport { SpeakingRateData } from './synchronizer.js';\n\ndescribe('SpeakingRateData', () => {\n describe('constructor', () => {\n it('should initialize with empty arrays', () => {\n const data = new SpeakingRateData();\n expect(data.timestamps).toEqual([]);\n expect(data.speakingRate).toEqual([]);\n expect(data.speakIntegrals).toEqual([]);\n expect(data.pushedDuration).toBe(0);\n });\n });\n\n describe('addByRate', () => {\n it('should add a single rate entry', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n\n expect(data.timestamps).toEqual([1.0]);\n expect(data.speakingRate).toEqual([5.0]);\n // integral = 0 + 5.0 * (1.0 - 0) = 5.0\n expect(data.speakIntegrals).toEqual([5.0]);\n expect(data.pushedDuration).toBe(1.0);\n });\n\n it('should accumulate integrals across multiple entries', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 0 + 4.0 * 1.0 = 4.0\n data.addByRate(2.0, 6.0); // integral = 4.0 + 6.0 * 1.0 = 10.0\n data.addByRate(3.5, 2.0); // integral = 10.0 + 2.0 * 1.5 = 13.0\n\n expect(data.timestamps).toEqual([1.0, 2.0, 3.5]);\n expect(data.speakingRate).toEqual([4.0, 6.0, 2.0]);\n expect(data.speakIntegrals).toEqual([4.0, 10.0, 13.0]);\n expect(data.pushedDuration).toBe(3.5);\n });\n\n it('should handle zero rate', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 0.0);\n\n expect(data.timestamps).toEqual([1.0]);\n expect(data.speakingRate).toEqual([0.0]);\n expect(data.speakIntegrals).toEqual([0.0]);\n });\n });\n\n describe('addByAnnotation', () => {\n it('should buffer text without startTime', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', undefined, undefined);\n\n // Text is buffered, no timestamp entry yet\n expect(data.timestamps).toEqual([]);\n expect(data.pushedDuration).toBe(0);\n });\n\n it('should add entry when startTime is provided', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', undefined, undefined); // buffer \"hello\"\n data.addByAnnotation('world', 1.0, undefined); // flush with startTime\n\n expect(data.timestamps).toEqual([1.0]);\n // textLen = 5 (hello), dt = 1.0, rate = 5/1 = 5.0\n expect(data.speakingRate).toEqual([5.0]);\n expect(data.speakIntegrals).toEqual([5.0]);\n });\n\n it('should handle startTime and endTime together', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello ', 0.0, 0.5);\n data.addByAnnotation('world', 0.5, 1.0);\n\n // First annotation: startTime=0.0, text=\"hello \", then recursively calls with endTime=0.5\n // Second annotation: startTime=0.5, text=\"world\", then recursively calls with endTime=1.0\n expect(data.timestamps.length).toBeGreaterThanOrEqual(2);\n expect(data.pushedDuration).toBe(1.0);\n });\n\n it('should calculate rate based on buffered text length', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('ab', undefined, undefined); // buffer 2 chars\n data.addByAnnotation('cde', undefined, undefined); // buffer 3 more chars\n data.addByAnnotation('', 2.0, undefined); // flush: textLen=5, dt=2.0, rate=2.5\n\n expect(data.timestamps).toEqual([2.0]);\n expect(data.speakingRate).toEqual([2.5]);\n expect(data.speakIntegrals).toEqual([5.0]);\n });\n\n it('should handle zero time delta gracefully', () => {\n const data = new SpeakingRateData();\n data.addByAnnotation('hello', 0.0, undefined); // dt=0, rate should be 0\n\n expect(data.timestamps).toEqual([0.0]);\n expect(data.speakingRate).toEqual([0.0]);\n expect(data.speakIntegrals).toEqual([0.0]);\n });\n });\n\n describe('accumulateTo', () => {\n it('should return 0 for empty data', () => {\n const data = new SpeakingRateData();\n expect(data.accumulateTo(1.0)).toBe(0);\n });\n\n it('should return 0 for timestamp before first entry', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n expect(data.accumulateTo(0.5)).toBe(0);\n });\n\n it('should return exact integral at timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n expect(data.accumulateTo(1.0)).toBe(4.0);\n expect(data.accumulateTo(2.0)).toBe(10.0);\n });\n\n it('should interpolate between timestamps', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n // At 1.5: integral = 4.0 + 6.0 * 0.5 = 7.0\n expect(data.accumulateTo(1.5)).toBe(7.0);\n });\n\n it('should extrapolate beyond last timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 4.0); // integral = 4.0\n data.addByRate(2.0, 6.0); // integral = 10.0\n\n // At 3.0: integral = 10.0 + 6.0 * 1.0 = 16.0\n expect(data.accumulateTo(3.0)).toBe(16.0);\n });\n\n it('should not exceed next integral when interpolating', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 100.0); // integral = 100.0 (very high rate)\n data.addByRate(2.0, 1.0); // integral = 101.0\n\n // At 1.5 with rate 1.0: would be 100.0 + 1.0 * 0.5 = 100.5\n // But capped at next integral 101.0, so result is min(100.5, 101.0) = 100.5\n expect(data.accumulateTo(1.5)).toBe(100.5);\n });\n });\n\n describe('pushedDuration', () => {\n it('should return 0 when empty', () => {\n const data = new SpeakingRateData();\n expect(data.pushedDuration).toBe(0);\n });\n\n it('should return last timestamp', () => {\n const data = new SpeakingRateData();\n data.addByRate(1.0, 5.0);\n data.addByRate(2.5, 3.0);\n data.addByRate(4.0, 7.0);\n\n expect(data.pushedDuration).toBe(4.0);\n });\n });\n\n describe('integration scenarios', () => {\n it('should handle typical TTS word timing scenario', () => {\n const data = new SpeakingRateData();\n\n // Simulating words with timing: \"Hello \" at 0-0.3s, \"world\" at 0.3-0.6s\n data.addByAnnotation('Hello ', 0.0, 0.3);\n data.addByAnnotation('world', 0.3, 0.6);\n\n // Should have accumulated text lengths at each timestamp\n expect(data.pushedDuration).toBe(0.6);\n\n // At 0.15s (middle of first word), should be partway through\n const mid1 = data.accumulateTo(0.15);\n expect(mid1).toBeGreaterThan(0);\n expect(mid1).toBeLessThan(6); // \"Hello \" is 6 chars\n\n // At 0.45s (middle of second word), should be past first word\n const mid2 = data.accumulateTo(0.45);\n expect(mid2).toBeGreaterThan(6);\n });\n\n it('should handle mixed rate and annotation data', () => {\n const data = new SpeakingRateData();\n\n // Start with rate-based data\n data.addByRate(0.5, 4.0); // integral = 2.0\n\n // Then add annotation\n data.addByAnnotation('test', undefined, undefined);\n data.addByAnnotation('', 1.0, undefined); // textLen=4, dt=0.5, rate=8.0, integral = 2.0 + 4.0 = 6.0\n\n expect(data.timestamps).toEqual([0.5, 1.0]);\n expect(data.speakIntegrals).toEqual([2.0, 6.0]);\n });\n });\n});\n"],"mappings":"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","names":[]}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var endpointing_exports = {};
20
+ __export(endpointing_exports, {
21
+ defaultEndpointingOptions: () => defaultEndpointingOptions
22
+ });
23
+ module.exports = __toCommonJS(endpointing_exports);
24
+ const defaultEndpointingOptions = {
25
+ mode: "fixed",
26
+ minDelay: 500,
27
+ maxDelay: 3e3
28
+ };
29
+ // Annotate the CommonJS export names for ESM import in node:
30
+ 0 && (module.exports = {
31
+ defaultEndpointingOptions
32
+ });
33
+ //# sourceMappingURL=endpointing.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/voice/turn_config/endpointing.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Configuration for endpointing, which determines when the user's turn is complete.\n */\nexport interface EndpointingOptions {\n /**\n * Endpointing mode. `\"fixed\"` uses a fixed delay, `\"dynamic\"` adjusts delay based on\n * end-of-utterance prediction.\n * @defaultValue \"fixed\"\n */\n mode: 'fixed' | 'dynamic';\n /**\n * Minimum time in milliseconds since the last detected speech before the agent declares the user's\n * turn complete. In VAD mode this effectively behaves like `max(VAD silence, minDelay)`;\n * in STT mode it is applied after the STT end-of-speech signal, so it can be additive with\n * the STT provider's endpointing delay.\n * @defaultValue 500\n */\n minDelay: number;\n /**\n * Maximum time in milliseconds the agent will wait before terminating the turn.\n * @defaultValue 3000\n */\n maxDelay: number;\n}\n\nexport const defaultEndpointingOptions = {\n mode: 'fixed',\n minDelay: 500,\n maxDelay: 3000,\n} as const satisfies EndpointingOptions;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA4BO,MAAM,4BAA4B;AAAA,EACvC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AACZ;","names":[]}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Configuration for endpointing, which determines when the user's turn is complete.
3
+ */
4
+ export interface EndpointingOptions {
5
+ /**
6
+ * Endpointing mode. `"fixed"` uses a fixed delay, `"dynamic"` adjusts delay based on
7
+ * end-of-utterance prediction.
8
+ * @defaultValue "fixed"
9
+ */
10
+ mode: 'fixed' | 'dynamic';
11
+ /**
12
+ * Minimum time in milliseconds since the last detected speech before the agent declares the user's
13
+ * turn complete. In VAD mode this effectively behaves like `max(VAD silence, minDelay)`;
14
+ * in STT mode it is applied after the STT end-of-speech signal, so it can be additive with
15
+ * the STT provider's endpointing delay.
16
+ * @defaultValue 500
17
+ */
18
+ minDelay: number;
19
+ /**
20
+ * Maximum time in milliseconds the agent will wait before terminating the turn.
21
+ * @defaultValue 3000
22
+ */
23
+ maxDelay: number;
24
+ }
25
+ export declare const defaultEndpointingOptions: {
26
+ readonly mode: "fixed";
27
+ readonly minDelay: 500;
28
+ readonly maxDelay: 3000;
29
+ };
30
+ //# sourceMappingURL=endpointing.d.ts.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Configuration for endpointing, which determines when the user's turn is complete.
3
+ */
4
+ export interface EndpointingOptions {
5
+ /**
6
+ * Endpointing mode. `"fixed"` uses a fixed delay, `"dynamic"` adjusts delay based on
7
+ * end-of-utterance prediction.
8
+ * @defaultValue "fixed"
9
+ */
10
+ mode: 'fixed' | 'dynamic';
11
+ /**
12
+ * Minimum time in milliseconds since the last detected speech before the agent declares the user's
13
+ * turn complete. In VAD mode this effectively behaves like `max(VAD silence, minDelay)`;
14
+ * in STT mode it is applied after the STT end-of-speech signal, so it can be additive with
15
+ * the STT provider's endpointing delay.
16
+ * @defaultValue 500
17
+ */
18
+ minDelay: number;
19
+ /**
20
+ * Maximum time in milliseconds the agent will wait before terminating the turn.
21
+ * @defaultValue 3000
22
+ */
23
+ maxDelay: number;
24
+ }
25
+ export declare const defaultEndpointingOptions: {
26
+ readonly mode: "fixed";
27
+ readonly minDelay: 500;
28
+ readonly maxDelay: 3000;
29
+ };
30
+ //# sourceMappingURL=endpointing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"endpointing.d.ts","sourceRoot":"","sources":["../../../src/voice/turn_config/endpointing.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,yBAAyB;;;;CAIC,CAAC"}
@@ -0,0 +1,9 @@
1
+ const defaultEndpointingOptions = {
2
+ mode: "fixed",
3
+ minDelay: 500,
4
+ maxDelay: 3e3
5
+ };
6
+ export {
7
+ defaultEndpointingOptions
8
+ };
9
+ //# sourceMappingURL=endpointing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/voice/turn_config/endpointing.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2026 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n/**\n * Configuration for endpointing, which determines when the user's turn is complete.\n */\nexport interface EndpointingOptions {\n /**\n * Endpointing mode. `\"fixed\"` uses a fixed delay, `\"dynamic\"` adjusts delay based on\n * end-of-utterance prediction.\n * @defaultValue \"fixed\"\n */\n mode: 'fixed' | 'dynamic';\n /**\n * Minimum time in milliseconds since the last detected speech before the agent declares the user's\n * turn complete. In VAD mode this effectively behaves like `max(VAD silence, minDelay)`;\n * in STT mode it is applied after the STT end-of-speech signal, so it can be additive with\n * the STT provider's endpointing delay.\n * @defaultValue 500\n */\n minDelay: number;\n /**\n * Maximum time in milliseconds the agent will wait before terminating the turn.\n * @defaultValue 3000\n */\n maxDelay: number;\n}\n\nexport const defaultEndpointingOptions = {\n mode: 'fixed',\n minDelay: 500,\n maxDelay: 3000,\n} as const satisfies EndpointingOptions;\n"],"mappings":"AA4BO,MAAM,4BAA4B;AAAA,EACvC,MAAM;AAAA,EACN,UAAU;AAAA,EACV,UAAU;AACZ;","names":[]}