@pipedog/core 0.0.0 → 0.1.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 (698) hide show
  1. package/LICENSE +25 -0
  2. package/NOTICE +9 -0
  3. package/dist/adapters/base_llm_adapter.d.ts +266 -0
  4. package/dist/adapters/base_llm_adapter.js +489 -0
  5. package/dist/adapters/index.d.ts +2 -0
  6. package/dist/adapters/index.js +8 -0
  7. package/dist/adapters/schemas/direct_function.d.ts +202 -0
  8. package/dist/adapters/schemas/direct_function.js +302 -0
  9. package/dist/adapters/schemas/function_schema.d.ts +67 -0
  10. package/dist/adapters/schemas/function_schema.js +72 -0
  11. package/dist/adapters/schemas/index.d.ts +2 -0
  12. package/dist/adapters/schemas/index.js +8 -0
  13. package/dist/adapters/schemas/tools_schema.d.ts +57 -0
  14. package/dist/adapters/schemas/tools_schema.js +79 -0
  15. package/dist/adapters/services/index.d.ts +2 -0
  16. package/dist/adapters/services/index.js +8 -0
  17. package/dist/adapters/services/open_ai_adapter.d.ts +147 -0
  18. package/dist/adapters/services/open_ai_adapter.js +250 -0
  19. package/dist/audio/dtmf/dtmf_data.d.ts +3 -0
  20. package/dist/audio/dtmf/dtmf_data.js +22 -0
  21. package/dist/audio/dtmf/index.d.ts +2 -0
  22. package/dist/audio/dtmf/index.js +8 -0
  23. package/dist/audio/dtmf/types.d.ts +31 -0
  24. package/dist/audio/dtmf/types.js +35 -0
  25. package/dist/audio/dtmf/utils.d.ts +13 -0
  26. package/dist/audio/dtmf/utils.js +62 -0
  27. package/dist/audio/filters/base_audio_filter.d.ts +51 -0
  28. package/dist/audio/filters/base_audio_filter.js +17 -0
  29. package/dist/audio/filters/index.d.ts +2 -0
  30. package/dist/audio/filters/index.js +8 -0
  31. package/dist/audio/index.d.ts +2 -0
  32. package/dist/audio/index.js +8 -0
  33. package/dist/audio/krisp_instance.d.ts +95 -0
  34. package/dist/audio/krisp_instance.js +229 -0
  35. package/dist/audio/mixers/base_audio_mixer.d.ts +56 -0
  36. package/dist/audio/mixers/base_audio_mixer.js +16 -0
  37. package/dist/audio/mixers/index.d.ts +2 -0
  38. package/dist/audio/mixers/index.js +8 -0
  39. package/dist/audio/resamplers/base_audio_resampler.d.ts +46 -0
  40. package/dist/audio/resamplers/base_audio_resampler.js +35 -0
  41. package/dist/audio/resamplers/index.d.ts +2 -0
  42. package/dist/audio/resamplers/index.js +8 -0
  43. package/dist/audio/resamplers/resampy_resampler.d.ts +10 -0
  44. package/dist/audio/resamplers/resampy_resampler.js +15 -0
  45. package/dist/audio/resamplers/soxr_resampler.d.ts +54 -0
  46. package/dist/audio/resamplers/soxr_resampler.js +69 -0
  47. package/dist/audio/resamplers/soxr_stream_resampler.d.ts +75 -0
  48. package/dist/audio/resamplers/soxr_stream_resampler.js +146 -0
  49. package/dist/audio/turn/base_turn_analyzer.d.ts +84 -0
  50. package/dist/audio/turn/base_turn_analyzer.js +61 -0
  51. package/dist/audio/turn/index.d.ts +2 -0
  52. package/dist/audio/turn/index.js +8 -0
  53. package/dist/audio/turn/smart_turn/_whisper_features.d.ts +30 -0
  54. package/dist/audio/turn/smart_turn/_whisper_features.js +225 -0
  55. package/dist/audio/turn/smart_turn/base_smart_turn.d.ts +94 -0
  56. package/dist/audio/turn/smart_turn/base_smart_turn.js +239 -0
  57. package/dist/audio/turn/smart_turn/local_smart_turn_v3.d.ts +76 -0
  58. package/dist/audio/turn/smart_turn/local_smart_turn_v3.js +143 -0
  59. package/dist/audio/utils.d.ts +224 -0
  60. package/dist/audio/utils.js +634 -0
  61. package/dist/audio/vad/data/index.d.ts +2 -0
  62. package/dist/audio/vad/data/index.js +8 -0
  63. package/dist/audio/vad/index.d.ts +2 -0
  64. package/dist/audio/vad/index.js +8 -0
  65. package/dist/audio/vad/vad_analyzer.d.ts +126 -0
  66. package/dist/audio/vad/vad_analyzer.js +211 -0
  67. package/dist/audio/vad/vad_controller.d.ts +143 -0
  68. package/dist/audio/vad/vad_controller.js +228 -0
  69. package/dist/audio/volume.d.ts +38 -0
  70. package/dist/audio/volume.js +75 -0
  71. package/dist/bus/adapters/base.d.ts +35 -0
  72. package/dist/bus/adapters/base.js +19 -0
  73. package/dist/bus/adapters/index.d.ts +10 -0
  74. package/dist/bus/adapters/index.js +15 -0
  75. package/dist/bus/adapters/llm_context_adapter.d.ts +36 -0
  76. package/dist/bus/adapters/llm_context_adapter.js +82 -0
  77. package/dist/bus/adapters/tools_schema_adapter.d.ts +25 -0
  78. package/dist/bus/adapters/tools_schema_adapter.js +70 -0
  79. package/dist/bus/bridge_processor.d.ts +111 -0
  80. package/dist/bus/bridge_processor.js +166 -0
  81. package/dist/bus/bus.d.ts +110 -0
  82. package/dist/bus/bus.js +200 -0
  83. package/dist/bus/index.d.ts +22 -0
  84. package/dist/bus/index.js +27 -0
  85. package/dist/bus/local/async_queue.d.ts +12 -0
  86. package/dist/bus/local/async_queue.js +21 -0
  87. package/dist/bus/local/index.d.ts +3 -0
  88. package/dist/bus/local/index.js +8 -0
  89. package/dist/bus/messages.d.ts +378 -0
  90. package/dist/bus/messages.js +357 -0
  91. package/dist/bus/network/index.d.ts +16 -0
  92. package/dist/bus/network/index.js +21 -0
  93. package/dist/bus/queue.d.ts +27 -0
  94. package/dist/bus/queue.js +51 -0
  95. package/dist/bus/serializers/base.d.ts +25 -0
  96. package/dist/bus/serializers/base.js +14 -0
  97. package/dist/bus/serializers/index.d.ts +9 -0
  98. package/dist/bus/serializers/index.js +14 -0
  99. package/dist/bus/serializers/json.d.ts +77 -0
  100. package/dist/bus/serializers/json.js +367 -0
  101. package/dist/bus/subscriber.d.ts +46 -0
  102. package/dist/bus/subscriber.js +27 -0
  103. package/dist/bus/ui/index.d.ts +3 -0
  104. package/dist/bus/ui/index.js +8 -0
  105. package/dist/bus/ui/messages.d.ts +186 -0
  106. package/dist/bus/ui/messages.js +171 -0
  107. package/dist/classifiers/base_classifier.d.ts +263 -0
  108. package/dist/classifiers/base_classifier.js +346 -0
  109. package/dist/classifiers/cloudflare/clef/classifier.d.ts +70 -0
  110. package/dist/classifiers/cloudflare/clef/classifier.js +204 -0
  111. package/dist/classifiers/cloudflare/clef/client.d.ts +104 -0
  112. package/dist/classifiers/cloudflare/clef/client.js +234 -0
  113. package/dist/classifiers/cloudflare/clef/index.d.ts +2 -0
  114. package/dist/classifiers/cloudflare/clef/index.js +8 -0
  115. package/dist/classifiers/cloudflare/index.d.ts +2 -0
  116. package/dist/classifiers/cloudflare/index.js +8 -0
  117. package/dist/classifiers/http_client.d.ts +70 -0
  118. package/dist/classifiers/http_client.js +161 -0
  119. package/dist/classifiers/index.d.ts +2 -0
  120. package/dist/classifiers/index.js +8 -0
  121. package/dist/classifiers/jev/classifier.d.ts +8 -0
  122. package/dist/classifiers/jev/classifier.js +13 -0
  123. package/dist/classifiers/jev/client.d.ts +8 -0
  124. package/dist/classifiers/jev/client.js +13 -0
  125. package/dist/classifiers/jev/index.d.ts +2 -0
  126. package/dist/classifiers/jev/index.js +8 -0
  127. package/dist/classifiers/llm/classifier.d.ts +72 -0
  128. package/dist/classifiers/llm/classifier.js +304 -0
  129. package/dist/classifiers/llm/index.d.ts +2 -0
  130. package/dist/classifiers/llm/index.js +8 -0
  131. package/dist/classifiers/openai/decisions/classifier.d.ts +77 -0
  132. package/dist/classifiers/openai/decisions/classifier.js +241 -0
  133. package/dist/classifiers/openai/decisions/client.d.ts +108 -0
  134. package/dist/classifiers/openai/decisions/client.js +234 -0
  135. package/dist/classifiers/openai/decisions/index.d.ts +2 -0
  136. package/dist/classifiers/openai/decisions/index.js +8 -0
  137. package/dist/classifiers/openai/index.d.ts +2 -0
  138. package/dist/classifiers/openai/index.js +8 -0
  139. package/dist/classifiers/typesafe/index.d.ts +2 -0
  140. package/dist/classifiers/typesafe/index.js +8 -0
  141. package/dist/classifiers/typesafe/jev/classifier.d.ts +70 -0
  142. package/dist/classifiers/typesafe/jev/classifier.js +220 -0
  143. package/dist/classifiers/typesafe/jev/client.d.ts +105 -0
  144. package/dist/classifiers/typesafe/jev/client.js +233 -0
  145. package/dist/classifiers/typesafe/jev/index.d.ts +2 -0
  146. package/dist/classifiers/typesafe/jev/index.js +8 -0
  147. package/dist/clocks/base_clock.d.ts +13 -0
  148. package/dist/clocks/base_clock.js +14 -0
  149. package/dist/clocks/index.d.ts +2 -0
  150. package/dist/clocks/index.js +8 -0
  151. package/dist/clocks/system_clock.d.ts +15 -0
  152. package/dist/clocks/system_clock.js +25 -0
  153. package/dist/frames/frames.d.ts +2533 -0
  154. package/dist/frames/frames.js +2485 -0
  155. package/dist/frames/index.d.ts +2 -0
  156. package/dist/frames/index.js +8 -0
  157. package/dist/frames/protobufs/frames_pb2.d.ts +229 -0
  158. package/dist/frames/protobufs/frames_pb2.js +466 -0
  159. package/dist/index.d.ts +11 -0
  160. package/dist/index.js +56 -0
  161. package/dist/metrics/index.d.ts +2 -0
  162. package/dist/metrics/index.js +8 -0
  163. package/dist/metrics/metrics.d.ts +276 -0
  164. package/dist/metrics/metrics.js +254 -0
  165. package/dist/observers/base_observer.d.ts +173 -0
  166. package/dist/observers/base_observer.js +151 -0
  167. package/dist/observers/error_observer.d.ts +75 -0
  168. package/dist/observers/error_observer.js +87 -0
  169. package/dist/observers/function_call_observer.d.ts +157 -0
  170. package/dist/observers/function_call_observer.js +222 -0
  171. package/dist/observers/index.d.ts +2 -0
  172. package/dist/observers/index.js +8 -0
  173. package/dist/observers/loggers/debug_log_observer.d.ts +91 -0
  174. package/dist/observers/loggers/debug_log_observer.js +228 -0
  175. package/dist/observers/loggers/index.d.ts +2 -0
  176. package/dist/observers/loggers/index.js +8 -0
  177. package/dist/observers/loggers/llm_log_observer.d.ts +24 -0
  178. package/dist/observers/loggers/llm_log_observer.js +110 -0
  179. package/dist/observers/loggers/metrics_log_observer.d.ts +85 -0
  180. package/dist/observers/loggers/metrics_log_observer.js +186 -0
  181. package/dist/observers/loggers/transcription_log_observer.d.ts +23 -0
  182. package/dist/observers/loggers/transcription_log_observer.js +74 -0
  183. package/dist/observers/service_metrics_observer.d.ts +168 -0
  184. package/dist/observers/service_metrics_observer.js +236 -0
  185. package/dist/observers/speaking_observer.d.ts +104 -0
  186. package/dist/observers/speaking_observer.js +150 -0
  187. package/dist/observers/startup_timing_observer.d.ts +221 -0
  188. package/dist/observers/startup_timing_observer.js +365 -0
  189. package/dist/observers/turn_tracking_observer.d.ts +78 -0
  190. package/dist/observers/turn_tracking_observer.js +189 -0
  191. package/dist/observers/user_bot_latency_observer.d.ts +411 -0
  192. package/dist/observers/user_bot_latency_observer.js +974 -0
  193. package/dist/pipeline/base_pipeline.d.ts +49 -0
  194. package/dist/pipeline/base_pipeline.js +101 -0
  195. package/dist/pipeline/capabilities.d.ts +76 -0
  196. package/dist/pipeline/capabilities.js +97 -0
  197. package/dist/pipeline/index.d.ts +2 -0
  198. package/dist/pipeline/index.js +8 -0
  199. package/dist/pipeline/job_context.d.ts +317 -0
  200. package/dist/pipeline/job_context.js +450 -0
  201. package/dist/pipeline/job_decorator.d.ts +54 -0
  202. package/dist/pipeline/job_decorator.js +92 -0
  203. package/dist/pipeline/llm_switcher.d.ts +84 -0
  204. package/dist/pipeline/llm_switcher.js +107 -0
  205. package/dist/pipeline/llm_with_backend.d.ts +435 -0
  206. package/dist/pipeline/llm_with_backend.js +806 -0
  207. package/dist/pipeline/parallel_pipeline.d.ts +83 -0
  208. package/dist/pipeline/parallel_pipeline.js +200 -0
  209. package/dist/pipeline/pipeline.d.ts +126 -0
  210. package/dist/pipeline/pipeline.js +196 -0
  211. package/dist/pipeline/service_switcher.d.ts +287 -0
  212. package/dist/pipeline/service_switcher.js +509 -0
  213. package/dist/pipeline/sync_parallel_pipeline.d.ts +192 -0
  214. package/dist/pipeline/sync_parallel_pipeline.js +387 -0
  215. package/dist/pipeline/worker.d.ts +814 -0
  216. package/dist/pipeline/worker.js +1639 -0
  217. package/dist/pipeline/worker_observer.d.ts +131 -0
  218. package/dist/pipeline/worker_observer.js +274 -0
  219. package/dist/pipeline/worker_ready_decorator.d.ts +45 -0
  220. package/dist/pipeline/worker_ready_decorator.js +76 -0
  221. package/dist/processors/aggregators/async_tool_messages.d.ts +146 -0
  222. package/dist/processors/aggregators/async_tool_messages.js +291 -0
  223. package/dist/processors/aggregators/dtmf_aggregator.d.ts +61 -0
  224. package/dist/processors/aggregators/dtmf_aggregator.js +144 -0
  225. package/dist/processors/aggregators/gated.d.ts +43 -0
  226. package/dist/processors/aggregators/gated.js +86 -0
  227. package/dist/processors/aggregators/gated_llm_context.d.ts +42 -0
  228. package/dist/processors/aggregators/gated_llm_context.js +86 -0
  229. package/dist/processors/aggregators/index.d.ts +2 -0
  230. package/dist/processors/aggregators/index.js +8 -0
  231. package/dist/processors/aggregators/llm_context.d.ts +370 -0
  232. package/dist/processors/aggregators/llm_context.js +580 -0
  233. package/dist/processors/aggregators/llm_context_summarizer.d.ts +219 -0
  234. package/dist/processors/aggregators/llm_context_summarizer.js +447 -0
  235. package/dist/processors/aggregators/llm_response.d.ts +37 -0
  236. package/dist/processors/aggregators/llm_response.js +71 -0
  237. package/dist/processors/aggregators/llm_response_universal.d.ts +966 -0
  238. package/dist/processors/aggregators/llm_response_universal.js +2394 -0
  239. package/dist/processors/aggregators/llm_text_processor.d.ts +53 -0
  240. package/dist/processors/aggregators/llm_text_processor.js +102 -0
  241. package/dist/processors/aggregators/sentence.d.ts +39 -0
  242. package/dist/processors/aggregators/sentence.js +69 -0
  243. package/dist/processors/async_generator.d.ts +42 -0
  244. package/dist/processors/async_generator.js +76 -0
  245. package/dist/processors/audio/audio_buffer_processor.d.ts +241 -0
  246. package/dist/processors/audio/audio_buffer_processor.js +565 -0
  247. package/dist/processors/audio/index.d.ts +2 -0
  248. package/dist/processors/audio/index.js +8 -0
  249. package/dist/processors/audio/vad_processor.d.ts +67 -0
  250. package/dist/processors/audio/vad_processor.js +103 -0
  251. package/dist/processors/consumer_processor.d.ts +49 -0
  252. package/dist/processors/consumer_processor.js +85 -0
  253. package/dist/processors/filters/frame_filter.d.ts +39 -0
  254. package/dist/processors/filters/frame_filter.js +52 -0
  255. package/dist/processors/filters/function_filter.d.ts +59 -0
  256. package/dist/processors/filters/function_filter.js +78 -0
  257. package/dist/processors/filters/identity_filter.d.ts +33 -0
  258. package/dist/processors/filters/identity_filter.js +37 -0
  259. package/dist/processors/filters/index.d.ts +2 -0
  260. package/dist/processors/filters/index.js +8 -0
  261. package/dist/processors/filters/null_filter.d.ts +34 -0
  262. package/dist/processors/filters/null_filter.js +47 -0
  263. package/dist/processors/filters/wake_notifier_filter.d.ts +48 -0
  264. package/dist/processors/filters/wake_notifier_filter.js +49 -0
  265. package/dist/processors/frame_processor.d.ts +360 -0
  266. package/dist/processors/frame_processor.js +932 -0
  267. package/dist/processors/frameworks/index.d.ts +2 -0
  268. package/dist/processors/frameworks/index.js +8 -0
  269. package/dist/processors/frameworks/rtvi/frames.d.ts +241 -0
  270. package/dist/processors/frameworks/rtvi/frames.js +259 -0
  271. package/dist/processors/frameworks/rtvi/index.d.ts +6 -0
  272. package/dist/processors/frameworks/rtvi/index.js +11 -0
  273. package/dist/processors/frameworks/rtvi/models.d.ts +1741 -0
  274. package/dist/processors/frameworks/rtvi/models.js +1807 -0
  275. package/dist/processors/frameworks/rtvi/observer.d.ts +340 -0
  276. package/dist/processors/frameworks/rtvi/observer.js +1113 -0
  277. package/dist/processors/frameworks/rtvi/processor.d.ts +182 -0
  278. package/dist/processors/frameworks/rtvi/processor.js +676 -0
  279. package/dist/processors/idle_frame_processor.d.ts +44 -0
  280. package/dist/processors/idle_frame_processor.js +86 -0
  281. package/dist/processors/index.d.ts +2 -0
  282. package/dist/processors/index.js +8 -0
  283. package/dist/processors/logger.d.ts +42 -0
  284. package/dist/processors/logger.js +87 -0
  285. package/dist/processors/metrics/frame_processor_metrics.d.ts +172 -0
  286. package/dist/processors/metrics/frame_processor_metrics.js +372 -0
  287. package/dist/processors/metrics/index.d.ts +2 -0
  288. package/dist/processors/metrics/index.js +8 -0
  289. package/dist/processors/producer_processor.d.ts +68 -0
  290. package/dist/processors/producer_processor.js +78 -0
  291. package/dist/processors/text_transformer.d.ts +30 -0
  292. package/dist/processors/text_transformer.js +48 -0
  293. package/dist/registry/index.d.ts +3 -0
  294. package/dist/registry/index.js +8 -0
  295. package/dist/registry/registry.d.ts +69 -0
  296. package/dist/registry/registry.js +149 -0
  297. package/dist/registry/types.d.ts +47 -0
  298. package/dist/registry/types.js +39 -0
  299. package/dist/serializers/base_serializer.d.ts +94 -0
  300. package/dist/serializers/base_serializer.js +82 -0
  301. package/dist/serializers/index.d.ts +2 -0
  302. package/dist/serializers/index.js +8 -0
  303. package/dist/services/ai_service.d.ts +145 -0
  304. package/dist/services/ai_service.js +262 -0
  305. package/dist/services/anthropic/index.d.ts +2 -0
  306. package/dist/services/anthropic/index.js +7 -0
  307. package/dist/services/assemblyai/index.d.ts +2 -0
  308. package/dist/services/assemblyai/index.js +7 -0
  309. package/dist/services/asyncai/index.d.ts +2 -0
  310. package/dist/services/asyncai/index.js +7 -0
  311. package/dist/services/aws/index.d.ts +2 -0
  312. package/dist/services/aws/index.js +7 -0
  313. package/dist/services/azure/index.d.ts +2 -0
  314. package/dist/services/azure/index.js +7 -0
  315. package/dist/services/baseten/index.d.ts +2 -0
  316. package/dist/services/baseten/index.js +7 -0
  317. package/dist/services/bland/index.d.ts +2 -0
  318. package/dist/services/bland/index.js +7 -0
  319. package/dist/services/camb/index.d.ts +2 -0
  320. package/dist/services/camb/index.js +7 -0
  321. package/dist/services/cartesia/index.d.ts +2 -0
  322. package/dist/services/cartesia/index.js +7 -0
  323. package/dist/services/cerebras/index.d.ts +2 -0
  324. package/dist/services/cerebras/index.js +7 -0
  325. package/dist/services/crusoe/index.d.ts +2 -0
  326. package/dist/services/crusoe/index.js +7 -0
  327. package/dist/services/deepgram/index.d.ts +2 -0
  328. package/dist/services/deepgram/index.js +7 -0
  329. package/dist/services/deepseek/index.d.ts +2 -0
  330. package/dist/services/deepseek/index.js +7 -0
  331. package/dist/services/elevenlabs/index.d.ts +2 -0
  332. package/dist/services/elevenlabs/index.js +7 -0
  333. package/dist/services/fal/index.d.ts +2 -0
  334. package/dist/services/fal/index.js +7 -0
  335. package/dist/services/fireworks/index.d.ts +2 -0
  336. package/dist/services/fireworks/index.js +7 -0
  337. package/dist/services/fish/index.d.ts +2 -0
  338. package/dist/services/fish/index.js +7 -0
  339. package/dist/services/funasr/index.d.ts +3 -0
  340. package/dist/services/funasr/index.js +8 -0
  341. package/dist/services/gladia/index.d.ts +2 -0
  342. package/dist/services/gladia/index.js +7 -0
  343. package/dist/services/google/index.d.ts +2 -0
  344. package/dist/services/google/index.js +7 -0
  345. package/dist/services/gradium/index.d.ts +2 -0
  346. package/dist/services/gradium/index.js +7 -0
  347. package/dist/services/grok/index.d.ts +2 -0
  348. package/dist/services/grok/index.js +7 -0
  349. package/dist/services/groq/index.d.ts +2 -0
  350. package/dist/services/groq/index.js +7 -0
  351. package/dist/services/heygen/index.d.ts +2 -0
  352. package/dist/services/heygen/index.js +7 -0
  353. package/dist/services/hume/index.d.ts +2 -0
  354. package/dist/services/hume/index.js +7 -0
  355. package/dist/services/image_service.d.ts +52 -0
  356. package/dist/services/image_service.js +60 -0
  357. package/dist/services/inception/index.d.ts +2 -0
  358. package/dist/services/inception/index.js +7 -0
  359. package/dist/services/index.d.ts +2 -0
  360. package/dist/services/index.js +8 -0
  361. package/dist/services/inworld/index.d.ts +2 -0
  362. package/dist/services/inworld/index.js +7 -0
  363. package/dist/services/keenable/index.d.ts +2 -0
  364. package/dist/services/keenable/index.js +7 -0
  365. package/dist/services/kokoro/index.d.ts +2 -0
  366. package/dist/services/kokoro/index.js +7 -0
  367. package/dist/services/llm_service.d.ts +1106 -0
  368. package/dist/services/llm_service.js +2231 -0
  369. package/dist/services/mem0/index.d.ts +2 -0
  370. package/dist/services/mem0/index.js +7 -0
  371. package/dist/services/meta/index.d.ts +2 -0
  372. package/dist/services/meta/index.js +7 -0
  373. package/dist/services/minimax/index.d.ts +2 -0
  374. package/dist/services/minimax/index.js +7 -0
  375. package/dist/services/mistral/index.d.ts +2 -0
  376. package/dist/services/mistral/index.js +7 -0
  377. package/dist/services/moondream/index.d.ts +2 -0
  378. package/dist/services/moondream/index.js +7 -0
  379. package/dist/services/moonshine/index.d.ts +2 -0
  380. package/dist/services/moonshine/index.js +7 -0
  381. package/dist/services/nebius/index.d.ts +2 -0
  382. package/dist/services/nebius/index.js +7 -0
  383. package/dist/services/neuphonic/index.d.ts +2 -0
  384. package/dist/services/neuphonic/index.js +7 -0
  385. package/dist/services/novita/index.d.ts +2 -0
  386. package/dist/services/novita/index.js +7 -0
  387. package/dist/services/nvidia/index.d.ts +2 -0
  388. package/dist/services/nvidia/index.js +11 -0
  389. package/dist/services/ollama/index.d.ts +2 -0
  390. package/dist/services/ollama/index.js +7 -0
  391. package/dist/services/openai/index.d.ts +2 -0
  392. package/dist/services/openai/index.js +7 -0
  393. package/dist/services/openclaw/index.d.ts +14 -0
  394. package/dist/services/openclaw/index.js +19 -0
  395. package/dist/services/openrouter/index.d.ts +2 -0
  396. package/dist/services/openrouter/index.js +7 -0
  397. package/dist/services/perplexity/index.d.ts +2 -0
  398. package/dist/services/perplexity/index.js +7 -0
  399. package/dist/services/piper/index.d.ts +2 -0
  400. package/dist/services/piper/index.js +7 -0
  401. package/dist/services/pocket_tts/index.d.ts +2 -0
  402. package/dist/services/pocket_tts/index.js +7 -0
  403. package/dist/services/qwen/index.d.ts +2 -0
  404. package/dist/services/qwen/index.js +7 -0
  405. package/dist/services/resembleai/index.d.ts +2 -0
  406. package/dist/services/resembleai/index.js +7 -0
  407. package/dist/services/rime/index.d.ts +2 -0
  408. package/dist/services/rime/index.js +7 -0
  409. package/dist/services/sambanova/index.d.ts +2 -0
  410. package/dist/services/sambanova/index.js +7 -0
  411. package/dist/services/sarvam/index.d.ts +2 -0
  412. package/dist/services/sarvam/index.js +7 -0
  413. package/dist/services/settings.d.ts +356 -0
  414. package/dist/services/settings.js +437 -0
  415. package/dist/services/simli/index.d.ts +2 -0
  416. package/dist/services/simli/index.js +7 -0
  417. package/dist/services/smallest/index.d.ts +2 -0
  418. package/dist/services/smallest/index.js +7 -0
  419. package/dist/services/soniox/index.d.ts +2 -0
  420. package/dist/services/soniox/index.js +7 -0
  421. package/dist/services/speechify/index.d.ts +2 -0
  422. package/dist/services/speechify/index.js +7 -0
  423. package/dist/services/speechmatics/index.d.ts +2 -0
  424. package/dist/services/speechmatics/index.js +7 -0
  425. package/dist/services/stt_latency.d.ts +62 -0
  426. package/dist/services/stt_latency.js +71 -0
  427. package/dist/services/stt_service.d.ts +601 -0
  428. package/dist/services/stt_service.js +1044 -0
  429. package/dist/services/tavus/index.d.ts +2 -0
  430. package/dist/services/tavus/index.js +7 -0
  431. package/dist/services/together/index.d.ts +2 -0
  432. package/dist/services/together/index.js +7 -0
  433. package/dist/services/tts_service.d.ts +909 -0
  434. package/dist/services/tts_service.js +2018 -0
  435. package/dist/services/ultravox/index.d.ts +2 -0
  436. package/dist/services/ultravox/index.js +9 -0
  437. package/dist/services/vision_service.d.ts +56 -0
  438. package/dist/services/vision_service.js +62 -0
  439. package/dist/services/websocket_service.d.ts +213 -0
  440. package/dist/services/websocket_service.js +654 -0
  441. package/dist/services/whisper/index.d.ts +2 -0
  442. package/dist/services/whisper/index.js +9 -0
  443. package/dist/services/xai/index.d.ts +2 -0
  444. package/dist/services/xai/index.js +9 -0
  445. package/dist/services/xtts/index.d.ts +2 -0
  446. package/dist/services/xtts/index.js +9 -0
  447. package/dist/tests/index.d.ts +2 -0
  448. package/dist/tests/index.js +8 -0
  449. package/dist/tests/utils.d.ts +123 -0
  450. package/dist/tests/utils.js +212 -0
  451. package/dist/transcriptions/index.d.ts +2 -0
  452. package/dist/transcriptions/index.js +8 -0
  453. package/dist/transcriptions/language.d.ts +376 -0
  454. package/dist/transcriptions/language.js +543 -0
  455. package/dist/transports/base_input.d.ts +149 -0
  456. package/dist/transports/base_input.js +338 -0
  457. package/dist/transports/base_output.d.ts +432 -0
  458. package/dist/transports/base_output.js +1231 -0
  459. package/dist/transports/base_transport.d.ts +223 -0
  460. package/dist/transports/base_transport.js +167 -0
  461. package/dist/transports/daily/index.d.ts +2 -0
  462. package/dist/transports/daily/index.js +9 -0
  463. package/dist/transports/heygen/index.d.ts +2 -0
  464. package/dist/transports/heygen/index.js +9 -0
  465. package/dist/transports/index.d.ts +2 -0
  466. package/dist/transports/index.js +8 -0
  467. package/dist/transports/lemonslice/index.d.ts +2 -0
  468. package/dist/transports/lemonslice/index.js +9 -0
  469. package/dist/transports/livekit/index.d.ts +2 -0
  470. package/dist/transports/livekit/index.js +9 -0
  471. package/dist/transports/local/index.d.ts +2 -0
  472. package/dist/transports/local/index.js +9 -0
  473. package/dist/transports/moq/index.d.ts +2 -0
  474. package/dist/transports/moq/index.js +9 -0
  475. package/dist/transports/smallwebrtc/index.d.ts +2 -0
  476. package/dist/transports/smallwebrtc/index.js +9 -0
  477. package/dist/transports/tavus/index.d.ts +2 -0
  478. package/dist/transports/tavus/index.js +9 -0
  479. package/dist/transports/video_in_sampler.d.ts +157 -0
  480. package/dist/transports/video_in_sampler.js +258 -0
  481. package/dist/transports/vonage/index.d.ts +2 -0
  482. package/dist/transports/vonage/index.js +9 -0
  483. package/dist/transports/websocket/index.d.ts +2 -0
  484. package/dist/transports/websocket/index.js +9 -0
  485. package/dist/transports/whatsapp/index.d.ts +2 -0
  486. package/dist/transports/whatsapp/index.js +9 -0
  487. package/dist/turns/eager_end_of_turn_mixin.d.ts +70 -0
  488. package/dist/turns/eager_end_of_turn_mixin.js +122 -0
  489. package/dist/turns/empty_user_turn.d.ts +55 -0
  490. package/dist/turns/empty_user_turn.js +53 -0
  491. package/dist/turns/index.d.ts +2 -0
  492. package/dist/turns/index.js +8 -0
  493. package/dist/turns/speculation_gate.d.ts +169 -0
  494. package/dist/turns/speculation_gate.js +294 -0
  495. package/dist/turns/types.d.ts +33 -0
  496. package/dist/turns/types.js +35 -0
  497. package/dist/turns/user_idle_controller.d.ts +94 -0
  498. package/dist/turns/user_idle_controller.js +187 -0
  499. package/dist/turns/user_mute/always_user_mute_strategy.d.ts +15 -0
  500. package/dist/turns/user_mute/always_user_mute_strategy.js +29 -0
  501. package/dist/turns/user_mute/base_user_mute_strategy.d.ts +39 -0
  502. package/dist/turns/user_mute/base_user_mute_strategy.js +45 -0
  503. package/dist/turns/user_mute/first_speech_user_mute_strategy.d.ts +28 -0
  504. package/dist/turns/user_mute/first_speech_user_mute_strategy.js +52 -0
  505. package/dist/turns/user_mute/function_call_user_mute_strategy.d.ts +25 -0
  506. package/dist/turns/user_mute/function_call_user_mute_strategy.js +50 -0
  507. package/dist/turns/user_mute/index.d.ts +6 -0
  508. package/dist/turns/user_mute/index.js +11 -0
  509. package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.d.ts +43 -0
  510. package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.js +76 -0
  511. package/dist/turns/user_start/base_user_turn_start_strategy.d.ts +135 -0
  512. package/dist/turns/user_start/base_user_turn_start_strategy.js +134 -0
  513. package/dist/turns/user_start/external_user_turn_start_strategy.d.ts +45 -0
  514. package/dist/turns/user_start/external_user_turn_start_strategy.js +64 -0
  515. package/dist/turns/user_start/index.d.ts +8 -0
  516. package/dist/turns/user_start/index.js +16 -0
  517. package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.d.ts +133 -0
  518. package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.js +243 -0
  519. package/dist/turns/user_start/min_words_user_turn_start_strategy.d.ts +60 -0
  520. package/dist/turns/user_start/min_words_user_turn_start_strategy.js +102 -0
  521. package/dist/turns/user_start/transcription_user_turn_start_strategy.d.ts +29 -0
  522. package/dist/turns/user_start/transcription_user_turn_start_strategy.js +43 -0
  523. package/dist/turns/user_start/vad_user_turn_start_strategy.d.ts +20 -0
  524. package/dist/turns/user_start/vad_user_turn_start_strategy.js +31 -0
  525. package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.d.ts +141 -0
  526. package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.js +238 -0
  527. package/dist/turns/user_stop/base_user_turn_stop_strategy.d.ts +174 -0
  528. package/dist/turns/user_stop/base_user_turn_stop_strategy.js +170 -0
  529. package/dist/turns/user_stop/deferred_user_turn_stop_strategy.d.ts +81 -0
  530. package/dist/turns/user_stop/deferred_user_turn_stop_strategy.js +107 -0
  531. package/dist/turns/user_stop/eager_match_policy.d.ts +36 -0
  532. package/dist/turns/user_stop/eager_match_policy.js +50 -0
  533. package/dist/turns/user_stop/eager_user_turn_stop_strategy.d.ts +84 -0
  534. package/dist/turns/user_stop/eager_user_turn_stop_strategy.js +154 -0
  535. package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.d.ts +45 -0
  536. package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.js +55 -0
  537. package/dist/turns/user_stop/external_user_turn_stop_strategy.d.ts +115 -0
  538. package/dist/turns/user_stop/external_user_turn_stop_strategy.js +205 -0
  539. package/dist/turns/user_stop/index.d.ts +10 -0
  540. package/dist/turns/user_stop/index.js +15 -0
  541. package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.d.ts +44 -0
  542. package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.js +59 -0
  543. package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.d.ts +140 -0
  544. package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.js +303 -0
  545. package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.d.ts +135 -0
  546. package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.js +332 -0
  547. package/dist/turns/user_turn_completion_mixin.d.ts +147 -0
  548. package/dist/turns/user_turn_completion_mixin.js +591 -0
  549. package/dist/turns/user_turn_controller.d.ts +134 -0
  550. package/dist/turns/user_turn_controller.js +335 -0
  551. package/dist/turns/user_turn_processor.d.ts +87 -0
  552. package/dist/turns/user_turn_processor.js +180 -0
  553. package/dist/turns/user_turn_strategies.d.ts +187 -0
  554. package/dist/turns/user_turn_strategies.js +222 -0
  555. package/dist/utils/async_tool_cancellation.d.ts +35 -0
  556. package/dist/utils/async_tool_cancellation.js +78 -0
  557. package/dist/utils/asyncio/index.d.ts +2 -0
  558. package/dist/utils/asyncio/index.js +7 -0
  559. package/dist/utils/asyncio/task_manager.d.ts +49 -0
  560. package/dist/utils/asyncio/task_manager.js +105 -0
  561. package/dist/utils/base_object.d.ts +76 -0
  562. package/dist/utils/base_object.js +154 -0
  563. package/dist/utils/context/aggregated_frame_sequencer.d.ts +491 -0
  564. package/dist/utils/context/aggregated_frame_sequencer.js +803 -0
  565. package/dist/utils/context/audio_context_manager.d.ts +131 -0
  566. package/dist/utils/context/audio_context_manager.js +233 -0
  567. package/dist/utils/context/index.d.ts +2 -0
  568. package/dist/utils/context/index.js +7 -0
  569. package/dist/utils/context/llm_context_summarization.d.ts +258 -0
  570. package/dist/utils/context/llm_context_summarization.js +511 -0
  571. package/dist/utils/context/text_segment_map.d.ts +611 -0
  572. package/dist/utils/context/text_segment_map.js +1132 -0
  573. package/dist/utils/context/word_completion_tracker.d.ts +273 -0
  574. package/dist/utils/context/word_completion_tracker.js +441 -0
  575. package/dist/utils/context/word_timestamp_tracker.d.ts +72 -0
  576. package/dist/utils/context/word_timestamp_tracker.js +98 -0
  577. package/dist/utils/deprecation.d.ts +28 -0
  578. package/dist/utils/deprecation.js +75 -0
  579. package/dist/utils/env.d.ts +38 -0
  580. package/dist/utils/env.js +62 -0
  581. package/dist/utils/errors.d.ts +75 -0
  582. package/dist/utils/errors.js +202 -0
  583. package/dist/utils/file_resolver.d.ts +126 -0
  584. package/dist/utils/file_resolver.js +314 -0
  585. package/dist/utils/file_storage.d.ts +99 -0
  586. package/dist/utils/file_storage.js +100 -0
  587. package/dist/utils/frame_queue.d.ts +36 -0
  588. package/dist/utils/frame_queue.js +66 -0
  589. package/dist/utils/http.d.ts +39 -0
  590. package/dist/utils/http.js +53 -0
  591. package/dist/utils/image.d.ts +15 -0
  592. package/dist/utils/image.js +30 -0
  593. package/dist/utils/index.d.ts +2 -0
  594. package/dist/utils/index.js +8 -0
  595. package/dist/utils/log_config.d.ts +34 -0
  596. package/dist/utils/log_config.js +76 -0
  597. package/dist/utils/logger.d.ts +40 -0
  598. package/dist/utils/logger.js +91 -0
  599. package/dist/utils/mixin.d.ts +14 -0
  600. package/dist/utils/mixin.js +63 -0
  601. package/dist/utils/network.d.ts +73 -0
  602. package/dist/utils/network.js +90 -0
  603. package/dist/utils/security/allowed_origins.d.ts +16 -0
  604. package/dist/utils/security/allowed_origins.js +34 -0
  605. package/dist/utils/security/index.d.ts +2 -0
  606. package/dist/utils/security/index.js +7 -0
  607. package/dist/utils/security/redaction.d.ts +27 -0
  608. package/dist/utils/security/redaction.js +123 -0
  609. package/dist/utils/security/ssrf.d.ts +79 -0
  610. package/dist/utils/security/ssrf.js +327 -0
  611. package/dist/utils/shared.d.ts +35 -0
  612. package/dist/utils/shared.js +136 -0
  613. package/dist/utils/startup.d.ts +17 -0
  614. package/dist/utils/startup.js +130 -0
  615. package/dist/utils/string.d.ts +116 -0
  616. package/dist/utils/string.js +460 -0
  617. package/dist/utils/sync/base_notifier.d.ts +25 -0
  618. package/dist/utils/sync/base_notifier.js +16 -0
  619. package/dist/utils/sync/event_notifier.d.ts +38 -0
  620. package/dist/utils/sync/event_notifier.js +51 -0
  621. package/dist/utils/sync/index.d.ts +2 -0
  622. package/dist/utils/sync/index.js +7 -0
  623. package/dist/utils/text/alnum_utils.d.ts +109 -0
  624. package/dist/utils/text/alnum_utils.js +262 -0
  625. package/dist/utils/text/base_text_aggregator.d.ts +146 -0
  626. package/dist/utils/text/base_text_aggregator.js +102 -0
  627. package/dist/utils/text/base_text_filter.d.ts +54 -0
  628. package/dist/utils/text/base_text_filter.js +50 -0
  629. package/dist/utils/text/index.d.ts +2 -0
  630. package/dist/utils/text/index.js +7 -0
  631. package/dist/utils/text/markdown_text_filter.d.ts +147 -0
  632. package/dist/utils/text/markdown_text_filter.js +522 -0
  633. package/dist/utils/text/markup_utils.d.ts +54 -0
  634. package/dist/utils/text/markup_utils.js +156 -0
  635. package/dist/utils/text/pattern_pair_aggregator.d.ts +251 -0
  636. package/dist/utils/text/pattern_pair_aggregator.js +478 -0
  637. package/dist/utils/text/phonemes.d.ts +60 -0
  638. package/dist/utils/text/phonemes.js +172 -0
  639. package/dist/utils/text/simple_text_aggregator.d.ts +135 -0
  640. package/dist/utils/text/simple_text_aggregator.js +254 -0
  641. package/dist/utils/text/skip_tags_aggregator.d.ts +85 -0
  642. package/dist/utils/text/skip_tags_aggregator.js +147 -0
  643. package/dist/utils/text/transforms/pronunciations.d.ts +65 -0
  644. package/dist/utils/text/transforms/pronunciations.js +111 -0
  645. package/dist/utils/text/word_timestamp_utils.d.ts +26 -0
  646. package/dist/utils/text/word_timestamp_utils.js +52 -0
  647. package/dist/utils/time.d.ts +13 -0
  648. package/dist/utils/time.js +31 -0
  649. package/dist/utils/tracing/index.d.ts +3 -0
  650. package/dist/utils/tracing/index.js +8 -0
  651. package/dist/utils/tracing/service_attributes.d.ts +198 -0
  652. package/dist/utils/tracing/service_attributes.js +434 -0
  653. package/dist/utils/tracing/service_decorators.d.ts +214 -0
  654. package/dist/utils/tracing/service_decorators.js +1662 -0
  655. package/dist/utils/tracing/setup.d.ts +95 -0
  656. package/dist/utils/tracing/setup.js +110 -0
  657. package/dist/utils/tracing/tracing_context.d.ts +55 -0
  658. package/dist/utils/tracing/tracing_context.js +97 -0
  659. package/dist/utils/tracing/turn_trace_observer.d.ts +90 -0
  660. package/dist/utils/tracing/turn_trace_observer.js +219 -0
  661. package/dist/utils/types.d.ts +80 -0
  662. package/dist/utils/types.js +108 -0
  663. package/dist/utils/utils.d.ts +5 -0
  664. package/dist/utils/utils.js +22 -0
  665. package/dist/utils/yaml.d.ts +64 -0
  666. package/dist/utils/yaml.js +41 -0
  667. package/dist/version.d.ts +3 -0
  668. package/dist/version.js +7 -0
  669. package/dist/workers/base_worker.d.ts +772 -0
  670. package/dist/workers/base_worker.js +1298 -0
  671. package/dist/workers/index.d.ts +2 -0
  672. package/dist/workers/index.js +8 -0
  673. package/dist/workers/llm/backend_llm_worker.d.ts +552 -0
  674. package/dist/workers/llm/backend_llm_worker.js +1055 -0
  675. package/dist/workers/llm/index.d.ts +6 -0
  676. package/dist/workers/llm/index.js +11 -0
  677. package/dist/workers/llm/llm_context_worker.d.ts +66 -0
  678. package/dist/workers/llm/llm_context_worker.js +72 -0
  679. package/dist/workers/llm/llm_worker.d.ts +180 -0
  680. package/dist/workers/llm/llm_worker.js +323 -0
  681. package/dist/workers/llm/tool_decorator.d.ts +68 -0
  682. package/dist/workers/llm/tool_decorator.js +60 -0
  683. package/dist/workers/runner.d.ts +268 -0
  684. package/dist/workers/runner.js +489 -0
  685. package/dist/workers/ui/index.d.ts +22 -0
  686. package/dist/workers/ui/index.js +27 -0
  687. package/dist/workers/ui/ui_event_decorator.d.ts +43 -0
  688. package/dist/workers/ui/ui_event_decorator.js +75 -0
  689. package/dist/workers/ui/ui_job_context.d.ts +12 -0
  690. package/dist/workers/ui/ui_job_context.js +17 -0
  691. package/dist/workers/ui/ui_prompts.d.ts +21 -0
  692. package/dist/workers/ui/ui_prompts.js +41 -0
  693. package/dist/workers/ui/ui_tools.d.ts +32 -0
  694. package/dist/workers/ui/ui_tools.js +112 -0
  695. package/dist/workers/ui/ui_worker.d.ts +571 -0
  696. package/dist/workers/ui/ui_worker.js +1156 -0
  697. package/package.json +46 -3
  698. package/README.md +0 -5
