@pipedog/core 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (736) 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/engine.d.ts +60 -0
  42. package/dist/audio/resamplers/engine.js +89 -0
  43. package/dist/audio/resamplers/index.d.ts +2 -0
  44. package/dist/audio/resamplers/index.js +8 -0
  45. package/dist/audio/resamplers/resampy_resampler.d.ts +10 -0
  46. package/dist/audio/resamplers/resampy_resampler.js +15 -0
  47. package/dist/audio/resamplers/sinc_engine.d.ts +10 -0
  48. package/dist/audio/resamplers/sinc_engine.js +235 -0
  49. package/dist/audio/resamplers/soxr_resampler.d.ts +58 -0
  50. package/dist/audio/resamplers/soxr_resampler.js +79 -0
  51. package/dist/audio/resamplers/soxr_stream_resampler.d.ts +78 -0
  52. package/dist/audio/resamplers/soxr_stream_resampler.js +159 -0
  53. package/dist/audio/turn/base_turn_analyzer.d.ts +84 -0
  54. package/dist/audio/turn/base_turn_analyzer.js +61 -0
  55. package/dist/audio/turn/index.d.ts +2 -0
  56. package/dist/audio/turn/index.js +8 -0
  57. package/dist/audio/turn/smart_turn/_whisper_features.d.ts +30 -0
  58. package/dist/audio/turn/smart_turn/_whisper_features.js +225 -0
  59. package/dist/audio/turn/smart_turn/base_smart_turn.d.ts +94 -0
  60. package/dist/audio/turn/smart_turn/base_smart_turn.js +239 -0
  61. package/dist/audio/turn/smart_turn/local_smart_turn_v3.d.ts +76 -0
  62. package/dist/audio/turn/smart_turn/local_smart_turn_v3.js +151 -0
  63. package/dist/audio/utils.d.ts +224 -0
  64. package/dist/audio/utils.js +634 -0
  65. package/dist/audio/vad/data/index.d.ts +2 -0
  66. package/dist/audio/vad/data/index.js +8 -0
  67. package/dist/audio/vad/index.d.ts +2 -0
  68. package/dist/audio/vad/index.js +8 -0
  69. package/dist/audio/vad/vad_analyzer.d.ts +134 -0
  70. package/dist/audio/vad/vad_analyzer.js +249 -0
  71. package/dist/audio/vad/vad_controller.d.ts +143 -0
  72. package/dist/audio/vad/vad_controller.js +228 -0
  73. package/dist/audio/volume.d.ts +38 -0
  74. package/dist/audio/volume.js +75 -0
  75. package/dist/bus/adapters/base.d.ts +35 -0
  76. package/dist/bus/adapters/base.js +19 -0
  77. package/dist/bus/adapters/index.d.ts +10 -0
  78. package/dist/bus/adapters/index.js +15 -0
  79. package/dist/bus/adapters/llm_context_adapter.d.ts +36 -0
  80. package/dist/bus/adapters/llm_context_adapter.js +82 -0
  81. package/dist/bus/adapters/tools_schema_adapter.d.ts +25 -0
  82. package/dist/bus/adapters/tools_schema_adapter.js +70 -0
  83. package/dist/bus/bridge_processor.d.ts +111 -0
  84. package/dist/bus/bridge_processor.js +166 -0
  85. package/dist/bus/bus.d.ts +110 -0
  86. package/dist/bus/bus.js +200 -0
  87. package/dist/bus/index.d.ts +22 -0
  88. package/dist/bus/index.js +27 -0
  89. package/dist/bus/local/async_queue.d.ts +12 -0
  90. package/dist/bus/local/async_queue.js +21 -0
  91. package/dist/bus/local/index.d.ts +3 -0
  92. package/dist/bus/local/index.js +8 -0
  93. package/dist/bus/messages.d.ts +378 -0
  94. package/dist/bus/messages.js +357 -0
  95. package/dist/bus/network/index.d.ts +16 -0
  96. package/dist/bus/network/index.js +21 -0
  97. package/dist/bus/queue.d.ts +27 -0
  98. package/dist/bus/queue.js +51 -0
  99. package/dist/bus/serializers/base.d.ts +25 -0
  100. package/dist/bus/serializers/base.js +14 -0
  101. package/dist/bus/serializers/index.d.ts +9 -0
  102. package/dist/bus/serializers/index.js +14 -0
  103. package/dist/bus/serializers/json.d.ts +77 -0
  104. package/dist/bus/serializers/json.js +367 -0
  105. package/dist/bus/subscriber.d.ts +46 -0
  106. package/dist/bus/subscriber.js +27 -0
  107. package/dist/bus/ui/index.d.ts +3 -0
  108. package/dist/bus/ui/index.js +8 -0
  109. package/dist/bus/ui/messages.d.ts +186 -0
  110. package/dist/bus/ui/messages.js +171 -0
  111. package/dist/classifiers/base_classifier.d.ts +263 -0
  112. package/dist/classifiers/base_classifier.js +346 -0
  113. package/dist/classifiers/cloudflare/clef/classifier.d.ts +70 -0
  114. package/dist/classifiers/cloudflare/clef/classifier.js +204 -0
  115. package/dist/classifiers/cloudflare/clef/client.d.ts +104 -0
  116. package/dist/classifiers/cloudflare/clef/client.js +234 -0
  117. package/dist/classifiers/cloudflare/clef/index.d.ts +2 -0
  118. package/dist/classifiers/cloudflare/clef/index.js +8 -0
  119. package/dist/classifiers/cloudflare/index.d.ts +2 -0
  120. package/dist/classifiers/cloudflare/index.js +8 -0
  121. package/dist/classifiers/http_client.d.ts +70 -0
  122. package/dist/classifiers/http_client.js +161 -0
  123. package/dist/classifiers/index.d.ts +2 -0
  124. package/dist/classifiers/index.js +8 -0
  125. package/dist/classifiers/jev/classifier.d.ts +8 -0
  126. package/dist/classifiers/jev/classifier.js +13 -0
  127. package/dist/classifiers/jev/client.d.ts +8 -0
  128. package/dist/classifiers/jev/client.js +13 -0
  129. package/dist/classifiers/jev/index.d.ts +2 -0
  130. package/dist/classifiers/jev/index.js +8 -0
  131. package/dist/classifiers/llm/classifier.d.ts +72 -0
  132. package/dist/classifiers/llm/classifier.js +304 -0
  133. package/dist/classifiers/llm/index.d.ts +2 -0
  134. package/dist/classifiers/llm/index.js +8 -0
  135. package/dist/classifiers/openai/decisions/classifier.d.ts +77 -0
  136. package/dist/classifiers/openai/decisions/classifier.js +241 -0
  137. package/dist/classifiers/openai/decisions/client.d.ts +108 -0
  138. package/dist/classifiers/openai/decisions/client.js +234 -0
  139. package/dist/classifiers/openai/decisions/index.d.ts +2 -0
  140. package/dist/classifiers/openai/decisions/index.js +8 -0
  141. package/dist/classifiers/openai/index.d.ts +2 -0
  142. package/dist/classifiers/openai/index.js +8 -0
  143. package/dist/classifiers/typesafe/index.d.ts +2 -0
  144. package/dist/classifiers/typesafe/index.js +8 -0
  145. package/dist/classifiers/typesafe/jev/classifier.d.ts +70 -0
  146. package/dist/classifiers/typesafe/jev/classifier.js +220 -0
  147. package/dist/classifiers/typesafe/jev/client.d.ts +105 -0
  148. package/dist/classifiers/typesafe/jev/client.js +233 -0
  149. package/dist/classifiers/typesafe/jev/index.d.ts +2 -0
  150. package/dist/classifiers/typesafe/jev/index.js +8 -0
  151. package/dist/clocks/base_clock.d.ts +13 -0
  152. package/dist/clocks/base_clock.js +14 -0
  153. package/dist/clocks/index.d.ts +2 -0
  154. package/dist/clocks/index.js +8 -0
  155. package/dist/clocks/system_clock.d.ts +15 -0
  156. package/dist/clocks/system_clock.js +25 -0
  157. package/dist/frames/frames.d.ts +2533 -0
  158. package/dist/frames/frames.js +2485 -0
  159. package/dist/frames/index.d.ts +2 -0
  160. package/dist/frames/index.js +8 -0
  161. package/dist/frames/protobufs/frames_pb2.d.ts +229 -0
  162. package/dist/frames/protobufs/frames_pb2.js +466 -0
  163. package/dist/index.d.ts +11 -0
  164. package/dist/index.js +56 -0
  165. package/dist/metrics/index.d.ts +2 -0
  166. package/dist/metrics/index.js +8 -0
  167. package/dist/metrics/metrics.d.ts +276 -0
  168. package/dist/metrics/metrics.js +254 -0
  169. package/dist/observers/base_observer.d.ts +173 -0
  170. package/dist/observers/base_observer.js +151 -0
  171. package/dist/observers/error_observer.d.ts +75 -0
  172. package/dist/observers/error_observer.js +87 -0
  173. package/dist/observers/function_call_observer.d.ts +157 -0
  174. package/dist/observers/function_call_observer.js +222 -0
  175. package/dist/observers/index.d.ts +2 -0
  176. package/dist/observers/index.js +8 -0
  177. package/dist/observers/loggers/debug_log_observer.d.ts +91 -0
  178. package/dist/observers/loggers/debug_log_observer.js +228 -0
  179. package/dist/observers/loggers/index.d.ts +2 -0
  180. package/dist/observers/loggers/index.js +8 -0
  181. package/dist/observers/loggers/llm_log_observer.d.ts +24 -0
  182. package/dist/observers/loggers/llm_log_observer.js +110 -0
  183. package/dist/observers/loggers/metrics_log_observer.d.ts +85 -0
  184. package/dist/observers/loggers/metrics_log_observer.js +186 -0
  185. package/dist/observers/loggers/transcription_log_observer.d.ts +23 -0
  186. package/dist/observers/loggers/transcription_log_observer.js +74 -0
  187. package/dist/observers/service_metrics_observer.d.ts +168 -0
  188. package/dist/observers/service_metrics_observer.js +236 -0
  189. package/dist/observers/speaking_observer.d.ts +104 -0
  190. package/dist/observers/speaking_observer.js +150 -0
  191. package/dist/observers/startup_timing_observer.d.ts +221 -0
  192. package/dist/observers/startup_timing_observer.js +365 -0
  193. package/dist/observers/turn_tracking_observer.d.ts +78 -0
  194. package/dist/observers/turn_tracking_observer.js +189 -0
  195. package/dist/observers/user_bot_latency_observer.d.ts +411 -0
  196. package/dist/observers/user_bot_latency_observer.js +974 -0
  197. package/dist/pipeline/base_pipeline.d.ts +49 -0
  198. package/dist/pipeline/base_pipeline.js +101 -0
  199. package/dist/pipeline/capabilities.d.ts +76 -0
  200. package/dist/pipeline/capabilities.js +97 -0
  201. package/dist/pipeline/index.d.ts +2 -0
  202. package/dist/pipeline/index.js +8 -0
  203. package/dist/pipeline/job_context.d.ts +317 -0
  204. package/dist/pipeline/job_context.js +450 -0
  205. package/dist/pipeline/job_decorator.d.ts +54 -0
  206. package/dist/pipeline/job_decorator.js +92 -0
  207. package/dist/pipeline/llm_switcher.d.ts +84 -0
  208. package/dist/pipeline/llm_switcher.js +107 -0
  209. package/dist/pipeline/llm_with_backend.d.ts +435 -0
  210. package/dist/pipeline/llm_with_backend.js +806 -0
  211. package/dist/pipeline/parallel_pipeline.d.ts +83 -0
  212. package/dist/pipeline/parallel_pipeline.js +200 -0
  213. package/dist/pipeline/pipeline.d.ts +126 -0
  214. package/dist/pipeline/pipeline.js +196 -0
  215. package/dist/pipeline/service_switcher.d.ts +287 -0
  216. package/dist/pipeline/service_switcher.js +509 -0
  217. package/dist/pipeline/sync_parallel_pipeline.d.ts +192 -0
  218. package/dist/pipeline/sync_parallel_pipeline.js +387 -0
  219. package/dist/pipeline/worker.d.ts +821 -0
  220. package/dist/pipeline/worker.js +1656 -0
  221. package/dist/pipeline/worker_observer.d.ts +131 -0
  222. package/dist/pipeline/worker_observer.js +274 -0
  223. package/dist/pipeline/worker_ready_decorator.d.ts +45 -0
  224. package/dist/pipeline/worker_ready_decorator.js +76 -0
  225. package/dist/processors/aggregators/async_tool_messages.d.ts +146 -0
  226. package/dist/processors/aggregators/async_tool_messages.js +291 -0
  227. package/dist/processors/aggregators/dtmf_aggregator.d.ts +61 -0
  228. package/dist/processors/aggregators/dtmf_aggregator.js +144 -0
  229. package/dist/processors/aggregators/gated.d.ts +43 -0
  230. package/dist/processors/aggregators/gated.js +86 -0
  231. package/dist/processors/aggregators/gated_llm_context.d.ts +42 -0
  232. package/dist/processors/aggregators/gated_llm_context.js +86 -0
  233. package/dist/processors/aggregators/index.d.ts +2 -0
  234. package/dist/processors/aggregators/index.js +8 -0
  235. package/dist/processors/aggregators/llm_context.d.ts +370 -0
  236. package/dist/processors/aggregators/llm_context.js +580 -0
  237. package/dist/processors/aggregators/llm_context_summarizer.d.ts +219 -0
  238. package/dist/processors/aggregators/llm_context_summarizer.js +447 -0
  239. package/dist/processors/aggregators/llm_response.d.ts +37 -0
  240. package/dist/processors/aggregators/llm_response.js +71 -0
  241. package/dist/processors/aggregators/llm_response_universal.d.ts +966 -0
  242. package/dist/processors/aggregators/llm_response_universal.js +2394 -0
  243. package/dist/processors/aggregators/llm_text_processor.d.ts +53 -0
  244. package/dist/processors/aggregators/llm_text_processor.js +102 -0
  245. package/dist/processors/aggregators/sentence.d.ts +39 -0
  246. package/dist/processors/aggregators/sentence.js +69 -0
  247. package/dist/processors/async_generator.d.ts +42 -0
  248. package/dist/processors/async_generator.js +76 -0
  249. package/dist/processors/audio/audio_buffer_processor.d.ts +241 -0
  250. package/dist/processors/audio/audio_buffer_processor.js +565 -0
  251. package/dist/processors/audio/index.d.ts +2 -0
  252. package/dist/processors/audio/index.js +8 -0
  253. package/dist/processors/audio/vad_processor.d.ts +67 -0
  254. package/dist/processors/audio/vad_processor.js +103 -0
  255. package/dist/processors/consumer_processor.d.ts +49 -0
  256. package/dist/processors/consumer_processor.js +85 -0
  257. package/dist/processors/filters/frame_filter.d.ts +39 -0
  258. package/dist/processors/filters/frame_filter.js +52 -0
  259. package/dist/processors/filters/function_filter.d.ts +59 -0
  260. package/dist/processors/filters/function_filter.js +78 -0
  261. package/dist/processors/filters/identity_filter.d.ts +33 -0
  262. package/dist/processors/filters/identity_filter.js +37 -0
  263. package/dist/processors/filters/index.d.ts +2 -0
  264. package/dist/processors/filters/index.js +8 -0
  265. package/dist/processors/filters/null_filter.d.ts +34 -0
  266. package/dist/processors/filters/null_filter.js +47 -0
  267. package/dist/processors/filters/wake_notifier_filter.d.ts +48 -0
  268. package/dist/processors/filters/wake_notifier_filter.js +49 -0
  269. package/dist/processors/frame_processor.d.ts +360 -0
  270. package/dist/processors/frame_processor.js +932 -0
  271. package/dist/processors/frameworks/index.d.ts +2 -0
  272. package/dist/processors/frameworks/index.js +8 -0
  273. package/dist/processors/frameworks/rtvi/frames.d.ts +241 -0
  274. package/dist/processors/frameworks/rtvi/frames.js +259 -0
  275. package/dist/processors/frameworks/rtvi/index.d.ts +6 -0
  276. package/dist/processors/frameworks/rtvi/index.js +11 -0
  277. package/dist/processors/frameworks/rtvi/models.d.ts +1741 -0
  278. package/dist/processors/frameworks/rtvi/models.js +1807 -0
  279. package/dist/processors/frameworks/rtvi/observer.d.ts +340 -0
  280. package/dist/processors/frameworks/rtvi/observer.js +1113 -0
  281. package/dist/processors/frameworks/rtvi/processor.d.ts +182 -0
  282. package/dist/processors/frameworks/rtvi/processor.js +676 -0
  283. package/dist/processors/idle_frame_processor.d.ts +44 -0
  284. package/dist/processors/idle_frame_processor.js +86 -0
  285. package/dist/processors/index.d.ts +2 -0
  286. package/dist/processors/index.js +8 -0
  287. package/dist/processors/logger.d.ts +42 -0
  288. package/dist/processors/logger.js +87 -0
  289. package/dist/processors/metrics/frame_processor_metrics.d.ts +172 -0
  290. package/dist/processors/metrics/frame_processor_metrics.js +372 -0
  291. package/dist/processors/metrics/index.d.ts +2 -0
  292. package/dist/processors/metrics/index.js +8 -0
  293. package/dist/processors/producer_processor.d.ts +68 -0
  294. package/dist/processors/producer_processor.js +78 -0
  295. package/dist/processors/text_transformer.d.ts +30 -0
  296. package/dist/processors/text_transformer.js +48 -0
  297. package/dist/registry/index.d.ts +3 -0
  298. package/dist/registry/index.js +8 -0
  299. package/dist/registry/registry.d.ts +69 -0
  300. package/dist/registry/registry.js +149 -0
  301. package/dist/registry/types.d.ts +47 -0
  302. package/dist/registry/types.js +39 -0
  303. package/dist/serializers/base_serializer.d.ts +94 -0
  304. package/dist/serializers/base_serializer.js +82 -0
  305. package/dist/serializers/index.d.ts +2 -0
  306. package/dist/serializers/index.js +8 -0
  307. package/dist/services/ai_service.d.ts +145 -0
  308. package/dist/services/ai_service.js +262 -0
  309. package/dist/services/anthropic/index.d.ts +2 -0
  310. package/dist/services/anthropic/index.js +7 -0
  311. package/dist/services/assemblyai/index.d.ts +2 -0
  312. package/dist/services/assemblyai/index.js +7 -0
  313. package/dist/services/asyncai/index.d.ts +2 -0
  314. package/dist/services/asyncai/index.js +7 -0
  315. package/dist/services/aws/index.d.ts +2 -0
  316. package/dist/services/aws/index.js +7 -0
  317. package/dist/services/azure/index.d.ts +2 -0
  318. package/dist/services/azure/index.js +7 -0
  319. package/dist/services/baseten/index.d.ts +2 -0
  320. package/dist/services/baseten/index.js +7 -0
  321. package/dist/services/bland/index.d.ts +2 -0
  322. package/dist/services/bland/index.js +7 -0
  323. package/dist/services/camb/index.d.ts +2 -0
  324. package/dist/services/camb/index.js +7 -0
  325. package/dist/services/cartesia/index.d.ts +2 -0
  326. package/dist/services/cartesia/index.js +7 -0
  327. package/dist/services/cerebras/index.d.ts +2 -0
  328. package/dist/services/cerebras/index.js +7 -0
  329. package/dist/services/crusoe/index.d.ts +2 -0
  330. package/dist/services/crusoe/index.js +7 -0
  331. package/dist/services/deepgram/index.d.ts +2 -0
  332. package/dist/services/deepgram/index.js +7 -0
  333. package/dist/services/deepseek/index.d.ts +2 -0
  334. package/dist/services/deepseek/index.js +7 -0
  335. package/dist/services/elevenlabs/index.d.ts +2 -0
  336. package/dist/services/elevenlabs/index.js +7 -0
  337. package/dist/services/fal/index.d.ts +2 -0
  338. package/dist/services/fal/index.js +7 -0
  339. package/dist/services/fireworks/index.d.ts +2 -0
  340. package/dist/services/fireworks/index.js +7 -0
  341. package/dist/services/fish/index.d.ts +2 -0
  342. package/dist/services/fish/index.js +7 -0
  343. package/dist/services/funasr/index.d.ts +3 -0
  344. package/dist/services/funasr/index.js +8 -0
  345. package/dist/services/gladia/index.d.ts +2 -0
  346. package/dist/services/gladia/index.js +7 -0
  347. package/dist/services/google/index.d.ts +2 -0
  348. package/dist/services/google/index.js +7 -0
  349. package/dist/services/gradium/index.d.ts +2 -0
  350. package/dist/services/gradium/index.js +7 -0
  351. package/dist/services/grok/index.d.ts +2 -0
  352. package/dist/services/grok/index.js +7 -0
  353. package/dist/services/groq/index.d.ts +2 -0
  354. package/dist/services/groq/index.js +7 -0
  355. package/dist/services/heygen/index.d.ts +2 -0
  356. package/dist/services/heygen/index.js +7 -0
  357. package/dist/services/hume/index.d.ts +2 -0
  358. package/dist/services/hume/index.js +7 -0
  359. package/dist/services/image_service.d.ts +52 -0
  360. package/dist/services/image_service.js +60 -0
  361. package/dist/services/inception/index.d.ts +2 -0
  362. package/dist/services/inception/index.js +7 -0
  363. package/dist/services/index.d.ts +2 -0
  364. package/dist/services/index.js +8 -0
  365. package/dist/services/inworld/index.d.ts +2 -0
  366. package/dist/services/inworld/index.js +7 -0
  367. package/dist/services/keenable/index.d.ts +2 -0
  368. package/dist/services/keenable/index.js +7 -0
  369. package/dist/services/kokoro/index.d.ts +2 -0
  370. package/dist/services/kokoro/index.js +7 -0
  371. package/dist/services/llm_service.d.ts +1106 -0
  372. package/dist/services/llm_service.js +2231 -0
  373. package/dist/services/mem0/index.d.ts +2 -0
  374. package/dist/services/mem0/index.js +7 -0
  375. package/dist/services/meta/index.d.ts +2 -0
  376. package/dist/services/meta/index.js +7 -0
  377. package/dist/services/minimax/index.d.ts +2 -0
  378. package/dist/services/minimax/index.js +7 -0
  379. package/dist/services/mistral/index.d.ts +2 -0
  380. package/dist/services/mistral/index.js +7 -0
  381. package/dist/services/moondream/index.d.ts +2 -0
  382. package/dist/services/moondream/index.js +7 -0
  383. package/dist/services/moonshine/index.d.ts +2 -0
  384. package/dist/services/moonshine/index.js +7 -0
  385. package/dist/services/nebius/index.d.ts +2 -0
  386. package/dist/services/nebius/index.js +7 -0
  387. package/dist/services/neuphonic/index.d.ts +2 -0
  388. package/dist/services/neuphonic/index.js +7 -0
  389. package/dist/services/novita/index.d.ts +2 -0
  390. package/dist/services/novita/index.js +7 -0
  391. package/dist/services/nvidia/index.d.ts +2 -0
  392. package/dist/services/nvidia/index.js +11 -0
  393. package/dist/services/ollama/index.d.ts +2 -0
  394. package/dist/services/ollama/index.js +7 -0
  395. package/dist/services/openai/index.d.ts +2 -0
  396. package/dist/services/openai/index.js +7 -0
  397. package/dist/services/openclaw/index.d.ts +14 -0
  398. package/dist/services/openclaw/index.js +19 -0
  399. package/dist/services/openrouter/index.d.ts +2 -0
  400. package/dist/services/openrouter/index.js +7 -0
  401. package/dist/services/perplexity/index.d.ts +2 -0
  402. package/dist/services/perplexity/index.js +7 -0
  403. package/dist/services/piper/index.d.ts +2 -0
  404. package/dist/services/piper/index.js +7 -0
  405. package/dist/services/pocket_tts/index.d.ts +2 -0
  406. package/dist/services/pocket_tts/index.js +7 -0
  407. package/dist/services/qwen/index.d.ts +2 -0
  408. package/dist/services/qwen/index.js +7 -0
  409. package/dist/services/resembleai/index.d.ts +2 -0
  410. package/dist/services/resembleai/index.js +7 -0
  411. package/dist/services/rime/index.d.ts +2 -0
  412. package/dist/services/rime/index.js +7 -0
  413. package/dist/services/sambanova/index.d.ts +2 -0
  414. package/dist/services/sambanova/index.js +7 -0
  415. package/dist/services/sarvam/index.d.ts +2 -0
  416. package/dist/services/sarvam/index.js +7 -0
  417. package/dist/services/settings.d.ts +356 -0
  418. package/dist/services/settings.js +437 -0
  419. package/dist/services/simli/index.d.ts +2 -0
  420. package/dist/services/simli/index.js +7 -0
  421. package/dist/services/smallest/index.d.ts +2 -0
  422. package/dist/services/smallest/index.js +7 -0
  423. package/dist/services/soniox/index.d.ts +2 -0
  424. package/dist/services/soniox/index.js +7 -0
  425. package/dist/services/speechify/index.d.ts +2 -0
  426. package/dist/services/speechify/index.js +7 -0
  427. package/dist/services/speechmatics/index.d.ts +2 -0
  428. package/dist/services/speechmatics/index.js +7 -0
  429. package/dist/services/stt_latency.d.ts +62 -0
  430. package/dist/services/stt_latency.js +71 -0
  431. package/dist/services/stt_service.d.ts +601 -0
  432. package/dist/services/stt_service.js +1044 -0
  433. package/dist/services/tavus/index.d.ts +2 -0
  434. package/dist/services/tavus/index.js +7 -0
  435. package/dist/services/together/index.d.ts +2 -0
  436. package/dist/services/together/index.js +7 -0
  437. package/dist/services/tts_service.d.ts +909 -0
  438. package/dist/services/tts_service.js +2018 -0
  439. package/dist/services/ultravox/index.d.ts +2 -0
  440. package/dist/services/ultravox/index.js +9 -0
  441. package/dist/services/vision_service.d.ts +56 -0
  442. package/dist/services/vision_service.js +62 -0
  443. package/dist/services/websocket_service.d.ts +213 -0
  444. package/dist/services/websocket_service.js +654 -0
  445. package/dist/services/whisper/index.d.ts +2 -0
  446. package/dist/services/whisper/index.js +9 -0
  447. package/dist/services/xai/index.d.ts +2 -0
  448. package/dist/services/xai/index.js +9 -0
  449. package/dist/services/xtts/index.d.ts +2 -0
  450. package/dist/services/xtts/index.js +9 -0
  451. package/dist/tests/index.d.ts +2 -0
  452. package/dist/tests/index.js +8 -0
  453. package/dist/tests/utils.d.ts +123 -0
  454. package/dist/tests/utils.js +212 -0
  455. package/dist/transcriptions/index.d.ts +2 -0
  456. package/dist/transcriptions/index.js +8 -0
  457. package/dist/transcriptions/language.d.ts +376 -0
  458. package/dist/transcriptions/language.js +543 -0
  459. package/dist/transports/base_input.d.ts +149 -0
  460. package/dist/transports/base_input.js +338 -0
  461. package/dist/transports/base_output.d.ts +432 -0
  462. package/dist/transports/base_output.js +1231 -0
  463. package/dist/transports/base_transport.d.ts +223 -0
  464. package/dist/transports/base_transport.js +167 -0
  465. package/dist/transports/daily/index.d.ts +2 -0
  466. package/dist/transports/daily/index.js +9 -0
  467. package/dist/transports/heygen/index.d.ts +2 -0
  468. package/dist/transports/heygen/index.js +9 -0
  469. package/dist/transports/index.d.ts +2 -0
  470. package/dist/transports/index.js +8 -0
  471. package/dist/transports/lemonslice/index.d.ts +2 -0
  472. package/dist/transports/lemonslice/index.js +9 -0
  473. package/dist/transports/livekit/index.d.ts +2 -0
  474. package/dist/transports/livekit/index.js +9 -0
  475. package/dist/transports/local/index.d.ts +2 -0
  476. package/dist/transports/local/index.js +9 -0
  477. package/dist/transports/moq/index.d.ts +2 -0
  478. package/dist/transports/moq/index.js +9 -0
  479. package/dist/transports/smallwebrtc/index.d.ts +2 -0
  480. package/dist/transports/smallwebrtc/index.js +9 -0
  481. package/dist/transports/tavus/index.d.ts +2 -0
  482. package/dist/transports/tavus/index.js +9 -0
  483. package/dist/transports/video_in_sampler.d.ts +157 -0
  484. package/dist/transports/video_in_sampler.js +258 -0
  485. package/dist/transports/vonage/index.d.ts +2 -0
  486. package/dist/transports/vonage/index.js +9 -0
  487. package/dist/transports/websocket/index.d.ts +2 -0
  488. package/dist/transports/websocket/index.js +9 -0
  489. package/dist/transports/whatsapp/index.d.ts +2 -0
  490. package/dist/transports/whatsapp/index.js +9 -0
  491. package/dist/turns/eager_end_of_turn_mixin.d.ts +70 -0
  492. package/dist/turns/eager_end_of_turn_mixin.js +122 -0
  493. package/dist/turns/empty_user_turn.d.ts +55 -0
  494. package/dist/turns/empty_user_turn.js +53 -0
  495. package/dist/turns/index.d.ts +2 -0
  496. package/dist/turns/index.js +8 -0
  497. package/dist/turns/speculation_gate.d.ts +169 -0
  498. package/dist/turns/speculation_gate.js +294 -0
  499. package/dist/turns/types.d.ts +33 -0
  500. package/dist/turns/types.js +35 -0
  501. package/dist/turns/user_idle_controller.d.ts +94 -0
  502. package/dist/turns/user_idle_controller.js +187 -0
  503. package/dist/turns/user_mute/always_user_mute_strategy.d.ts +15 -0
  504. package/dist/turns/user_mute/always_user_mute_strategy.js +29 -0
  505. package/dist/turns/user_mute/base_user_mute_strategy.d.ts +39 -0
  506. package/dist/turns/user_mute/base_user_mute_strategy.js +45 -0
  507. package/dist/turns/user_mute/first_speech_user_mute_strategy.d.ts +28 -0
  508. package/dist/turns/user_mute/first_speech_user_mute_strategy.js +52 -0
  509. package/dist/turns/user_mute/function_call_user_mute_strategy.d.ts +25 -0
  510. package/dist/turns/user_mute/function_call_user_mute_strategy.js +50 -0
  511. package/dist/turns/user_mute/index.d.ts +6 -0
  512. package/dist/turns/user_mute/index.js +11 -0
  513. package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.d.ts +43 -0
  514. package/dist/turns/user_mute/mute_until_first_bot_complete_user_mute_strategy.js +76 -0
  515. package/dist/turns/user_start/base_user_turn_start_strategy.d.ts +135 -0
  516. package/dist/turns/user_start/base_user_turn_start_strategy.js +134 -0
  517. package/dist/turns/user_start/external_user_turn_start_strategy.d.ts +45 -0
  518. package/dist/turns/user_start/external_user_turn_start_strategy.js +64 -0
  519. package/dist/turns/user_start/index.d.ts +8 -0
  520. package/dist/turns/user_start/index.js +16 -0
  521. package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.d.ts +133 -0
  522. package/dist/turns/user_start/krisp_viva_ip_user_turn_start_strategy.js +243 -0
  523. package/dist/turns/user_start/min_words_user_turn_start_strategy.d.ts +60 -0
  524. package/dist/turns/user_start/min_words_user_turn_start_strategy.js +102 -0
  525. package/dist/turns/user_start/transcription_user_turn_start_strategy.d.ts +29 -0
  526. package/dist/turns/user_start/transcription_user_turn_start_strategy.js +43 -0
  527. package/dist/turns/user_start/vad_user_turn_start_strategy.d.ts +20 -0
  528. package/dist/turns/user_start/vad_user_turn_start_strategy.js +31 -0
  529. package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.d.ts +141 -0
  530. package/dist/turns/user_start/wake_phrase_user_turn_start_strategy.js +238 -0
  531. package/dist/turns/user_stop/base_user_turn_stop_strategy.d.ts +174 -0
  532. package/dist/turns/user_stop/base_user_turn_stop_strategy.js +170 -0
  533. package/dist/turns/user_stop/deferred_user_turn_stop_strategy.d.ts +81 -0
  534. package/dist/turns/user_stop/deferred_user_turn_stop_strategy.js +107 -0
  535. package/dist/turns/user_stop/eager_match_policy.d.ts +36 -0
  536. package/dist/turns/user_stop/eager_match_policy.js +50 -0
  537. package/dist/turns/user_stop/eager_user_turn_stop_strategy.d.ts +84 -0
  538. package/dist/turns/user_stop/eager_user_turn_stop_strategy.js +154 -0
  539. package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.d.ts +45 -0
  540. package/dist/turns/user_stop/external_user_turn_completion_stop_strategy.js +55 -0
  541. package/dist/turns/user_stop/external_user_turn_stop_strategy.d.ts +115 -0
  542. package/dist/turns/user_stop/external_user_turn_stop_strategy.js +205 -0
  543. package/dist/turns/user_stop/index.d.ts +10 -0
  544. package/dist/turns/user_stop/index.js +15 -0
  545. package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.d.ts +44 -0
  546. package/dist/turns/user_stop/llm_turn_completion_user_turn_stop_strategy.js +59 -0
  547. package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.d.ts +140 -0
  548. package/dist/turns/user_stop/speech_timeout_user_turn_stop_strategy.js +303 -0
  549. package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.d.ts +135 -0
  550. package/dist/turns/user_stop/turn_analyzer_user_turn_stop_strategy.js +332 -0
  551. package/dist/turns/user_turn_completion_mixin.d.ts +147 -0
  552. package/dist/turns/user_turn_completion_mixin.js +591 -0
  553. package/dist/turns/user_turn_controller.d.ts +134 -0
  554. package/dist/turns/user_turn_controller.js +335 -0
  555. package/dist/turns/user_turn_processor.d.ts +87 -0
  556. package/dist/turns/user_turn_processor.js +180 -0
  557. package/dist/turns/user_turn_strategies.d.ts +187 -0
  558. package/dist/turns/user_turn_strategies.js +222 -0
  559. package/dist/utils/async_tool_cancellation.d.ts +35 -0
  560. package/dist/utils/async_tool_cancellation.js +78 -0
  561. package/dist/utils/asyncio/index.d.ts +2 -0
  562. package/dist/utils/asyncio/index.js +7 -0
  563. package/dist/utils/asyncio/task_manager.d.ts +49 -0
  564. package/dist/utils/asyncio/task_manager.js +105 -0
  565. package/dist/utils/base_object.d.ts +76 -0
  566. package/dist/utils/base_object.js +154 -0
  567. package/dist/utils/context/aggregated_frame_sequencer.d.ts +491 -0
  568. package/dist/utils/context/aggregated_frame_sequencer.js +803 -0
  569. package/dist/utils/context/audio_context_manager.d.ts +131 -0
  570. package/dist/utils/context/audio_context_manager.js +233 -0
  571. package/dist/utils/context/index.d.ts +2 -0
  572. package/dist/utils/context/index.js +7 -0
  573. package/dist/utils/context/llm_context_summarization.d.ts +258 -0
  574. package/dist/utils/context/llm_context_summarization.js +511 -0
  575. package/dist/utils/context/text_segment_map.d.ts +611 -0
  576. package/dist/utils/context/text_segment_map.js +1132 -0
  577. package/dist/utils/context/word_completion_tracker.d.ts +273 -0
  578. package/dist/utils/context/word_completion_tracker.js +441 -0
  579. package/dist/utils/context/word_timestamp_tracker.d.ts +72 -0
  580. package/dist/utils/context/word_timestamp_tracker.js +98 -0
  581. package/dist/utils/deprecation.d.ts +28 -0
  582. package/dist/utils/deprecation.js +75 -0
  583. package/dist/utils/env.d.ts +38 -0
  584. package/dist/utils/env.js +62 -0
  585. package/dist/utils/errors.d.ts +75 -0
  586. package/dist/utils/errors.js +202 -0
  587. package/dist/utils/file_resolver.d.ts +126 -0
  588. package/dist/utils/file_resolver.js +314 -0
  589. package/dist/utils/file_storage.d.ts +115 -0
  590. package/dist/utils/file_storage.js +178 -0
  591. package/dist/utils/frame_queue.d.ts +36 -0
  592. package/dist/utils/frame_queue.js +66 -0
  593. package/dist/utils/http.d.ts +39 -0
  594. package/dist/utils/http.js +53 -0
  595. package/dist/utils/image.d.ts +14 -0
  596. package/dist/utils/image.js +63 -0
  597. package/dist/utils/index.d.ts +2 -0
  598. package/dist/utils/index.js +8 -0
  599. package/dist/utils/jpeg_encoder.d.ts +14 -0
  600. package/dist/utils/jpeg_encoder.js +315 -0
  601. package/dist/utils/log_config.d.ts +34 -0
  602. package/dist/utils/log_config.js +76 -0
  603. package/dist/utils/logger.d.ts +40 -0
  604. package/dist/utils/logger.js +91 -0
  605. package/dist/utils/mixin.d.ts +14 -0
  606. package/dist/utils/mixin.js +63 -0
  607. package/dist/utils/network.d.ts +73 -0
  608. package/dist/utils/network.js +90 -0
  609. package/dist/utils/security/allowed_origins.d.ts +16 -0
  610. package/dist/utils/security/allowed_origins.js +34 -0
  611. package/dist/utils/security/index.d.ts +2 -0
  612. package/dist/utils/security/index.js +7 -0
  613. package/dist/utils/security/redaction.d.ts +27 -0
  614. package/dist/utils/security/redaction.js +123 -0
  615. package/dist/utils/security/ssrf.d.ts +79 -0
  616. package/dist/utils/security/ssrf.js +327 -0
  617. package/dist/utils/shared.d.ts +35 -0
  618. package/dist/utils/shared.js +136 -0
  619. package/dist/utils/startup.d.ts +17 -0
  620. package/dist/utils/startup.js +130 -0
  621. package/dist/utils/string.d.ts +116 -0
  622. package/dist/utils/string.js +460 -0
  623. package/dist/utils/sync/base_notifier.d.ts +25 -0
  624. package/dist/utils/sync/base_notifier.js +16 -0
  625. package/dist/utils/sync/event_notifier.d.ts +38 -0
  626. package/dist/utils/sync/event_notifier.js +51 -0
  627. package/dist/utils/sync/index.d.ts +2 -0
  628. package/dist/utils/sync/index.js +7 -0
  629. package/dist/utils/text/alnum_utils.d.ts +109 -0
  630. package/dist/utils/text/alnum_utils.js +262 -0
  631. package/dist/utils/text/base_text_aggregator.d.ts +146 -0
  632. package/dist/utils/text/base_text_aggregator.js +102 -0
  633. package/dist/utils/text/base_text_filter.d.ts +54 -0
  634. package/dist/utils/text/base_text_filter.js +50 -0
  635. package/dist/utils/text/index.d.ts +2 -0
  636. package/dist/utils/text/index.js +7 -0
  637. package/dist/utils/text/markdown_text_filter.d.ts +147 -0
  638. package/dist/utils/text/markdown_text_filter.js +522 -0
  639. package/dist/utils/text/markup_utils.d.ts +54 -0
  640. package/dist/utils/text/markup_utils.js +156 -0
  641. package/dist/utils/text/pattern_pair_aggregator.d.ts +251 -0
  642. package/dist/utils/text/pattern_pair_aggregator.js +478 -0
  643. package/dist/utils/text/phonemes.d.ts +60 -0
  644. package/dist/utils/text/phonemes.js +172 -0
  645. package/dist/utils/text/simple_text_aggregator.d.ts +135 -0
  646. package/dist/utils/text/simple_text_aggregator.js +254 -0
  647. package/dist/utils/text/skip_tags_aggregator.d.ts +85 -0
  648. package/dist/utils/text/skip_tags_aggregator.js +147 -0
  649. package/dist/utils/text/transforms/acronyms.d.ts +21 -0
  650. package/dist/utils/text/transforms/acronyms.js +31 -0
  651. package/dist/utils/text/transforms/currency.d.ts +16 -0
  652. package/dist/utils/text/transforms/currency.js +56 -0
  653. package/dist/utils/text/transforms/dates.d.ts +18 -0
  654. package/dist/utils/text/transforms/dates.js +102 -0
  655. package/dist/utils/text/transforms/email.d.ts +17 -0
  656. package/dist/utils/text/transforms/email.js +35 -0
  657. package/dist/utils/text/transforms/index.d.ts +37 -0
  658. package/dist/utils/text/transforms/index.js +42 -0
  659. package/dist/utils/text/transforms/numbers.d.ts +25 -0
  660. package/dist/utils/text/transforms/numbers.js +59 -0
  661. package/dist/utils/text/transforms/percentages.d.ts +16 -0
  662. package/dist/utils/text/transforms/percentages.js +32 -0
  663. package/dist/utils/text/transforms/phone.d.ts +21 -0
  664. package/dist/utils/text/transforms/phone.js +39 -0
  665. package/dist/utils/text/transforms/pronunciations.d.ts +65 -0
  666. package/dist/utils/text/transforms/pronunciations.js +112 -0
  667. package/dist/utils/text/transforms/replacements.d.ts +39 -0
  668. package/dist/utils/text/transforms/replacements.js +46 -0
  669. package/dist/utils/text/transforms/strip_markdown.d.ts +25 -0
  670. package/dist/utils/text/transforms/strip_markdown.js +60 -0
  671. package/dist/utils/text/transforms/units.d.ts +22 -0
  672. package/dist/utils/text/transforms/units.js +81 -0
  673. package/dist/utils/text/transforms/voice_formatter.d.ts +86 -0
  674. package/dist/utils/text/transforms/voice_formatter.js +98 -0
  675. package/dist/utils/text/unicode_digits.d.ts +3 -0
  676. package/dist/utils/text/unicode_digits.js +34 -0
  677. package/dist/utils/text/word_timestamp_utils.d.ts +26 -0
  678. package/dist/utils/text/word_timestamp_utils.js +52 -0
  679. package/dist/utils/time.d.ts +13 -0
  680. package/dist/utils/time.js +31 -0
  681. package/dist/utils/tracing/index.d.ts +3 -0
  682. package/dist/utils/tracing/index.js +8 -0
  683. package/dist/utils/tracing/service_attributes.d.ts +198 -0
  684. package/dist/utils/tracing/service_attributes.js +434 -0
  685. package/dist/utils/tracing/service_decorators.d.ts +214 -0
  686. package/dist/utils/tracing/service_decorators.js +1662 -0
  687. package/dist/utils/tracing/setup.d.ts +107 -0
  688. package/dist/utils/tracing/setup.js +160 -0
  689. package/dist/utils/tracing/tracing_context.d.ts +55 -0
  690. package/dist/utils/tracing/tracing_context.js +97 -0
  691. package/dist/utils/tracing/turn_trace_observer.d.ts +95 -0
  692. package/dist/utils/tracing/turn_trace_observer.js +233 -0
  693. package/dist/utils/types.d.ts +80 -0
  694. package/dist/utils/types.js +108 -0
  695. package/dist/utils/utils.d.ts +5 -0
  696. package/dist/utils/utils.js +22 -0
  697. package/dist/utils/yaml.d.ts +179 -0
  698. package/dist/utils/yaml.js +556 -0
  699. package/dist/version.d.ts +3 -0
  700. package/dist/version.js +7 -0
  701. package/dist/workers/base_worker.d.ts +772 -0
  702. package/dist/workers/base_worker.js +1298 -0
  703. package/dist/workers/index.d.ts +2 -0
  704. package/dist/workers/index.js +8 -0
  705. package/dist/workers/llm/backend_llm_worker.d.ts +552 -0
  706. package/dist/workers/llm/backend_llm_worker.js +1055 -0
  707. package/dist/workers/llm/index.d.ts +6 -0
  708. package/dist/workers/llm/index.js +11 -0
  709. package/dist/workers/llm/llm_context_worker.d.ts +66 -0
  710. package/dist/workers/llm/llm_context_worker.js +72 -0
  711. package/dist/workers/llm/llm_worker.d.ts +180 -0
  712. package/dist/workers/llm/llm_worker.js +323 -0
  713. package/dist/workers/llm/tool_decorator.d.ts +68 -0
  714. package/dist/workers/llm/tool_decorator.js +60 -0
  715. package/dist/workers/proxy/index.d.ts +3 -0
  716. package/dist/workers/proxy/index.js +9 -0
  717. package/dist/workers/proxy/websocket/client.d.ts +110 -0
  718. package/dist/workers/proxy/websocket/client.js +196 -0
  719. package/dist/workers/proxy/websocket/index.d.ts +3 -0
  720. package/dist/workers/proxy/websocket/index.js +10 -0
  721. package/dist/workers/runner.d.ts +268 -0
  722. package/dist/workers/runner.js +489 -0
  723. package/dist/workers/ui/index.d.ts +22 -0
  724. package/dist/workers/ui/index.js +27 -0
  725. package/dist/workers/ui/ui_event_decorator.d.ts +43 -0
  726. package/dist/workers/ui/ui_event_decorator.js +75 -0
  727. package/dist/workers/ui/ui_job_context.d.ts +12 -0
  728. package/dist/workers/ui/ui_job_context.js +17 -0
  729. package/dist/workers/ui/ui_prompts.d.ts +21 -0
  730. package/dist/workers/ui/ui_prompts.js +41 -0
  731. package/dist/workers/ui/ui_tools.d.ts +32 -0
  732. package/dist/workers/ui/ui_tools.js +112 -0
  733. package/dist/workers/ui/ui_worker.d.ts +571 -0
  734. package/dist/workers/ui/ui_worker.js +1156 -0
  735. package/package.json +61 -3
  736. package/README.md +0 -5
