@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,1662 @@
1
+ // Copyright (c) 2024-2026, Daily
2
+ // Copyright (c) 2026, pipedog contributors
3
+ // SPDX-License-Identifier: BSD-2-Clause
4
+ //
5
+ // Ported from pipecat: src/pipecat/utils/tracing/service_decorators.py
6
+ /**
7
+ * Service-specific OpenTelemetry tracing decorators for Pipecat.
8
+ *
9
+ * This module provides specialized decorators that automatically capture
10
+ * rich information about service execution including configuration,
11
+ * parameters, and performance metrics.
12
+ *
13
+ * Porting notes:
14
+ * - Upstream decides at import time whether tracing is available and returns
15
+ * a no-op decorator otherwise. Here OpenTelemetry is imported asynchronously
16
+ * or registered at runtime (see `setup.ts`), possibly after service classes are defined,
17
+ * so the wrappers check `isTracingAvailable()` when they run instead. With
18
+ * tracing unavailable they call straight through, as upstream's no-op does.
19
+ * - Every decorator works three ways:
20
+ * - as a standard (TC39) method decorator: `@tracedTts async runTts(...)`;
21
+ * class-level patches are installed per instance via `addInitializer`;
22
+ * - as a legacy (`experimentalDecorators`) method decorator;
23
+ * - imperatively, for runtimes that strip types and do not run decorator
24
+ * syntax (Node): `static { installTracedTts(this.prototype, "runTts"); }`.
25
+ * This is the equivalent of upstream's `__set_name__` hook.
26
+ * - `start_as_current_span` → the span is started with the parent context and
27
+ * made current with `context.with()` when the registered API provides it.
28
+ * - Gemini Live messages are read with the camelCase field names of the
29
+ * Gemini Live JSON protocol (`toolCall.functionCalls`, `usageMetadata.promptTokenCount`);
30
+ * OpenAI Realtime events keep their snake_case wire names.
31
+ * - JSON serialization (`jsonDumps`) matches Python's `json.dumps` defaults:
32
+ * `", "`/`": "` separators and ASCII-escaped output.
33
+ */
34
+ import { CancelledError, rethrowIfCancelled } from "@pipedog/asyncio";
35
+ import { MetricsFrame, TranscriptionFrame, TTSStoppedFrame, UserStoppedSpeakingFrame, VADUserStartedSpeakingFrame, } from "../../frames/frames.js";
36
+ import { LLMTokenUsage, TTFBMetricsData } from "../../metrics/metrics.js";
37
+ import { NOT_GIVEN } from "../../processors/aggregators/llm_context.js";
38
+ import { FrameDirection } from "../../processors/frame_processor.js";
39
+ import { logger } from "../logger.js";
40
+ import { isGiven } from "../types.js";
41
+ import { addGeminiLiveSpanAttributes, addLlmSpanAttributes, addOpenaiRealtimeSpanAttributes, addSttSpanAttributes, addTtsSpanAttributes, } from "./service_attributes.js";
42
+ import { getOpenTelemetry, isTracingAvailable, } from "./setup.js";
43
+ /** `SpanStatusCode.ERROR` from `@opentelemetry/api`. */
44
+ const SPAN_STATUS_ERROR = 2;
45
+ /** Seconds since the epoch → OpenTelemetry `HrTime` (`[seconds, nanoseconds]`). */
46
+ function toHrTime(seconds) {
47
+ const whole = Math.floor(seconds);
48
+ return [whole, Math.round((seconds - whole) * 1e9)];
49
+ }
50
+ function errorMessage(e) {
51
+ return e instanceof Error ? e.message : String(e);
52
+ }
53
+ function isDict(value) {
54
+ return (typeof value === "object" &&
55
+ value !== null &&
56
+ !Array.isArray(value) &&
57
+ (Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null));
58
+ }
59
+ function isObject(value) {
60
+ return typeof value === "object" && value !== null;
61
+ }
62
+ /**
63
+ * Python `json.dumps(value)` with default arguments: `JSON.stringify` output with
64
+ * `", "` and `": "` separators and non-ASCII characters escaped (`ensure_ascii`).
65
+ */
66
+ function jsonDumps(value) {
67
+ return jsonDumpsRaw(value).replace(/[\u0080-\uffff]/g, (c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`);
68
+ }
69
+ function jsonDumpsRaw(value) {
70
+ if (value === undefined)
71
+ return "null";
72
+ if (value === null || typeof value !== "object") {
73
+ return JSON.stringify(value) ?? "null";
74
+ }
75
+ const withToJson = value;
76
+ if (typeof withToJson.toJSON === "function") {
77
+ return jsonDumpsRaw(withToJson.toJSON());
78
+ }
79
+ if (Array.isArray(value)) {
80
+ return `[${value.map((v) => jsonDumpsRaw(v)).join(", ")}]`;
81
+ }
82
+ const entries = Object.entries(value)
83
+ .filter(([, v]) => v !== undefined && typeof v !== "function")
84
+ .map(([k, v]) => `${JSON.stringify(k)}: ${jsonDumpsRaw(v)}`);
85
+ return `{${entries.join(", ")}}`;
86
+ }
87
+ /** Python truthiness for values that may be `NOT_GIVEN` (falsy upstream). */
88
+ function truthy(value) {
89
+ return Boolean(value) && isGiven(value);
90
+ }
91
+ function getTracer() {
92
+ const otel = getOpenTelemetry();
93
+ if (!otel) {
94
+ throw new Error("OpenTelemetry is not registered");
95
+ }
96
+ return otel.api.trace.getTracer("pipecat");
97
+ }
98
+ /**
99
+ * Start a span as child of `parent`, make it current while `fn` runs, and end
100
+ * it afterwards. Exceptions are recorded on the span with an ERROR status, as
101
+ * `start_as_current_span` does upstream.
102
+ */
103
+ async function withCurrentSpan(name, parent, fn) {
104
+ const otel = getOpenTelemetry();
105
+ if (!otel) {
106
+ throw new Error("OpenTelemetry is not registered");
107
+ }
108
+ const api = otel.api;
109
+ const span = api.trace
110
+ .getTracer("pipecat")
111
+ .startSpan(name, {}, parent ?? undefined);
112
+ const contextApi = api.context;
113
+ try {
114
+ if (typeof contextApi.with === "function") {
115
+ const ctx = api.trace.setSpan(parent ?? api.context.active(), span);
116
+ return await contextApi.with(ctx, () => fn(span));
117
+ }
118
+ return await fn(span);
119
+ }
120
+ catch (e) {
121
+ span.recordException?.(e);
122
+ span.setStatus?.({
123
+ code: SPAN_STATUS_ERROR,
124
+ message: `${e instanceof Error ? e.constructor.name : typeof e}: ${errorMessage(e)}`,
125
+ });
126
+ throw e;
127
+ }
128
+ finally {
129
+ span.end();
130
+ }
131
+ }
132
+ /**
133
+ * Wrap `target[name]` with `makeWrapper(original)`, once: a method already
134
+ * wrapped (marked with `marker`, e.g. by a parent class) is left alone.
135
+ */
136
+ function wrapMethod(target, name, marker, makeWrapper) {
137
+ const original = target[name];
138
+ if (typeof original !== "function" || original[marker]) {
139
+ return;
140
+ }
141
+ const wrapped = makeWrapper(original);
142
+ wrapped[marker] = true;
143
+ target[name] = wrapped;
144
+ }
145
+ /**
146
+ * Build a method decorator usable as a TC39 decorator, a legacy
147
+ * (`experimentalDecorators`) decorator, or a plain function.
148
+ *
149
+ * @param wrap Builds the replacement method.
150
+ * @param install Class-level patches (upstream's `__set_name__` work), run on
151
+ * the prototype (legacy) or on each instance (TC39).
152
+ */
153
+ function makeMethodDecorator(wrap, install) {
154
+ return function decorator(value, context, descriptor) {
155
+ if (typeof context === "string" || typeof context === "symbol") {
156
+ // Legacy decorator: (prototype, key, descriptor).
157
+ install?.(value);
158
+ const desc = descriptor ?? Object.getOwnPropertyDescriptor(value, context);
159
+ if (desc && typeof desc.value === "function") {
160
+ desc.value = wrap(desc.value);
161
+ }
162
+ return desc;
163
+ }
164
+ if (isObject(context) && typeof context.addInitializer === "function") {
165
+ // TC39 decorator: (method, context).
166
+ if (install) {
167
+ context.addInitializer(function () {
168
+ install(this);
169
+ });
170
+ }
171
+ }
172
+ return wrap(value);
173
+ };
174
+ }
175
+ /**
176
+ * Get the model name from a service instance.
177
+ *
178
+ * This is a bit of a mess — there were multiple places a model name could live.
179
+ * Soon, `this._settings` should be the only source of truth about model name.
180
+ * In fact...it might already be the case, but juuuuust to be safe, we'll
181
+ * check all the places we used to store it.
182
+ */
183
+ export function _getModelName(service) {
184
+ // `NOT_GIVEN` is falsy upstream, so `or` skips it.
185
+ const candidates = [
186
+ // Some services store an API-response-provided detailed "full" name,
187
+ // which is distinct from the user-provided model name
188
+ service?._fullModelName,
189
+ service?._settings?.model,
190
+ service?.modelName,
191
+ service?._modelName,
192
+ ];
193
+ return candidates.find(truthy) ?? "unknown";
194
+ }
195
+ /**
196
+ * Get the current turn's tracing context if available.
197
+ *
198
+ * @param service The service instance.
199
+ * @returns The turn context, or null if unavailable.
200
+ */
201
+ export function _getTurnContext(service) {
202
+ const tracingCtx = service?._tracingContext;
203
+ return tracingCtx ? tracingCtx.getTurnContext() : null;
204
+ }
205
+ /**
206
+ * Get the parent service span context (internal use only).
207
+ *
208
+ * This looks for the service span that was created when the service was initialized,
209
+ * or falls back to the conversation context if available.
210
+ *
211
+ * @param service The service instance.
212
+ * @returns The parent service context, or null if unavailable.
213
+ */
214
+ export function _getParentServiceContext(service) {
215
+ const otel = getOpenTelemetry();
216
+ if (!otel) {
217
+ return null;
218
+ }
219
+ // Use the conversation context set by TurnTraceObserver via TracingContext.
220
+ const tracingCtx = service?._tracingContext;
221
+ const conversationContext = tracingCtx ? tracingCtx.getConversationContext() : null;
222
+ if (conversationContext) {
223
+ return conversationContext;
224
+ }
225
+ // Last resort: use current context (may create orphan spans)
226
+ return otel.api.context.active();
227
+ }
228
+ /**
229
+ * Resolve the effective system instruction for tracing (internal use only).
230
+ *
231
+ * System instructions can live on the LLM service (`settings.systemInstruction`,
232
+ * the norm — and the composed source of truth, see
233
+ * `LLMService._composeSystemInstruction`) or be supplied as an initial
234
+ * system message in the context. The service setting takes priority,
235
+ * matching service behavior.
236
+ *
237
+ * @param service The LLM service instance.
238
+ * @param context The universal `LLMContext`, or null.
239
+ * @returns The system instruction text, or null if none is present.
240
+ */
241
+ export function _getSystemInstruction(service, context) {
242
+ const systemInstruction = service?.settings?.systemInstruction;
243
+ if (truthy(systemInstruction)) {
244
+ return systemInstruction;
245
+ }
246
+ if (!context) {
247
+ return null;
248
+ }
249
+ // Fall back to extracting from context messages
250
+ const ctxMessages = context.getMessages();
251
+ if (ctxMessages && ctxMessages.length > 0) {
252
+ const first = ctxMessages[0];
253
+ if (isObject(first) && first.role === "system") {
254
+ const content = first.content;
255
+ if (typeof content === "string") {
256
+ return content;
257
+ }
258
+ else if (Array.isArray(content)) {
259
+ return content
260
+ .filter((part) => isObject(part) && part.type === "text")
261
+ .map((part) => part.text ?? "")
262
+ .join(" ");
263
+ }
264
+ }
265
+ }
266
+ return null;
267
+ }
268
+ const TOKEN_USAGE_ATTRIBUTES = [
269
+ // [dict key, LLMTokenUsage field, attribute]
270
+ ["cache_read_input_tokens", "cacheReadInputTokens", "gen_ai.usage.cache_read.input_tokens"],
271
+ [
272
+ "cache_creation_input_tokens",
273
+ "cacheCreationInputTokens",
274
+ "gen_ai.usage.cache_creation.input_tokens",
275
+ ],
276
+ ["reasoning_tokens", "reasoningTokens", "gen_ai.usage.reasoning.output_tokens"],
277
+ ["input_audio_tokens", "inputAudioTokens", "gen_ai.usage.audio.input_tokens"],
278
+ ["output_audio_tokens", "outputAudioTokens", "gen_ai.usage.audio.output_tokens"],
279
+ [
280
+ "cache_read_input_audio_tokens",
281
+ "cacheReadInputAudioTokens",
282
+ "gen_ai.usage.audio.cache_read.input_tokens",
283
+ ],
284
+ ];
285
+ /**
286
+ * Add token usage metrics to a span (internal use only).
287
+ *
288
+ * @param span The span to add token metrics to.
289
+ * @param tokenUsage A dict (snake_case keys) or `LLMTokenUsage` object
290
+ * containing token usage information.
291
+ */
292
+ export function _addTokenUsageToSpan(span, tokenUsage) {
293
+ if (!isTracingAvailable() || !tokenUsage) {
294
+ return;
295
+ }
296
+ if (isDict(tokenUsage) && !(tokenUsage instanceof LLMTokenUsage)) {
297
+ if ("prompt_tokens" in tokenUsage) {
298
+ span.setAttribute("gen_ai.usage.input_tokens", tokenUsage.prompt_tokens);
299
+ }
300
+ if ("completion_tokens" in tokenUsage) {
301
+ span.setAttribute("gen_ai.usage.output_tokens", tokenUsage.completion_tokens);
302
+ }
303
+ // Add cached token metrics for dictionary
304
+ for (const [key, , attribute] of TOKEN_USAGE_ATTRIBUTES) {
305
+ const value = tokenUsage[key];
306
+ if (key in tokenUsage && value !== null && value !== undefined) {
307
+ span.setAttribute(attribute, value);
308
+ }
309
+ }
310
+ }
311
+ else {
312
+ // Handle LLMTokenUsage object
313
+ const usage = tokenUsage;
314
+ span.setAttribute("gen_ai.usage.input_tokens", usage.promptTokens ?? 0);
315
+ span.setAttribute("gen_ai.usage.output_tokens", usage.completionTokens ?? 0);
316
+ // Add cached token metrics for LLMTokenUsage object
317
+ for (const [, field, attribute] of TOKEN_USAGE_ATTRIBUTES) {
318
+ const value = usage[field];
319
+ if (value !== null && value !== undefined) {
320
+ span.setAttribute(attribute, value);
321
+ }
322
+ }
323
+ }
324
+ }
325
+ /**
326
+ * Add STT usage metrics to a span (internal use only).
327
+ *
328
+ * @param span The span to add usage metrics to.
329
+ * @param usage `STTUsage` object with audio seconds.
330
+ */
331
+ export function _addSttUsageToSpan(span, usage) {
332
+ if (!isTracingAvailable() || usage === null || usage === undefined) {
333
+ return;
334
+ }
335
+ span.setAttribute("metrics.audio_seconds", usage.audioSeconds);
336
+ }
337
+ /** Normalize the `(func | options, context)` overloads of the `traced*` decorators. */
338
+ function resolveDecorator(decorator, funcOrOptions, context, descriptor) {
339
+ if (typeof funcOrOptions === "function" || typeof context === "string") {
340
+ return decorator(funcOrOptions, context, descriptor);
341
+ }
342
+ return decorator;
343
+ }
344
+ /** End the TTS span for `contextId` if still open. Idempotent. */
345
+ function endTtsSpan(service, contextId, options = {}) {
346
+ service._ttsTexts?.delete(contextId);
347
+ const spans = service._ttsSpans;
348
+ const entry = spans?.get(contextId);
349
+ spans?.delete(contextId);
350
+ if (!entry) {
351
+ return;
352
+ }
353
+ try {
354
+ const span = entry.span;
355
+ if (options.interrupted) {
356
+ span.setAttribute("tts.interrupted", true);
357
+ }
358
+ span.end();
359
+ }
360
+ catch (e) {
361
+ rethrowIfCancelled(e);
362
+ logger.warning(`Error closing TTS span: ${errorMessage(e)}`);
363
+ }
364
+ }
365
+ /**
366
+ * Set the accumulated text attributes on a TTS span.
367
+ *
368
+ * One audio context (and thus one span) typically receives
369
+ * several `runTts` calls — one per aggregated sentence — so
370
+ * the attributes always reflect the full accumulated text, not
371
+ * just the latest call.
372
+ */
373
+ function applyTextAttributes(span, texts) {
374
+ span.setAttribute("text", texts.join(" "));
375
+ span.setAttribute("metrics.character_count", texts.reduce((sum, t) => sum + [...t].length, 0));
376
+ }
377
+ /**
378
+ * Install per-instance wrappers on the audio-context methods.
379
+ *
380
+ * The wrappers own the lifetime of the TTS span:
381
+ *
382
+ * - `createAudioContext`: opens the span and records baseline attributes.
383
+ * - `appendToAudioContext`: ends the span on `TTSStoppedFrame`.
384
+ * - `pushFrame`: records `metrics.ttfb` from the canonical `TTFBMetricsData`
385
+ * payload of any `MetricsFrame` pushed by `stopTtfbMetrics`. Reading the
386
+ * value from the metrics event (instead of polling `_metrics.ttfb` when the
387
+ * first audio is queued) avoids the `ttfb` property's in-progress fallback,
388
+ * which would otherwise report an under-estimate whenever a context's audio
389
+ * waits behind earlier queued audio before `_handleAudioContext` actually
390
+ * stops the TTFB measurement.
391
+ * - `removeAudioContext`: ends any still-open span as a safety net for error
392
+ * and cancellation paths.
393
+ * - `onAudioContextCompleted`: ends the span on natural completion. Needed
394
+ * because services that rely on the base class to auto-push
395
+ * `TTSStoppedFrame` (via `pushFrame` in `_handleAudioContext`) bypass the
396
+ * `appendToAudioContext` hook entirely.
397
+ * - `resetActiveAudioContext`: ends the currently playing context's span if
398
+ * still open. Always called from `_handleInterruption`, so this is the
399
+ * interruption hook.
400
+ *
401
+ * The patches check `_tracingEnabled` at invocation time, so they are safe to
402
+ * install regardless of whether tracing is enabled.
403
+ */
404
+ function installAudioContextPatches(service) {
405
+ if (service.__ttsTracingPatchesInstalled__) {
406
+ return;
407
+ }
408
+ service.__ttsTracingPatchesInstalled__ = true;
409
+ service._ttsSpans = new Map();
410
+ service._ttsTexts = new Map();
411
+ const spans = service._ttsSpans;
412
+ const texts = service._ttsTexts;
413
+ const origCreate = service.createAudioContext.bind(service);
414
+ const origAppend = service.appendToAudioContext.bind(service);
415
+ const origRemove = service.removeAudioContext.bind(service);
416
+ const origCompleted = service.onAudioContextCompleted.bind(service);
417
+ const origResetActive = service.resetActiveAudioContext.bind(service);
418
+ const origPushFrame = service.pushFrame.bind(service);
419
+ const tracingOn = () => Boolean(service._tracingEnabled) && isTracingAvailable();
420
+ service.createAudioContext = async (contextId) => {
421
+ if (tracingOn()) {
422
+ try {
423
+ const parent = _getTurnContext(service) || _getParentServiceContext(service);
424
+ const span = getTracer().startSpan("tts", {}, parent ?? undefined);
425
+ spans.set(contextId, { span, ttfbRecorded: false });
426
+ const settings = service._settings ?? null;
427
+ addTtsSpanAttributes(span, {
428
+ serviceName: service.constructor.name,
429
+ model: _getModelName(service),
430
+ voiceId: settings?.voice ?? "unknown",
431
+ settings,
432
+ operationName: "tts",
433
+ });
434
+ // Apply any text buffered by runTts before the span existed — some
435
+ // services (e.g. the ElevenLabs websocket path) create the audio
436
+ // context inside runTts, after the text-attach hook has run.
437
+ const buffered = texts.get(contextId);
438
+ if (buffered && buffered.length > 0) {
439
+ applyTextAttributes(span, buffered);
440
+ }
441
+ }
442
+ catch (e) {
443
+ rethrowIfCancelled(e);
444
+ logger.warning(`Error opening TTS span: ${errorMessage(e)}`);
445
+ }
446
+ }
447
+ return await origCreate(contextId);
448
+ };
449
+ service.appendToAudioContext = async (contextId, frame) => {
450
+ const entry = spans.get(contextId);
451
+ if (entry && frame !== null && frame !== undefined) {
452
+ try {
453
+ if (frame instanceof TTSStoppedFrame) {
454
+ entry.span.end();
455
+ spans.delete(contextId);
456
+ texts.delete(contextId);
457
+ }
458
+ }
459
+ catch (e) {
460
+ rethrowIfCancelled(e);
461
+ logger.warning(`Error updating TTS span: ${errorMessage(e)}`);
462
+ }
463
+ }
464
+ return await origAppend(contextId, frame);
465
+ };
466
+ service.pushFrame = async (frame, direction = FrameDirection.DOWNSTREAM) => {
467
+ await origPushFrame(frame, direction);
468
+ if (!tracingOn()) {
469
+ return;
470
+ }
471
+ if (!(frame instanceof MetricsFrame)) {
472
+ return;
473
+ }
474
+ try {
475
+ const playingId = service._audioContextManager.playingContextId;
476
+ if (playingId === null || playingId === undefined) {
477
+ return;
478
+ }
479
+ const entry = spans.get(playingId);
480
+ if (!entry || entry.ttfbRecorded) {
481
+ return;
482
+ }
483
+ for (const data of frame.data) {
484
+ if (data instanceof TTFBMetricsData) {
485
+ entry.span.setAttribute("metrics.ttfb", data.value);
486
+ entry.ttfbRecorded = true;
487
+ break;
488
+ }
489
+ }
490
+ }
491
+ catch (e) {
492
+ rethrowIfCancelled(e);
493
+ logger.warning(`Error recording TTS ttfb from MetricsFrame: ${errorMessage(e)}`);
494
+ }
495
+ };
496
+ service.removeAudioContext = async (contextId) => {
497
+ if (contextId !== null) {
498
+ texts.delete(contextId);
499
+ const entry = spans.get(contextId);
500
+ spans.delete(contextId);
501
+ if (entry) {
502
+ try {
503
+ entry.span.end();
504
+ }
505
+ catch (e) {
506
+ rethrowIfCancelled(e);
507
+ logger.warning(`Error closing TTS span: ${errorMessage(e)}`);
508
+ }
509
+ }
510
+ }
511
+ return await origRemove(contextId);
512
+ };
513
+ service.onAudioContextCompleted = async (contextId) => {
514
+ endTtsSpan(service, contextId);
515
+ return await origCompleted(contextId);
516
+ };
517
+ service.resetActiveAudioContext = () => {
518
+ const playingId = service._audioContextManager.playingContextId;
519
+ if (playingId !== null && playingId !== undefined) {
520
+ endTtsSpan(service, playingId, { interrupted: true });
521
+ }
522
+ return origResetActive();
523
+ };
524
+ }
525
+ /**
526
+ * Wrap `owner.setup` so audio-context patches install per-instance.
527
+ *
528
+ * Idempotent: if a parent class has already been wrapped, skip. The patches
529
+ * check `_tracingEnabled` at invocation time, so wrapping is always safe.
530
+ */
531
+ function patchTtsSetup(owner) {
532
+ wrapMethod(owner, "setup", "__ttsTracingSetupWrapped__", (originalSetup) => {
533
+ return async function patchedSetup(setup) {
534
+ await originalSetup.call(this, setup);
535
+ installAudioContextPatches(this);
536
+ };
537
+ });
538
+ }
539
+ /**
540
+ * Attach text-specific attributes to the in-flight TTS span.
541
+ *
542
+ * Texts accumulate per audio context: with sentence aggregation, one span
543
+ * covers several `runTts` calls. The text may also arrive before the span
544
+ * exists — some services create the audio context inside `runTts` — in which
545
+ * case it is buffered here and applied by `createAudioContext` once the span
546
+ * opens.
547
+ */
548
+ function attachRunTtsAttributes(service, text, args) {
549
+ if (!service._tracingEnabled || !isTracingAvailable()) {
550
+ return;
551
+ }
552
+ try {
553
+ if (!text) {
554
+ return;
555
+ }
556
+ const buffers = service._ttsTexts;
557
+ if (buffers === undefined || buffers === null) {
558
+ return;
559
+ }
560
+ const contextId = args.length > 0 ? args[0] : undefined;
561
+ let texts = buffers.get(contextId);
562
+ if (!texts) {
563
+ texts = [];
564
+ buffers.set(contextId, texts);
565
+ }
566
+ texts.push(text);
567
+ const entry = service._ttsSpans?.get(contextId);
568
+ if (entry) {
569
+ applyTextAttributes(entry.span, texts);
570
+ }
571
+ }
572
+ catch (e) {
573
+ rethrowIfCancelled(e);
574
+ logger.warning(`Error attaching TTS text to span: ${errorMessage(e)}`);
575
+ }
576
+ }
577
+ function isAsyncGeneratorFunction(fn) {
578
+ return Object.prototype.toString.call(fn) === "[object AsyncGeneratorFunction]";
579
+ }
580
+ /**
581
+ * Build the wrapper around `runTts` that adds per-call attributes.
582
+ *
583
+ * Span lifetime is owned by the audio-context patches. This wrapper only
584
+ * records the call's text against the audio context, accumulating across
585
+ * calls; the attributes land on the span opened by `createAudioContext`
586
+ * whether that happened before this call or happens inside it.
587
+ */
588
+ function makeRunTtsWrapper(f) {
589
+ if (isAsyncGeneratorFunction(f)) {
590
+ return async function* genWrapper(text, ...args) {
591
+ attachRunTtsAttributes(this, text, args);
592
+ yield* f.call(this, text, ...args);
593
+ };
594
+ }
595
+ return async function coroWrapper(text, ...args) {
596
+ attachRunTtsAttributes(this, text, args);
597
+ return await f.call(this, text, ...args);
598
+ };
599
+ }
600
+ export function tracedTts(funcOrOptions, context, descriptor) {
601
+ const decorator = makeMethodDecorator(makeRunTtsWrapper, patchTtsSetup);
602
+ return resolveDecorator(decorator, funcOrOptions, context, descriptor);
603
+ }
604
+ /**
605
+ * Apply `tracedTts` to `owner[attrName]` without decorator syntax.
606
+ *
607
+ * Equivalent to upstream's class-definition-time `__set_name__` hook: wraps
608
+ * `owner.setup` so the audio-context patches install on every instance, and
609
+ * replaces the method with its traced wrapper.
610
+ *
611
+ * @example
612
+ * class MyTTSService extends TTSService {
613
+ * async *runTts(text: string, contextId: string) { ... }
614
+ * static {
615
+ * installTracedTts(this.prototype, "runTts");
616
+ * }
617
+ * }
618
+ */
619
+ export function installTracedTts(owner, attrName, _options) {
620
+ patchTtsSetup(owner);
621
+ const target = owner;
622
+ target[attrName] = makeRunTtsWrapper(target[attrName]);
623
+ }
624
+ function getSttSpanState(service) {
625
+ let state = service._sttSpanState;
626
+ if (state === undefined || state === null) {
627
+ state = { span: null, segmentStartTime: null, segments: [], pendingUsage: null };
628
+ service._sttSpanState = state;
629
+ }
630
+ return state;
631
+ }
632
+ /**
633
+ * Attach accumulated usage to the open span and clear it.
634
+ *
635
+ * Called at every span-close site so usage can't bleed into a later span.
636
+ * Usage is accumulated (rather than attached as it arrives) because services
637
+ * emit it just before pushing the finalized transcript — at which point the
638
+ * span may not be open yet — and a single span may cover several usage events.
639
+ */
640
+ function attachPendingUsage(state) {
641
+ const usage = state.pendingUsage;
642
+ if (usage !== null && usage !== undefined) {
643
+ if (state.span) {
644
+ _addSttUsageToSpan(state.span, usage);
645
+ }
646
+ state.pendingUsage = null;
647
+ }
648
+ }
649
+ /**
650
+ * Append or extend the current segment in `state.segments`.
651
+ *
652
+ * If `newText` starts with the last recorded segment, treat it as a
653
+ * continuation (interim accumulation) and replace the last segment. Otherwise
654
+ * treat it as a new segment and append. Some STT services (Deepgram with
655
+ * utterance_end_ms enabled, for example) emit several `TranscriptionFrame`s
656
+ * per turn where each carries a different segment rather than a cumulative
657
+ * update — without this logic the span's transcript would only show the last
658
+ * segment and the beginning would be lost.
659
+ */
660
+ function updateTranscript(state, newText) {
661
+ if (!newText) {
662
+ return;
663
+ }
664
+ const segments = state.segments;
665
+ const last = segments[segments.length - 1];
666
+ if (last === undefined) {
667
+ segments.push(newText);
668
+ }
669
+ else if (newText.startsWith(last)) {
670
+ segments[segments.length - 1] = newText;
671
+ }
672
+ else {
673
+ segments.push(newText);
674
+ }
675
+ }
676
+ /** Open the STT span, anchored at `segmentStartTime` if set. */
677
+ function openSttSpan(service, state) {
678
+ const parent = _getTurnContext(service) || _getParentServiceContext(service);
679
+ const spanOptions = {};
680
+ if (state.segmentStartTime !== null) {
681
+ spanOptions.startTime = toHrTime(state.segmentStartTime);
682
+ }
683
+ const span = getTracer().startSpan("stt", spanOptions, parent ?? undefined);
684
+ try {
685
+ const settings = service._settings ?? null;
686
+ addSttSpanAttributes(span, {
687
+ serviceName: service.constructor.name,
688
+ model: _getModelName(service),
689
+ settings,
690
+ vadEnabled: service.vadEnabled ?? false,
691
+ });
692
+ }
693
+ catch (e) {
694
+ rethrowIfCancelled(e);
695
+ logger.warning(`Error setting STT span baseline attributes: ${errorMessage(e)}`);
696
+ }
697
+ state.span = span;
698
+ }
699
+ /**
700
+ * Record speech-start anchor; lazy-open span on first transcript.
701
+ *
702
+ * Lazy-opening on `TranscriptionFrame` (rather than on
703
+ * `VADUserStartedSpeakingFrame` or `UserStartedSpeakingFrame`) avoids racing
704
+ * with `TurnTraceObserver._handleTurnStarted`, which runs in a background
705
+ * task fired by `_callEventHandler` and may not have set the new turn's
706
+ * context yet — that produces STT spans parented to the previous turn. By the
707
+ * time STT actually emits a transcript, the turn observer has run.
708
+ *
709
+ * Opening happens in pre-push (rather than post-push) so that the recursive
710
+ * `pushFrame` that `STTService.pushFrame` triggers for the `MetricsFrame` (via
711
+ * `stopTtfbMetrics`) sees the span already open and can attribute
712
+ * `metrics.ttfb` to it.
713
+ */
714
+ function handleSttPrePush(service, frame, state) {
715
+ if (frame instanceof VADUserStartedSpeakingFrame) {
716
+ // Anchor the next span at the moment speech began.
717
+ // Skip if we already have an anchor (intra-turn VAD
718
+ // re-trigger) or a span open.
719
+ if (state.span === null && state.segmentStartTime === null) {
720
+ state.segmentStartTime = frame.timestamp - frame.startSecs;
721
+ }
722
+ }
723
+ else if (frame instanceof TranscriptionFrame && state.span === null) {
724
+ openSttSpan(service, state);
725
+ }
726
+ }
727
+ /**
728
+ * Attach per-frame attrs; close on finalized; record TTFB from MetricsFrame.
729
+ *
730
+ * `metrics.ttfb` is read off the `TTFBMetricsData` payload of any
731
+ * `MetricsFrame` pushed by `stopTtfbMetrics` — the canonical value the rest
732
+ * of the system uses — rather than from `_metrics.ttfb`, which has an
733
+ * in-progress fallback branch that would return an under-estimate if read at
734
+ * the wrong time.
735
+ *
736
+ * One STT span per finalized transcript: the span opens lazily on the first
737
+ * `TranscriptionFrame` (pre-push, anchored at speech start via
738
+ * `segmentStartTime`) and closes on `finalized=true`. Multiple finalized
739
+ * transcripts in a single turn produce multiple spans.
740
+ *
741
+ * For services that never set `frame.finalized=true` (e.g. Deepgram, which
742
+ * only marks it via `confirmFinalize()`), the span closes on
743
+ * `UserStoppedSpeakingFrame`. To capture `metrics.ttfb` for those spans we
744
+ * force-stop any pending TTFB measurement before closing — that pushes a
745
+ * `MetricsFrame`, our post-push attributes the value, and the patched
746
+ * `stopTtfbMetrics` closes the span. The `stt.incomplete=true` flag is only
747
+ * set if neither a finalized transcript nor a TTFB measurement ever finalized
748
+ * for the span.
749
+ */
750
+ async function handleSttPostPush(service, frame, state) {
751
+ if (frame instanceof UserStoppedSpeakingFrame) {
752
+ const prevSpan = state.span;
753
+ if (prevSpan === null) {
754
+ return;
755
+ }
756
+ const metrics = service._metrics;
757
+ if (metrics !== null && metrics !== undefined && (metrics._startTtfbTime ?? 0) > 0) {
758
+ const lastTranscriptTime = service._lastTranscriptTime || null;
759
+ try {
760
+ await service.stopTtfbMetrics({ endTime: lastTranscriptTime });
761
+ }
762
+ catch (e) {
763
+ rethrowIfCancelled(e);
764
+ logger.warning(`Error force-stopping STT TTFB on user turn end: ${errorMessage(e)}`);
765
+ }
766
+ }
767
+ // The patched stopTtfbMetrics may have closed the span
768
+ // via the timeout path; re-check.
769
+ if (state.span === null) {
770
+ state.segments = [];
771
+ return;
772
+ }
773
+ state.span.setAttribute("stt.incomplete", true);
774
+ attachPendingUsage(state);
775
+ state.span.end();
776
+ state.span = null;
777
+ state.segmentStartTime = null;
778
+ state.segments = [];
779
+ }
780
+ else if (frame instanceof MetricsFrame) {
781
+ const span = state.span;
782
+ if (span === null) {
783
+ return;
784
+ }
785
+ for (const data of frame.data) {
786
+ if (data instanceof TTFBMetricsData) {
787
+ span.setAttribute("metrics.ttfb", data.value);
788
+ break;
789
+ }
790
+ }
791
+ }
792
+ else if (frame instanceof TranscriptionFrame) {
793
+ const span = state.span;
794
+ if (span === null) {
795
+ return;
796
+ }
797
+ if (frame.text) {
798
+ updateTranscript(state, frame.text);
799
+ span.setAttribute("transcript", state.segments.join(" ").trim());
800
+ }
801
+ span.setAttribute("is_final", Boolean(frame.finalized));
802
+ if (frame.language) {
803
+ span.setAttribute("language", String(frame.language));
804
+ }
805
+ if (frame.userId) {
806
+ span.setAttribute("user_id", frame.userId);
807
+ }
808
+ if (frame.finalized) {
809
+ attachPendingUsage(state);
810
+ span.end();
811
+ state.span = null;
812
+ state.segmentStartTime = null;
813
+ state.segments = [];
814
+ }
815
+ }
816
+ }
817
+ /**
818
+ * Wrap `owner.pushFrame` to drive the STT span lifecycle.
819
+ *
820
+ * Idempotent: if a parent class has already been wrapped, skip. The wrapper
821
+ * checks `_tracingEnabled` at invocation time, so it is safe to install
822
+ * regardless of whether tracing is enabled.
823
+ */
824
+ function patchSttPushFrame(owner) {
825
+ wrapMethod(owner, "pushFrame", "__sttTracingPushFrameWrapped__", (originalPushFrame) => {
826
+ return async function patchedPushFrame(frame, direction = FrameDirection.DOWNSTREAM) {
827
+ const state = getSttSpanState(this);
828
+ const tracingOn = Boolean(this._tracingEnabled) && isTracingAvailable();
829
+ if (tracingOn) {
830
+ try {
831
+ handleSttPrePush(this, frame, state);
832
+ }
833
+ catch (e) {
834
+ rethrowIfCancelled(e);
835
+ logger.warning(`Error in STT pre-push tracing: ${errorMessage(e)}`);
836
+ }
837
+ }
838
+ await originalPushFrame.call(this, frame, direction);
839
+ if (this._tracingEnabled && isTracingAvailable()) {
840
+ try {
841
+ await handleSttPostPush(this, frame, state);
842
+ }
843
+ catch (e) {
844
+ rethrowIfCancelled(e);
845
+ logger.warning(`Error in STT post-push tracing: ${errorMessage(e)}`);
846
+ }
847
+ }
848
+ };
849
+ });
850
+ }
851
+ /**
852
+ * Wrap `owner.stopTtfbMetrics` to close the span on the timeout path.
853
+ *
854
+ * When `stopTtfbMetrics` is invoked with `endTime` set, that signals the
855
+ * TTFB-timeout handler firing, or our own force-stop from the
856
+ * `UserStoppedSpeakingFrame` handler. In either case we anchor the span's end
857
+ * at `endTime` (= `_lastTranscriptTime`) rather than at whenever the
858
+ * coroutine resumed.
859
+ *
860
+ * `metrics.ttfb` attribution is not done here — the `MetricsFrame` that
861
+ * `stopTtfbMetrics` pushes flows through `pushFrame` and gets recorded by the
862
+ * post-push handler, which reads the canonical `TTFBMetricsData.value` rather
863
+ * than the in-progress `_metrics.ttfb` property.
864
+ */
865
+ function patchSttStopTtfbMetrics(owner) {
866
+ wrapMethod(owner, "stopTtfbMetrics", "__sttTracingStopTtfbWrapped__", (originalStop) => {
867
+ return async function patchedStop(options = {}) {
868
+ const endTime = options.endTime ?? null;
869
+ await originalStop.call(this, { endTime });
870
+ if (endTime === null) {
871
+ return;
872
+ }
873
+ if (!this._tracingEnabled || !isTracingAvailable()) {
874
+ return;
875
+ }
876
+ const state = this._sttSpanState;
877
+ if (!state || state.span === null) {
878
+ return;
879
+ }
880
+ try {
881
+ const span = state.span;
882
+ attachPendingUsage(state);
883
+ span.end(toHrTime(endTime));
884
+ state.span = null;
885
+ state.segmentStartTime = null;
886
+ state.segments = [];
887
+ }
888
+ catch (e) {
889
+ rethrowIfCancelled(e);
890
+ logger.warning(`Error in STT stopTtfbMetrics tracing: ${errorMessage(e)}`);
891
+ }
892
+ };
893
+ });
894
+ }
895
+ /**
896
+ * Wrap `owner.startSttUsageMetrics` to accumulate usage for the span.
897
+ *
898
+ * Usage is merged into the span state's `pendingUsage` and attached when the
899
+ * span closes (see `attachPendingUsage`). Values are summed across events.
900
+ */
901
+ function patchSttStartUsageMetrics(owner) {
902
+ wrapMethod(owner, "startSttUsageMetrics", "__sttTracingUsageWrapped__", (originalStart) => {
903
+ return async function patchedStart(usage) {
904
+ await originalStart.call(this, usage);
905
+ if (!this._tracingEnabled || !isTracingAvailable()) {
906
+ return;
907
+ }
908
+ try {
909
+ const state = getSttSpanState(this);
910
+ const pending = state.pendingUsage;
911
+ if (pending === null || pending === undefined) {
912
+ state.pendingUsage = Object.assign(Object.create(Object.getPrototypeOf(usage)), usage);
913
+ }
914
+ else {
915
+ pending.audioSeconds += usage.audioSeconds;
916
+ }
917
+ }
918
+ catch (e) {
919
+ rethrowIfCancelled(e);
920
+ logger.warning(`Error in STT usage tracing: ${errorMessage(e)}`);
921
+ }
922
+ };
923
+ });
924
+ }
925
+ function installSttPatches(owner) {
926
+ patchSttPushFrame(owner);
927
+ patchSttStopTtfbMetrics(owner);
928
+ patchSttStartUsageMetrics(owner);
929
+ }
930
+ export function tracedStt(funcOrOptions, context, descriptor) {
931
+ const decorator = makeMethodDecorator((f) => f, installSttPatches);
932
+ return resolveDecorator(decorator, funcOrOptions, context, descriptor);
933
+ }
934
+ /**
935
+ * Apply `tracedStt` to `owner` without decorator syntax.
936
+ *
937
+ * Equivalent to upstream's class-definition-time `__set_name__` hook: wraps
938
+ * `pushFrame`, `stopTtfbMetrics` and `startSttUsageMetrics` so VAD,
939
+ * transcription, and finalization events drive the span lifecycle. The
940
+ * decorated method itself runs unchanged.
941
+ *
942
+ * @example
943
+ * class MySTTService extends STTService {
944
+ * static {
945
+ * installTracedStt(this.prototype, "_handleTranscription");
946
+ * }
947
+ * }
948
+ */
949
+ export function installTracedStt(owner, _attrName, _options) {
950
+ installSttPatches(owner);
951
+ }
952
+ //
953
+ // LLM
954
+ //
955
+ function makeLlmWrapper(f) {
956
+ return async function wrapper(context, ...args) {
957
+ if (!this._tracingEnabled || !isTracingAvailable()) {
958
+ return await f.call(this, context, ...args);
959
+ }
960
+ let fnCalled = false;
961
+ try {
962
+ const serviceClassName = this.constructor.name;
963
+ const spanName = "llm";
964
+ // Get the parent context - turn context if available, otherwise service context
965
+ const parentContext = _getTurnContext(this) || _getParentServiceContext(this);
966
+ // Create a new span as child of the turn span or service span
967
+ return await withCurrentSpan(spanName, parentContext, async (currentSpan) => {
968
+ // Store original method and output aggregator
969
+ const hadOwnPushFrame = Object.hasOwn(this, "pushFrame");
970
+ const ownPushFrame = this.pushFrame;
971
+ const originalPushFrame = this.pushFrame.bind(this);
972
+ let outputText = ""; // Simple string accumulation
973
+ const tracedPushFrame = async (frame, direction) => {
974
+ // Capture text from LLMTextFrame during streaming
975
+ if (frame?.constructor?.name === "LLMTextFrame" &&
976
+ typeof frame.text === "string") {
977
+ outputText += frame.text;
978
+ }
979
+ // Call original
980
+ if (direction !== undefined && direction !== null) {
981
+ return await originalPushFrame(frame, direction);
982
+ }
983
+ return await originalPushFrame(frame);
984
+ };
985
+ // For token usage monitoring
986
+ let hadOwnStartUsage = false;
987
+ let ownStartUsage;
988
+ let originalStartLlmUsageMetrics = null;
989
+ if (typeof this.startLlmUsageMetrics === "function") {
990
+ hadOwnStartUsage = Object.hasOwn(this, "startLlmUsageMetrics");
991
+ ownStartUsage = this.startLlmUsageMetrics;
992
+ const original = this.startLlmUsageMetrics.bind(this);
993
+ originalStartLlmUsageMetrics = original;
994
+ // Override the method to capture token usage
995
+ const wrappedStartLlmUsageMetrics = async (tokens) => {
996
+ // Call the original method
997
+ await original(tokens);
998
+ // Add token usage to the current span
999
+ _addTokenUsageToSpan(currentSpan, tokens);
1000
+ };
1001
+ // Replace the method temporarily
1002
+ this.startLlmUsageMetrics = wrappedStartLlmUsageMetrics;
1003
+ }
1004
+ try {
1005
+ try {
1006
+ // Replace pushFrame to capture output
1007
+ this.pushFrame = tracedPushFrame;
1008
+ // Get messages for logging
1009
+ // Use adapter's getMessagesForLogging() which returns
1010
+ // messages in provider's native format with sensitive data sanitized
1011
+ let messages = null;
1012
+ let serializedMessages = null;
1013
+ // Use adapter for provider-native format
1014
+ if (typeof this.getLlmAdapter === "function") {
1015
+ const adapter = this.getLlmAdapter();
1016
+ messages = adapter.getMessagesForLogging(context);
1017
+ }
1018
+ // Serialize messages if available
1019
+ if (Array.isArray(messages) ? messages.length > 0 : messages) {
1020
+ serializedMessages = jsonDumps(messages);
1021
+ }
1022
+ // Get tools
1023
+ // Use adapter's fromStandardTools() to convert ToolsSchema
1024
+ let tools = null;
1025
+ let serializedTools = null;
1026
+ let toolCount = 0;
1027
+ // Use adapter to convert ToolsSchema
1028
+ if (typeof this.getLlmAdapter === "function" &&
1029
+ isObject(context) &&
1030
+ "tools" in context) {
1031
+ const adapter = this.getLlmAdapter();
1032
+ tools = adapter.fromStandardTools(context.tools);
1033
+ }
1034
+ // Serialize and count tools if available
1035
+ // Check if tools is not null and not NOT_GIVEN
1036
+ if (tools !== null && tools !== undefined && tools !== NOT_GIVEN) {
1037
+ serializedTools = jsonDumps(tools);
1038
+ toolCount = Array.isArray(tools) ? tools.length : 1;
1039
+ }
1040
+ // Handle system message for different services
1041
+ // settings.systemInstruction takes priority (matches service behavior)
1042
+ const systemMessage = _getSystemInstruction(this, context);
1043
+ // Use givenFields() defensively in case a service doesn't
1044
+ // initialize all settings.
1045
+ const params = {};
1046
+ if (this._settings) {
1047
+ for (const [key, value] of Object.entries(this._settings.givenFields())) {
1048
+ // systemInstruction is already captured as the
1049
+ // "system_instructions" span attribute above.
1050
+ if (key === "systemInstruction") {
1051
+ continue;
1052
+ }
1053
+ if (typeof value === "number" ||
1054
+ typeof value === "boolean" ||
1055
+ typeof value === "string") {
1056
+ params[key] = value;
1057
+ }
1058
+ else if (value === null || value === undefined) {
1059
+ params[key] = "NOT_GIVEN";
1060
+ }
1061
+ }
1062
+ }
1063
+ // Add all available attributes to the span
1064
+ const attributeOptions = {
1065
+ serviceName: serviceClassName,
1066
+ model: _getModelName(this),
1067
+ stream: true, // Most LLM services use streaming
1068
+ parameters: params,
1069
+ };
1070
+ // Add optional attributes only if they exist
1071
+ if (serializedMessages) {
1072
+ attributeOptions.messages = serializedMessages;
1073
+ }
1074
+ if (serializedTools) {
1075
+ attributeOptions.tools = serializedTools;
1076
+ attributeOptions.toolCount = toolCount;
1077
+ }
1078
+ if (systemMessage) {
1079
+ attributeOptions.systemInstructions = systemMessage;
1080
+ }
1081
+ // Add all gathered attributes to the span
1082
+ addLlmSpanAttributes(currentSpan, attributeOptions);
1083
+ }
1084
+ catch (e) {
1085
+ rethrowIfCancelled(e);
1086
+ logger.warning(`Error setting up LLM tracing: ${errorMessage(e)}`);
1087
+ // Don't raise - let the function execute anyway
1088
+ }
1089
+ // Run function with modified pushFrame to capture the output
1090
+ fnCalled = true;
1091
+ return await f.call(this, context, ...args);
1092
+ }
1093
+ finally {
1094
+ // Always restore the original methods
1095
+ if (hadOwnPushFrame) {
1096
+ this.pushFrame = ownPushFrame;
1097
+ }
1098
+ else {
1099
+ delete this.pushFrame;
1100
+ }
1101
+ if (originalStartLlmUsageMetrics) {
1102
+ if (hadOwnStartUsage) {
1103
+ this.startLlmUsageMetrics = ownStartUsage;
1104
+ }
1105
+ else {
1106
+ delete this.startLlmUsageMetrics;
1107
+ }
1108
+ }
1109
+ // Attach whatever output text we accumulated so far. Doing this in
1110
+ // finally captures partial output when `f` is cancelled or raises
1111
+ // mid-stream (e.g. interruption during LLM generation), rather than
1112
+ // only on clean completion.
1113
+ if (outputText) {
1114
+ currentSpan.setAttribute("output", outputText);
1115
+ }
1116
+ // Update TTFB metric
1117
+ const ttfb = this._metrics?.ttfb;
1118
+ if (ttfb !== null && ttfb !== undefined) {
1119
+ currentSpan.setAttribute("metrics.ttfb", ttfb);
1120
+ }
1121
+ }
1122
+ });
1123
+ }
1124
+ catch (e) {
1125
+ if (fnCalled || e instanceof CancelledError) {
1126
+ throw e;
1127
+ }
1128
+ logger.error(`Error in LLM tracing (continuing without tracing): ${errorMessage(e)}`);
1129
+ return await f.call(this, context, ...args);
1130
+ }
1131
+ };
1132
+ }
1133
+ export function tracedLlm(funcOrOptions, context, descriptor) {
1134
+ const decorator = makeMethodDecorator(makeLlmWrapper, null);
1135
+ return resolveDecorator(decorator, funcOrOptions, context, descriptor);
1136
+ }
1137
+ //
1138
+ // Gemini Live
1139
+ //
1140
+ /**
1141
+ * Get the AUDIO token count from a Gemini modality breakdown, if reported.
1142
+ *
1143
+ * Duck-typed (`entry.modality` / `entry.tokenCount`) so this module doesn't
1144
+ * depend on Google types. Gemini omits modalities with no tokens (and omits
1145
+ * the list entirely when there is no breakdown), so absence means "not
1146
+ * reported" rather than zero.
1147
+ *
1148
+ * @param details A modality breakdown from usage metadata, e.g. `promptTokensDetails`.
1149
+ * @returns The AUDIO token count, or null if not reported.
1150
+ */
1151
+ export function _geminiAudioModalityTokens(details) {
1152
+ for (const entry of details ?? []) {
1153
+ const modality = entry?.modality;
1154
+ const value = isObject(modality) && "value" in modality ? modality.value : modality;
1155
+ if (value === "AUDIO") {
1156
+ return entry.tokenCount;
1157
+ }
1158
+ }
1159
+ return null;
1160
+ }
1161
+ function toolField(tool, field, fallback) {
1162
+ return isObject(tool) && field in tool ? tool[field] : fallback;
1163
+ }
1164
+ function serializeToolList(tools) {
1165
+ return jsonDumps(tools.map((tool) => ({
1166
+ name: toolField(tool, "name", "unknown"),
1167
+ description: toolField(tool, "description", ""),
1168
+ properties: toolField(tool, "properties", {}),
1169
+ required: toolField(tool, "required", []),
1170
+ })));
1171
+ }
1172
+ /** Collect the operation-specific Gemini Live attributes. */
1173
+ function geminiOperationAttributes(self, operation, args) {
1174
+ const operationAttrs = {};
1175
+ if (operation === "llm_setup") {
1176
+ // Capture detailed tool information
1177
+ const tools = self._tools;
1178
+ if (tools) {
1179
+ // Handle different tool formats
1180
+ let toolsList = [];
1181
+ let toolsSerialized = null;
1182
+ try {
1183
+ if (isObject(tools) && "standardTools" in tools) {
1184
+ // ToolsSchema object
1185
+ toolsList = tools.standardTools;
1186
+ // Serialize the tools for detailed inspection
1187
+ toolsSerialized = serializeToolList(toolsList);
1188
+ }
1189
+ else if (Array.isArray(tools)) {
1190
+ // List of tool dictionaries or objects
1191
+ toolsList = tools;
1192
+ toolsSerialized = serializeToolList(toolsList);
1193
+ }
1194
+ if (toolsList.length > 0) {
1195
+ operationAttrs.tools = toolsList;
1196
+ operationAttrs.toolsSerialized = toolsSerialized;
1197
+ }
1198
+ }
1199
+ catch (e) {
1200
+ rethrowIfCancelled(e);
1201
+ logger.warning(`Error serializing tools for tracing: ${errorMessage(e)}`);
1202
+ // Fallback to basic tool count
1203
+ if (toolsList.length > 0) {
1204
+ operationAttrs.tools = toolsList;
1205
+ }
1206
+ }
1207
+ }
1208
+ // Capture the effective system instruction: settings.systemInstruction
1209
+ // takes priority, else an initial system message from the context.
1210
+ const systemInstruction = _getSystemInstruction(self, self._context ?? null);
1211
+ if (systemInstruction) {
1212
+ // Truncate if very long
1213
+ operationAttrs["gen_ai.system_instructions"] = systemInstruction.slice(0, 500);
1214
+ }
1215
+ }
1216
+ else if (operation === "llm_tool_call" && args.length > 0) {
1217
+ // Extract tool call information
1218
+ const msg = args[0];
1219
+ const functionCalls = msg?.toolCall?.functionCalls;
1220
+ if (msg && functionCalls && functionCalls.length > 0) {
1221
+ // Add information about the first function call
1222
+ const call = functionCalls[0];
1223
+ operationAttrs["tool.function_name"] = call.name;
1224
+ operationAttrs["tool.call_id"] = call.id;
1225
+ operationAttrs["tool.calls_count"] = functionCalls.length;
1226
+ // Add all function names being called
1227
+ const allFunctionNames = functionCalls.map((c) => c.name);
1228
+ operationAttrs["tool.all_function_names"] = allFunctionNames.join(",");
1229
+ // Add arguments for the first call (truncated if too long)
1230
+ try {
1231
+ let argsStr = call.args ? jsonDumps(call.args) : "{}";
1232
+ if (argsStr.length > 1000) {
1233
+ argsStr = `${argsStr.slice(0, 1000)}...`;
1234
+ }
1235
+ operationAttrs["tool.arguments"] = argsStr;
1236
+ }
1237
+ catch (e) {
1238
+ rethrowIfCancelled(e);
1239
+ operationAttrs["tool.arguments"] = String(call.args).slice(0, 1000);
1240
+ }
1241
+ }
1242
+ }
1243
+ else if (operation === "llm_tool_result" && args.length >= 3) {
1244
+ // _toolResult(toolCallId, toolCallName, result); its positional args, in
1245
+ // order. `result` is expected to be Gemini's FunctionResponse.response
1246
+ // payload (a dict), but is validated at runtime rather than assumed.
1247
+ const [toolCallId, toolCallName, result] = args;
1248
+ try {
1249
+ if (toolCallId) {
1250
+ operationAttrs["tool.call_id"] = toolCallId;
1251
+ }
1252
+ if (toolCallName) {
1253
+ operationAttrs["tool.function_name"] = toolCallName;
1254
+ }
1255
+ if (isDict(result)) {
1256
+ let resultStr = jsonDumps(result);
1257
+ if (resultStr.length > 2000) {
1258
+ // larger limit for results
1259
+ resultStr = `${resultStr.slice(0, 2000)}...`;
1260
+ }
1261
+ operationAttrs["tool.result"] = resultStr;
1262
+ if ("error" in result) {
1263
+ operationAttrs["tool.result_status"] = "error";
1264
+ }
1265
+ else if ("success" in result) {
1266
+ operationAttrs["tool.result_status"] = "success";
1267
+ }
1268
+ else {
1269
+ operationAttrs["tool.result_status"] = "completed";
1270
+ }
1271
+ }
1272
+ }
1273
+ catch (e) {
1274
+ rethrowIfCancelled(e);
1275
+ logger.warning(`Error capturing tool result attributes for tracing: ${errorMessage(e)}`);
1276
+ }
1277
+ }
1278
+ else if (operation === "llm_response" && args.length > 0) {
1279
+ // Extract usage and response metadata from turn complete event
1280
+ const msg = args[0];
1281
+ if (msg?.usageMetadata) {
1282
+ const usage = msg.usageMetadata;
1283
+ operationAttrs["gen_ai.usage.input_tokens"] =
1284
+ (usage.promptTokenCount || 0) + (usage.toolUsePromptTokenCount || 0);
1285
+ operationAttrs["gen_ai.usage.output_tokens"] =
1286
+ (usage.responseTokenCount || 0) + (usage.thoughtsTokenCount || 0);
1287
+ if (usage.cachedContentTokenCount !== null && usage.cachedContentTokenCount !== undefined) {
1288
+ operationAttrs["gen_ai.usage.cache_read.input_tokens"] = usage.cachedContentTokenCount;
1289
+ }
1290
+ if (usage.thoughtsTokenCount !== null && usage.thoughtsTokenCount !== undefined) {
1291
+ operationAttrs["gen_ai.usage.reasoning.output_tokens"] = usage.thoughtsTokenCount;
1292
+ }
1293
+ const inputAudioTokens = _geminiAudioModalityTokens(usage.promptTokensDetails);
1294
+ if (inputAudioTokens !== null && inputAudioTokens !== undefined) {
1295
+ operationAttrs["gen_ai.usage.audio.input_tokens"] = inputAudioTokens;
1296
+ }
1297
+ const outputAudioTokens = _geminiAudioModalityTokens(usage.responseTokensDetails);
1298
+ if (outputAudioTokens !== null && outputAudioTokens !== undefined) {
1299
+ operationAttrs["gen_ai.usage.audio.output_tokens"] = outputAudioTokens;
1300
+ }
1301
+ const cachedAudioTokens = _geminiAudioModalityTokens(usage.cacheTokensDetails);
1302
+ if (cachedAudioTokens !== null && cachedAudioTokens !== undefined) {
1303
+ operationAttrs["gen_ai.usage.audio.cache_read.input_tokens"] = cachedAudioTokens;
1304
+ }
1305
+ }
1306
+ // Get output text and modality from service state
1307
+ const text = self._botTextBuffer ?? "";
1308
+ const audioText = self._llmOutputBuffer ?? "";
1309
+ if (text) {
1310
+ // TEXT modality
1311
+ operationAttrs.output = text;
1312
+ operationAttrs.output_modality = "TEXT";
1313
+ }
1314
+ else if (audioText) {
1315
+ // AUDIO modality
1316
+ operationAttrs.output = audioText;
1317
+ operationAttrs.output_modality = "AUDIO";
1318
+ }
1319
+ // Add turn completion status
1320
+ if (msg?.serverContent?.turnComplete) {
1321
+ operationAttrs.turn_complete = true;
1322
+ }
1323
+ }
1324
+ return operationAttrs;
1325
+ }
1326
+ function makeGeminiLiveWrapper(operation, func) {
1327
+ return async function wrapper(...args) {
1328
+ if (!this._tracingEnabled || !isTracingAvailable()) {
1329
+ return await func.apply(this, args);
1330
+ }
1331
+ let fnCalled = false;
1332
+ try {
1333
+ const serviceClassName = this.constructor.name;
1334
+ const spanName = `${operation}`;
1335
+ // Get the parent context - turn context if available, otherwise service context
1336
+ const parentContext = _getTurnContext(this) || _getParentServiceContext(this);
1337
+ // Create a new span as child of the turn span or service span
1338
+ return await withCurrentSpan(spanName, parentContext, async (currentSpan) => {
1339
+ try {
1340
+ // Base service attributes
1341
+ const modelName = _getModelName(this);
1342
+ const voiceId = this._voiceId ?? null;
1343
+ const languageCode = this._languageCode ?? null;
1344
+ const settings = this._settings ?? null;
1345
+ // Get modalities if available
1346
+ let modalities = null;
1347
+ if (settings && isObject(settings) && "modalities" in settings) {
1348
+ const modalityObj = settings.modalities;
1349
+ if (isObject(modalityObj) && "value" in modalityObj) {
1350
+ modalities = modalityObj.value;
1351
+ }
1352
+ else {
1353
+ modalities = String(modalityObj);
1354
+ }
1355
+ }
1356
+ // Operation-specific attribute collection
1357
+ const operationAttrs = geminiOperationAttributes(this, operation, args);
1358
+ // Add all attributes to the span
1359
+ addGeminiLiveSpanAttributes(currentSpan, {
1360
+ ...operationAttrs,
1361
+ serviceName: serviceClassName,
1362
+ model: modelName,
1363
+ operationName: operation,
1364
+ voiceId,
1365
+ language: languageCode,
1366
+ modalities,
1367
+ settings,
1368
+ });
1369
+ // Capture TTFB metric if available
1370
+ const ttfb = this._metrics?.ttfb;
1371
+ if (ttfb !== null && ttfb !== undefined) {
1372
+ currentSpan.setAttribute("metrics.ttfb", ttfb);
1373
+ }
1374
+ // Run the original function
1375
+ fnCalled = true;
1376
+ return await func.apply(this, args);
1377
+ }
1378
+ catch (e) {
1379
+ // Upstream's `except Exception` does not see CancelledError.
1380
+ if (!(e instanceof CancelledError)) {
1381
+ currentSpan.recordException?.(e);
1382
+ currentSpan.setStatus?.({ code: SPAN_STATUS_ERROR, message: errorMessage(e) });
1383
+ }
1384
+ throw e;
1385
+ }
1386
+ });
1387
+ }
1388
+ catch (e) {
1389
+ if (fnCalled || e instanceof CancelledError) {
1390
+ throw e;
1391
+ }
1392
+ logger.error(`Error in Gemini Live tracing (continuing without tracing): ${errorMessage(e)}`);
1393
+ return await func.apply(this, args);
1394
+ }
1395
+ };
1396
+ }
1397
+ /**
1398
+ * Trace Gemini Live service methods with operation-specific attributes.
1399
+ *
1400
+ * This decorator automatically captures relevant information based on the operation type:
1401
+ *
1402
+ * - llm_setup: Configuration, tools definitions, and system instructions
1403
+ * - llm_tool_call: Function call information
1404
+ * - llm_tool_result: Function execution results
1405
+ * - llm_response: Complete LLM response with usage and output
1406
+ *
1407
+ * @param operation The operation name (matches the event type being handled).
1408
+ * @returns A decorator that wraps the method with Gemini Live specific tracing.
1409
+ */
1410
+ export function tracedGeminiLive(operation) {
1411
+ return makeMethodDecorator((func) => makeGeminiLiveWrapper(operation, func), null);
1412
+ }
1413
+ //
1414
+ // OpenAI Realtime
1415
+ //
1416
+ /** Collect the operation-specific OpenAI Realtime attributes. */
1417
+ function openaiRealtimeOperationAttributes(self, operation, args) {
1418
+ const operationAttrs = {};
1419
+ if (operation === "llm_setup") {
1420
+ // Capture session properties and tools
1421
+ const sessionProperties = self._sessionProperties;
1422
+ if (sessionProperties) {
1423
+ try {
1424
+ // Convert to dict for easier processing
1425
+ let propsDict;
1426
+ if (typeof sessionProperties.modelDump === "function") {
1427
+ propsDict = sessionProperties.modelDump();
1428
+ }
1429
+ else if (isObject(sessionProperties)) {
1430
+ propsDict = { ...sessionProperties };
1431
+ }
1432
+ else {
1433
+ propsDict = {};
1434
+ }
1435
+ operationAttrs.sessionProperties = propsDict;
1436
+ // Extract tools if available
1437
+ const tools = propsDict.tools;
1438
+ if (Array.isArray(tools) ? tools.length > 0 : tools) {
1439
+ operationAttrs.tools = tools;
1440
+ try {
1441
+ operationAttrs.toolsSerialized = jsonDumps(tools);
1442
+ }
1443
+ catch (e) {
1444
+ rethrowIfCancelled(e);
1445
+ logger.warning(`Error serializing OpenAI tools: ${errorMessage(e)}`);
1446
+ }
1447
+ }
1448
+ // Extract instructions
1449
+ const instructions = propsDict.instructions;
1450
+ if (typeof instructions === "string" && instructions) {
1451
+ operationAttrs.instructions = instructions.slice(0, 500);
1452
+ }
1453
+ }
1454
+ catch (e) {
1455
+ rethrowIfCancelled(e);
1456
+ logger.warning(`Error processing session properties: ${errorMessage(e)}`);
1457
+ }
1458
+ }
1459
+ // Also check context for tools
1460
+ if (self._context) {
1461
+ try {
1462
+ const contextTools = self._context.tools;
1463
+ if (truthy(contextTools) && !operationAttrs.tools) {
1464
+ let tools = null;
1465
+ if (typeof self.getLlmAdapter === "function") {
1466
+ const adapter = self.getLlmAdapter();
1467
+ tools = adapter.fromStandardTools(contextTools);
1468
+ }
1469
+ if (tools !== null && tools !== undefined && tools !== NOT_GIVEN) {
1470
+ operationAttrs.tools = tools;
1471
+ try {
1472
+ operationAttrs.toolsSerialized = jsonDumps(tools);
1473
+ }
1474
+ catch (e) {
1475
+ rethrowIfCancelled(e);
1476
+ logger.warning(`Error serializing OpenAI Realtime tools: ${errorMessage(e)}`);
1477
+ }
1478
+ }
1479
+ }
1480
+ }
1481
+ catch (e) {
1482
+ rethrowIfCancelled(e);
1483
+ logger.warning(`Error extracting context tools: ${errorMessage(e)}`);
1484
+ }
1485
+ }
1486
+ }
1487
+ else if (operation === "llm_request") {
1488
+ // Capture context messages being sent
1489
+ if (self._context) {
1490
+ try {
1491
+ const messages = self.getLlmAdapter().getMessagesForLogging(self._context);
1492
+ if (messages && messages.length > 0) {
1493
+ operationAttrs.contextMessages = jsonDumps(messages);
1494
+ }
1495
+ }
1496
+ catch (e) {
1497
+ rethrowIfCancelled(e);
1498
+ logger.warning(`Error getting context messages: ${errorMessage(e)}`);
1499
+ }
1500
+ }
1501
+ }
1502
+ else if (operation === "llm_response" && args.length > 0) {
1503
+ // Extract usage and response metadata
1504
+ const evt = args[0];
1505
+ if (isObject(evt) && "response" in evt) {
1506
+ const response = evt.response;
1507
+ // Token usage, including the audio/cached breakdown needed for cost
1508
+ // attribution (realtime audio tokens are priced separately from text
1509
+ // and cached-audio tokens).
1510
+ if (isObject(response) && "usage" in response) {
1511
+ const usage = response.usage;
1512
+ if (isObject(usage) && "input_tokens" in usage) {
1513
+ operationAttrs["gen_ai.usage.input_tokens"] = usage.input_tokens;
1514
+ }
1515
+ if (isObject(usage) && "output_tokens" in usage) {
1516
+ operationAttrs["gen_ai.usage.output_tokens"] = usage.output_tokens;
1517
+ }
1518
+ const inputDetails = usage?.input_token_details;
1519
+ if (inputDetails) {
1520
+ if (inputDetails.cached_tokens !== null && inputDetails.cached_tokens !== undefined) {
1521
+ operationAttrs["gen_ai.usage.cache_read.input_tokens"] = inputDetails.cached_tokens;
1522
+ }
1523
+ if (inputDetails.audio_tokens !== null && inputDetails.audio_tokens !== undefined) {
1524
+ operationAttrs["gen_ai.usage.audio.input_tokens"] = inputDetails.audio_tokens;
1525
+ }
1526
+ const cachedDetails = inputDetails.cached_tokens_details;
1527
+ if (cachedDetails &&
1528
+ cachedDetails.audio_tokens !== null &&
1529
+ cachedDetails.audio_tokens !== undefined) {
1530
+ operationAttrs["gen_ai.usage.audio.cache_read.input_tokens"] =
1531
+ cachedDetails.audio_tokens;
1532
+ }
1533
+ }
1534
+ const outputDetails = usage?.output_token_details;
1535
+ if (outputDetails &&
1536
+ outputDetails.audio_tokens !== null &&
1537
+ outputDetails.audio_tokens !== undefined) {
1538
+ operationAttrs["gen_ai.usage.audio.output_tokens"] = outputDetails.audio_tokens;
1539
+ }
1540
+ }
1541
+ // Response status and metadata
1542
+ if (isObject(response) && "status" in response) {
1543
+ operationAttrs["response.status"] = response.status;
1544
+ }
1545
+ if (isObject(response) && "id" in response) {
1546
+ operationAttrs["response.id"] = response.id;
1547
+ }
1548
+ // Output items and extract transcript for output field
1549
+ const output = response?.output;
1550
+ if (output && output.length > 0) {
1551
+ operationAttrs["response.output_items"] = output.length;
1552
+ // Extract assistant transcript and function calls
1553
+ let assistantTranscript = "";
1554
+ const functionCalls = [];
1555
+ for (const item of output) {
1556
+ if (item?.content && item.content.length > 0 && item.role === "assistant") {
1557
+ for (const content of item.content) {
1558
+ if (content?.transcript) {
1559
+ assistantTranscript += `${content.transcript} `;
1560
+ }
1561
+ }
1562
+ }
1563
+ else if (item?.type === "function_call") {
1564
+ const functionCallInfo = {
1565
+ name: item.name ?? "unknown",
1566
+ call_id: item.call_id ?? "unknown",
1567
+ };
1568
+ if (isObject(item) && "arguments" in item) {
1569
+ let argsStr = String(item.arguments);
1570
+ if (argsStr.length > 500) {
1571
+ argsStr = `${argsStr.slice(0, 500)}...`;
1572
+ }
1573
+ functionCallInfo.arguments = argsStr;
1574
+ }
1575
+ functionCalls.push(functionCallInfo);
1576
+ }
1577
+ }
1578
+ if (assistantTranscript.trim()) {
1579
+ operationAttrs.output = assistantTranscript.trim();
1580
+ }
1581
+ if (functionCalls.length > 0) {
1582
+ operationAttrs.functionCalls = functionCalls;
1583
+ operationAttrs["function_calls.count"] = functionCalls.length;
1584
+ const allNames = functionCalls.map((call) => call.name);
1585
+ operationAttrs["function_calls.all_names"] = allNames.join(",");
1586
+ }
1587
+ }
1588
+ }
1589
+ }
1590
+ return operationAttrs;
1591
+ }
1592
+ function makeOpenaiRealtimeWrapper(operation, func) {
1593
+ return async function wrapper(...args) {
1594
+ if (!this._tracingEnabled || !isTracingAvailable()) {
1595
+ return await func.apply(this, args);
1596
+ }
1597
+ let fnCalled = false;
1598
+ try {
1599
+ const serviceClassName = this.constructor.name;
1600
+ const spanName = `${operation}`;
1601
+ // Get the parent context - turn context if available, otherwise service context
1602
+ const parentContext = _getTurnContext(this) || _getParentServiceContext(this);
1603
+ // Create a new span as child of the turn span or service span
1604
+ return await withCurrentSpan(spanName, parentContext, async (currentSpan) => {
1605
+ try {
1606
+ // Base service attributes
1607
+ const modelName = _getModelName(this);
1608
+ // Operation-specific attribute collection
1609
+ const operationAttrs = openaiRealtimeOperationAttributes(this, operation, args);
1610
+ // Add all attributes to the span
1611
+ addOpenaiRealtimeSpanAttributes(currentSpan, {
1612
+ ...operationAttrs,
1613
+ serviceName: serviceClassName,
1614
+ model: modelName,
1615
+ operationName: operation,
1616
+ });
1617
+ // Capture TTFB metric if available
1618
+ if (operation === "llm_response") {
1619
+ const ttfb = this._metrics?.ttfb;
1620
+ if (ttfb !== null && ttfb !== undefined) {
1621
+ currentSpan.setAttribute("metrics.ttfb", ttfb);
1622
+ }
1623
+ }
1624
+ // Run the original function
1625
+ fnCalled = true;
1626
+ return await func.apply(this, args);
1627
+ }
1628
+ catch (e) {
1629
+ // Upstream's `except Exception` does not see CancelledError.
1630
+ if (!(e instanceof CancelledError)) {
1631
+ currentSpan.recordException?.(e);
1632
+ currentSpan.setStatus?.({ code: SPAN_STATUS_ERROR, message: errorMessage(e) });
1633
+ }
1634
+ throw e;
1635
+ }
1636
+ });
1637
+ }
1638
+ catch (e) {
1639
+ if (fnCalled || e instanceof CancelledError) {
1640
+ throw e;
1641
+ }
1642
+ logger.error(`Error in OpenAI Realtime tracing (continuing without tracing): ${errorMessage(e)}`);
1643
+ return await func.apply(this, args);
1644
+ }
1645
+ };
1646
+ }
1647
+ /**
1648
+ * Trace OpenAI Realtime service methods with operation-specific attributes.
1649
+ *
1650
+ * This decorator automatically captures relevant information based on the operation type:
1651
+ *
1652
+ * - llm_setup: Session configuration and tools
1653
+ * - llm_request: Context and input messages
1654
+ * - llm_response: Usage metadata, output, and function calls
1655
+ *
1656
+ * @param operation The operation name (matches the event type being handled).
1657
+ * @returns A decorator that wraps the method with OpenAI Realtime specific tracing.
1658
+ */
1659
+ export function tracedOpenaiRealtime(operation) {
1660
+ return makeMethodDecorator((func) => makeOpenaiRealtimeWrapper(operation, func), null);
1661
+ }
1662
+ //# sourceMappingURL=service_decorators.js.map