@@ -0,0 +1,803 @@
1
+ // Copyright (c) 2024-2026, Daily
2
+ // Copyright (c) 2026, pipedog contributors
3
+ // SPDX-License-Identifier: BSD-2-Clause
4
+ //
5
+ // Ported from pipecat: src/pipecat/utils/context/aggregated_frame_sequencer.py
6
+ /** Ordered sequencer for AggregatedTextFrame slots through TTS processing. */
7
+ import { AggregatedTextFrame, AggregatedTextProgressFrame, TextType, TTSTextFrame, } from "../../frames/frames.js";
8
+ import { logger } from "../logger.js";
9
+ import { matchEndofsentence } from "../string.js";
10
+ import { SimpleTextAggregator } from "../text/simple_text_aggregator.js";
11
+ import { WordCompletionTracker } from "./word_completion_tracker.js";
12
+ /** Python's `repr()` of a string, for log lines. */
13
+ function repr(text) {
14
+ return `'${text.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n")}'`;
15
+ }
16
+ /**
17
+ * Internal: groups streamed tokens back into sentences for the sequencer.
18
+ *
19
+ * Used by `AggregatedFrameSequencer` when a TTS service streams tokens
20
+ * individually (`TextAggregationMode.TOKEN`) but still needs whole-sentence
21
+ * units for word-timestamp tracking and RTVI progress. Each token contributes
22
+ * one part to each of the three channels (tts / llm / user-facing); sentence
23
+ * completion is driven by the **TTS text** and completes all three together.
24
+ *
25
+ * Boundary timing: a sentence-ending boundary is only *confirmed* by
26
+ * lookahead — the first non-whitespace character of the *next* sentence. So
27
+ * the underlying `SimpleTextAggregator` only yields "Hi there!" once the
28
+ * following non-whitespace character has arrived.
29
+ *
30
+ * Splitting granularity: token streams are not guaranteed to be one word or
31
+ * one punctuation mark per token — an upstream can deliver a coarse chunk that
32
+ * carries the tail of one sentence *and* the head of the next (e.g. "Hey" then
33
+ * " there! I'm ..."). Two cases:
34
+ *
35
+ * - When every token accumulated for the pending sentence has identical text
36
+ * in all three channels (the common case — no TTS transform/tag rewriting is
37
+ * active), a boundary is sliced *inside* the triggering token at its
38
+ * confirmed offset, so " there!" stays with "Hey" and only " I'm ..."
39
+ * carries over.
40
+ * - When a transform has made the channels differ in length, the channels can
41
+ * no longer be split at a shared character offset, so the boundary is cut at
42
+ * the token boundary instead: the text accumulated *before* the triggering
43
+ * token is emitted and the whole triggering token begins the next sentence's
44
+ * buffer.
45
+ */
46
+ export class _ParallelSentenceAggregator {
47
+ _aggregator;
48
+ // Tokens accumulated since the last emitted sentence, per channel.
49
+ _tts = "";
50
+ _llm = "";
51
+ _user = "";
52
+ // Whether every token accumulated for the pending sentence has identical
53
+ // text across the three channels. While true the tts buffer mirrors the
54
+ // inner aggregator's buffer, so a boundary can be sliced inside a token.
55
+ _aligned = true;
56
+ /**
57
+ * Initialize empty channels using the context's sentence language.
58
+ *
59
+ * @param language Language code for this context.
60
+ */
61
+ constructor(language = null) {
62
+ // A plain SENTENCE-mode aggregator drives boundary detection on the TTS
63
+ // text. The TTS text is already post-transform, so tag/pattern-aware
64
+ // boundary rules are not needed here.
65
+ this._aggregator = new SimpleTextAggregator({
66
+ aggregationType: TextType.SENTENCE,
67
+ language,
68
+ });
69
+ this._reset();
70
+ }
71
+ /** Use the supplied language for subsequent text without clearing buffers. */
72
+ setLanguage(language) {
73
+ this._aggregator.setLanguage(language);
74
+ }
75
+ _reset() {
76
+ this._tts = "";
77
+ this._llm = "";
78
+ this._user = "";
79
+ this._aligned = true;
80
+ }
81
+ /** The inner aggregator's buffer (its protected `_text`). */
82
+ get _aggregatorText() {
83
+ return this._aggregator._text;
84
+ }
85
+ /**
86
+ * Feed one token (all three channels) and yield any completed sentences.
87
+ *
88
+ * @param ttsText The token as sent to the TTS service.
89
+ * @param llmText The token in the original LLM text.
90
+ * @param userFacingText The token as shown to the user.
91
+ * @returns A `_ParallelAggregation` for each sentence this token confirms.
92
+ * Usually zero or one, but a coarse chunk carrying several sentence
93
+ * endings can confirm more than one at once.
94
+ */
95
+ async *aggregate(ttsText, llmText, userFacingText) {
96
+ const tokenIdentical = ttsText === llmText && llmText === userFacingText;
97
+ // The inner SENTENCE aggregator is the source of truth for *when* and *how
98
+ // many* boundaries are confirmed (it applies the lookahead rule char by
99
+ // char on the TTS text).
100
+ let boundaryCount = 0;
101
+ for await (const _ of this._aggregator.aggregate(ttsText)) {
102
+ boundaryCount += 1;
103
+ }
104
+ if (boundaryCount && this._aligned && tokenIdentical) {
105
+ // Channels are identical for the whole pending sentence, so the tts
106
+ // buffer mirrors the inner aggregator's buffer and we can slice inside
107
+ // this token at each confirmed boundary, keeping the channels aligned.
108
+ const combined = this._tts + ttsText;
109
+ let idx = 0;
110
+ for (let n = 0; n < boundaryCount; n++) {
111
+ const boundary = matchEndofsentence(combined.slice(idx), {
112
+ language: this._aggregator.language,
113
+ });
114
+ if (boundary <= 0) {
115
+ break;
116
+ }
117
+ const sentence = combined.slice(idx, idx + boundary);
118
+ yield { ttsText: sentence, llmText: sentence, userFacingText: sentence };
119
+ idx += boundary;
120
+ }
121
+ // The remainder came from identical channels, so the next pending
122
+ // sentence starts aligned and its buffer still mirrors the inner one.
123
+ const rest = combined.slice(idx);
124
+ this._tts = rest;
125
+ this._llm = rest;
126
+ this._user = rest;
127
+ this._aligned = true;
128
+ return;
129
+ }
130
+ // A transform has diverged the channels (or nothing is buffered before this
131
+ // token): we can only cut at the token boundary. Emit whatever was buffered
132
+ // before this token and let this token begin the next sentence's buffer.
133
+ if (boundaryCount && (this._tts || this._llm || this._user)) {
134
+ yield { ttsText: this._tts, llmText: this._llm, userFacingText: this._user };
135
+ this._tts = "";
136
+ this._llm = "";
137
+ this._user = "";
138
+ }
139
+ // Plain concatenation: LLM tokens already carry their own spacing.
140
+ this._tts += ttsText;
141
+ this._llm += llmText;
142
+ this._user += userFacingText;
143
+ // Re-derive alignment from ground truth rather than assuming a token-boundary
144
+ // cut restores it.
145
+ this._aligned =
146
+ this._tts === this._llm && this._llm === this._user && this._tts === this._aggregatorText;
147
+ }
148
+ /**
149
+ * Emit any trailing partial sentence at end of turn.
150
+ *
151
+ * @returns A `_ParallelAggregation` for the accumulated-but-unemitted text,
152
+ * or `null` when nothing substantive is buffered.
153
+ */
154
+ async flush() {
155
+ await this._aggregator.flush();
156
+ if (this._user.trim()) {
157
+ const result = { ttsText: this._tts, llmText: this._llm, userFacingText: this._user };
158
+ this._reset();
159
+ return result;
160
+ }
161
+ return null;
162
+ }
163
+ /** Discard all buffered text (called on interruption/reset). */
164
+ async handleInterruption() {
165
+ await this._aggregator.handleInterruption();
166
+ this._reset();
167
+ }
168
+ }
169
+ function newSlot(options) {
170
+ return {
171
+ tracker: null,
172
+ transportDestination: null,
173
+ complete: false,
174
+ includesInterFrameSpaces: false,
175
+ ...options,
176
+ };
177
+ }
178
+ /**
179
+ * Sequences AggregatedTextFrame slots to preserve TTS context ordering.
180
+ *
181
+ * Manages an ordered queue of spoken and skipped TTS slots. Spoken slots are
182
+ * tracked via a `WordCompletionTracker`; skipped slots (e.g. code blocks
183
+ * excluded from TTS synthesis) wait in-place until all preceding spoken slots
184
+ * are complete, then are flushed downstream with `appendToContext=true`.
185
+ *
186
+ * Most methods are synchronous and return lists of frames the caller should
187
+ * push downstream, making the sequencer easily testable. The exceptions are
188
+ * `registerSpoken`, `registerSkipped`, and `finalize`, which are async because
189
+ * — when the sequencer is built with `streaming: true` — they drive an async
190
+ * `_ParallelSentenceAggregator` to group streamed tokens into sentences.
191
+ *
192
+ * Contexts can be in flight concurrently (e.g. two back-to-back
193
+ * `TTSSpeakFrame` utterances on a websocket TTS service whose `runTts`
194
+ * returns before synthesis finishes). State is organised in three tiers so
195
+ * concurrent contexts never bleed into each other:
196
+ *
197
+ * - `_slots`: the single global ordered timeline across all contexts.
198
+ * Ordering is the whole point of the sequencer, so this stays one list.
199
+ * - `_contextAppendToContext`: a per-context flag whose presence also marks
200
+ * the context as *live* — an entry existing is the "process this context's
201
+ * words" signal in `processWord`'s stale gate. Created at slot registration,
202
+ * removed when the context is fully done (`forceComplete`).
203
+ * - `_streamingContexts`: transient per-context pending-sentence state
204
+ * (`_StreamingContext`), present only while a sentence is being accumulated
205
+ * from tokens. Released at end of text input (`finalize`).
206
+ *
207
+ * @example
208
+ * const sequencer = new AggregatedFrameSequencer();
209
+ * await sequencer.registerSpoken(frame, ctxId, ttsText, true);
210
+ * for (const f of sequencer.processWord("hello", 1000, ctxId)) {
211
+ * await this.pushFrame(f);
212
+ * }
213
+ */
214
+ export class AggregatedFrameSequencer {
215
+ _name;
216
+ _streaming;
217
+ _slots = [];
218
+ _contextAppendToContext = new Map();
219
+ // Transient per-context pending-sentence state, keyed by context ID. An entry
220
+ // exists only while a sentence is being accumulated from tokens for that
221
+ // context (streaming mode only). Aggregators are created lazily per context.
222
+ _streamingContexts = new Map();
223
+ _bufferedWords = [];
224
+ /**
225
+ * Initialize the sequencer.
226
+ *
227
+ * @param options.name Label used in log messages. Defaults to
228
+ * "AggregatedFrameSequencer".
229
+ * @param options.streaming True when tokens are dispatched to the TTS
230
+ * individually (see `AggregatedFrameSequencerOptions.streaming`).
231
+ */
232
+ constructor(options = {}) {
233
+ this._name = options.name ?? "AggregatedFrameSequencer";
234
+ this._streaming = options.streaming ?? false;
235
+ }
236
+ /**
237
+ * Register a spoken AggregatedTextFrame slot.
238
+ *
239
+ * Called from _handleAggregatedText for every frame sent to the TTS service
240
+ * (one call per token when `streaming`, one call per complete unit
241
+ * otherwise). Builds the `WordCompletionTracker` internally when
242
+ * `buildTracker` is set — callers never construct one themselves. A
243
+ * registered slot is marked complete either via `processWord`
244
+ * (word-timestamp services) or `completeSpokenSlot` (pushTextFrames=true
245
+ * services).
246
+ *
247
+ * When the sequencer is non-streaming, this registers a slot immediately.
248
+ * When streaming — whether or not `buildTracker` is set — the call instead
249
+ * feeds this token to the `_ParallelSentenceAggregator` and only registers a
250
+ * real slot once a sentence boundary is confirmed there; a
251
+ * pushTextFrames=true (no-tracker) slot completes immediately on promotion
252
+ * (see `_promote`) since there is no word-timestamp signal to complete it
253
+ * progressively.
254
+ *
255
+ * @param frame The AggregatedTextFrame being spoken (one token when streaming).
256
+ * @param contextId The TTS context ID assigned to this frame.
257
+ * @param ttsText The text actually sent to the TTS for this call (may differ
258
+ * from `frame.text` after filters/transforms).
259
+ * @param appendToContext Whether word frames built for this context should
260
+ * carry appendToContext=true.
261
+ * @param options See `RegisterSpokenOptions`.
262
+ * @returns Frames unblocked by this call (a promoted sentence and/or
263
+ * buffered words replayed once a pending sentence promotes). Always empty
264
+ * for the non-streaming case.
265
+ */
266
+ async registerSpoken(frame, contextId, ttsText, appendToContext, options = {}) {
267
+ const buildTracker = options.buildTracker ?? true;
268
+ const includesInterFrameSpaces = options.includesInterFrameSpaces ?? false;
269
+ const language = options.language ?? null;
270
+ if (!this._streaming) {
271
+ this._appendSpokenSlot(frame, contextId, buildTracker
272
+ ? new WordCompletionTracker(ttsText, {
273
+ llmText: frame.rawText || frame.text,
274
+ userFacingText: frame.text,
275
+ })
276
+ : null, appendToContext, includesInterFrameSpaces);
277
+ return [];
278
+ }
279
+ let sc = this._streamingContexts.get(contextId);
280
+ if (!sc) {
281
+ sc = {
282
+ aggregator: new _ParallelSentenceAggregator(language),
283
+ appendToContext,
284
+ buildTracker,
285
+ textType: frame.textType === TextType.TOKEN ? TextType.SENTENCE : frame.textType,
286
+ };
287
+ this._streamingContexts.set(contextId, sc);
288
+ }
289
+ sc.aggregator.setLanguage(language);
290
+ const frames = [];
291
+ for await (const agg of sc.aggregator.aggregate(ttsText, frame.rawText || frame.text, frame.text)) {
292
+ frames.push(...this._promote(agg, contextId, sc));
293
+ }
294
+ return frames;
295
+ }
296
+ /**
297
+ * Register a skipped AggregatedTextFrame and attempt an immediate flush.
298
+ *
299
+ * Any sentence still pending for this context is finalized first, so a real
300
+ * spoken slot exists immediately before the skipped slot in the queue —
301
+ * `flush`'s "stop at first incomplete spoken slot" logic then blocks this
302
+ * skipped frame correctly until that sentence is actually spoken.
303
+ *
304
+ * The frame is appended as a skipped slot. If no incomplete spoken slot
305
+ * precedes it, the frame is returned right away; otherwise it waits until a
306
+ * later `flush` unblocks it.
307
+ *
308
+ * @param frame The skipped AggregatedTextFrame (e.g. a code block).
309
+ * @param contextId The context ID assigned in _handleAggregatedText.
310
+ * @param transportDestination Transport routing value to attach at flush time.
311
+ * @returns The frames to push downstream — any sentence promoted by the
312
+ * initial `finalize` (streaming mode), followed by this skipped frame once
313
+ * it is unblocked. The skipped frame itself is absent while a preceding
314
+ * spoken slot is still incomplete — the promoted-sentence frame can still
315
+ * be returned in that case, so the list is not necessarily empty when
316
+ * blocked.
317
+ */
318
+ async registerSkipped(frame, contextId, transportDestination) {
319
+ const frames = await this.finalize(contextId);
320
+ frame.contextId = contextId;
321
+ this._slots.push(newSlot({
322
+ frame,
323
+ contextId,
324
+ spoken: false,
325
+ transportDestination,
326
+ }));
327
+ frames.push(...this.flush());
328
+ return frames;
329
+ }
330
+ /**
331
+ * Force-promote a context's still-pending sentence into a real slot.
332
+ *
333
+ * Called at end of text input for a context (no more tokens are coming), to
334
+ * handle a response that ends with no terminal punctuation. The context's
335
+ * pending-sentence state is released here; its `_contextAppendToContext`
336
+ * entry is deliberately kept, since word-timestamp events for the
337
+ * just-promoted slot still arrive later (during audio playback) and must be
338
+ * recognised as live. A no-op when nothing is pending for the context (or
339
+ * the sequencer is not streaming).
340
+ *
341
+ * @param contextId The context whose pending sentence should be finalized.
342
+ * @returns Frames unblocked by finalizing (e.g. buffered words that can now
343
+ * be replayed against the newly-registered slot).
344
+ */
345
+ async finalize(contextId) {
346
+ if (!this._streaming || contextId === null) {
347
+ return [];
348
+ }
349
+ const sc = this._streamingContexts.get(contextId);
350
+ this._streamingContexts.delete(contextId);
351
+ if (!sc) {
352
+ return [];
353
+ }
354
+ const agg = await sc.aggregator.flush();
355
+ return agg ? this._promote(agg, contextId, sc) : [];
356
+ }
357
+ /**
358
+ * Process one word-timestamp event and return frames to push downstream.
359
+ *
360
+ * Locates the active slot (the first incomplete spoken slot with a tracker
361
+ * for this `contextId`), advances it by the incoming word, and builds a
362
+ * `TTSTextFrame`. Handles:
363
+ *
364
+ * - Words from a context that was never registered, was wiped by `clear` on
365
+ * interruption, or has already completed (`forceComplete` removes its
366
+ * entry): dropped as stale (returns an empty list).
367
+ * - Normal words that fit entirely within the active slot.
368
+ * - Overflow words straddling two slot boundaries.
369
+ * - Force-complete when the TTS drops an event (word belongs to the next slot).
370
+ * - Drop for a word no slot recognises, which is not this turn's text. The
371
+ * slot is left where it is, and the next word that does match carries the
372
+ * text this one should have covered; only what no later word accounts for
373
+ * waits for `forceComplete` at the end of the audio context. When
374
+ * streaming, such a word is buffered instead, since the slot it belongs to
375
+ * may simply not be promoted yet.
376
+ * - Flushes any skipped slots unblocked by slot completion.
377
+ *
378
+ * @param word A word token from the TTS service word-timestamp stream.
379
+ * @param pts Presentation timestamp (nanoseconds) to assign to the frame.
380
+ * @param contextId TTS context ID from the word-timestamp event.
381
+ * @param includesInterFrameSpaces Stamped onto the emitted TTSTextFrame so
382
+ * downstream consumers know not to inject extra spaces between frames.
383
+ * @returns Ordered list of frames (TTSTextFrame and/or AggregatedTextFrame)
384
+ * to push.
385
+ */
386
+ processWord(word, pts, contextId, includesInterFrameSpaces = false) {
387
+ // Drop words from contexts we never registered, that were wiped by clear() on
388
+ // interruption, or that have already fully completed (forceComplete removes the
389
+ // entry). Such a word is stale (e.g. delayed word-timestamps the TTS server
390
+ // delivers seconds after the context was cancelled); emitting it would interleave
391
+ // it into the current turn's transcript. A null contextId is left untouched:
392
+ // services without audio contexts legitimately use the passthrough path below. A
393
+ // word for a context still streaming a pending sentence (no slot promoted yet) is
394
+ // not stale — it's handled by the buffering below.
395
+ const isPendingStreamingCtx = contextId !== null && this._streamingContexts.has(contextId);
396
+ if (contextId !== null &&
397
+ !this._contextAppendToContext.has(contextId) &&
398
+ !isPendingStreamingCtx) {
399
+ logger.debug(`${this._name} Dropping stale word '${word}' from unknown/cleared context ${contextId}`);
400
+ return [];
401
+ }
402
+ const active = this._getActiveSlot(contextId);
403
+ let isComplete = false;
404
+ let rawOverflowWord = null;
405
+ if (active?.tracker) {
406
+ if (!active.tracker.wordBelongsHere(word)) {
407
+ const nextSlot = this._getNextActiveSlot(active, contextId);
408
+ const wordFitsNext = nextSlot?.tracker?.wordBelongsHere(word) ?? false;
409
+ if (!wordFitsNext) {
410
+ if (this._streaming) {
411
+ this._bufferedWords.push({ word, pts, contextId, includesInterFrameSpaces });
412
+ return [];
413
+ }
414
+ // Nothing left in this turn's text can be what was reported.
415
+ // Emitting it anyway would write words the LLM never wrote into
416
+ // the conversation context. Dropping it loses nothing: the slot
417
+ // keeps its place, so the next word that does match is matched
418
+ // past this one and carries the text it should have covered.
419
+ // Only text no later word accounts for waits for
420
+ // forceComplete, when the audio context ends.
421
+ logger.warning(`${this._name} Dropping word '${word}' not recognised by any slot.`);
422
+ return [];
423
+ }
424
+ }
425
+ isComplete = active.tracker.addWordAndCheckComplete(word);
426
+ rawOverflowWord = active.tracker.getOverflowWord();
427
+ }
428
+ else if (this._streaming && active === null) {
429
+ this._bufferedWords.push({ word, pts, contextId, includesInterFrameSpaces });
430
+ return [];
431
+ }
432
+ // Give preference to the per-call flag; fall back to the slot's flag.
433
+ // Also propagate the per-call flag onto the slot so forceComplete inherits it.
434
+ if (active && includesInterFrameSpaces) {
435
+ active.includesInterFrameSpaces = true;
436
+ }
437
+ const slotIfs = includesInterFrameSpaces || (active ? active.includesInterFrameSpaces : false);
438
+ const frameText = active?.tracker ? active.tracker.getWordForFrame() : word;
439
+ const rawText = active?.tracker ? active.tracker.getLlmConsumed() : null;
440
+ const suppress = active?.tracker ? active.tracker.suppressInContext() : false;
441
+ const emitContextId = active ? active.contextId : contextId;
442
+ const frames = [];
443
+ if (frameText) {
444
+ frames.push(this._buildWordFrame(frameText, pts, emitContextId, {
445
+ segmentId: active ? active.frame.id : null,
446
+ rawText,
447
+ suppressInContext: suppress,
448
+ includesInterFrameSpaces: slotIfs,
449
+ }));
450
+ }
451
+ if (active?.tracker && !suppress) {
452
+ frames.push(this._buildProgressFrame(active, pts));
453
+ }
454
+ if (isComplete && active) {
455
+ active.complete = true;
456
+ frames.push(...this.flush(pts));
457
+ if (rawOverflowWord) {
458
+ logger.debug(`${this._name} Emitting overflow word '${rawOverflowWord}'`);
459
+ frames.push(...this.processWord(rawOverflowWord, pts, contextId));
460
+ }
461
+ }
462
+ return frames;
463
+ }
464
+ /**
465
+ * Mark the first pending spoken slot complete and flush unblocked skipped frames.
466
+ *
467
+ * Used by pushTextFrames=true services: non-streaming callers invoke this
468
+ * directly after appending the unit's TTSTextFrame to the audio context;
469
+ * `_promote` calls it internally for a streaming (no-tracker) sentence,
470
+ * immediately on promotion. Only ever targets a buildTracker=false slot (a
471
+ * word-timestamp slot completes via `processWord` instead), so "first
472
+ * pending" is always the caller's own slot even with multiple contexts in
473
+ * flight — such slots complete synchronously, one at a time, in the same
474
+ * call chain that registers them.
475
+ *
476
+ * @returns AggregatedTextFrame(s) that are now unblocked and should be pushed.
477
+ */
478
+ completeSpokenSlot() {
479
+ const slot = this._slots.find((s) => s.spoken && !s.complete);
480
+ if (slot) {
481
+ slot.complete = true;
482
+ }
483
+ return this.flush();
484
+ }
485
+ /**
486
+ * Walk the slot queue and return all skipped frames that are now unblocked.
487
+ *
488
+ * Removes complete spoken slots from the head of the queue, then emits (and
489
+ * removes) skipped slots whose preceding spoken slots are all done. Stops at
490
+ * the first incomplete spoken slot.
491
+ *
492
+ * @param lastWordPts When provided, skipped frames receive this PTS so they
493
+ * appear immediately after the last spoken word in the timeline.
494
+ * @returns AggregatedTextFrame(s) ready to be pushed downstream.
495
+ */
496
+ flush(lastWordPts = null) {
497
+ const frames = [];
498
+ while (this._slots.length > 0) {
499
+ const slot = this._slots[0];
500
+ if (slot.spoken && slot.complete) {
501
+ this._slots.shift();
502
+ }
503
+ else if (!slot.spoken && !slot.complete) {
504
+ slot.frame.appendToContext = true;
505
+ slot.frame.transportDestination = slot.transportDestination;
506
+ if (lastWordPts) {
507
+ slot.frame.pts = lastWordPts;
508
+ }
509
+ logger.debug(`${this._name}: Flushing Aggregated Frame ${slot.frame}`);
510
+ frames.push(slot.frame);
511
+ slot.complete = true;
512
+ this._slots.shift();
513
+ }
514
+ else {
515
+ break; // spoken but not yet complete — wait
516
+ }
517
+ }
518
+ return frames;
519
+ }
520
+ /**
521
+ * Force-complete a context's incomplete spoken slots and flush skipped frames.
522
+ *
523
+ * Called at the end of an audio context to handle TTS providers that
524
+ * silently drop word-timestamp events. Emits a TTSTextFrame for any
525
+ * remaining unspoken text in each incomplete slot *belonging to this
526
+ * context*, marks it complete, then flushes all now-unblocked skipped
527
+ * frames. Slots for other contexts still in flight are left untouched so
528
+ * their own word events (or their own forceComplete) finish them.
529
+ *
530
+ * Audio contexts are drained one at a time in registration order (a
531
+ * context's whole `_handleAudioContext` runs, ending with its own
532
+ * forceComplete, before the next starts), so an earlier context is never
533
+ * still open here — every earlier context has already been force-completed
534
+ * by the time this one ends.
535
+ *
536
+ * The context is fully done once this returns, so its live-context and
537
+ * pending state are forgotten — any word that arrives afterwards is dropped
538
+ * as stale.
539
+ *
540
+ * @param contextId The audio context that has ended.
541
+ * @param lastWordPts PTS of the last received word frame, used as the PTS
542
+ * for force-completed frames and forwarded to `flush`.
543
+ * @returns Combined list of TTSTextFrames (for incomplete spoken slots),
544
+ * AggregatedTextProgressFrames pairing the forced text, and
545
+ * AggregatedTextFrames (skipped slots now unblocked), in emission order.
546
+ */
547
+ forceComplete(contextId, lastWordPts) {
548
+ const frames = [];
549
+ if (this._streaming) {
550
+ this._discardBufferedWords(contextId);
551
+ }
552
+ for (const slot of this._slots) {
553
+ if (slot.spoken && !slot.complete && slot.contextId === contextId) {
554
+ if (slot.tracker) {
555
+ const remainingText = slot.tracker.getRemainingTtsText();
556
+ let rawRemaining = slot.tracker.getRemainingLlmText();
557
+ if (rawRemaining && remainingText && !rawRemaining.includes(remainingText)) {
558
+ logger.warning(`${this._name} force-complete: raw_remaining ${repr(rawRemaining)} ` +
559
+ `does not contain remaining_text ${repr(remainingText)}, discarding`);
560
+ rawRemaining = null;
561
+ }
562
+ if (remainingText) {
563
+ logger.debug(`${this._name} force-completing slot with remaining text ${repr(remainingText)}`);
564
+ frames.push(this._buildWordFrame(remainingText, lastWordPts, slot.contextId, {
565
+ segmentId: slot.frame.id,
566
+ rawText: rawRemaining,
567
+ includesInterFrameSpaces: slot.includesInterFrameSpaces,
568
+ }));
569
+ // That frame carries the rest of the text, so the progress view
570
+ // has to reach the end alongside it.
571
+ slot.tracker.takeRemainingAsSpoken();
572
+ frames.push(this._buildProgressFrame(slot, lastWordPts));
573
+ }
574
+ }
575
+ slot.complete = true;
576
+ }
577
+ }
578
+ frames.push(...this.flush(lastWordPts));
579
+ // Context is fully done: forget it so any later word is dropped as stale.
580
+ this._contextAppendToContext.delete(contextId);
581
+ this._streamingContexts.delete(contextId);
582
+ return frames;
583
+ }
584
+ /**
585
+ * Clear slots and metadata, optionally preserving selected contexts.
586
+ *
587
+ * @param options.keepContexts Synthesis contexts that survive the interruption.
588
+ */
589
+ clear(options = {}) {
590
+ const keep = options.keepContexts ?? new Set();
591
+ this._slots = this._slots.filter((slot) => keep.has(slot.contextId));
592
+ this._contextAppendToContext = new Map([...this._contextAppendToContext].filter(([key]) => keep.has(key)));
593
+ this._bufferedWords = this._bufferedWords.filter((word) => word.contextId !== null && keep.has(word.contextId));
594
+ this._streamingContexts = new Map([...this._streamingContexts].filter(([key]) => keep.has(key)));
595
+ }
596
+ // -------------------------------------------------------------------------
597
+ // Internal helpers
598
+ // -------------------------------------------------------------------------
599
+ /**
600
+ * Append a real, immediately-registered spoken slot.
601
+ *
602
+ * Shared by the non-streaming path of `registerSpoken` and by `_promote`
603
+ * once a streamed sentence's boundary is confirmed.
604
+ */
605
+ _appendSpokenSlot(frame, contextId, tracker, appendToContext, includesInterFrameSpaces) {
606
+ this._contextAppendToContext.set(contextId, appendToContext);
607
+ this._slots.push(newSlot({
608
+ frame,
609
+ contextId,
610
+ spoken: true,
611
+ tracker,
612
+ includesInterFrameSpaces,
613
+ }));
614
+ }
615
+ /**
616
+ * Turn a completed parallel-aggregated sentence into a real spoken slot.
617
+ *
618
+ * Builds an AggregatedTextFrame of the context's text type (usually
619
+ * `TextType.SENTENCE`) from the three aggregated text channels and appends
620
+ * the slot for `contextId`, then replays any words that were buffered
621
+ * waiting for it.
622
+ *
623
+ * The sentence frame is **emitted downstream** (first in the returned list)
624
+ * with `willBeSpoken=true`: it is the streaming-mode equivalent of the
625
+ * pre-synthesis sentence frame that SENTENCE mode pushes, giving RTVI
626
+ * clients the initial `spoken_status="new"` event whose `segment_id` the
627
+ * subsequent progress frames (built from this same frame's id) reference.
628
+ * `appendToContext` is false on it — the conversation context is built from
629
+ * the per-word TTSTextFrames (or, for pushTextFrames=true services, the
630
+ * whole-sentence TTSTextFrame built below), not this announcement.
631
+ *
632
+ * When `buildTracker` is true (word-timestamp services), the slot gets a
633
+ * real `WordCompletionTracker` and completes progressively as `processWord`
634
+ * events arrive. When false (pushTextFrames=true services, e.g.
635
+ * streamed-token providers with no word-timestamp signal), the slot gets no
636
+ * tracker and is completed immediately: by the time a sentence promotes
637
+ * (confirmed only by the *next* sentence's first token, or by `finalize` at
638
+ * end of turn), every token making it up has already been sent to the TTS,
639
+ * so there is nothing further to wait for. A whole-sentence TTSTextFrame is
640
+ * built here to carry that content into the conversation context, mirroring
641
+ * the single TTSTextFrame a non-streaming pushTextFrames=true service emits
642
+ * per unit.
643
+ *
644
+ * The slot's `includesInterFrameSpaces` is left false: for a streamed
645
+ * (TOKEN-mode) sentence, per-word CJK spacing is stamped by `processWord`
646
+ * from `addWordTimestamps`, never from the incoming LLM token's own
647
+ * inter-frame-space flag.
648
+ *
649
+ * @param agg The completed sentence, in the three parallel text channels.
650
+ * @param contextId The context the promoted slot belongs to.
651
+ * @param sc The context's streaming state.
652
+ * @returns The sentence frame, followed — for pushTextFrames=true services —
653
+ * by the whole-sentence TTSTextFrame and any skipped frames it unblocks,
654
+ * followed by any frames unblocked by replaying previously-buffered words.
655
+ * Empty if the aggregated text is entirely whitespace.
656
+ */
657
+ _promote(agg, contextId, sc) {
658
+ if (!agg.userFacingText.trim()) {
659
+ return [];
660
+ }
661
+ const frame = new AggregatedTextFrame({
662
+ text: agg.userFacingText,
663
+ textType: sc.textType,
664
+ rawText: agg.llmText || null,
665
+ });
666
+ frame.contextId = contextId;
667
+ frame.willBeSpoken = true;
668
+ frame.appendToContext = false;
669
+ const tracker = sc.buildTracker
670
+ ? new WordCompletionTracker(agg.ttsText, {
671
+ llmText: agg.llmText || null,
672
+ userFacingText: agg.userFacingText,
673
+ })
674
+ : null;
675
+ this._appendSpokenSlot(frame, contextId, tracker, sc.appendToContext, false);
676
+ const frames = [frame];
677
+ if (!sc.buildTracker) {
678
+ const wordFrame = new TTSTextFrame({
679
+ text: agg.userFacingText,
680
+ textType: sc.textType,
681
+ rawText: agg.llmText || null,
682
+ segmentId: frame.id,
683
+ });
684
+ wordFrame.contextId = contextId;
685
+ wordFrame.willBeSpoken = true;
686
+ wordFrame.appendToContext = sc.appendToContext;
687
+ frames.push(wordFrame);
688
+ frames.push(...this.completeSpokenSlot());
689
+ }
690
+ frames.push(...this._drainBufferedWords());
691
+ return frames;
692
+ }
693
+ /**
694
+ * Replay previously-buffered word events now that a new slot may match them.
695
+ *
696
+ * Snapshots and clears the buffer before replaying so a word that still
697
+ * doesn't match anything (it belongs to a later, still-pending sentence)
698
+ * gets re-buffered by `processWord` itself and waits for the next
699
+ * promotion, rather than looping here.
700
+ */
701
+ _drainBufferedWords() {
702
+ const buffered = this._bufferedWords;
703
+ this._bufferedWords = [];
704
+ const frames = [];
705
+ for (const w of buffered) {
706
+ frames.push(...this.processWord(w.word, w.pts, w.contextId, w.includesInterFrameSpaces));
707
+ }
708
+ return frames;
709
+ }
710
+ /**
711
+ * Drop this context's still-buffered words, which no slot ever matched.
712
+ *
713
+ * Sentence mode drops an unrecognised word where it arrives; streaming only
714
+ * knows one will never match once the context it belongs to has ended.
715
+ */
716
+ _discardBufferedWords(contextId) {
717
+ const keep = [];
718
+ for (const w of this._bufferedWords) {
719
+ if (w.contextId !== contextId) {
720
+ keep.push(w);
721
+ continue;
722
+ }
723
+ logger.warning(`${this._name} Dropping buffered word '${w.word}' not recognised by any slot.`);
724
+ }
725
+ this._bufferedWords = keep;
726
+ }
727
+ /**
728
+ * Whether `slot` is an eligible active slot for `contextId`.
729
+ *
730
+ * A `null` contextId (legacy providers with no per-context word tagging,
731
+ * where concurrency can't occur) matches any slot, preserving the
732
+ * untargeted behaviour. Otherwise word routing is scoped to the slot's own
733
+ * context so concurrently-live contexts never consume each other's words.
734
+ */
735
+ _slotMatchesContext(slot, contextId) {
736
+ if (!(slot.spoken && !slot.complete && slot.tracker !== null)) {
737
+ return false;
738
+ }
739
+ return contextId === null || slot.contextId === contextId;
740
+ }
741
+ /** Return the first incomplete spoken slot with a tracker for `contextId`. */
742
+ _getActiveSlot(contextId = null) {
743
+ return this._slots.find((s) => this._slotMatchesContext(s, contextId)) ?? null;
744
+ }
745
+ /**
746
+ * Return the first incomplete spoken slot with a tracker for `contextId`
747
+ * after `current`.
748
+ */
749
+ _getNextActiveSlot(current, contextId = null) {
750
+ let found = false;
751
+ for (const s of this._slots) {
752
+ if (s === current) {
753
+ found = true;
754
+ continue;
755
+ }
756
+ if (found && this._slotMatchesContext(s, contextId)) {
757
+ return s;
758
+ }
759
+ }
760
+ return null;
761
+ }
762
+ /**
763
+ * Build an AggregatedTextProgressFrame reflecting the current
764
+ * spoken/remaining state of a slot.
765
+ */
766
+ _buildProgressFrame(slot, pts) {
767
+ const tracker = slot.tracker;
768
+ if (tracker === null) {
769
+ throw new Error("_buildProgressFrame requires a slot with a tracker");
770
+ }
771
+ const frame = new AggregatedTextProgressFrame({
772
+ segmentId: slot.frame.id,
773
+ contextId: slot.contextId,
774
+ text: slot.frame.text,
775
+ textType: slot.frame.textType,
776
+ accumulatedText: tracker.getAccumulatedUserFacingText(),
777
+ remainingText: tracker.getRemainingUserFacingText(false),
778
+ });
779
+ frame.pts = pts;
780
+ return frame;
781
+ }
782
+ /** Build a TTSTextFrame with all standard word-timestamp attributes set. */
783
+ _buildWordFrame(text, pts, contextId, options = {}) {
784
+ const frame = new TTSTextFrame({
785
+ text,
786
+ textType: TextType.WORD,
787
+ segmentId: options.segmentId ?? null,
788
+ });
789
+ frame.pts = pts;
790
+ frame.contextId = contextId;
791
+ if (options.suppressInContext) {
792
+ frame.appendToContext = false;
793
+ }
794
+ else {
795
+ frame.appendToContext =
796
+ contextId !== null ? (this._contextAppendToContext.get(contextId) ?? true) : true;
797
+ }
798
+ frame.rawText = options.rawText ?? null;
799
+ frame.includesInterFrameSpaces = options.includesInterFrameSpaces ?? false;
800
+ return frame;
801
+ }
802
+ }
803
+ //# sourceMappingURL=aggregated_frame_sequencer.js.map