@@ -0,0 +1,1055 @@
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/workers/llm/backend_llm_worker.py
6
+ /**
7
+ * Backend LLM worker for two-tier voice agents.
8
+ *
9
+ * A frontend holds the conversation and hands off requests that need tools or
10
+ * careful reasoning. What the frontend is does not matter to this contract: a
11
+ * speech-to-speech model delegating on its own, or a pipeline calling a tool.
12
+ * A `BackendLLMWorker` runs any Pipecat LLM service, with its own context and
13
+ * multi-step tool calling, to do that work.
14
+ *
15
+ * The frontend and the backend exchange messages. A frontend *attaches* to the
16
+ * worker once, for as long as it lives, and from then on hears everything the
17
+ * backend produces as a stream: each of its model's turns and reasoning
18
+ * summaries as a `BackendOutput`, each phase of the function calls it makes as
19
+ * a `BackendToolCall`, and when it has run out of things to do, that it is
20
+ * idle. Requests go the other way as messages appended to the backend's
21
+ * conversation, each answered at once; a message that arrives while the
22
+ * backend is working joins that work, and the backend's model sorts out what
23
+ * it means. `_BackendSession` is the caller side of that contract.
24
+ *
25
+ * A request is text, so how a frontend words one is its own business.
26
+ * `_renderTranscriptRequest` renders the conversation as a labelled
27
+ * transcript, which is what a frontend hands over when its model signals a
28
+ * handoff without wording a request; a frontend whose model does word one
29
+ * sends that instead. `OpenAILiveLLMService`'s client delegation and
30
+ * `LLMWithBackend` both drive the worker this way.
31
+ *
32
+ * Porting notes:
33
+ * - DIVERGENCE: upstream overrides `PipelineWorker.capabilities` with a plain
34
+ * dict. TypeScript does not allow an override of a different type, so the
35
+ * backend's capabilities are exposed as `backendCapabilities`, and
36
+ * `PipelineWorker.capabilities` keeps its meaning.
37
+ * - `_BackendSession`'s `async with` is `enter()` / `exit()` (or `run(fn)`),
38
+ * and its `async for` is `for await`.
39
+ */
40
+ import { Event, Queue, rethrowIfCancelled, shield } from "@pipedog/asyncio";
41
+ import { ExternalFunctionCall, ExternalFunctionCallCancelFrame, ExternalFunctionCallInProgressFrame, ExternalFunctionCallResultFrame, ExternalFunctionCallsStartedFrame, FunctionCallCancelFrame, FunctionCallInProgressFrame, FunctionCallResultFrame, FunctionCallsStartedFrame, InterruptionFrame, LLMContextFrame, LLMMessagesAppendFrame, LLMRunFrame, } from "../../frames/frames.js";
42
+ import { JobError, JobEvent, JobGroupError, JobGroupParams, JobParams, JobStatus, } from "../../pipeline/job_context.js";
43
+ import { job } from "../../pipeline/job_decorator.js";
44
+ import { LLMSpecificMessage, standardMessageText, } from "../../processors/aggregators/llm_context.js";
45
+ import { LLMUserAggregatorParams, } from "../../processors/aggregators/llm_response_universal.js";
46
+ import { LLMSettings } from "../../services/settings.js";
47
+ import { ExternalUserTurnStrategies } from "../../turns/user_turn_strategies.js";
48
+ import { ErrorCategory } from "../../utils/errors.js";
49
+ import { logger } from "../../utils/logger.js";
50
+ import { LLMContextWorker } from "./llm_context_worker.js";
51
+ /** Name of the job that attaches a frontend to a `BackendLLMWorker`. */
52
+ export const ATTACH_JOB_NAME = "attach";
53
+ /** Name of the job that puts a message to an attached `BackendLLMWorker`. */
54
+ export const MESSAGE_JOB_NAME = "message";
55
+ /** The `type` of a job update that carries a `BackendOutput`. */
56
+ export const OUTPUT_UPDATE_TYPE = "output";
57
+ /** The `type` of a job update that carries a `BackendToolCall`. */
58
+ export const TOOL_CALL_UPDATE_TYPE = "tool_call";
59
+ /** The `type` of the update that opens an `attach` job's stream. */
60
+ export const ATTACHED_UPDATE_TYPE = "attached";
61
+ /** The `type` of the update that says the backend has nothing left to do. */
62
+ export const IDLE_UPDATE_TYPE = "idle";
63
+ /** The `type` of the update that says the backend could not go on. */
64
+ export const ERROR_UPDATE_TYPE = "error";
65
+ /**
66
+ * The mark the backend's model puts at the start of a message it wants the
67
+ * user told. The message is sent with `prefersSpoken: true`, mark stripped; a
68
+ * message without it is a note (`prefersSpoken: false`). Strict prompt,
69
+ * flexible parsing: the model is told to put the mark first, but sometimes
70
+ * writes a note paragraph and then a marked one, so a mark at the start of a
71
+ * later line splits the message, a note before it and a spoken message from it
72
+ * on, rather than losing the result. The turn-completion markers are read the
73
+ * same way (`user_turn_completion_mixin`).
74
+ */
75
+ export const SPOKEN_MARK = ">>";
76
+ function escapeRegExp(text) {
77
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
78
+ }
79
+ /** Where a message's spoken part starts: the first line that begins with the mark. */
80
+ const SPOKEN_MARK_LINE_SOURCE = `^[ \\t]*${escapeRegExp(SPOKEN_MARK)}[ \\t]*`;
81
+ /**
82
+ * How the backend's model works with the assistant, appended to its system
83
+ * instruction: who it is, which of what it writes the user hears, and how it
84
+ * takes requests. The backend's own system instruction says what it does;
85
+ * this says how what it writes reaches the user.
86
+ * `new BackendLLMWorker({ pairingInstruction })` replaces it.
87
+ */
88
+ export const BACKEND_PAIRING_INSTRUCTION = "You are the backend of a voice assistant. The assistant talks with the user and sends " +
89
+ "you requests: the conversation it is having, or a request it worded for you. Do the " +
90
+ "parts that need your tools or careful reasoning; the assistant handles the rest of the " +
91
+ "conversation itself, such as small talk, jokes and stories, so leave those to it.\n\n" +
92
+ `WHAT THE USER HEARS: Begin a message with ${SPOKEN_MARK} and the whole of it is told to ` +
93
+ "the user, in the assistant's own words, tool calls or not. Anything you write without " +
94
+ "the mark is not told to the user: the assistant keeps it as notes on your work, and " +
95
+ "draws on them only if the user asks how the work is going. " +
96
+ "Mark a message to give a result, to ask a question you cannot proceed without, or for " +
97
+ "news the user should have now. A result you do not mark is " +
98
+ `lost: the message that gives it begins with ${SPOKEN_MARK}. Never say again what you ` +
99
+ "have already marked, and never mark that you have stopped or that there is nothing to " +
100
+ "do: the assistant has already told the user. Write a marked message as plain spoken " +
101
+ "sentences the assistant can say as they are: no headings, lists, Markdown or raw " +
102
+ "JSON.\n\n" +
103
+ "REQUESTS: A request may arrive while you are working on an earlier one. It may add " +
104
+ "work, change it, or stop some or all of it: follow the latest, cancel the tools whose " +
105
+ "results are no longer wanted, and do not repeat work already done. With several " +
106
+ "requests in hand, do the newest first unless it depends on an earlier one, and give " +
107
+ "each one's result as soon as you have it, not when everything is done.";
108
+ /**
109
+ * One piece of output from a backend, on its way to the frontend.
110
+ *
111
+ * - `text`: the text the backend produced.
112
+ * - `isThought`: whether this is a reasoning summary rather than a response.
113
+ * - `prefersSpoken`: whether the backend would like the user to hear this.
114
+ * This is just a hint to the frontend: it may choose to follow it or not.
115
+ * `OpenAILiveLLMService`'s live model takes it into consideration, but
116
+ * ultimately decides what to speak (or not) based on the conversation.
117
+ */
118
+ export class BackendOutput {
119
+ text;
120
+ isThought;
121
+ prefersSpoken;
122
+ constructor(options) {
123
+ this.text = options.text;
124
+ this.isThought = options.isThought ?? false;
125
+ this.prefersSpoken = options.prefersSpoken ?? true;
126
+ }
127
+ /**
128
+ * Render the output as a job update payload.
129
+ *
130
+ * @returns The payload.
131
+ */
132
+ toPayload() {
133
+ return {
134
+ type: OUTPUT_UPDATE_TYPE,
135
+ text: this.text,
136
+ is_thought: this.isThought,
137
+ prefers_spoken: this.prefersSpoken,
138
+ };
139
+ }
140
+ /**
141
+ * Rebuild an output from a job update payload.
142
+ *
143
+ * @param payload The update payload.
144
+ * @returns The output.
145
+ */
146
+ static fromPayload(payload) {
147
+ // A flag the payload omits is left to the field's default, so the
148
+ // defaults live in one place; one it carries is coerced, since a payload
149
+ // can arrive from another process.
150
+ const options = { text: String(payload.text || "") };
151
+ if ("is_thought" in payload)
152
+ options.isThought = Boolean(payload.is_thought);
153
+ if ("prefers_spoken" in payload)
154
+ options.prefersSpoken = Boolean(payload.prefers_spoken);
155
+ return new BackendOutput(options);
156
+ }
157
+ }
158
+ /**
159
+ * One phase of a function call the backend made while working, on its way to
160
+ * the frontend.
161
+ *
162
+ * The call ran in the backend's own pipeline; the frontend reports it to
163
+ * clients, as the `ExternalFunctionCall*Frame` `toFrame` builds, and does
164
+ * nothing else with it. The phases mirror the pipeline's own function-call
165
+ * frames.
166
+ *
167
+ * - `phase`: `started`, `in_progress`, `result` or `cancelled`.
168
+ * - `functionName`: name of the function called.
169
+ * - `toolCallId`: unique identifier of the call.
170
+ * - `arguments`: arguments passed to the function, once known.
171
+ * - `result`: the result, for the `result` phase.
172
+ * - `isFinal`: for the `result` phase, whether the result completes the call
173
+ * rather than being one of a stream of intermediate results.
174
+ */
175
+ export class BackendToolCall {
176
+ phase;
177
+ functionName;
178
+ toolCallId;
179
+ arguments;
180
+ result;
181
+ isFinal;
182
+ constructor(options) {
183
+ this.phase = options.phase;
184
+ this.functionName = options.functionName;
185
+ this.toolCallId = options.toolCallId;
186
+ this.arguments = options.arguments ?? null;
187
+ this.result = options.result ?? null;
188
+ this.isFinal = options.isFinal ?? true;
189
+ }
190
+ /**
191
+ * Render the call phase as a job update payload.
192
+ *
193
+ * @returns The payload.
194
+ */
195
+ toPayload() {
196
+ return {
197
+ type: TOOL_CALL_UPDATE_TYPE,
198
+ phase: this.phase,
199
+ function_name: this.functionName,
200
+ tool_call_id: this.toolCallId,
201
+ arguments: this.arguments !== null ? { ...this.arguments } : null,
202
+ result: this.result,
203
+ is_final: this.isFinal,
204
+ };
205
+ }
206
+ /**
207
+ * Rebuild a call phase from a job update payload.
208
+ *
209
+ * @param payload The update payload.
210
+ * @returns The call phase.
211
+ */
212
+ static fromPayload(payload) {
213
+ return new BackendToolCall({
214
+ phase: (payload.phase || "in_progress"),
215
+ functionName: String(payload.function_name || ""),
216
+ toolCallId: String(payload.tool_call_id || ""),
217
+ arguments: payload.arguments ?? null,
218
+ result: payload.result ?? null,
219
+ isFinal: Boolean("is_final" in payload ? payload.is_final : true),
220
+ });
221
+ }
222
+ /**
223
+ * Build the frame that reports this phase in the frontend's pipeline.
224
+ *
225
+ * @returns The frame for the phase.
226
+ */
227
+ toFrame() {
228
+ if (this.phase === "started") {
229
+ // The frame can announce several calls at once, as the pipeline's own
230
+ // FunctionCallsStartedFrame does; a backend reports its calls one at a
231
+ // time, so here it carries one.
232
+ return new ExternalFunctionCallsStartedFrame({
233
+ functionCalls: [
234
+ new ExternalFunctionCall({
235
+ functionName: this.functionName,
236
+ toolCallId: this.toolCallId,
237
+ }),
238
+ ],
239
+ });
240
+ }
241
+ if (this.phase === "in_progress") {
242
+ return new ExternalFunctionCallInProgressFrame({
243
+ functionName: this.functionName,
244
+ toolCallId: this.toolCallId,
245
+ arguments: this.arguments,
246
+ });
247
+ }
248
+ if (this.phase === "result") {
249
+ return new ExternalFunctionCallResultFrame({
250
+ functionName: this.functionName,
251
+ toolCallId: this.toolCallId,
252
+ arguments: this.arguments,
253
+ result: this.result,
254
+ isFinal: this.isFinal,
255
+ });
256
+ }
257
+ return new ExternalFunctionCallCancelFrame({
258
+ functionName: this.functionName,
259
+ toolCallId: this.toolCallId,
260
+ });
261
+ }
262
+ }
263
+ /** The backend has nothing left to do: no run outstanding, no call in flight. */
264
+ export class BackendIdle {
265
+ }
266
+ /**
267
+ * The backend could not go on with what it was doing.
268
+ *
269
+ * - `error`: what went wrong.
270
+ */
271
+ export class BackendError {
272
+ error;
273
+ constructor(options) {
274
+ this.error = options.error;
275
+ }
276
+ }
277
+ /** What `_renderTranscriptRequest` tells the backend to do with a transcript. */
278
+ export const TRANSCRIPT_REQUEST_INSTRUCTION = "Act on the user's most recent request in the conversation above. If it asks for " +
279
+ "something, tell the user the result as soon as you have it, before going on with other " +
280
+ `work, in a message that begins with ${SPOKEN_MARK}. If it only stops or changes work ` +
281
+ "already under way, tell them nothing: the assistant already has.";
282
+ /**
283
+ * Appended to a request the frontend worded itself, so the backend tells the
284
+ * user its result on its own rather than with whatever else it is doing.
285
+ */
286
+ export const EXPLICIT_REQUEST_INSTRUCTION = "If this request asks for something, tell the user the result as soon as you have it, " +
287
+ `before going on with other work, in a message that begins with ${SPOKEN_MARK}. If it ` +
288
+ "only stops or changes work already under way, tell them nothing: the assistant already " +
289
+ "has.";
290
+ /**
291
+ * Render a request the frontend worded itself, with what the backend should do
292
+ * with it after.
293
+ *
294
+ * @param request The request, as the frontend's model worded it.
295
+ * @param options.instruction What the backend should do with the request,
296
+ * placed after it.
297
+ * @returns The rendered request.
298
+ */
299
+ export function _renderExplicitRequest(request, options = {}) {
300
+ const instruction = options.instruction ?? EXPLICIT_REQUEST_INSTRUCTION;
301
+ return `${request}\n\n${instruction}`;
302
+ }
303
+ /** Strip like Python's `str.strip()` (whitespace at both ends). */
304
+ function strip(text) {
305
+ return text.trim();
306
+ }
307
+ /**
308
+ * Split a message the model wrote into its note and its spoken part, by the
309
+ * mark.
310
+ *
311
+ * @param text The message, stripped.
312
+ * @returns The note (what precedes the first line that begins with the mark,
313
+ * or the whole message when no line does) and the spoken part (what follows
314
+ * the mark, with any later mark removed, or empty), each stripped.
315
+ */
316
+ export function _splitSpoken(text) {
317
+ const match = new RegExp(SPOKEN_MARK_LINE_SOURCE, "m").exec(text);
318
+ if (match === null) {
319
+ return [text, ""];
320
+ }
321
+ // From the first marked line on, the whole of it is spoken; a mark the model
322
+ // put on a later line is not meant for the user's ear.
323
+ const end = match.index + match[0].length;
324
+ const spoken = text.slice(end).replace(new RegExp(SPOKEN_MARK_LINE_SOURCE, "gm"), "");
325
+ return [strip(text.slice(0, match.index)), strip(spoken)];
326
+ }
327
+ /** Python-style `repr()` of a role, for log lines. */
328
+ function reprRole(role) {
329
+ if (role === undefined || role === null)
330
+ return "None";
331
+ if (typeof role === "string")
332
+ return `'${role}'`;
333
+ return String(role);
334
+ }
335
+ /**
336
+ * Render a conversation as a labelled transcript for the backend to act on.
337
+ *
338
+ * Flattening the conversation into one message keeps the two conversations
339
+ * apart: the backend's context holds only what the backend itself said as
340
+ * assistant messages, so it never mistakes the frontend's speech for its own.
341
+ *
342
+ * Only user and assistant text crosses today: tool calls, tool results,
343
+ * images, other non-text content and messages in a service's own format are
344
+ * skipped, and say so at debug level. Carrying more is a question of how to
345
+ * render it.
346
+ *
347
+ * @param conversation The conversation the user is having with the frontend,
348
+ * as standard context messages.
349
+ * @param options.instruction What the backend should do with the transcript,
350
+ * placed after it.
351
+ * @param options.first Whether this is the backend's first request in the
352
+ * conversation. It decides how the transcript is introduced: as the
353
+ * conversation so far, or as what has been said since the previous
354
+ * delegation. Defaults to true.
355
+ * @returns The rendered request.
356
+ */
357
+ export function _renderTranscriptRequest(conversation, options = {}) {
358
+ const instruction = options.instruction ?? TRANSCRIPT_REQUEST_INSTRUCTION;
359
+ const first = options.first ?? true;
360
+ const lines = [];
361
+ if (conversation.length > 0) {
362
+ lines.push(first ? "Voice conversation so far:" : "Voice conversation since the previous delegation:");
363
+ for (const message of conversation) {
364
+ if (message instanceof LLMSpecificMessage) {
365
+ logger.debug(`Skipping delegated message in ${message.llm} format`);
366
+ continue;
367
+ }
368
+ const role = message.role;
369
+ const text = standardMessageText(message);
370
+ if ((role === "user" || role === "assistant") && text) {
371
+ lines.push(`${String(role).toUpperCase()}: ${text}`);
372
+ }
373
+ else {
374
+ logger.debug(`Skipping delegated message with no transcript form: role=${reprRole(role)}`);
375
+ }
376
+ }
377
+ lines.push("");
378
+ }
379
+ lines.push(instruction);
380
+ return lines.join("\n");
381
+ }
382
+ /** The frontend attached to the worker, for as long as its `attach` job lives. */
383
+ class _AttachedFrontend {
384
+ jobId;
385
+ detached = new Event();
386
+ constructor(options) {
387
+ this.jobId = options.jobId;
388
+ if (options.detached)
389
+ this.detached = options.detached;
390
+ }
391
+ }
392
+ /**
393
+ * A worker that runs an LLM service as the backend a frontend delegates to.
394
+ *
395
+ * The worker owns the backend's conversation: an `LLMContext` plus the
396
+ * aggregator pair, so multi-step tool calling works as it does in any
397
+ * pipeline. A frontend attaches once, for as long as it lives, and from then
398
+ * on hears everything the backend produces; requests it sends are appended to
399
+ * that conversation as user messages and run the model. `_BackendSession` is
400
+ * the caller side.
401
+ *
402
+ * The worker's LLM does the reasoning its frontend hands off, so it runs at
403
+ * its provider's default thinking unless its settings configure thinking (see
404
+ * `LLMSettings.useProviderThinkingDefault`).
405
+ *
406
+ * Job contract:
407
+ *
408
+ * - `attach`: no payload. Its first update is
409
+ * `{"type": "attached", "capabilities": {...}}`; after that, a
410
+ * `BackendOutput` payload for each of the model's turns and reasoning
411
+ * summaries and each output the app sends with `sendOutput`, a
412
+ * `BackendToolCall` payload for each phase of each function call the
413
+ * backend makes, `{"type": "idle"}` when the backend has nothing left to
414
+ * do, and `{"type": "error", "error": ...}` when it could not go on. The job
415
+ * lives until the requester cancels it, which stops the backend's work.
416
+ * Refused while another frontend is attached.
417
+ * - `message`: `{"request": str}`, appended as a user message; responds at
418
+ * once with `{"backend": "idle" | "working"}`, what the backend was doing
419
+ * when the message arrived. A message that arrives mid-run is taken up
420
+ * after the current step, with everything that came before it in view.
421
+ *
422
+ * The model decides what the user hears: a message it begins with
423
+ * `SPOKEN_MARK` asks to be spoken, whether or not the turn also calls tools,
424
+ * and the mark is stripped; anything else it writes, reasoning summaries
425
+ * included, is a note that the frontend keeps but does not speak. A mark at
426
+ * the start of a later line splits the message into a note and a spoken part,
427
+ * since the model sometimes writes it that way. The backend's own system
428
+ * instruction says what it does and, in plain words, what the user should be
429
+ * told; the pairing instruction the worker appends
430
+ * (`BACKEND_PAIRING_INSTRUCTION`, or the `pairingInstruction` given) is the
431
+ * only place the mark is named. `transformOutput` can change the flag, or the
432
+ * text, or drop the output.
433
+ *
434
+ * @example
435
+ * const backend = new BackendLLMWorker({
436
+ * llm: new AnthropicLLMService({ apiKey: ... }),
437
+ * context: new LLMContext({
438
+ * messages: [{ role: "system", content: BACKEND_INSTRUCTIONS }],
439
+ * tools: [getWeather],
440
+ * }),
441
+ * });
442
+ */
443
+ export class BackendLLMWorker extends LLMContextWorker {
444
+ _attached = null;
445
+ _transformOutput;
446
+ // Whether the backend is working is read off its pipeline: requests queued
447
+ // but not yet taken up, LLM runs picked up but not yet ended, function calls
448
+ // in flight, and context frames queued for the LLM. A request takes one or
449
+ // more runs: the first for the request itself, then one per round of tool
450
+ // results (the assistant aggregator pushes an LLMContextFrame back to the
451
+ // LLM after each). Runs are counted as the LLM picks them up and as their
452
+ // turns end; an interruption cancels whatever was outstanding.
453
+ _requestsPending = 0;
454
+ _runsRequested = 0;
455
+ _runsCompleted = 0;
456
+ // Whether the idle update has been sent for the current lull, so a call
457
+ // that settles after the last turn stopped does not send it twice.
458
+ _idleAnnounced = false;
459
+ // The index in the context of a request appended while the model was busy,
460
+ // as an LLMMessagesAppendFrame with runLlm=false: the run that follows the
461
+ // current step takes it up, and the model sees the request alongside
462
+ // whatever that step produced. Running it separately would run the model
463
+ // twice on the same context, the second time with nothing new to react to.
464
+ // Cleared once an LLMContextFrame that holds the request reaches the LLM.
465
+ _requestAwaitingRun = null;
466
+ /**
467
+ * Initialize the backend worker.
468
+ *
469
+ * @throws Error If the LLM service declines the role.
470
+ */
471
+ constructor(options) {
472
+ const llm = options.llm;
473
+ const objection = llm.llmWithBackendRoleObjection("backend");
474
+ if (objection) {
475
+ throw new Error(objection);
476
+ }
477
+ llm.settings.applyUpdate(new LLMSettings({ useProviderThinkingDefault: true }));
478
+ let userParams = options.userParams ?? null;
479
+ if (userParams === null) {
480
+ userParams = new LLMUserAggregatorParams({
481
+ userTurnStrategies: new ExternalUserTurnStrategies(),
482
+ });
483
+ }
484
+ super({
485
+ name: options.name ?? null,
486
+ llm,
487
+ active: true,
488
+ context: options.context ?? null,
489
+ userParams,
490
+ assistantParams: options.assistantParams ?? null,
491
+ });
492
+ this._transformOutput = options.transformOutput ?? null;
493
+ this.llm.appendSystemInstruction(options.pairingInstruction ?? BACKEND_PAIRING_INSTRUCTION);
494
+ this.llm.addEventHandler("on_before_process_frame", async (_llm, frame) => {
495
+ if (frame instanceof LLMContextFrame) {
496
+ this._runsRequested += 1;
497
+ if (this._requestAwaitingRun !== null &&
498
+ frame.context.messages.length > this._requestAwaitingRun) {
499
+ this._requestAwaitingRun = null;
500
+ }
501
+ }
502
+ else if (frame instanceof InterruptionFrame) {
503
+ // The run in progress, if any, is cancelled by this frame and ends as
504
+ // an interrupted turn, which is not counted as completed; a run queued
505
+ // behind it is dropped. A run that starts after the frame has passed
506
+ // is a new one, so the counters are reset here, as the LLM sees the
507
+ // interruption, and not later at the assistant aggregator, where a
508
+ // reset could erase a run that had just started.
509
+ this._runsRequested = 0;
510
+ this._runsCompleted = 0;
511
+ }
512
+ });
513
+ this.userAggregator.addEventHandler("on_before_process_frame", async (_aggregator, frame) => {
514
+ if (frame instanceof LLMMessagesAppendFrame && this._requestsPending) {
515
+ this._requestsPending -= 1;
516
+ if (!frame.runLlm) {
517
+ this._requestAwaitingRun = this.context.messages.length;
518
+ // The synchronous call the request was held for may have settled
519
+ // by the time the request lands in the context, as when the model
520
+ // cancelled it with its cancel_<tool> tool meanwhile, so check
521
+ // again whether the request can run now.
522
+ await this._runAwaitingRequest();
523
+ }
524
+ }
525
+ });
526
+ // The backend's function calls are relayed as they pass the assistant
527
+ // aggregator, which every phase of a call reaches.
528
+ this.assistantAggregator.addEventHandler("on_before_process_frame", async (_aggregator, frame) => {
529
+ await this._onFunctionCallFrame(frame);
530
+ });
531
+ this.assistantAggregator.addEventHandler("on_after_process_frame", async (_aggregator, frame) => {
532
+ // A FunctionCallResultFrame with runLlm=false may have settled the last
533
+ // synchronous call a waiting request was held for, so the request runs
534
+ // now; one with runLlm=true brings the run itself.
535
+ if ((frame instanceof FunctionCallResultFrame &&
536
+ frame.properties !== null &&
537
+ frame.properties.runLlm === false) ||
538
+ frame instanceof FunctionCallCancelFrame) {
539
+ await this._runAwaitingRequest();
540
+ // A call that settles (its result or cancellation arrives) after the
541
+ // last turn stopped may have been the last thing the backend had in
542
+ // flight, so the idle update is sent from here as well as from the
543
+ // end of a turn.
544
+ await this._announceIdleIfDone();
545
+ }
546
+ });
547
+ this.assistantAggregator.addEventHandler("on_assistant_turn_stopped", async (_aggregator, message) => {
548
+ await this._onAssistantTurnStopped(message);
549
+ });
550
+ this.assistantAggregator.addEventHandler("on_assistant_thought", async (_aggregator, message) => {
551
+ await this._onAssistantThought(message);
552
+ });
553
+ this.addEventHandler("on_pipeline_error", async (_worker, frame) => {
554
+ await this._onPipelineError(frame);
555
+ });
556
+ }
557
+ /** Whether the backend has work in hand: a request or run outstanding, or a call in flight. */
558
+ get working() {
559
+ return (this._requestsPending > 0 ||
560
+ this._modelBusy ||
561
+ this.assistantAggregator.hasFunctionCallsInProgress);
562
+ }
563
+ /** Whether a run is in progress or queued for the model. */
564
+ get _modelBusy() {
565
+ return this._runsRequested > this._runsCompleted || this.llm.hasQueuedFrame(LLMContextFrame);
566
+ }
567
+ /**
568
+ * What an attached frontend can count on; all of it, for a backend that is
569
+ * an LLM.
570
+ *
571
+ * DIVERGENCE: `capabilities` upstream (see the module notes).
572
+ */
573
+ get backendCapabilities() {
574
+ return { steering: true, progress: true };
575
+ }
576
+ // -----------------------------------------------------------------------
577
+ // The attached contract
578
+ // -----------------------------------------------------------------------
579
+ /**
580
+ * Attach a frontend: stream everything the backend produces until the job
581
+ * is cancelled.
582
+ *
583
+ * @param message The job request.
584
+ */
585
+ async attachFrontend(message) {
586
+ if (this._attached !== null) {
587
+ await this._refuse(message.jobId, "a frontend is already attached");
588
+ return;
589
+ }
590
+ const attached = new _AttachedFrontend({ jobId: message.jobId });
591
+ this._attached = attached;
592
+ await this.sendJobUpdate(message.jobId, {
593
+ type: ATTACHED_UPDATE_TYPE,
594
+ capabilities: this.backendCapabilities,
595
+ });
596
+ try {
597
+ // The attach job ends only when the frontend cancels it, which is how
598
+ // the frontend detaches.
599
+ await attached.detached.wait();
600
+ }
601
+ finally {
602
+ if (this._attached === attached) {
603
+ this._attached = null;
604
+ }
605
+ }
606
+ }
607
+ /**
608
+ * Put a message to the backend, and say what it was doing when it arrived.
609
+ *
610
+ * @param message The job request; see the class docs for the payload.
611
+ */
612
+ async handleMessage(message) {
613
+ if (this._attached === null) {
614
+ await this._refuse(message.jobId, "no frontend is attached; open an attach job first");
615
+ return;
616
+ }
617
+ const request = String(message.payload?.request || "").trim();
618
+ if (!request) {
619
+ await this._refuse(message.jobId, "the message has no request");
620
+ return;
621
+ }
622
+ const status = this.working ? "working" : "idle";
623
+ await this._queueRequest(request);
624
+ await this.sendJobResponse(message.jobId, { backend: status });
625
+ }
626
+ static {
627
+ job({ name: ATTACH_JOB_NAME })(BackendLLMWorker.prototype.attachFrontend);
628
+ job({ name: MESSAGE_JOB_NAME })(BackendLLMWorker.prototype.handleMessage);
629
+ }
630
+ async _refuse(jobId, reason) {
631
+ logger.warning(`Worker '${this.name}': refusing job ${jobId}: ${reason}`);
632
+ await this.sendJobResponse(jobId, { error: reason }, { status: JobStatus.ERROR });
633
+ }
634
+ /**
635
+ * Append a request to the backend's conversation, and run the model on it
636
+ * if it is idle.
637
+ *
638
+ * While a run is in progress or queued, or a synchronous call is in flight,
639
+ * the request asks for no run of its own: the run that follows the current
640
+ * step takes it up (see `_requestAwaitingRun`). A run while a synchronous
641
+ * call is in flight would see the call without its result, which the
642
+ * adapter cannot send as it is.
643
+ */
644
+ async _queueRequest(request) {
645
+ this._requestsPending += 1;
646
+ this._idleAnnounced = false;
647
+ const runLlm = !this._modelBusy && !this._synchronousCallInFlight;
648
+ if (runLlm) {
649
+ logger.debug(`Worker '${this.name}': running the model on the request`);
650
+ }
651
+ else {
652
+ logger.debug(`Worker '${this.name}': holding the request for the current step ` +
653
+ `(model busy=${pyBool(this._modelBusy)}, synchronous call in flight=${pyBool(this._synchronousCallInFlight)})`);
654
+ }
655
+ const frame = new LLMMessagesAppendFrame({
656
+ messages: [{ role: "user", content: request }],
657
+ runLlm,
658
+ });
659
+ await this.queueFrame(frame);
660
+ }
661
+ /** Whether a synchronous call is in flight, whose result will bring the next run. */
662
+ get _synchronousCallInFlight() {
663
+ return this.assistantAggregator.hasSynchronousFunctionCallsInProgress;
664
+ }
665
+ /** Run the model on a request that was waiting for the current step, if nothing else will. */
666
+ async _runAwaitingRequest() {
667
+ if (this._requestAwaitingRun !== null && !this._modelBusy && !this._synchronousCallInFlight) {
668
+ logger.debug(`Worker '${this.name}': running the model on the request that was held`);
669
+ await this.queueFrame(new LLMRunFrame());
670
+ }
671
+ }
672
+ /**
673
+ * Interrupt the pipeline and cancel every function call in flight.
674
+ *
675
+ * An interruption stops the run in progress and the calls that opt into
676
+ * cancellation on interruption; the async ones, which an interruption
677
+ * leaves running by design, are cancelled on their own. A request not yet
678
+ * taken up is dropped with the interruption, and one held for the current
679
+ * step is let go of, so that neither runs the model on work the frontend
680
+ * has abandoned: the cancellations that follow would otherwise run a held
681
+ * request as they settle.
682
+ */
683
+ async _stopWork(reason) {
684
+ this._requestsPending = 0;
685
+ this._requestAwaitingRun = null;
686
+ await this.queueFrame(new InterruptionFrame());
687
+ await this.llm.cancelFunctionCalls({ reason });
688
+ }
689
+ // -----------------------------------------------------------------------
690
+ // Outputs
691
+ // -----------------------------------------------------------------------
692
+ /**
693
+ * Send one output of the app's own to the frontend.
694
+ *
695
+ * It reaches the frontend as the model's outputs do, but as given unless
696
+ * asked: `transformOutput` is not applied to it by default. With no
697
+ * frontend attached there is nowhere for it to go, and it is dropped with a
698
+ * warning.
699
+ *
700
+ * @param output The output.
701
+ * @param options.applyTransformOutput Whether to run the output through
702
+ * `transformOutput` as the model's outputs are. Defaults to false.
703
+ */
704
+ async sendOutput(output, options = {}) {
705
+ if (this._attached === null) {
706
+ logger.warning(`Worker '${this.name}': no frontend to send output to`);
707
+ return;
708
+ }
709
+ // Not passed through transformOutput by default (applyTransformOutput:
710
+ // false), so an app can silence the model's outputs with a
711
+ // transformOutput that drops them and still send outputs of its own, such
712
+ // as from a tool the model calls to talk to the user.
713
+ await this._emit(output, { applyTransformOutput: options.applyTransformOutput ?? false });
714
+ }
715
+ async _onAssistantTurnStopped(message) {
716
+ if (message.interrupted) {
717
+ // Text from an interrupted turn is not sent, and the run counters were
718
+ // reset as the LLM saw the InterruptionFrame.
719
+ return;
720
+ }
721
+ this._runsCompleted += 1;
722
+ const text = (message.content || "").trim();
723
+ if (this._attached === null) {
724
+ return;
725
+ }
726
+ try {
727
+ const [note, spoken] = _splitSpoken(text);
728
+ if (note) {
729
+ await this._emit(new BackendOutput({ text: note, prefersSpoken: false }));
730
+ }
731
+ if (spoken) {
732
+ await this._emit(new BackendOutput({ text: spoken, prefersSpoken: true }));
733
+ }
734
+ }
735
+ catch (e) {
736
+ rethrowIfCancelled(e);
737
+ const error = e instanceof Error ? e.message : String(e);
738
+ logger.error(`Worker '${this.name}': transform_output failed: ${error}`);
739
+ await this._sendUpdate({ type: ERROR_UPDATE_TYPE, error });
740
+ }
741
+ await this._runAwaitingRequest();
742
+ await this._announceIdleIfDone();
743
+ }
744
+ /** Send the idle update once the backend has nothing left to do, once per lull. */
745
+ async _announceIdleIfDone() {
746
+ if (this._attached === null || this.working || this._idleAnnounced) {
747
+ return;
748
+ }
749
+ this._idleAnnounced = true;
750
+ await this._sendUpdate({ type: IDLE_UPDATE_TYPE });
751
+ }
752
+ /** Relay a phase of one of the backend's own function calls as a job update. */
753
+ async _onFunctionCallFrame(frame) {
754
+ let calls = [];
755
+ if (frame instanceof FunctionCallsStartedFrame) {
756
+ calls = frame.functionCalls.map((call) => new BackendToolCall({
757
+ phase: "started",
758
+ functionName: call.functionName,
759
+ toolCallId: call.toolCallId,
760
+ }));
761
+ }
762
+ else if (frame instanceof FunctionCallInProgressFrame) {
763
+ calls = [
764
+ new BackendToolCall({
765
+ phase: "in_progress",
766
+ functionName: frame.functionName,
767
+ toolCallId: frame.toolCallId,
768
+ arguments: frame.arguments,
769
+ }),
770
+ ];
771
+ }
772
+ else if (frame instanceof FunctionCallResultFrame) {
773
+ calls = [
774
+ new BackendToolCall({
775
+ phase: "result",
776
+ functionName: frame.functionName,
777
+ toolCallId: frame.toolCallId,
778
+ arguments: frame.arguments,
779
+ result: frame.result,
780
+ isFinal: frame.properties === null || frame.properties.isFinal,
781
+ }),
782
+ ];
783
+ }
784
+ else if (frame instanceof FunctionCallCancelFrame) {
785
+ calls = [
786
+ new BackendToolCall({
787
+ phase: "cancelled",
788
+ functionName: frame.functionName,
789
+ toolCallId: frame.toolCallId,
790
+ }),
791
+ ];
792
+ }
793
+ for (const call of calls) {
794
+ await this._sendUpdate(call.toPayload());
795
+ }
796
+ }
797
+ /**
798
+ * Stop the model: the frontend that was attached is gone.
799
+ *
800
+ * The `attach` job's handler is already cancelled by the time this runs,
801
+ * and has let go of the attachment; the work in flight stops too, so none
802
+ * of it is reported after the frontend has let go of it.
803
+ */
804
+ async onJobCancelled(message) {
805
+ const request = this.activeJobs.get(message.jobId);
806
+ if (request !== undefined && request.jobName === ATTACH_JOB_NAME) {
807
+ await this._stopWork("frontend detached");
808
+ }
809
+ }
810
+ /**
811
+ * Tell the frontend the backend cannot go on with what it was doing.
812
+ *
813
+ * A tool handler that raises is the exception. The LLM service reports that
814
+ * as `ErrorCategory.APPLICATION`, settles the call with an error result and
815
+ * carries on, so the model still has something coming.
816
+ */
817
+ async _onPipelineError(frame) {
818
+ if (frame.category === ErrorCategory.APPLICATION) {
819
+ return;
820
+ }
821
+ if (this._attached !== null) {
822
+ await this._sendUpdate({ type: ERROR_UPDATE_TYPE, error: frame.error });
823
+ }
824
+ }
825
+ async _onAssistantThought(message) {
826
+ const text = (message.content || "").trim();
827
+ if (text && this._attached !== null) {
828
+ await this._emit(new BackendOutput({ text, isThought: true, prefersSpoken: false }));
829
+ }
830
+ }
831
+ /** Run an output through `transformOutput`, if there is one. */
832
+ async _shape(output) {
833
+ if (this._transformOutput === null) {
834
+ return output;
835
+ }
836
+ return await this._transformOutput(output);
837
+ }
838
+ /** Send one output as a job update, after any configured transform. */
839
+ async _emit(output, options = {}) {
840
+ const applyTransformOutput = options.applyTransformOutput ?? true;
841
+ const shaped = applyTransformOutput ? await this._shape(output) : output;
842
+ if (shaped !== null) {
843
+ await this._sendUpdate(shaped.toPayload());
844
+ }
845
+ }
846
+ /** Send a job update to the attached frontend. */
847
+ async _sendUpdate(payload) {
848
+ if (this._attached !== null) {
849
+ await this.sendJobUpdate(this._attached.jobId, payload);
850
+ }
851
+ }
852
+ }
853
+ /** Python-style bool for log lines. */
854
+ function pyBool(value) {
855
+ return value ? "True" : "False";
856
+ }
857
+ /** Turn an `attach` stream update into the event it carries, if it carries one. */
858
+ export function _eventFromPayload(payload) {
859
+ const updateType = "type" in payload ? payload.type : OUTPUT_UPDATE_TYPE;
860
+ if (updateType === OUTPUT_UPDATE_TYPE) {
861
+ return BackendOutput.fromPayload(payload);
862
+ }
863
+ if (updateType === TOOL_CALL_UPDATE_TYPE) {
864
+ return BackendToolCall.fromPayload(payload);
865
+ }
866
+ if (updateType === IDLE_UPDATE_TYPE) {
867
+ return new BackendIdle();
868
+ }
869
+ if (updateType === ERROR_UPDATE_TYPE) {
870
+ return new BackendError({ error: String(payload.error || "") });
871
+ }
872
+ return null;
873
+ }
874
+ /**
875
+ * A frontend's side of the attached contract: one worker, attached for the
876
+ * session's life.
877
+ *
878
+ * Opening the session sends the `attach` job; closing it cancels the job,
879
+ * which stops the backend's work. In between, the session is an async
880
+ * iterator over what the backend produces, and `send` puts messages to it.
881
+ * The iteration ends when the backend goes away, with a `JobError` if it
882
+ * failed.
883
+ *
884
+ * @example
885
+ * const session = new _BackendSession(worker, "backend");
886
+ * await session.run(async (session) => {
887
+ * await session.send(request);
888
+ * for await (const event of session) {
889
+ * ...
890
+ * }
891
+ * });
892
+ */
893
+ export class _BackendSession {
894
+ _worker;
895
+ _backendName;
896
+ _timeoutSecs;
897
+ _group = null;
898
+ _capabilities = {};
899
+ /**
900
+ * Whether the stream ended because this side let go of the backend (the
901
+ * session was closed, or the attaching worker stopped), as opposed to the
902
+ * backend going away.
903
+ */
904
+ detached = false;
905
+ /**
906
+ * Initialize the session.
907
+ *
908
+ * @param worker The worker attaching (for a pipeline processor,
909
+ * `this.pipelineWorker`).
910
+ * @param backendName Name of the backend worker.
911
+ * @param options.timeoutSecs How long a message may take to be
912
+ * acknowledged, including the wait for the backend to become ready. The
913
+ * attachment itself has no timeout. Defaults to 30.
914
+ */
915
+ constructor(worker, backendName, options = {}) {
916
+ this._worker = worker;
917
+ this._backendName = backendName;
918
+ this._timeoutSecs = options.timeoutSecs === undefined ? 30 : options.timeoutSecs;
919
+ }
920
+ /** Name of the backend worker. */
921
+ get backendName() {
922
+ return this._backendName;
923
+ }
924
+ /** What the backend said it can do, once its stream has opened. */
925
+ get capabilities() {
926
+ return this._capabilities;
927
+ }
928
+ /** Open the session (`__aenter__`): send the `attach` job. */
929
+ async enter() {
930
+ this._group = await this._worker.createJobGroupAndRequestJob([this._backendName], {
931
+ params: new JobGroupParams({ name: ATTACH_JOB_NAME }),
932
+ });
933
+ this._group.eventQueue = new Queue();
934
+ return this;
935
+ }
936
+ /** Leave the session (`__aexit__`): close it. */
937
+ async exit(_error) {
938
+ await this.close();
939
+ }
940
+ /**
941
+ * Run `fn` inside the session, as `async with` does: open, run the block,
942
+ * then close (whether or not the block threw).
943
+ */
944
+ async run(fn) {
945
+ await this.enter();
946
+ try {
947
+ return await fn(this);
948
+ }
949
+ finally {
950
+ await this.exit();
951
+ }
952
+ }
953
+ /** Detach: cancel the `attach` job, which stops the backend's work. */
954
+ async close() {
955
+ if (this._group && this._worker.jobGroups.has(this._group.jobId)) {
956
+ const jobId = this._group.jobId;
957
+ await shield(() => this._worker.cancelJobGroup(jobId, { reason: "frontend detached" }));
958
+ }
959
+ }
960
+ [Symbol.asyncIterator]() {
961
+ return this;
962
+ }
963
+ /** The next event from the backend (`__anext__`). */
964
+ async next() {
965
+ if (this._group === null || this._group.eventQueue === null) {
966
+ throw new Error("not open");
967
+ }
968
+ const group = this._group;
969
+ const queue = group.eventQueue;
970
+ while (true) {
971
+ const event = await queue.get();
972
+ if (event === null) {
973
+ try {
974
+ await group.wait();
975
+ }
976
+ catch (e) {
977
+ if (e instanceof JobGroupError) {
978
+ const response = group.responses[this._backendName];
979
+ if (response === undefined) {
980
+ // Cancelled from this side: the session was closed, or the
981
+ // requester's worker stopped.
982
+ this.detached = true;
983
+ return { done: true, value: undefined };
984
+ }
985
+ throw new JobError(_withReason(e.message, response), { cause: e });
986
+ }
987
+ throw e;
988
+ }
989
+ return { done: true, value: undefined };
990
+ }
991
+ if (event.type !== JobEvent.UPDATE || !event.data || Object.keys(event.data).length === 0) {
992
+ continue;
993
+ }
994
+ if (event.data.type === ATTACHED_UPDATE_TYPE) {
995
+ this._capabilities = { ...(event.data.capabilities || {}) };
996
+ continue;
997
+ }
998
+ const parsed = _eventFromPayload(event.data);
999
+ if (parsed !== null) {
1000
+ return { done: false, value: parsed };
1001
+ }
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Put a message to the backend.
1006
+ *
1007
+ * @param request The text to append to the backend's conversation.
1008
+ * @returns What the backend was doing when the message arrived: `"idle"` or
1009
+ * `"working"`.
1010
+ * @throws JobError If the backend refused the message or did not answer in
1011
+ * time.
1012
+ */
1013
+ async send(request) {
1014
+ const params = new JobParams({
1015
+ name: MESSAGE_JOB_NAME,
1016
+ payload: { request },
1017
+ timeout: this._timeoutSecs,
1018
+ });
1019
+ const response = await this._ask(params);
1020
+ return String(response.backend || "idle");
1021
+ }
1022
+ /**
1023
+ * Run one short job on the backend and return its response.
1024
+ *
1025
+ * @throws JobError If the backend refused, naming its reason, or did not
1026
+ * answer in time.
1027
+ */
1028
+ async _ask(params) {
1029
+ const shortJob = this._worker.job(this._backendName, { params });
1030
+ try {
1031
+ await shortJob.run(async () => { });
1032
+ }
1033
+ catch (e) {
1034
+ if (e instanceof JobError) {
1035
+ // The job may have failed before it started (no response to read).
1036
+ let response = null;
1037
+ try {
1038
+ response = shortJob.response;
1039
+ }
1040
+ catch {
1041
+ response = null;
1042
+ }
1043
+ throw new JobError(_withReason(e.message, response), { cause: e });
1044
+ }
1045
+ throw e;
1046
+ }
1047
+ return shortJob.response;
1048
+ }
1049
+ }
1050
+ /** The job error with the backend's own reason for it, when its response gave one. */
1051
+ export function _withReason(error, response) {
1052
+ const reason = response?.error;
1053
+ return reason ? `${error}: ${reason}` : error;
1054
+ }
1055
+ //# sourceMappingURL=backend_llm_worker.js.map