@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,1639 @@
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/pipeline/worker.py
6
+ /**
7
+ * Pipeline worker implementation for managing frame processing pipelines.
8
+ *
9
+ * Provides the main PipelineWorker class that orchestrates pipeline
10
+ * execution, frame routing, lifecycle management, and monitoring capabilities
11
+ * including heartbeats, idle detection, and observer integration.
12
+ *
13
+ * Porting notes: the deprecated `PipelineTask` and `PipelineTaskParams`
14
+ * aliases, the `toolResources` option, the `ErrorFrame.fatal` branch, the
15
+ * deprecated `StartFrame` configuration fields and the legacy
16
+ * `setup_pipeline_task` setup hook name are not ported.
17
+ */
18
+ import { CancelledError, Event, monotonic, Queue, shield, sleep, TimeoutError, wait, waitFor, } from "@pipedog/asyncio";
19
+ import { _BusEdgeProcessor } from "../bus/bridge_processor.js";
20
+ import { BusCancelWorkerMessage, BusFlushProgressMessage, BusFrameMessage, BusTTSSpeakMessage, } from "../bus/index.js";
21
+ import { BusUICommandMessage, BusUIDataMessage, BusUIEventMessage, BusUIJobCompletedMessage, BusUIJobGroupCompletedMessage, BusUIJobGroupStartedMessage, BusUIJobUpdateMessage, UI_CANCEL_JOB_GROUP_EVENT_NAME, UI_SNAPSHOT_EVENT_NAME, } from "../bus/ui/messages.js";
22
+ import { SystemClock } from "../clocks/system_clock.js";
23
+ import { BotSpeakingFrame, CancelFrame, CancelWorkerFrame, EndFrame, EndWorkerFrame, ErrorFrame, HeartbeatFrame, InterimTranscriptionFrame, InterruptionFrame, InterruptionWorkerFrame, MetricsFrame, PipelineFlushFrame, StartFrame, StopFrame, StopWorkerFrame, TranscriptionFrame, TTSSpeakFrame, UserSpeakingFrame, UserStartedSpeakingFrame, } from "../frames/frames.js";
24
+ import { ProcessingMetricsData, TTFBMetricsData } from "../metrics/metrics.js";
25
+ import { BaseObserver } from "../observers/base_observer.js";
26
+ import { TurnTrackingObserver } from "../observers/turn_tracking_observer.js";
27
+ import { UserBotLatencyObserver } from "../observers/user_bot_latency_observer.js";
28
+ import { FrameDirection, FrameProcessorSetup, } from "../processors/frame_processor.js";
29
+ import { RTVIUICommandFrame, RTVIUIJobGroupFrame } from "../processors/frameworks/rtvi/frames.js";
30
+ import { RTVIObserver, RTVIProcessor, } from "../processors/frameworks/rtvi/index.js";
31
+ import * as RTVI from "../processors/frameworks/rtvi/models.js";
32
+ import { BaseInputTransport } from "../transports/base_input.js";
33
+ import { BaseOutputTransport } from "../transports/base_output.js";
34
+ import { logger } from "../utils/logger.js";
35
+ import { runSetupHook } from "../utils/startup.js";
36
+ import { isTracingAvailable } from "../utils/tracing/setup.js";
37
+ import { TracingContext } from "../utils/tracing/tracing_context.js";
38
+ import { TurnTraceObserver } from "../utils/tracing/turn_trace_observer.js";
39
+ import { BaseWorker, } from "../workers/base_worker.js";
40
+ import { BotCapabilities } from "./capabilities.js";
41
+ import { Pipeline, PipelineSink, PipelineSource } from "./pipeline.js";
42
+ import { WorkerObserver } from "./worker_observer.js";
43
+ export const HEARTBEAT_SECS = 1.0;
44
+ export const HEARTBEAT_MONITOR_SECS = 10.0;
45
+ /** How often a pipeline answering someone else's flush says it is still working. */
46
+ export const FLUSH_PROGRESS_PERIOD_SECS = 1.0;
47
+ export const IDLE_TIMEOUT_SECS = 300;
48
+ export const CANCEL_TIMEOUT_SECS = 20.0;
49
+ export const SETUP_TIMEOUT_SECS = 20.0;
50
+ export const START_TIMEOUT_SECS = 20.0;
51
+ /**
52
+ * Idle timeout observer.
53
+ *
54
+ * Waits for specific frames being generated in the pipeline. If the frames
55
+ * are generated the given event is set. If the event is not set it means the
56
+ * pipeline is probably idle.
57
+ */
58
+ export class IdleFrameObserver extends BaseObserver {
59
+ _idleEvent;
60
+ _idleTimeoutFrames;
61
+ /**
62
+ * Initialize the observer.
63
+ *
64
+ * @param options.idleEvent The event to set if the idle timeout frames are being pushed.
65
+ * @param options.idleTimeoutFrames The frames that should set the event when received.
66
+ */
67
+ constructor(options) {
68
+ super({ observeEveryPush: false });
69
+ this._idleEvent = options.idleEvent;
70
+ this._idleTimeoutFrames = options.idleTimeoutFrames;
71
+ }
72
+ /**
73
+ * Callback executed when a frame is pushed in the pipeline.
74
+ *
75
+ * @param data The frame push event data.
76
+ */
77
+ async onPushFrame(data) {
78
+ if (data.frame instanceof StartFrame ||
79
+ this._idleTimeoutFrames.some((cls) => data.frame instanceof cls)) {
80
+ this._idleEvent.set();
81
+ }
82
+ }
83
+ }
84
+ /**
85
+ * What a pipeline worker does when a processor can no longer do its job.
86
+ *
87
+ * An unusable processor keeps failing for as long as the pipeline keeps using
88
+ * it, so the pipeline has to decide whether it is still worth running. A
89
+ * processor becomes unusable through a permanent error category or through
90
+ * `FrameProcessor.pushError` with `forceTreatAsPermanent: true`.
91
+ *
92
+ * - `CONTINUE`: report the error and keep running. The application can decide
93
+ * what to do, for example by failing over to another provider.
94
+ * - `END`: end the pipeline gracefully, letting queued frames drain first.
95
+ * - `CANCEL`: cancel the pipeline immediately, abandoning queued frames.
96
+ */
97
+ export const ProcessorUnusablePolicy = {
98
+ CONTINUE: "continue",
99
+ END: "end",
100
+ CANCEL: "cancel",
101
+ };
102
+ /**
103
+ * Configuration parameters for pipeline execution.
104
+ *
105
+ * These parameters are usually passed to all frame processors through
106
+ * FrameProcessorSetup. For other generic pipeline worker parameters use
107
+ * PipelineWorker constructor options instead.
108
+ *
109
+ * - `audioInSampleRate`: input audio sample rate in Hz.
110
+ * - `audioOutSampleRate`: output audio sample rate in Hz.
111
+ * - `enableHeartbeats`: whether to enable heartbeat monitoring.
112
+ * - `enableMetrics`: whether to enable metrics collection.
113
+ * - `enableUsageMetrics`: whether to enable usage metrics.
114
+ * - `heartbeatsPeriodSecs`: period between heartbeats in seconds.
115
+ * - `heartbeatsMonitorSecs`: timeout (in seconds) before warning about missed
116
+ * heartbeats. Defaults to 10 seconds.
117
+ * - `reportOnlyInitialTtfb`: whether to report only initial time to first byte.
118
+ * - `sendInitialEmptyMetrics`: whether to send initial empty metrics.
119
+ * - `startMetadata`: additional metadata for pipeline start.
120
+ */
121
+ export class PipelineParams {
122
+ audioInSampleRate;
123
+ audioOutSampleRate;
124
+ enableHeartbeats;
125
+ enableMetrics;
126
+ enableUsageMetrics;
127
+ heartbeatsPeriodSecs;
128
+ heartbeatsMonitorSecs;
129
+ reportOnlyInitialTtfb;
130
+ sendInitialEmptyMetrics;
131
+ startMetadata;
132
+ constructor(options = {}) {
133
+ this.audioInSampleRate = options.audioInSampleRate ?? 16000;
134
+ this.audioOutSampleRate = options.audioOutSampleRate ?? 24000;
135
+ this.enableHeartbeats = options.enableHeartbeats ?? false;
136
+ this.enableMetrics = options.enableMetrics ?? false;
137
+ this.enableUsageMetrics = options.enableUsageMetrics ?? false;
138
+ this.heartbeatsPeriodSecs = options.heartbeatsPeriodSecs ?? HEARTBEAT_SECS;
139
+ this.heartbeatsMonitorSecs = options.heartbeatsMonitorSecs ?? HEARTBEAT_MONITOR_SECS;
140
+ this.reportOnlyInitialTtfb = options.reportOnlyInitialTtfb ?? false;
141
+ this.sendInitialEmptyMetrics = options.sendInitialEmptyMetrics ?? true;
142
+ this.startMetadata = options.startMetadata ?? {};
143
+ }
144
+ }
145
+ /**
146
+ * Manages the execution of a pipeline, handling frame processing and worker
147
+ * lifecycle.
148
+ *
149
+ * Orchestrates pipeline execution with comprehensive monitoring, event
150
+ * handling, and lifecycle management. Provides event handlers for various
151
+ * pipeline states and frame types, idle detection, heartbeat monitoring, and
152
+ * observer integration.
153
+ *
154
+ * Event handlers available:
155
+ *
156
+ * - on_frame_reached_upstream: called when upstream frames reach the source
157
+ * - on_frame_reached_downstream: called when downstream frames reach the sink
158
+ * - on_heartbeat_timeout: called when a heartbeat frame is not received within
159
+ * the monitor timeout. Fires repeatedly every `heartbeatsMonitorSecs` for as
160
+ * long as the stall persists.
161
+ * - on_idle_timeout: called when pipeline is idle beyond timeout threshold
162
+ * - on_pipeline_started: called when pipeline starts with StartFrame
163
+ * - on_pipeline_finished: called after the pipeline has reached any terminal
164
+ * state. This includes:
165
+ * - StopFrame: pipeline was stopped (processors keep connections open)
166
+ * - EndFrame: pipeline ended normally
167
+ * - CancelFrame: pipeline was cancelled
168
+ *
169
+ * Use this event for cleanup, logging, or post-processing tasks. Users can
170
+ * inspect the frame if they need to handle specific cases.
171
+ * - on_pipeline_timeout: called when a frame the worker was waiting on never
172
+ * reached the end of the pipeline, meaning a processor is blocked. Inspect
173
+ * the frame to tell the cases apart:
174
+ * - StartFrame: the pipeline never started and is being torn down
175
+ * - CancelFrame: the pipeline was cancelled but did not drain
176
+ *
177
+ * There is no EndFrame case: ending flushes whatever is queued, so the
178
+ * worker waits for it however long that takes.
179
+ * - on_pipeline_error: called when an error occurs with ErrorFrame. Handler
180
+ * can read `frame.processor.isUsable` to distinguish between errors that
181
+ * end a processor's usability from those that don't.
182
+ * - on_setup_timeout: called when setting the pipeline up times out.
183
+ *
184
+ * @example
185
+ * worker.eventHandler("on_frame_reached_upstream", async (worker, frame) => { ... });
186
+ * worker.eventHandler("on_heartbeat_timeout", async (worker) => { ... });
187
+ * worker.eventHandler("on_idle_timeout", async (worker) => { ... });
188
+ * worker.eventHandler("on_pipeline_started", async (worker, frame) => { ... });
189
+ * worker.eventHandler("on_pipeline_finished", async (worker, frame) => { ... });
190
+ * worker.eventHandler("on_pipeline_timeout", async (worker, frame) => { ... });
191
+ * worker.eventHandler("on_pipeline_error", async (worker, frame) => { ... });
192
+ * worker.eventHandler("on_setup_timeout", async (worker) => { ... });
193
+ */
194
+ export class PipelineWorker extends BaseWorker {
195
+ _bridged;
196
+ _handleFlushFrame;
197
+ _params;
198
+ _capabilitiesOverride;
199
+ _additionalSpanAttributes;
200
+ _cancelOnIdleTimeout;
201
+ _cancelRunnerOnIdleTimeout;
202
+ _cancelTimeoutSecs;
203
+ // Frames that have reached the sink. A flush watches this to tell a
204
+ // pipeline that is still working from one that is stuck.
205
+ _framesReachingSink = 0;
206
+ // Probes from other workers that this pipeline is answering, by flush id,
207
+ // and the progress reported back for our own.
208
+ _foreignProbes = new Map();
209
+ _flushProgress = new Map();
210
+ _flushProgressSent = 0.0;
211
+ _setupTimeoutSecs;
212
+ _startTimeoutSecs;
213
+ _clock;
214
+ _conversationId;
215
+ _enableTracing;
216
+ _enableTurnTracking;
217
+ _idleTimeoutSecs;
218
+ _appResources;
219
+ _processorUnusablePolicy;
220
+ // Processors the policy has already been applied for, so a service that
221
+ // keeps failing is acted on once instead of on every frame it can't handle.
222
+ _unusableProcessors = new Set();
223
+ _turnTrackingObserver = null;
224
+ _userBotLatencyObserver = null;
225
+ _turnTraceObserver = null;
226
+ _tracingContext = null;
227
+ _finished = false;
228
+ _cancelled = false;
229
+ // This queue is the queue used to push frames to the pipeline.
230
+ _pushQueue = new Queue();
231
+ _processPushTask = null;
232
+ _pipelineSetupTask = null;
233
+ _pipelineStartTask = null;
234
+ _setupWatchTask = null;
235
+ // This is the heartbeat queue. When a heartbeat frame is received in the
236
+ // down queue we add it to the heartbeat queue for processing.
237
+ _heartbeatQueue = new Queue();
238
+ _heartbeatPushTask = null;
239
+ _heartbeatMonitorTask = null;
240
+ // RTVI support
241
+ _rtvi = null;
242
+ // This is the idle event. When selected frames are pushed from any
243
+ // processor we consider the pipeline is not idle. We use an observer which
244
+ // will be listening any part of the pipeline.
245
+ _idleEvent = new Event();
246
+ _idleMonitorTask = null;
247
+ // This event is used to indicate the StartFrame has been received at the
248
+ // end of the pipeline.
249
+ _pipelineStartEvent = new Event();
250
+ // This event is used to indicate a finalize frame (e.g. EndFrame,
251
+ // StopFrame) has been received at the end of the pipeline.
252
+ _pipelineEndEvent = new Event();
253
+ _source;
254
+ _sink;
255
+ _pipeline;
256
+ _observer;
257
+ // These events can be used to check which frames make it to the source or
258
+ // sink processors. Instead of calling the event handlers for every frame
259
+ // the user needs to specify which events they are interested in. This is
260
+ // mainly for efficiency reason because each event handler creates a task
261
+ // and most likely you only care about one or two frame types.
262
+ _reachedUpstreamTypes = new Set();
263
+ _reachedDownstreamTypes = new Set();
264
+ /**
265
+ * Initialize the PipelineWorker.
266
+ *
267
+ * @param pipeline The pipeline to execute.
268
+ * @param options See `PipelineWorkerOptions`.
269
+ */
270
+ constructor(pipeline, options = {}) {
271
+ super({
272
+ name: options.name ?? null,
273
+ active: options.active ?? true,
274
+ taskManager: options.taskManager ?? null,
275
+ checkDanglingTasks: options.checkDanglingTasks ?? true,
276
+ });
277
+ const bridged = options.bridged ?? null;
278
+ this._bridged = bridged;
279
+ this._handleFlushFrame =
280
+ options.handleFlushFrame !== null && options.handleFlushFrame !== undefined
281
+ ? options.handleFlushFrame
282
+ : bridged === null;
283
+ this._params = options.params ?? new PipelineParams();
284
+ this._capabilitiesOverride = options.capabilities ?? new BotCapabilities();
285
+ this._additionalSpanAttributes = options.additionalSpanAttributes ?? {};
286
+ this._cancelOnIdleTimeout = options.cancelOnIdleTimeout ?? true;
287
+ this._cancelRunnerOnIdleTimeout = options.cancelRunnerOnIdleTimeout ?? true;
288
+ this._cancelTimeoutSecs = options.cancelTimeoutSecs ?? CANCEL_TIMEOUT_SECS;
289
+ this._setupTimeoutSecs = options.setupTimeoutSecs ?? SETUP_TIMEOUT_SECS;
290
+ this._startTimeoutSecs = options.startTimeoutSecs ?? START_TIMEOUT_SECS;
291
+ this._clock = options.clock ?? new SystemClock();
292
+ this._conversationId = options.conversationId ?? null;
293
+ this._enableTracing = (options.enableTracing ?? false) && isTracingAvailable();
294
+ this._enableTurnTracking = options.enableTurnTracking ?? true;
295
+ this._idleTimeoutSecs =
296
+ options.idleTimeoutSecs === undefined ? IDLE_TIMEOUT_SECS : options.idleTimeoutSecs;
297
+ this._appResources = options.appResources ?? null;
298
+ this._processorUnusablePolicy =
299
+ options.processorUnusablePolicy ?? ProcessorUnusablePolicy.CONTINUE;
300
+ const idleTimeoutFrames = options.idleTimeoutFrames ?? [
301
+ BotSpeakingFrame,
302
+ InterimTranscriptionFrame,
303
+ TranscriptionFrame,
304
+ UserSpeakingFrame,
305
+ UserStartedSpeakingFrame,
306
+ ];
307
+ // Like upstream (`observers or []`), observers are added to the caller's
308
+ // list, unless it is empty (falsy in Python), in which case a new one is used.
309
+ const observers = options.observers && options.observers.length > 0 ? options.observers : [];
310
+ if (this._enableTurnTracking) {
311
+ this._turnTrackingObserver = new TurnTrackingObserver();
312
+ observers.push(this._turnTrackingObserver);
313
+ }
314
+ if (this._enableTracing && this._turnTrackingObserver) {
315
+ // Create pipeline-scoped tracing context
316
+ this._tracingContext = new TracingContext();
317
+ // Create latency observer for tracing
318
+ this._userBotLatencyObserver = new UserBotLatencyObserver();
319
+ observers.push(this._userBotLatencyObserver);
320
+ // Create turn trace observer with latency tracking
321
+ this._turnTraceObserver = new TurnTraceObserver({
322
+ turnTracker: this._turnTrackingObserver,
323
+ latencyTracker: this._userBotLatencyObserver,
324
+ conversationId: this._conversationId,
325
+ additionalSpanAttributes: this._additionalSpanAttributes,
326
+ tracingContext: this._tracingContext,
327
+ });
328
+ observers.push(this._turnTraceObserver);
329
+ }
330
+ // RTVI support
331
+ const enableRtvi = options.enableRtvi ?? bridged === null;
332
+ let prependRtvi = false;
333
+ const externalRtvi = firstOf(this._iterProcessors(pipeline, [RTVIProcessor]));
334
+ const externalObserverFound = observers.some((o) => o instanceof RTVIObserver);
335
+ if (externalRtvi && !externalObserverFound) {
336
+ logger.error(`${this}: RTVIProcessor found in pipeline but no RTVIObserver in observers. ` +
337
+ "Make sure to add both.");
338
+ }
339
+ else if (!externalRtvi && externalObserverFound) {
340
+ logger.error(`${this}: RTVIObserver found in observers but no RTVIProcessor in pipeline. ` +
341
+ "Make sure to add both.");
342
+ }
343
+ else if (externalRtvi && externalObserverFound) {
344
+ logger.warning(`${this}: RTVIProcessor and RTVIObserver found, skipping default ones. ` +
345
+ "They are both added by default, no need to add them yourself.");
346
+ this._rtvi = externalRtvi;
347
+ }
348
+ else if (enableRtvi) {
349
+ this._rtvi = options.rtviProcessor ?? new RTVIProcessor();
350
+ observers.push(this._rtvi.createRtviObserver({ params: options.rtviObserverParams ?? null }));
351
+ prependRtvi = true;
352
+ }
353
+ if (this._rtvi) {
354
+ // Automatically call RTVIProcessor.setBotReady()
355
+ this.rtvi.eventHandler("on_client_ready", async (rtvi) => {
356
+ await rtvi.setBotReady();
357
+ });
358
+ // Republish inbound client UI messages onto the bus so UIWorker
359
+ // subscribers can dispatch them.
360
+ this.rtvi.eventHandler("on_ui_message", async (_rtvi, message) => {
361
+ await this._republishUiMessageOnBus(message);
362
+ });
363
+ }
364
+ if (this._idleTimeoutSecs) {
365
+ const idleFrameObserver = new IdleFrameObserver({
366
+ idleEvent: this._idleEvent,
367
+ idleTimeoutFrames,
368
+ });
369
+ observers.push(idleFrameObserver);
370
+ }
371
+ // When bridged, wrap the user pipeline with bus edge processors so frames
372
+ // tee onto the bus at the source/sink and incoming bus frames are
373
+ // injected back into the local pipeline. The edges read the worker's bus
374
+ // lazily, so the bus only needs to be set (via `attach()`) before `run()`
375
+ // is called.
376
+ let userPipeline = pipeline;
377
+ if (bridged !== null) {
378
+ const edgeSource = new _BusEdgeProcessor({
379
+ worker: this,
380
+ direction: FrameDirection.UPSTREAM,
381
+ excludeFrames: options.excludeFrames ?? null,
382
+ name: `${this}::EdgeSource`,
383
+ });
384
+ const edgeSink = new _BusEdgeProcessor({
385
+ worker: this,
386
+ direction: FrameDirection.DOWNSTREAM,
387
+ excludeFrames: options.excludeFrames ?? null,
388
+ name: `${this}::EdgeSink`,
389
+ });
390
+ userPipeline = new Pipeline([edgeSource, pipeline, edgeSink]);
391
+ }
392
+ // This is the final pipeline. It is composed of a source processor,
393
+ // followed by the user pipeline, and ending with a sink processor. The
394
+ // source allows us to receive and react to upstream frames, and the sink
395
+ // allows us to receive and react to downstream frames.
396
+ this._source = new PipelineSource((frame, direction) => this._sourcePushFrame(frame, direction), {
397
+ name: `${this}::Source`,
398
+ });
399
+ this._sink = new PipelineSink((frame, direction) => this._sinkPushFrame(frame, direction), {
400
+ name: `${this}::Sink`,
401
+ });
402
+ // Only prepend the RTVIProcessor if we created it ourselves. When the
403
+ // user already placed it inside their pipeline we must not insert it
404
+ // again or it will appear twice in the frame chain.
405
+ const processors = prependRtvi && this._rtvi ? [this._rtvi, userPipeline] : [userPipeline];
406
+ this._pipeline = new Pipeline(processors, { source: this._source, sink: this._sink });
407
+ // The worker observer acts as a proxy to the provided observers. This
408
+ // way, we only need to pass a single observer (using the
409
+ // FrameProcessorSetup) which then just acts as a proxy.
410
+ this._observer = new WorkerObserver({ observers });
411
+ this._registerEventHandler("on_frame_reached_upstream");
412
+ this._registerEventHandler("on_frame_reached_downstream");
413
+ this._registerEventHandler("on_heartbeat_timeout");
414
+ this._registerEventHandler("on_idle_timeout");
415
+ this._registerEventHandler("on_pipeline_started");
416
+ this._registerEventHandler("on_pipeline_finished");
417
+ this._registerEventHandler("on_pipeline_timeout");
418
+ this._registerEventHandler("on_setup_timeout");
419
+ this._registerEventHandler("on_pipeline_error");
420
+ // Bridge pipeline lifecycle to the BaseWorker lifecycle so the bus
421
+ // registry sees this worker as ready/finished.
422
+ this.eventHandler("on_pipeline_started", async (_task, _frame) => {
423
+ await this.start();
424
+ });
425
+ this.eventHandler("on_pipeline_finished", async (_task, _frame) => {
426
+ await this.stop();
427
+ });
428
+ }
429
+ /** The pipeline parameters for this worker. */
430
+ get params() {
431
+ return this._params;
432
+ }
433
+ /**
434
+ * What the bot does in this session.
435
+ *
436
+ * Derived from the media the pipeline's transports send and receive and
437
+ * from `PipelineParams.enableMetrics`, with the `capabilities` passed to the
438
+ * constructor applied on top. A field no transport reports is `null`
439
+ * (unknown). RTVI sends this to the client in `bot-ready`.
440
+ */
441
+ get capabilities() {
442
+ let derived = new BotCapabilities({ metrics: this._params.enableMetrics });
443
+ for (const processor of this._iterProcessors(this._pipeline, [BaseInputTransport, BaseOutputTransport])) {
444
+ derived = derived.combine(processor.capabilities);
445
+ }
446
+ return derived.override(this._capabilitiesOverride);
447
+ }
448
+ /** Whether this pipeline is bridged onto the bus. */
449
+ get bridged() {
450
+ return this._bridged !== null;
451
+ }
452
+ /**
453
+ * The application-defined resources passed to this worker.
454
+ *
455
+ * This is the same object passed to the constructor as `appResources`. Tool
456
+ * handlers can also access it via `FunctionCallParams.appResources`. The
457
+ * framework returns the original reference; mutations are visible to all
458
+ * callers. `null` if none were passed.
459
+ */
460
+ get appResources() {
461
+ return this._appResources;
462
+ }
463
+ /**
464
+ * The full pipeline managed by this pipeline worker. This also includes any
465
+ * internal processors added by the pipeline worker.
466
+ */
467
+ get pipeline() {
468
+ return this._pipeline;
469
+ }
470
+ /** The turn tracking observer if enabled, or null. */
471
+ get turnTrackingObserver() {
472
+ return this._turnTrackingObserver;
473
+ }
474
+ /** The turn trace observer if enabled, or null. */
475
+ get turnTraceObserver() {
476
+ return this._turnTraceObserver;
477
+ }
478
+ /** The RTVI processor added to the pipeline when RTVI is enabled. */
479
+ get rtvi() {
480
+ if (!this._rtvi) {
481
+ throw new Error(`${this} RTVI is not enabled.`);
482
+ }
483
+ return this._rtvi;
484
+ }
485
+ /** Frame types that trigger the on_frame_reached_upstream event. */
486
+ get reachedUpstreamTypes() {
487
+ return [...this._reachedUpstreamTypes];
488
+ }
489
+ /** Frame types that trigger the on_frame_reached_downstream event. */
490
+ get reachedDownstreamTypes() {
491
+ return [...this._reachedDownstreamTypes];
492
+ }
493
+ /**
494
+ * Add an observer to monitor pipeline execution.
495
+ *
496
+ * @param observer The observer to add to the pipeline monitoring.
497
+ */
498
+ addObserver(observer) {
499
+ this._observer.addObserver(observer);
500
+ }
501
+ /**
502
+ * Remove an observer from pipeline monitoring.
503
+ *
504
+ * @param observer The observer to remove from pipeline monitoring.
505
+ */
506
+ async removeObserver(observer) {
507
+ await this._observer.removeObserver(observer);
508
+ }
509
+ /**
510
+ * Set which frame types trigger the on_frame_reached_upstream event.
511
+ *
512
+ * @param types Frame types to monitor for upstream events.
513
+ */
514
+ setReachedUpstreamFilter(types) {
515
+ this._reachedUpstreamTypes = new Set(types);
516
+ }
517
+ /**
518
+ * Set which frame types trigger the on_frame_reached_downstream event.
519
+ *
520
+ * @param types Frame types to monitor for downstream events.
521
+ */
522
+ setReachedDownstreamFilter(types) {
523
+ this._reachedDownstreamTypes = new Set(types);
524
+ }
525
+ /**
526
+ * Add frame types to trigger the on_frame_reached_upstream event.
527
+ *
528
+ * @param types Frame types to add to upstream monitoring.
529
+ */
530
+ addReachedUpstreamFilter(types) {
531
+ for (const t of types)
532
+ this._reachedUpstreamTypes.add(t);
533
+ }
534
+ /**
535
+ * Add frame types to trigger the on_frame_reached_downstream event.
536
+ *
537
+ * @param types Frame types to add to downstream monitoring.
538
+ */
539
+ addReachedDownstreamFilter(types) {
540
+ for (const t of types)
541
+ this._reachedDownstreamTypes.add(t);
542
+ }
543
+ /**
544
+ * Check if the pipeline worker has finished execution.
545
+ *
546
+ * This indicates whether the worker has finished, meaning all processors
547
+ * have stopped.
548
+ *
549
+ * @returns True if all processors have stopped and the worker is complete.
550
+ */
551
+ hasFinished() {
552
+ return this._finished;
553
+ }
554
+ /**
555
+ * Schedule the pipeline to stop after processing all queued frames.
556
+ *
557
+ * Sends an EndFrame to gracefully terminate the pipeline once all current
558
+ * processing is complete.
559
+ */
560
+ async stopWhenDone() {
561
+ logger.debug(`Task ${this} scheduled to stop when done`);
562
+ await this.queueFrame(new EndFrame());
563
+ }
564
+ /**
565
+ * Request a graceful end of the session, draining the pipeline first.
566
+ *
567
+ * Whatever this worker has already pushed reaches the end of the pipeline
568
+ * before the session goes away, rather than being cut off wherever it
569
+ * happened to be.
570
+ *
571
+ * @param options.reason Optional human-readable reason for ending.
572
+ */
573
+ async end(options = {}) {
574
+ await this._drainPipeline();
575
+ await super.end({ reason: options.reason ?? null });
576
+ }
577
+ /**
578
+ * Activate a worker by name, draining this pipeline when handing over.
579
+ *
580
+ * Deactivating this worker before its pipeline drains would let the target
581
+ * start producing while this worker's output is still in flight, and both
582
+ * would arrive interleaved. A worker that stays active is handing nothing
583
+ * over, so it doesn't wait: the first activation of a session would
584
+ * otherwise wait on the very worker it is about to wake.
585
+ *
586
+ * @param workerName The name of the worker to activate.
587
+ * @param options.args Optional `WorkerActivationArgs` forwarded to the
588
+ * target worker's `onActivated`.
589
+ * @param options.deactivateSelf Whether to deactivate this worker before
590
+ * activating the target. Deactivating this worker drains its pipeline
591
+ * first; staying active does not. A worker that stays active and wants to
592
+ * drain anyway can call `flushPipeline` before this.
593
+ */
594
+ async activateWorker(workerName, options = {}) {
595
+ const deactivateSelf = options.deactivateSelf ?? false;
596
+ if (deactivateSelf) {
597
+ await this._drainPipeline();
598
+ }
599
+ await super.activateWorker(workerName, { args: options.args ?? null, deactivateSelf });
600
+ }
601
+ /**
602
+ * Wait for in-flight frames to be processed, if any can be.
603
+ *
604
+ * A pipeline that is not running has no one left to bounce the flush probe
605
+ * back, so waiting on it would only spend the flush timeout.
606
+ * `_processPushTask` is created with the worker's tasks and cleared once
607
+ * they are torn down, so it stands for whether there is a pipeline to
608
+ * drain.
609
+ */
610
+ async _drainPipeline() {
611
+ if (this._processPushTask === null || this.hasFinished()) {
612
+ return;
613
+ }
614
+ if (!(await this.flushPipeline())) {
615
+ logger.warning(`${this}: proceeding without draining; whatever is still in flight will be cut off`);
616
+ }
617
+ }
618
+ /**
619
+ * Request the running pipeline to cancel.
620
+ *
621
+ * @param options.reason Optional reason to indicate why the pipeline is being cancelled.
622
+ */
623
+ async cancel(options = {}) {
624
+ if (!this._finished) {
625
+ await this._cancel({ reason: options.reason ?? null });
626
+ }
627
+ }
628
+ /**
629
+ * Start and manage the pipeline execution until completion or cancellation.
630
+ *
631
+ * @param params Configuration parameters for pipeline execution.
632
+ */
633
+ async run(params) {
634
+ if (this.hasFinished()) {
635
+ return;
636
+ }
637
+ try {
638
+ // Set the worker up and start setting processors up. Processors are set
639
+ // up in the background, see _watchPipelineSetup().
640
+ if (!(await this._setupWithinTimeout(params))) {
641
+ // Nothing was pushed into the pipeline, so there is nothing to drain:
642
+ // release whatever was set up and give up.
643
+ await this._cleanup(true);
644
+ return;
645
+ }
646
+ // Create the worker's tasks and wait for the push task, which feeds
647
+ // frames to the very beginning of our pipeline (i.e. to our controlled
648
+ // source processor).
649
+ await this._createTasks();
650
+ try {
651
+ // Wait for pipeline to finish.
652
+ await this._waitForPipelineFinished();
653
+ }
654
+ catch (e) {
655
+ if (!(e instanceof CancelledError))
656
+ throw e;
657
+ logger.debug(`Pipeline worker ${this} got cancelled from outside...`);
658
+ // We have been cancelled from outside, let's just cancel everything.
659
+ await this._cancel();
660
+ // Wait again for pipeline to finish. This time we have really
661
+ // cancelled, so it should really finish.
662
+ await this._waitForPipelineFinished();
663
+ // Re-throw in case there's more cleanup to do.
664
+ throw e;
665
+ }
666
+ }
667
+ finally {
668
+ // We can reach this point for different reasons:
669
+ //
670
+ // 1. The pipeline worker has finished (try case).
671
+ // 2. By a task cancellation (catch case).
672
+ logger.debug(`Pipeline worker ${this} is finishing...`);
673
+ await this._cancelTasks();
674
+ this._printDanglingTasks();
675
+ await this._cancelChildren();
676
+ this._finished = true;
677
+ logger.debug(`Pipeline worker ${this} has finished`);
678
+ }
679
+ }
680
+ /**
681
+ * Queue a single frame to be pushed through the pipeline.
682
+ *
683
+ * Downstream frames are pushed from the beginning of the pipeline. Upstream
684
+ * frames are pushed from the end of the pipeline.
685
+ *
686
+ * @param frame The frame to be processed.
687
+ * @param direction The direction to push the frame. Defaults to downstream.
688
+ */
689
+ async queueFrame(frame, direction = FrameDirection.DOWNSTREAM) {
690
+ if (direction === FrameDirection.DOWNSTREAM) {
691
+ await this._pushQueue.put(frame);
692
+ }
693
+ else {
694
+ await this._sink.queueFrame(frame, direction);
695
+ }
696
+ }
697
+ /**
698
+ * Queue multiple frames to be pushed through the pipeline.
699
+ *
700
+ * Downstream frames are pushed from the beginning of the pipeline. Upstream
701
+ * frames are pushed from the end of the pipeline.
702
+ *
703
+ * @param frames An iterable or async iterable of frames to be processed.
704
+ * @param direction The direction to push the frames. Defaults to downstream.
705
+ */
706
+ async queueFrames(frames, direction = FrameDirection.DOWNSTREAM) {
707
+ if (Symbol.asyncIterator in frames) {
708
+ for await (const frame of frames) {
709
+ await this.queueFrame(frame, direction);
710
+ }
711
+ }
712
+ else if (Symbol.iterator in frames) {
713
+ for (const frame of frames) {
714
+ await this.queueFrame(frame, direction);
715
+ }
716
+ }
717
+ }
718
+ /**
719
+ * Flush all in-flight frames from the pipeline and wait for it to drain.
720
+ *
721
+ * Pushes a `PipelineFlushFrame` downstream; the sink bounces it back
722
+ * upstream, the source turns it around, and its event is set when it
723
+ * reaches the sink a second time. By then every frame queued ahead of it
724
+ * has been processed, along with anything a processor started by pushing
725
+ * upstream. The probe goes on the worker's push queue, behind whatever is
726
+ * already waiting there, and bypasses any `queueFrame` override (e.g.
727
+ * tool-call deferral).
728
+ *
729
+ * @param timeout Seconds of no progress before giving up. Progress is a
730
+ * frame reaching this worker's sink, or a report from the pipeline
731
+ * answering the probe when it crossed into another worker. A pipeline
732
+ * still working keeps the wait alive, however long it takes; one that is
733
+ * stuck, or that nobody will answer, gives up after this much quiet. On
734
+ * giving up a warning is logged and `false` is returned rather than
735
+ * blocking forever.
736
+ * @returns True if the pipeline drained, false if it went quiet first.
737
+ */
738
+ async flushPipeline(timeout = 5.0) {
739
+ const event = new Event();
740
+ const probe = new PipelineFlushFrame({ event, origin: this.name });
741
+ logger.debug(`${this}: pushing flush probe downstream`);
742
+ this._flushProgress.set(probe.id, 0);
743
+ try {
744
+ await this._pushQueue.put(probe);
745
+ for (;;) {
746
+ const frames = this._framesReachingSink;
747
+ const reports = this._flushProgress.get(probe.id);
748
+ try {
749
+ await waitFor(() => event.wait(), timeout);
750
+ return true;
751
+ }
752
+ catch (e) {
753
+ if (!(e instanceof TimeoutError))
754
+ throw e;
755
+ const reported = this._flushProgress.get(probe.id) !== reports;
756
+ if (this._framesReachingSink === frames && !reported) {
757
+ logger.warning(`${this}: pipeline flush gave up after ${timeout}s of no progress`);
758
+ return false;
759
+ }
760
+ if (reported) {
761
+ logger.debug(`${this}: flush still in flight, whoever has it is working`);
762
+ }
763
+ }
764
+ }
765
+ }
766
+ finally {
767
+ this._flushProgress.delete(probe.id);
768
+ }
769
+ }
770
+ /**
771
+ * Report progress on a probe from another worker while we hold it.
772
+ *
773
+ * Called by whoever brings the probe into this pipeline, since only they
774
+ * know it is really coming in: every worker on the bus sees it, but most of
775
+ * them have nowhere to put it.
776
+ *
777
+ * @param frame The flush probe entering this pipeline.
778
+ */
779
+ trackFlushProbe(frame) {
780
+ if (this._handleFlushFrame && frame.origin && frame.origin !== this.name) {
781
+ this._foreignProbes.set(frame.id, frame.origin);
782
+ }
783
+ }
784
+ /**
785
+ * Handle outbound bus messages: TTS playback and RTVI UI translation.
786
+ *
787
+ * Runs the base lifecycle/job dispatch first. A `BusTTSSpeakMessage`
788
+ * targeted at this worker is queued as a `TTSSpeakFrame` (pipelines without
789
+ * a TTS service let it flow through). When this worker owns the RTVI
790
+ * processor, UI carriers produced by a `UIWorker` (`BusUIDataMessage`
791
+ * subclasses) are translated into RTVI frames by `_handleUiBusMessage`;
792
+ * other workers skip the translation.
793
+ */
794
+ async onBusMessage(message) {
795
+ await super.onBusMessage(message);
796
+ // `BaseWorker.onBusMessage` already drops targeted messages for other
797
+ // workers, but it returns early before reaching here -- re-apply the
798
+ // filter before queueing pipeline frames.
799
+ if (message.target && message.target !== this.name) {
800
+ return;
801
+ }
802
+ if (message instanceof BusFrameMessage) {
803
+ await this._queueBridgedFrame(message);
804
+ }
805
+ else if (message instanceof BusFlushProgressMessage) {
806
+ const progress = this._flushProgress.get(message.flushId);
807
+ if (progress !== undefined) {
808
+ this._flushProgress.set(message.flushId, progress + 1);
809
+ }
810
+ }
811
+ else if (message instanceof BusTTSSpeakMessage) {
812
+ await this.queueFrame(new TTSSpeakFrame({ text: message.text, appendToContext: message.appendToContext }));
813
+ }
814
+ else if (this._rtvi && message instanceof BusUIDataMessage) {
815
+ await this._handleUiBusMessage(message);
816
+ }
817
+ }
818
+ /**
819
+ * Queue a frame that reached this worker over the bus.
820
+ *
821
+ * Queued rather than pushed from the edge that captured the outbound
822
+ * direction, so bus inbound serialises with the frames this worker queues
823
+ * itself (e.g. those a flow framework enqueues from `setNode`).
824
+ *
825
+ * @param message The frame carrier to inject.
826
+ */
827
+ async _queueBridgedFrame(message) {
828
+ if (this._bridged === null) {
829
+ return;
830
+ }
831
+ if (message.source === this.name) {
832
+ return;
833
+ }
834
+ if (this._bridged.length > 0 && !this._bridged.includes(message.bridge)) {
835
+ return;
836
+ }
837
+ await this.queueFrame(message.frame, message.direction);
838
+ }
839
+ /**
840
+ * Translate a UI carrier into the matching RTVI frame and queue it.
841
+ *
842
+ * Called only when this worker owns the RTVI processor. The `RTVIObserver`
843
+ * later wraps the queued frame into a typed `ui-command` / `ui-job-group`
844
+ * envelope for the client. Inbound carriers (e.g. `BusUIEventMessage`)
845
+ * match no branch and are ignored.
846
+ */
847
+ async _handleUiBusMessage(message) {
848
+ let frame = null;
849
+ if (message instanceof BusUICommandMessage) {
850
+ frame = new RTVIUICommandFrame({
851
+ command: message.commandName,
852
+ payload: message.payload,
853
+ });
854
+ }
855
+ else if (message instanceof BusUIJobGroupStartedMessage) {
856
+ frame = new RTVIUIJobGroupFrame({
857
+ data: new RTVI.UIJobGroupStartedData({
858
+ jobId: message.jobId,
859
+ workers: [...(message.workers ?? [])],
860
+ label: message.label,
861
+ cancellable: message.cancellable,
862
+ at: message.at,
863
+ }),
864
+ });
865
+ }
866
+ else if (message instanceof BusUIJobUpdateMessage) {
867
+ frame = new RTVIUIJobGroupFrame({
868
+ data: new RTVI.UIJobUpdateData({
869
+ jobId: message.jobId,
870
+ workerName: message.workerName,
871
+ data: message.data,
872
+ at: message.at,
873
+ }),
874
+ });
875
+ }
876
+ else if (message instanceof BusUIJobCompletedMessage) {
877
+ frame = new RTVIUIJobGroupFrame({
878
+ data: new RTVI.UIJobCompletedData({
879
+ jobId: message.jobId,
880
+ workerName: message.workerName,
881
+ status: message.status,
882
+ response: message.response,
883
+ at: message.at,
884
+ }),
885
+ });
886
+ }
887
+ else if (message instanceof BusUIJobGroupCompletedMessage) {
888
+ frame = new RTVIUIJobGroupFrame({
889
+ data: new RTVI.UIJobGroupCompletedData({
890
+ jobId: message.jobId,
891
+ at: message.at,
892
+ }),
893
+ });
894
+ }
895
+ if (frame !== null) {
896
+ await this.queueFrame(frame);
897
+ }
898
+ }
899
+ /**
900
+ * Republish an inbound client UI message onto the bus.
901
+ *
902
+ * Translates a typed RTVI UI message from the client (`UIEventMessage`,
903
+ * `UISnapshotMessage`, or `UICancelJobGroupMessage`) into a single
904
+ * `BusUIEventMessage` carrier so `UIWorker` subscribers can dispatch it.
905
+ * This is the inbound counterpart of `onBusMessage`. Unrecognized message
906
+ * types are ignored.
907
+ */
908
+ async _republishUiMessageOnBus(message) {
909
+ let eventName;
910
+ let payload;
911
+ if (message instanceof RTVI.UIEventMessage) {
912
+ eventName = message.data.event;
913
+ payload = message.data.payload;
914
+ }
915
+ else if (message instanceof RTVI.UISnapshotMessage) {
916
+ eventName = UI_SNAPSHOT_EVENT_NAME;
917
+ payload = message.data.tree.modelDump({ excludeNone: true });
918
+ }
919
+ else if (message instanceof RTVI.UICancelJobGroupMessage) {
920
+ eventName = UI_CANCEL_JOB_GROUP_EVENT_NAME;
921
+ payload = {
922
+ job_id: message.data.jobId,
923
+ reason: message.data.reason,
924
+ };
925
+ }
926
+ else {
927
+ return;
928
+ }
929
+ await this.sendBusMessage(new BusUIEventMessage({
930
+ source: this.name,
931
+ target: null,
932
+ eventName,
933
+ payload,
934
+ }));
935
+ }
936
+ /** Stop processors still setting up; they keep holding the StartFrame. */
937
+ async _cancelPipelineSetup() {
938
+ if (this._pipelineSetupTask && !this._pipelineSetupTask.done()) {
939
+ await this.cancelTask(this._pipelineSetupTask);
940
+ }
941
+ }
942
+ /** Stop waiting for the pipeline to start, so it is only cleaned up. */
943
+ async _cancelPipelineStart() {
944
+ if (this._pipelineStartTask && !this._pipelineStartTask.done()) {
945
+ await this.cancelTask(this._pipelineStartTask);
946
+ }
947
+ }
948
+ /**
949
+ * Internal cancellation logic for the pipeline worker.
950
+ *
951
+ * @param options.reason Optional reason to indicate why the pipeline is being cancelled.
952
+ */
953
+ async _cancel(options = {}) {
954
+ if (!this._cancelled) {
955
+ logger.debug(`Cancelling pipeline worker ${this}`);
956
+ this._cancelled = true;
957
+ // Only pushed into a started pipeline: if the StartFrame hasn't reached
958
+ // the end, cancelling the start task below means the pipeline is only
959
+ // cleaned up and this CancelFrame is never pushed.
960
+ await this.queueFrame(new CancelFrame({ reason: options.reason ?? null }));
961
+ // Only request them: a processor's setup() may be what is cancelling,
962
+ // and awaiting the setup task from inside it never returns.
963
+ if (this._pipelineSetupTask && !this._pipelineSetupTask.done()) {
964
+ this._pipelineSetupTask.cancel();
965
+ }
966
+ if (this._pipelineStartTask && !this._pipelineStartTask.done()) {
967
+ this._pipelineStartTask.cancel();
968
+ }
969
+ }
970
+ }
971
+ /** Create and start all pipeline processing tasks. */
972
+ async _createTasks() {
973
+ this._processPushTask = this.createTask(() => this._processPushQueue(), "_process_push_queue");
974
+ this._setupWatchTask = this.createTask(() => this._watchPipelineSetup(), "_watch_pipeline_setup");
975
+ }
976
+ /**
977
+ * Give up on a pipeline whose processors don't finish setting up.
978
+ *
979
+ * Setting up is cancelled, so the processors still being set up keep
980
+ * holding the StartFrame: the pipeline never starts and is only cleaned up,
981
+ * as when setting the worker up times out.
982
+ */
983
+ async _watchPipelineSetup() {
984
+ const setupTask = this._pipelineSetupTask;
985
+ if (!setupTask) {
986
+ return;
987
+ }
988
+ try {
989
+ await waitFor(() => shield(setupTask), this._setupTimeoutSecs);
990
+ }
991
+ catch (e) {
992
+ if (!(e instanceof TimeoutError))
993
+ throw e;
994
+ logger.error(`${this}: timeout setting the pipeline up ` +
995
+ "(a processor blocked while connecting?), stopping the pipeline.");
996
+ await this._callEventHandler("on_setup_timeout");
997
+ // Not _cancel(), which does nothing if the worker was cancelled before
998
+ // setting up began.
999
+ await this._cancelPipelineSetup();
1000
+ await this._cancelPipelineStart();
1001
+ }
1002
+ }
1003
+ /** Start heartbeat tasks if heartbeats are enabled and not already running. */
1004
+ _maybeStartHeartbeatTasks() {
1005
+ if (this._params.enableHeartbeats && this._heartbeatPushTask === null) {
1006
+ this._heartbeatPushTask = this.createTask(() => this._heartbeatPushHandler(), "_heartbeat_push_handler");
1007
+ this._heartbeatMonitorTask = this.createTask(() => this._heartbeatMonitorHandler(), "_heartbeat_monitor_handler");
1008
+ }
1009
+ }
1010
+ /** Start the idle monitoring task if an idle timeout is configured. */
1011
+ _maybeStartIdleTask() {
1012
+ if (this._idleTimeoutSecs) {
1013
+ this._idleMonitorTask = this.createTask(() => this._idleMonitorHandler(), "_idle_monitor_handler");
1014
+ }
1015
+ }
1016
+ /** Cancel all running pipeline tasks. */
1017
+ async _cancelTasks() {
1018
+ await this._cancelPipelineSetup();
1019
+ await this._cancelPipelineStart();
1020
+ if (this._setupWatchTask) {
1021
+ await this.cancelTask(this._setupWatchTask);
1022
+ this._setupWatchTask = null;
1023
+ }
1024
+ if (this._processPushTask) {
1025
+ await this.cancelTask(this._processPushTask);
1026
+ this._processPushTask = null;
1027
+ }
1028
+ await this._maybeCancelHeartbeatTasks();
1029
+ await this._maybeCancelIdleTask();
1030
+ }
1031
+ /** Cancel heartbeat tasks if they are running. */
1032
+ async _maybeCancelHeartbeatTasks() {
1033
+ if (!this._params.enableHeartbeats) {
1034
+ return;
1035
+ }
1036
+ if (this._heartbeatPushTask) {
1037
+ await this.cancelTask(this._heartbeatPushTask);
1038
+ this._heartbeatPushTask = null;
1039
+ }
1040
+ if (this._heartbeatMonitorTask) {
1041
+ await this.cancelTask(this._heartbeatMonitorTask);
1042
+ this._heartbeatMonitorTask = null;
1043
+ }
1044
+ }
1045
+ /** Cancel the idle monitoring task if it is running. */
1046
+ async _maybeCancelIdleTask() {
1047
+ if (this._idleMonitorTask) {
1048
+ await this.cancelTask(this._idleMonitorTask);
1049
+ this._idleMonitorTask = null;
1050
+ }
1051
+ }
1052
+ /** Create an initial metrics frame with zero values for all processors. */
1053
+ _initialMetricsFrame() {
1054
+ const processors = this._pipeline.processorsWithMetrics();
1055
+ const data = [];
1056
+ for (const p of processors) {
1057
+ data.push(new TTFBMetricsData({ processor: p.name, value: 0.0 }));
1058
+ data.push(new ProcessingMetricsData({ processor: p.name, value: 0.0 }));
1059
+ }
1060
+ return new MetricsFrame({ data });
1061
+ }
1062
+ /**
1063
+ * Push the StartFrame and wait for the pipeline to start.
1064
+ *
1065
+ * Pushing it waits too, while the pipeline is still held, so cancelling
1066
+ * this task gives up on both.
1067
+ *
1068
+ * @returns Whether the pipeline started; see `_waitForPipelineStart`.
1069
+ */
1070
+ async _startPipeline() {
1071
+ // Processors read the pipeline configuration from FrameProcessorSetup.
1072
+ const frame = new StartFrame();
1073
+ frame.metadata = this._createStartMetadata();
1074
+ await this._pipeline.queueFrame(frame);
1075
+ return await this._waitForPipelineStart(frame);
1076
+ }
1077
+ /**
1078
+ * Wait for the start task, and return whether it started the pipeline (a
1079
+ * cancelled one didn't).
1080
+ */
1081
+ async _waitForStartTask() {
1082
+ const startTask = this._pipelineStartTask;
1083
+ if (!startTask) {
1084
+ return false;
1085
+ }
1086
+ await wait([startTask]);
1087
+ if (startTask.cancelled()) {
1088
+ return false;
1089
+ }
1090
+ return Boolean(startTask.result());
1091
+ }
1092
+ /**
1093
+ * Wait for the specified start frame to reach the end of the pipeline.
1094
+ *
1095
+ * @returns Whether the pipeline started. A pipeline that doesn't start
1096
+ * within `startTimeoutSecs` is torn down, since nothing pushed into it
1097
+ * afterwards would be processed.
1098
+ */
1099
+ async _waitForPipelineStart(frame) {
1100
+ logger.debug(`${this}: Starting. Waiting for ${frame} to reach the end of the pipeline...`);
1101
+ // Setting up has its own timeout, so the start timeout only counts from
1102
+ // the moment every processor is set up.
1103
+ if (this._pipelineSetupTask) {
1104
+ await wait([this._pipelineSetupTask]);
1105
+ }
1106
+ try {
1107
+ await waitFor(() => this._pipelineStartEvent.wait(), this._startTimeoutSecs);
1108
+ }
1109
+ catch (e) {
1110
+ if (!(e instanceof TimeoutError))
1111
+ throw e;
1112
+ logger.error(`${this}: timeout waiting for ${frame} to reach the end of the pipeline ` +
1113
+ "(being blocked somewhere?), stopping the pipeline.");
1114
+ await this._callEventHandler("on_pipeline_timeout", frame);
1115
+ return false;
1116
+ }
1117
+ this._pipelineStartEvent.clear();
1118
+ logger.debug(`${this}: ${frame} reached the end of the pipeline, pipeline is now ready.`);
1119
+ return true;
1120
+ }
1121
+ /** Wait for the specified frame to reach the end of the pipeline. */
1122
+ async _waitForPipelineEnd(frame) {
1123
+ const waitForCancel = async () => {
1124
+ try {
1125
+ await waitFor(() => this._pipelineEndEvent.wait(), this._cancelTimeoutSecs);
1126
+ logger.debug(`${this}: ${frame} reached the end of the pipeline.`);
1127
+ }
1128
+ catch (e) {
1129
+ if (!(e instanceof TimeoutError))
1130
+ throw e;
1131
+ logger.warning(`${this}: timeout waiting for ${frame} to reach the end of the pipeline (being blocked somewhere?).`);
1132
+ await this._callEventHandler("on_pipeline_timeout", frame);
1133
+ }
1134
+ finally {
1135
+ await this._callEventHandler("on_pipeline_finished", frame);
1136
+ }
1137
+ };
1138
+ logger.debug(`${this}: Closing. Waiting for ${frame} to reach the end of the pipeline...`);
1139
+ if (frame instanceof CancelFrame) {
1140
+ await waitForCancel();
1141
+ }
1142
+ else {
1143
+ // Ending flushes what is queued, so cutting the wait short would drop
1144
+ // the audio the EndFrame exists to play out. A processor that could
1145
+ // hold it up watches for that itself.
1146
+ await this._pipelineEndEvent.wait();
1147
+ // In asyncio the processor that pushed the EndFrame keeps running until
1148
+ // it next suspends, so work it does right after pushing it (e.g. the
1149
+ // user aggregator flushing its turn at session end) completes before
1150
+ // teardown. Promise continuations interleave instead, so yield one
1151
+ // macrotask to let that processor finish before cleaning up.
1152
+ await sleep(0);
1153
+ logger.debug(`${this}: ${frame} reached the end of the pipeline, pipeline is closing.`);
1154
+ }
1155
+ this._pipelineEndEvent.clear();
1156
+ // We are really done. Setting `_finishedEvent` makes `BaseWorker.wait()`
1157
+ // resolve for callers awaiting this worker.
1158
+ this._finishedEvent.set();
1159
+ }
1160
+ async _waitForPipelineFinished() {
1161
+ await this._finishedEvent.wait();
1162
+ // Make sure we wait for the push task to complete.
1163
+ if (this._processPushTask) {
1164
+ await this._processPushTask;
1165
+ this._processPushTask = null;
1166
+ }
1167
+ }
1168
+ /**
1169
+ * Set up the pipeline worker, bounded by a timeout.
1170
+ *
1171
+ * Processors are only started being set up here; their setup is bounded
1172
+ * separately by `_watchPipelineSetup`.
1173
+ *
1174
+ * @returns Whether the worker was set up. Setup files and observers that
1175
+ * block never let the pipeline start, so setting up is abandoned once
1176
+ * `setupTimeoutSecs` elapses.
1177
+ */
1178
+ async _setupWithinTimeout(params) {
1179
+ try {
1180
+ await waitFor(() => this._setup(params), this._setupTimeoutSecs);
1181
+ return true;
1182
+ }
1183
+ catch (e) {
1184
+ if (!(e instanceof TimeoutError))
1185
+ throw e;
1186
+ logger.error(`${this}: timeout setting the pipeline up ` +
1187
+ "(a processor blocked while connecting?), stopping the pipeline.");
1188
+ await this._callEventHandler("on_setup_timeout");
1189
+ return false;
1190
+ }
1191
+ }
1192
+ /** Set up the pipeline worker and all processors. */
1193
+ async _setup(params) {
1194
+ // Processors connect while they are set up and push frames as they do, so
1195
+ // the clock runs from here rather than from the StartFrame, which would
1196
+ // leave those frames timestamped zero.
1197
+ this._clock.start();
1198
+ await super.setup(this._taskManager ?? params.taskManager);
1199
+ // Do any additional pipeline worker setup externally.
1200
+ await this._loadSetupFiles();
1201
+ // Start worker observer.
1202
+ await this._observer.setup(this.taskManager);
1203
+ // Setup processors
1204
+ const setup = new FrameProcessorSetup({
1205
+ audioInSampleRate: this._params.audioInSampleRate,
1206
+ audioOutSampleRate: this._params.audioOutSampleRate,
1207
+ clock: this._clock,
1208
+ enableMetrics: this._params.enableMetrics,
1209
+ enableTracing: this._enableTracing,
1210
+ enableUsageMetrics: this._params.enableUsageMetrics,
1211
+ observer: this._observer,
1212
+ pipelineWorker: this,
1213
+ reportOnlyInitialTtfb: this._params.reportOnlyInitialTtfb,
1214
+ taskManager: this.taskManager,
1215
+ tracingContext: this._tracingContext,
1216
+ });
1217
+ this._pipeline._setupStarted();
1218
+ const setupTask = this.createTask(() => this._pipeline.setup(setup), "setup");
1219
+ this._pipelineSetupTask = setupTask;
1220
+ // The StartFrame is pushed right away and each processor holds it until
1221
+ // its own setup is done (see BasePipeline._setupProcessors()).
1222
+ setupTask.addDoneCallback((task) => this._pipelineSetupDone(task));
1223
+ // Created together, so a cancel always finds both: with the setup
1224
+ // cancelled but no start task to cancel, the start would wait forever on
1225
+ // a pipeline that is never released.
1226
+ this._pipelineStartTask = this.createTask(() => this._startPipeline(), "_start_pipeline");
1227
+ }
1228
+ /**
1229
+ * Release the pipeline once it is set up, unless setting up was abandoned.
1230
+ *
1231
+ * A cancelled setup (timed out, or the worker was cancelled) keeps the
1232
+ * StartFrame held, so the pipeline never starts and is only cleaned up.
1233
+ */
1234
+ _pipelineSetupDone(task) {
1235
+ if (!task.cancelled()) {
1236
+ this._pipeline._setupFinished();
1237
+ }
1238
+ }
1239
+ /** Clean up the pipeline worker and processors. */
1240
+ async _cleanup(cleanupPipeline) {
1241
+ // Cleanup base object.
1242
+ await this.cleanup();
1243
+ // Cleanup observers.
1244
+ await this._observer.cleanup();
1245
+ // End conversation tracing if it's active - this will also close any
1246
+ // active turn span
1247
+ if (this._enableTracing && this._turnTraceObserver) {
1248
+ this._turnTraceObserver.endConversationTracing();
1249
+ }
1250
+ // Cleanup pipeline processors.
1251
+ if (cleanupPipeline) {
1252
+ await this._pipeline.cleanup();
1253
+ }
1254
+ // Nothing left to answer a probe we are still holding.
1255
+ this._foreignProbes.clear();
1256
+ }
1257
+ /**
1258
+ * Cancel the children once this worker's pipeline is over.
1259
+ *
1260
+ * A pipeline that ends on its own (an `EndFrame` it queued itself, an idle
1261
+ * timeout, a permanent error) is never told to end over the bus, so nothing
1262
+ * has passed the end on to its children. A child that has already finished
1263
+ * takes no notice.
1264
+ */
1265
+ async _cancelChildren() {
1266
+ for (const child of this._children) {
1267
+ await this.sendBusMessage(new BusCancelWorkerMessage({
1268
+ source: this.name,
1269
+ target: child.name,
1270
+ reason: `${this.name} finished`,
1271
+ }));
1272
+ }
1273
+ }
1274
+ /**
1275
+ * End the pipeline after propagating end to children.
1276
+ *
1277
+ * Drives shutdown through the pipeline (`EndFrame`) so `_finishedEvent`
1278
+ * fires once the frame drains through the sink, rather than calling
1279
+ * `stop()` directly.
1280
+ */
1281
+ async _handleWorkerEnd(message) {
1282
+ logger.debug(`Worker '${this}': received end`);
1283
+ await this._propagateEndToChildren(message);
1284
+ await this.queueFrame(new EndFrame({ reason: message.reason }));
1285
+ }
1286
+ /**
1287
+ * Cancel the pipeline after propagating cancel to children.
1288
+ *
1289
+ * Drives shutdown through the pipeline (`CancelFrame`) so `_finishedEvent`
1290
+ * fires once the frame drains, rather than calling `stop()` directly.
1291
+ *
1292
+ * Children are told whatever state this worker is in: one that cancelled
1293
+ * itself earlier, on an idle timeout or a permanent error, still has to
1294
+ * pass the runner's cancel on.
1295
+ */
1296
+ async _handleWorkerCancel(message) {
1297
+ logger.debug(`Worker '${this}': received cancel`);
1298
+ await this._propagateCancelToChildren(message);
1299
+ await this.cancel({ reason: message.reason });
1300
+ }
1301
+ /**
1302
+ * Process frames from the push queue and send them through the pipeline.
1303
+ *
1304
+ * Once the start task has started the pipeline, it pushes the frames queued
1305
+ * by the user. It runs until the worker is cancelled or stopped (e.g. with
1306
+ * an EndFrame).
1307
+ */
1308
+ async _processPushQueue() {
1309
+ // No other frame goes in before the pipeline has started; one that
1310
+ // hasn't started is only cleaned up.
1311
+ let running = await this._waitForStartTask();
1312
+ if (running) {
1313
+ this._maybeStartIdleTask();
1314
+ }
1315
+ if (running && this._params.enableMetrics && this._params.sendInitialEmptyMetrics) {
1316
+ await this._pipeline.queueFrame(this._initialMetricsFrame());
1317
+ }
1318
+ // A pipeline that never started can't process anything we push into it,
1319
+ // so skip straight to cleanup.
1320
+ let cleanupPipeline = true;
1321
+ while (running) {
1322
+ const frame = await this._pushQueue.get();
1323
+ await this._pipeline.queueFrame(frame);
1324
+ const isLast = frame instanceof CancelFrame || frame instanceof EndFrame || frame instanceof StopFrame;
1325
+ if (isLast) {
1326
+ await this._waitForPipelineEnd(frame);
1327
+ }
1328
+ running = !isLast;
1329
+ cleanupPipeline = !(frame instanceof StopFrame);
1330
+ this._pushQueue.taskDone();
1331
+ }
1332
+ await this._cleanup(cleanupPipeline);
1333
+ }
1334
+ /**
1335
+ * Move a flush probe along its trip, settling it once it is done.
1336
+ *
1337
+ * The probe travels down to the sink, back up to the source, then down
1338
+ * again, and only the second arrival at the sink settles it. The extra leg
1339
+ * is what makes the probe wait for work a processor starts by pushing
1340
+ * upstream: an `LLMContextFrame` pushed up after a function call result,
1341
+ * say, whose response only comes back down afterwards. A probe that stopped
1342
+ * at the source would return while that response was still being
1343
+ * generated, or still being synthesized.
1344
+ *
1345
+ * @param frame The probe, carrying the event its initiator awaits.
1346
+ * @param atSink Whether the probe has arrived at the sink or the source.
1347
+ */
1348
+ async _advanceFlushProbe(frame, atSink) {
1349
+ if (!atSink) {
1350
+ logger.debug(`${this}: flush probe reached source — sending it down again`);
1351
+ frame.returning = true;
1352
+ await this._source.pushFrame(frame, FrameDirection.DOWNSTREAM);
1353
+ }
1354
+ else if (!frame.returning) {
1355
+ logger.debug(`${this}: flush probe reached sink — bouncing upstream`);
1356
+ await this._sink.pushFrame(frame, FrameDirection.UPSTREAM);
1357
+ }
1358
+ else {
1359
+ logger.debug(`${this}: flush probe reached sink again — pipeline drained`);
1360
+ this._foreignProbes.delete(frame.id);
1361
+ if (frame.event) {
1362
+ frame.event.set();
1363
+ }
1364
+ }
1365
+ }
1366
+ /**
1367
+ * Tell whoever is waiting on a probe we hold that we are still working.
1368
+ *
1369
+ * Their own sink sees nothing while we drain, so without this they can only
1370
+ * conclude the pipeline has gone quiet. Sent no more than once a second: it
1371
+ * says the flush is alive, not how much is left.
1372
+ */
1373
+ async _reportFlushProgress() {
1374
+ const now = monotonic();
1375
+ if (now - this._flushProgressSent < FLUSH_PROGRESS_PERIOD_SECS) {
1376
+ return;
1377
+ }
1378
+ this._flushProgressSent = now;
1379
+ // Over a snapshot: sending suspends on a network bus, and a probe arriving
1380
+ // meanwhile would change the map under the loop.
1381
+ for (const [flushId, origin] of [...this._foreignProbes.entries()]) {
1382
+ await this.bus.send(new BusFlushProgressMessage({ source: this.name, target: origin, flushId }));
1383
+ }
1384
+ }
1385
+ /**
1386
+ * Process frames coming upstream from the pipeline.
1387
+ *
1388
+ * These frames might indicate, for example, that we want the pipeline to be
1389
+ * stopped (e.g. EndWorkerFrame) in which case we would send an EndFrame
1390
+ * down the pipeline.
1391
+ */
1392
+ async _sourcePushFrame(frame, _direction) {
1393
+ if (isInstanceOfAny(frame, this._reachedUpstreamTypes)) {
1394
+ await this._callEventHandler("on_frame_reached_upstream", frame);
1395
+ }
1396
+ if (frame instanceof EndWorkerFrame) {
1397
+ // Tell the worker we should end nicely.
1398
+ logger.debug(`${this}: received end worker frame upstream ${frame}`);
1399
+ await this.queueFrame(new EndFrame({ reason: frame.reason }));
1400
+ }
1401
+ else if (frame instanceof CancelWorkerFrame) {
1402
+ // Tell the worker we should end right away.
1403
+ logger.debug(`${this}: received cancel worker frame upstream ${frame}`);
1404
+ await this.queueFrame(new CancelFrame({ reason: frame.reason }));
1405
+ }
1406
+ else if (frame instanceof StopWorkerFrame) {
1407
+ // Tell the worker we should stop nicely.
1408
+ logger.debug(`${this}: received stop worker frame upstream ${frame}`);
1409
+ await this.queueFrame(new StopFrame());
1410
+ }
1411
+ else if (frame instanceof InterruptionWorkerFrame) {
1412
+ // Tell the worker we should interrupt the pipeline. Note that we are
1413
+ // bypassing the push queue and directly queue into the pipeline. This
1414
+ // is in case the push task is blocked waiting for a pipeline-ending
1415
+ // frame to finish traversing the pipeline.
1416
+ logger.debug(`${this}: received interruption worker frame upstream ${frame}`);
1417
+ await this._pipeline.queueFrame(new InterruptionFrame());
1418
+ }
1419
+ else if (frame instanceof ErrorFrame) {
1420
+ await this._callEventHandler("on_pipeline_error", frame);
1421
+ if (frame.processor && !frame.processor.isUsable) {
1422
+ await this._handleUnusableProcessor(frame.processor);
1423
+ }
1424
+ else {
1425
+ logger.warning(`${this}: Something went wrong: ${frame}`);
1426
+ }
1427
+ }
1428
+ else if (frame instanceof PipelineFlushFrame && this._handleFlushFrame) {
1429
+ await this._advanceFlushProbe(frame, false);
1430
+ }
1431
+ }
1432
+ /**
1433
+ * Apply the unusable-processor policy, once per processor.
1434
+ *
1435
+ * A processor that can no longer do its job keeps failing for as long as
1436
+ * the pipeline keeps using it, so the policy is applied to the first error
1437
+ * that says so and not to the ones that follow.
1438
+ *
1439
+ * @param processor The processor that can no longer do its job.
1440
+ */
1441
+ async _handleUnusableProcessor(processor) {
1442
+ if (this._unusableProcessors.has(processor)) {
1443
+ return;
1444
+ }
1445
+ this._unusableProcessors.add(processor);
1446
+ logger.error(`${this}: ${processor} can no longer do its job`);
1447
+ if (this._processorUnusablePolicy === ProcessorUnusablePolicy.END) {
1448
+ await this.stopWhenDone();
1449
+ }
1450
+ else if (this._processorUnusablePolicy === ProcessorUnusablePolicy.CANCEL) {
1451
+ await this._cancel({ reason: `${processor} can no longer do its job` });
1452
+ }
1453
+ }
1454
+ /**
1455
+ * Process frames coming downstream from the pipeline.
1456
+ *
1457
+ * Processes frames coming downstream from the pipeline. For example,
1458
+ * heartbeat frames or an EndFrame which would indicate all processors have
1459
+ * handled the EndFrame and therefore we can exit the worker cleanly.
1460
+ */
1461
+ async _sinkPushFrame(frame, _direction) {
1462
+ if (isInstanceOfAny(frame, this._reachedDownstreamTypes)) {
1463
+ await this._callEventHandler("on_frame_reached_downstream", frame);
1464
+ }
1465
+ // Heartbeats don't count: they arrive whether or not the pipeline is
1466
+ // doing anything, so counting them would make it always look busy.
1467
+ if (!(frame instanceof HeartbeatFrame)) {
1468
+ this._framesReachingSink += 1;
1469
+ if (this._foreignProbes.size > 0) {
1470
+ await this._reportFlushProgress();
1471
+ }
1472
+ }
1473
+ if (frame instanceof StartFrame) {
1474
+ await this._callEventHandler("on_pipeline_started", frame);
1475
+ await this._observer.onPipelineStarted();
1476
+ // Start heartbeat tasks now that StartFrame has been processed by all
1477
+ // processors in the pipeline
1478
+ this._maybeStartHeartbeatTasks();
1479
+ this._pipelineStartEvent.set();
1480
+ }
1481
+ else if (frame instanceof EndFrame) {
1482
+ await this._callEventHandler("on_pipeline_finished", frame);
1483
+ this._pipelineEndEvent.set();
1484
+ }
1485
+ else if (frame instanceof StopFrame) {
1486
+ await this._callEventHandler("on_pipeline_finished", frame);
1487
+ this._pipelineEndEvent.set();
1488
+ }
1489
+ else if (frame instanceof CancelFrame) {
1490
+ this._pipelineEndEvent.set();
1491
+ }
1492
+ else if (frame instanceof HeartbeatFrame) {
1493
+ await this._heartbeatQueue.put(frame);
1494
+ }
1495
+ else if (frame instanceof EndWorkerFrame) {
1496
+ logger.debug(`${this}: received end worker frame downstream ${frame}`);
1497
+ await this.queueFrame(new EndWorkerFrame({ reason: frame.reason }), FrameDirection.UPSTREAM);
1498
+ }
1499
+ else if (frame instanceof StopWorkerFrame) {
1500
+ logger.debug(`${this}: received stop worker frame downstream ${frame}`);
1501
+ await this.queueFrame(new StopWorkerFrame(), FrameDirection.UPSTREAM);
1502
+ }
1503
+ else if (frame instanceof CancelWorkerFrame) {
1504
+ logger.debug(`${this}: received cancel worker frame downstream ${frame}`);
1505
+ await this.queueFrame(new CancelWorkerFrame({ reason: frame.reason }), FrameDirection.UPSTREAM);
1506
+ }
1507
+ else if (frame instanceof InterruptionWorkerFrame) {
1508
+ logger.debug(`${this}: received interruption worker frame downstream ${frame}`);
1509
+ await this.queueFrame(new InterruptionWorkerFrame(), FrameDirection.UPSTREAM);
1510
+ }
1511
+ else if (frame instanceof PipelineFlushFrame && this._handleFlushFrame) {
1512
+ await this._advanceFlushProbe(frame, true);
1513
+ }
1514
+ }
1515
+ /** Push heartbeat frames at regular intervals. */
1516
+ async _heartbeatPushHandler() {
1517
+ for (;;) {
1518
+ // Don't use `queueFrame()` because if an EndFrame is queued the worker
1519
+ // will just stop waiting for the pipeline to finish not allowing more
1520
+ // frames to be pushed.
1521
+ await this._pipeline.queueFrame(new HeartbeatFrame({ timestamp: this._clock.getTime() }));
1522
+ await sleep(this._params.heartbeatsPeriodSecs);
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Monitor heartbeat frames for processing time and timeout detection.
1527
+ *
1528
+ * Logs the time each heartbeat takes to traverse the pipeline. If no
1529
+ * heartbeat arrives within `heartbeatsMonitorSecs`, logs a warning and
1530
+ * fires `on_heartbeat_timeout`. The event fires repeatedly every
1531
+ * `heartbeatsMonitorSecs` for as long as the stall persists.
1532
+ */
1533
+ async _heartbeatMonitorHandler() {
1534
+ const waitTime = this._params.heartbeatsMonitorSecs;
1535
+ for (;;) {
1536
+ try {
1537
+ const frame = await waitFor(() => this._heartbeatQueue.get(), waitTime);
1538
+ const processTime = (this._clock.getTime() - frame.timestamp) / 1_000_000_000;
1539
+ logger.trace(`${this}: heartbeat frame processed in ${processTime} seconds`);
1540
+ this._heartbeatQueue.taskDone();
1541
+ }
1542
+ catch (e) {
1543
+ if (!(e instanceof TimeoutError))
1544
+ throw e;
1545
+ logger.warning(`${this}: heartbeat frame not received for more than ${waitTime} seconds`);
1546
+ await this._callEventHandler("on_heartbeat_timeout");
1547
+ }
1548
+ }
1549
+ }
1550
+ /**
1551
+ * Monitor pipeline activity and detect idle conditions.
1552
+ *
1553
+ * Tracks frame activity and triggers idle timeout events when the pipeline
1554
+ * hasn't received relevant frames within the timeout period.
1555
+ *
1556
+ * Note: Heartbeats are excluded from idle detection.
1557
+ */
1558
+ async _idleMonitorHandler() {
1559
+ let running = true;
1560
+ while (running) {
1561
+ try {
1562
+ await waitFor(() => this._idleEvent.wait(), this._idleTimeoutSecs);
1563
+ this._idleEvent.clear();
1564
+ }
1565
+ catch (e) {
1566
+ if (!(e instanceof TimeoutError))
1567
+ throw e;
1568
+ running = await this._idleTimeoutDetected();
1569
+ }
1570
+ }
1571
+ }
1572
+ /**
1573
+ * Handle idle timeout detection and optional cancellation.
1574
+ *
1575
+ * @returns Whether the pipeline worker should continue running.
1576
+ */
1577
+ async _idleTimeoutDetected() {
1578
+ // If we are cancelling, just exit the task.
1579
+ if (this._cancelled) {
1580
+ return false;
1581
+ }
1582
+ logger.warning("Idle timeout detected.");
1583
+ await this._callEventHandler("on_idle_timeout");
1584
+ if (!this._cancelOnIdleTimeout) {
1585
+ return true;
1586
+ }
1587
+ logger.warning("Idle pipeline detected, cancelling pipeline worker...");
1588
+ await this.cancel({ reason: "idle timeout" });
1589
+ if (this._cancelRunnerOnIdleTimeout) {
1590
+ logger.warning("...and cancelling the runner.");
1591
+ // `BaseWorker.cancel` sends `BusCancelMessage` on the bus so the runner
1592
+ // broadcasts cancellation to every other root worker too. This
1593
+ // worker's pipeline is already cancelling from the call above.
1594
+ await BaseWorker.prototype.cancel.call(this, { reason: "idle timeout" });
1595
+ }
1596
+ return false;
1597
+ }
1598
+ /**
1599
+ * Run `setupPipelineWorker` from each file in `PIPECAT_SETUP_FILES`.
1600
+ *
1601
+ * A setup file may define `setupPipelineWorker(worker)` to attach event
1602
+ * handlers, observers, or other per-worker wiring.
1603
+ */
1604
+ async _loadSetupFiles() {
1605
+ await runSetupHook({
1606
+ target: this,
1607
+ functionName: "setup_pipeline_worker",
1608
+ });
1609
+ }
1610
+ /** Build and return start metadata including user-provided values. */
1611
+ _createStartMetadata() {
1612
+ const startMetadata = {};
1613
+ // Update with user provided metadata.
1614
+ Object.assign(startMetadata, this._params.startMetadata);
1615
+ return startMetadata;
1616
+ }
1617
+ /** Recursively yield the processors of the given type(s) in the pipeline. */
1618
+ *_iterProcessors(processor, processorTypes) {
1619
+ if (processorTypes.some((cls) => processor instanceof cls)) {
1620
+ yield processor;
1621
+ }
1622
+ for (const p of processor.processors) {
1623
+ yield* this._iterProcessors(p, processorTypes);
1624
+ }
1625
+ }
1626
+ }
1627
+ function firstOf(iterable) {
1628
+ for (const item of iterable)
1629
+ return item;
1630
+ return null;
1631
+ }
1632
+ function isInstanceOfAny(frame, types) {
1633
+ for (const cls of types) {
1634
+ if (frame instanceof cls)
1635
+ return true;
1636
+ }
1637
+ return false;
1638
+ }
1639
+ //# sourceMappingURL=worker.js.map