@moureau/pipeflow 0.0.1 → 0.0.3

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 (336) hide show
  1. package/README.md +280 -130
  2. package/dist/cjs/agents/agent.js +1 -1
  3. package/dist/cjs/agents/agent.js.map +3 -3
  4. package/dist/cjs/agents/tools/tools.js +1 -1
  5. package/dist/cjs/agents/tools/tools.js.map +3 -3
  6. package/dist/cjs/client/client.js +2 -0
  7. package/dist/cjs/client/client.js.map +7 -0
  8. package/dist/cjs/client/index.js +2 -0
  9. package/dist/cjs/client/index.js.map +7 -0
  10. package/dist/cjs/client/json.js +2 -0
  11. package/dist/cjs/client/json.js.map +7 -0
  12. package/dist/cjs/client/protocol/websocket.js +2 -0
  13. package/dist/cjs/client/protocol/websocket.js.map +7 -0
  14. package/dist/cjs/client/protocol.js +2 -0
  15. package/dist/cjs/client/protocol.js.map +7 -0
  16. package/dist/cjs/conversations/conversation/conversation.js +1 -1
  17. package/dist/cjs/conversations/conversation/conversation.js.map +3 -3
  18. package/dist/cjs/conversations/conversations.js +1 -1
  19. package/dist/cjs/conversations/conversations.js.map +3 -3
  20. package/dist/cjs/conversations/index.js +1 -1
  21. package/dist/cjs/conversations/index.js.map +3 -3
  22. package/dist/cjs/conversations/orchestration/coordination/coordination.js +10 -0
  23. package/dist/cjs/conversations/orchestration/coordination/coordination.js.map +7 -0
  24. package/dist/cjs/conversations/orchestration/coordination/index.js +2 -0
  25. package/dist/cjs/conversations/orchestration/coordination/index.js.map +7 -0
  26. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js +4 -0
  27. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js.map +7 -0
  28. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/index.js +2 -0
  29. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/index.js.map +7 -0
  30. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js +2 -0
  31. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js.map +7 -0
  32. package/dist/cjs/conversations/orchestration/orchestrator/generation/index.js +2 -0
  33. package/dist/cjs/conversations/orchestration/orchestrator/generation/index.js.map +7 -0
  34. package/dist/cjs/conversations/orchestration/orchestrator/history/history.js +4 -0
  35. package/dist/cjs/conversations/orchestration/orchestrator/history/history.js.map +7 -0
  36. package/dist/cjs/conversations/orchestration/orchestrator/history/index.js +2 -0
  37. package/dist/cjs/conversations/orchestration/orchestrator/history/index.js.map +7 -0
  38. package/dist/cjs/conversations/orchestration/orchestrator/index.js +1 -1
  39. package/dist/cjs/conversations/orchestration/orchestrator/index.js.map +3 -3
  40. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js +1 -2
  41. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  42. package/dist/cjs/conversations/orchestration/orchestrator/routing/index.js +2 -0
  43. package/dist/cjs/conversations/orchestration/orchestrator/routing/index.js.map +7 -0
  44. package/dist/cjs/conversations/orchestration/orchestrator/routing/routing.js +23 -0
  45. package/dist/cjs/conversations/orchestration/orchestrator/routing/routing.js.map +7 -0
  46. package/dist/cjs/conversations/orchestration/orchestrator/speech/index.js +2 -0
  47. package/dist/cjs/conversations/orchestration/orchestrator/speech/index.js.map +7 -0
  48. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js +2 -0
  49. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js.map +7 -0
  50. package/dist/cjs/conversations/orchestration/orchestrator/tools/index.js +2 -0
  51. package/dist/cjs/conversations/orchestration/orchestrator/tools/index.js.map +7 -0
  52. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js +2 -0
  53. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js.map +7 -0
  54. package/dist/cjs/conversations/orchestration/test-harness.js +2 -0
  55. package/dist/cjs/conversations/orchestration/test-harness.js.map +7 -0
  56. package/dist/cjs/conversations/orchestration/text-chunker/index.js +2 -0
  57. package/dist/cjs/conversations/orchestration/text-chunker/index.js.map +7 -0
  58. package/dist/cjs/conversations/orchestration/text-chunker/text-chunker.js +3 -0
  59. package/dist/cjs/conversations/orchestration/text-chunker/text-chunker.js.map +7 -0
  60. package/dist/cjs/conversations/stream/conversation-stream.js +2 -0
  61. package/dist/cjs/conversations/stream/conversation-stream.js.map +7 -0
  62. package/dist/cjs/conversations/stream/index.js +2 -0
  63. package/dist/cjs/conversations/stream/index.js.map +7 -0
  64. package/dist/cjs/conversations/types.js +1 -1
  65. package/dist/cjs/conversations/types.js.map +2 -2
  66. package/dist/cjs/index.js +1 -1
  67. package/dist/cjs/index.js.map +3 -3
  68. package/dist/cjs/persistence/adapters/sqlite/sqlite.js +18 -14
  69. package/dist/cjs/persistence/adapters/sqlite/sqlite.js.map +3 -3
  70. package/dist/cjs/pipeflow/index.js +2 -0
  71. package/dist/cjs/pipeflow/index.js.map +7 -0
  72. package/dist/cjs/pipeflow/pipeflow.js +2 -0
  73. package/dist/cjs/pipeflow/pipeflow.js.map +7 -0
  74. package/dist/cjs/providers/index.js +1 -1
  75. package/dist/cjs/providers/index.js.map +2 -2
  76. package/dist/cjs/providers/llm/adapters/claude/claude.js +5 -0
  77. package/dist/cjs/providers/llm/adapters/claude/claude.js.map +7 -0
  78. package/dist/cjs/providers/llm/adapters/claude/index.js +2 -0
  79. package/dist/cjs/providers/llm/adapters/claude/index.js.map +7 -0
  80. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js +1 -1
  81. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js.map +3 -3
  82. package/dist/cjs/providers/llm/adapters/index.js +1 -1
  83. package/dist/cjs/providers/llm/adapters/index.js.map +3 -3
  84. package/dist/cjs/providers/llm/adapters/openai/index.js +2 -0
  85. package/dist/cjs/providers/llm/adapters/openai/index.js.map +7 -0
  86. package/dist/cjs/providers/llm/adapters/openai/openai.js +2 -0
  87. package/dist/cjs/providers/llm/adapters/openai/openai.js.map +7 -0
  88. package/dist/cjs/providers/llm/adapters/openai-compatible.js +10 -0
  89. package/dist/cjs/providers/llm/adapters/openai-compatible.js.map +7 -0
  90. package/dist/cjs/providers/llm/adapters/openrouter/index.js +2 -0
  91. package/dist/cjs/providers/llm/adapters/openrouter/index.js.map +7 -0
  92. package/dist/cjs/providers/llm/adapters/openrouter/openrouter.js +2 -0
  93. package/dist/cjs/providers/llm/adapters/openrouter/openrouter.js.map +7 -0
  94. package/dist/cjs/providers/llm/index.js +1 -1
  95. package/dist/cjs/providers/llm/index.js.map +3 -3
  96. package/dist/cjs/providers/llm/toolmode/index.js +2 -0
  97. package/dist/cjs/providers/llm/toolmode/index.js.map +7 -0
  98. package/dist/cjs/providers/llm/toolmode/toolmode.js +2 -0
  99. package/dist/cjs/providers/llm/toolmode/toolmode.js.map +7 -0
  100. package/dist/cjs/providers/llm/types.js +1 -1
  101. package/dist/cjs/providers/llm/types.js.map +3 -3
  102. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js +1 -1
  103. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js.map +3 -3
  104. package/dist/cjs/providers/stt/adapters/index.js +1 -1
  105. package/dist/cjs/providers/stt/adapters/index.js.map +3 -3
  106. package/dist/cjs/providers/stt/adapters/openrouter/index.js +2 -0
  107. package/dist/cjs/providers/stt/adapters/openrouter/index.js.map +7 -0
  108. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js +2 -0
  109. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js.map +7 -0
  110. package/dist/cjs/providers/tts/adapters/index.js +1 -1
  111. package/dist/cjs/providers/tts/adapters/index.js.map +3 -3
  112. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js +4 -1
  113. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  114. package/dist/cjs/providers/tts/adapters/openrouter/index.js +2 -0
  115. package/dist/cjs/providers/tts/adapters/openrouter/index.js.map +7 -0
  116. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js +2 -0
  117. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js.map +7 -0
  118. package/dist/cjs/transport/streamobject/field-stream/field-stream.js +2 -0
  119. package/dist/cjs/transport/streamobject/field-stream/field-stream.js.map +7 -0
  120. package/dist/cjs/transport/streamobject/field-stream/index.js +2 -0
  121. package/dist/cjs/transport/streamobject/field-stream/index.js.map +7 -0
  122. package/dist/cjs/transport/streamobject/index.js +2 -0
  123. package/dist/cjs/transport/streamobject/index.js.map +7 -0
  124. package/dist/cjs/transport/streamobject/json-adapter/index.js +2 -0
  125. package/dist/cjs/transport/streamobject/json-adapter/index.js.map +7 -0
  126. package/dist/cjs/transport/streamobject/json-adapter/json-adapter.js +4 -0
  127. package/dist/cjs/transport/streamobject/json-adapter/json-adapter.js.map +7 -0
  128. package/dist/cjs/transport/streamobject/reference/index.js +2 -0
  129. package/dist/cjs/transport/streamobject/reference/index.js.map +7 -0
  130. package/dist/cjs/transport/streamobject/reference/reference.js +2 -0
  131. package/dist/cjs/transport/streamobject/reference/reference.js.map +7 -0
  132. package/dist/cjs/transport/streamobject/stream-object/index.js +2 -0
  133. package/dist/cjs/transport/streamobject/stream-object/index.js.map +7 -0
  134. package/dist/cjs/transport/streamobject/stream-object/stream-object.js +2 -0
  135. package/dist/cjs/transport/streamobject/stream-object/stream-object.js.map +7 -0
  136. package/dist/esm/agents/agent.js +1 -1
  137. package/dist/esm/agents/agent.js.map +3 -3
  138. package/dist/esm/agents/tools/tools.js +1 -1
  139. package/dist/esm/agents/tools/tools.js.map +3 -3
  140. package/dist/esm/client/client.js +2 -0
  141. package/dist/esm/client/client.js.map +7 -0
  142. package/dist/esm/client/index.js +2 -0
  143. package/dist/esm/client/index.js.map +7 -0
  144. package/dist/esm/client/json.js +2 -0
  145. package/dist/esm/client/json.js.map +7 -0
  146. package/dist/esm/client/protocol/websocket.js +2 -0
  147. package/dist/esm/client/protocol/websocket.js.map +7 -0
  148. package/dist/esm/client/protocol.js +1 -0
  149. package/dist/esm/client/protocol.js.map +7 -0
  150. package/dist/esm/conversations/conversation/conversation.js +1 -1
  151. package/dist/esm/conversations/conversation/conversation.js.map +3 -3
  152. package/dist/esm/conversations/conversations.js +1 -1
  153. package/dist/esm/conversations/conversations.js.map +3 -3
  154. package/dist/esm/conversations/index.js +1 -1
  155. package/dist/esm/conversations/index.js.map +3 -3
  156. package/dist/esm/conversations/orchestration/coordination/coordination.js +10 -0
  157. package/dist/esm/conversations/orchestration/coordination/coordination.js.map +7 -0
  158. package/dist/esm/conversations/orchestration/coordination/index.js +2 -0
  159. package/dist/esm/conversations/orchestration/coordination/index.js.map +7 -0
  160. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js +4 -0
  161. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js.map +7 -0
  162. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/index.js +2 -0
  163. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/index.js.map +7 -0
  164. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js +2 -0
  165. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js.map +7 -0
  166. package/dist/esm/conversations/orchestration/orchestrator/generation/index.js +2 -0
  167. package/dist/esm/conversations/orchestration/orchestrator/generation/index.js.map +7 -0
  168. package/dist/esm/conversations/orchestration/orchestrator/history/history.js +4 -0
  169. package/dist/esm/conversations/orchestration/orchestrator/history/history.js.map +7 -0
  170. package/dist/esm/conversations/orchestration/orchestrator/history/index.js +2 -0
  171. package/dist/esm/conversations/orchestration/orchestrator/history/index.js.map +7 -0
  172. package/dist/esm/conversations/orchestration/orchestrator/index.js +1 -1
  173. package/dist/esm/conversations/orchestration/orchestrator/index.js.map +3 -3
  174. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js +1 -2
  175. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  176. package/dist/esm/conversations/orchestration/orchestrator/routing/index.js +2 -0
  177. package/dist/esm/conversations/orchestration/orchestrator/routing/index.js.map +7 -0
  178. package/dist/esm/conversations/orchestration/orchestrator/routing/routing.js +23 -0
  179. package/dist/esm/conversations/orchestration/orchestrator/routing/routing.js.map +7 -0
  180. package/dist/esm/conversations/orchestration/orchestrator/speech/index.js +2 -0
  181. package/dist/esm/conversations/orchestration/orchestrator/speech/index.js.map +7 -0
  182. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js +2 -0
  183. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js.map +7 -0
  184. package/dist/esm/conversations/orchestration/orchestrator/tools/index.js +2 -0
  185. package/dist/esm/conversations/orchestration/orchestrator/tools/index.js.map +7 -0
  186. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js +2 -0
  187. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js.map +7 -0
  188. package/dist/esm/conversations/orchestration/test-harness.js +2 -0
  189. package/dist/esm/conversations/orchestration/test-harness.js.map +7 -0
  190. package/dist/esm/conversations/orchestration/text-chunker/index.js +2 -0
  191. package/dist/esm/conversations/orchestration/text-chunker/index.js.map +7 -0
  192. package/dist/esm/conversations/orchestration/text-chunker/text-chunker.js +3 -0
  193. package/dist/esm/conversations/orchestration/text-chunker/text-chunker.js.map +7 -0
  194. package/dist/esm/conversations/stream/conversation-stream.js +2 -0
  195. package/dist/esm/conversations/stream/conversation-stream.js.map +7 -0
  196. package/dist/esm/conversations/stream/index.js +2 -0
  197. package/dist/esm/conversations/stream/index.js.map +7 -0
  198. package/dist/esm/conversations/types.js +1 -1
  199. package/dist/esm/conversations/types.js.map +2 -2
  200. package/dist/esm/index.js +1 -1
  201. package/dist/esm/index.js.map +3 -3
  202. package/dist/esm/persistence/adapters/sqlite/sqlite.js +18 -14
  203. package/dist/esm/persistence/adapters/sqlite/sqlite.js.map +3 -3
  204. package/dist/esm/pipeflow/index.js +2 -0
  205. package/dist/esm/pipeflow/index.js.map +7 -0
  206. package/dist/esm/pipeflow/pipeflow.js +2 -0
  207. package/dist/esm/pipeflow/pipeflow.js.map +7 -0
  208. package/dist/esm/providers/index.js +1 -1
  209. package/dist/esm/providers/index.js.map +3 -3
  210. package/dist/esm/providers/llm/adapters/claude/claude.js +5 -0
  211. package/dist/esm/providers/llm/adapters/claude/claude.js.map +7 -0
  212. package/dist/esm/providers/llm/adapters/claude/index.js +2 -0
  213. package/dist/esm/providers/llm/adapters/claude/index.js.map +7 -0
  214. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js +1 -1
  215. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js.map +3 -3
  216. package/dist/esm/providers/llm/adapters/index.js +1 -1
  217. package/dist/esm/providers/llm/adapters/index.js.map +3 -3
  218. package/dist/esm/providers/llm/adapters/openai/index.js +2 -0
  219. package/dist/esm/providers/llm/adapters/openai/index.js.map +7 -0
  220. package/dist/esm/providers/llm/adapters/openai/openai.js +2 -0
  221. package/dist/esm/providers/llm/adapters/openai/openai.js.map +7 -0
  222. package/dist/esm/providers/llm/adapters/openai-compatible.js +10 -0
  223. package/dist/esm/providers/llm/adapters/openai-compatible.js.map +7 -0
  224. package/dist/esm/providers/llm/adapters/openrouter/index.js +2 -0
  225. package/dist/esm/providers/llm/adapters/openrouter/index.js.map +7 -0
  226. package/dist/esm/providers/llm/adapters/openrouter/openrouter.js +2 -0
  227. package/dist/esm/providers/llm/adapters/openrouter/openrouter.js.map +7 -0
  228. package/dist/esm/providers/llm/index.js +1 -1
  229. package/dist/esm/providers/llm/index.js.map +3 -3
  230. package/dist/esm/providers/llm/toolmode/index.js +2 -0
  231. package/dist/esm/providers/llm/toolmode/index.js.map +7 -0
  232. package/dist/esm/providers/llm/toolmode/toolmode.js +2 -0
  233. package/dist/esm/providers/llm/toolmode/toolmode.js.map +7 -0
  234. package/dist/esm/providers/llm/types.js +1 -0
  235. package/dist/esm/providers/llm/types.js.map +4 -4
  236. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js +1 -1
  237. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js.map +3 -3
  238. package/dist/esm/providers/stt/adapters/index.js +1 -1
  239. package/dist/esm/providers/stt/adapters/index.js.map +3 -3
  240. package/dist/esm/providers/stt/adapters/openrouter/index.js +2 -0
  241. package/dist/esm/providers/stt/adapters/openrouter/index.js.map +7 -0
  242. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js +2 -0
  243. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js.map +7 -0
  244. package/dist/esm/providers/tts/adapters/index.js +1 -1
  245. package/dist/esm/providers/tts/adapters/index.js.map +3 -3
  246. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js +4 -1
  247. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  248. package/dist/esm/providers/tts/adapters/openrouter/index.js +2 -0
  249. package/dist/esm/providers/tts/adapters/openrouter/index.js.map +7 -0
  250. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js +2 -0
  251. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js.map +7 -0
  252. package/dist/esm/transport/streamobject/field-stream/field-stream.js +2 -0
  253. package/dist/esm/transport/streamobject/field-stream/field-stream.js.map +7 -0
  254. package/dist/esm/transport/streamobject/field-stream/index.js +2 -0
  255. package/dist/esm/transport/streamobject/field-stream/index.js.map +7 -0
  256. package/dist/esm/transport/streamobject/index.js +2 -0
  257. package/dist/esm/transport/streamobject/index.js.map +7 -0
  258. package/dist/esm/transport/streamobject/json-adapter/index.js +2 -0
  259. package/dist/esm/transport/streamobject/json-adapter/index.js.map +7 -0
  260. package/dist/esm/transport/streamobject/json-adapter/json-adapter.js +4 -0
  261. package/dist/esm/transport/streamobject/json-adapter/json-adapter.js.map +7 -0
  262. package/dist/esm/transport/streamobject/reference/index.js +2 -0
  263. package/dist/esm/transport/streamobject/reference/index.js.map +7 -0
  264. package/dist/esm/transport/streamobject/reference/reference.js +2 -0
  265. package/dist/esm/transport/streamobject/reference/reference.js.map +7 -0
  266. package/dist/esm/transport/streamobject/stream-object/index.js +2 -0
  267. package/dist/esm/transport/streamobject/stream-object/index.js.map +7 -0
  268. package/dist/esm/transport/streamobject/stream-object/stream-object.js +2 -0
  269. package/dist/esm/transport/streamobject/stream-object/stream-object.js.map +7 -0
  270. package/dist/types/agents/agent.d.ts +3 -0
  271. package/dist/types/agents/tools/tools.d.ts +28 -6
  272. package/dist/types/client/client.d.ts +186 -0
  273. package/dist/types/client/index.d.ts +31 -0
  274. package/dist/types/client/json.d.ts +13 -0
  275. package/dist/types/client/protocol/websocket.d.ts +56 -0
  276. package/dist/types/client/protocol.d.ts +46 -0
  277. package/dist/types/conversations/conversation/conversation.d.ts +130 -6
  278. package/dist/types/conversations/conversations.d.ts +35 -1
  279. package/dist/types/conversations/index.d.ts +2 -1
  280. package/dist/types/conversations/orchestration/coordination/coordination.d.ts +192 -0
  281. package/dist/types/conversations/orchestration/coordination/index.d.ts +2 -0
  282. package/dist/types/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.d.ts +108 -0
  283. package/dist/types/conversations/orchestration/orchestrator/coordination-runner/index.d.ts +2 -0
  284. package/dist/types/conversations/orchestration/orchestrator/generation/generation.d.ts +60 -0
  285. package/dist/types/conversations/orchestration/orchestrator/generation/index.d.ts +2 -0
  286. package/dist/types/conversations/orchestration/orchestrator/history/history.d.ts +62 -0
  287. package/dist/types/conversations/orchestration/orchestrator/history/index.d.ts +1 -0
  288. package/dist/types/conversations/orchestration/orchestrator/index.d.ts +10 -0
  289. package/dist/types/conversations/orchestration/orchestrator/orchestrator.d.ts +68 -31
  290. package/dist/types/conversations/orchestration/orchestrator/routing/index.d.ts +1 -0
  291. package/dist/types/conversations/orchestration/orchestrator/routing/routing.d.ts +19 -0
  292. package/dist/types/conversations/orchestration/orchestrator/speech/index.d.ts +2 -0
  293. package/dist/types/conversations/orchestration/orchestrator/speech/speech.d.ts +78 -0
  294. package/dist/types/conversations/orchestration/orchestrator/tools/index.d.ts +2 -0
  295. package/dist/types/conversations/orchestration/orchestrator/tools/tools.d.ts +65 -0
  296. package/dist/types/conversations/orchestration/test-harness.d.ts +100 -0
  297. package/dist/types/conversations/orchestration/text-chunker/index.d.ts +1 -0
  298. package/dist/types/conversations/orchestration/text-chunker/text-chunker.d.ts +39 -0
  299. package/dist/types/conversations/stream/conversation-stream.d.ts +42 -0
  300. package/dist/types/conversations/stream/index.d.ts +1 -0
  301. package/dist/types/conversations/types.d.ts +32 -0
  302. package/dist/types/index.d.ts +5 -38
  303. package/dist/types/pipeflow/index.d.ts +2 -0
  304. package/dist/types/pipeflow/pipeflow.d.ts +43 -0
  305. package/dist/types/providers/index.d.ts +1 -1
  306. package/dist/types/providers/llm/adapters/claude/claude.d.ts +45 -0
  307. package/dist/types/providers/llm/adapters/claude/index.d.ts +2 -0
  308. package/dist/types/providers/llm/adapters/deepseek/deepseek.d.ts +23 -2
  309. package/dist/types/providers/llm/adapters/index.d.ts +3 -0
  310. package/dist/types/providers/llm/adapters/openai/index.d.ts +2 -0
  311. package/dist/types/providers/llm/adapters/openai/openai.d.ts +45 -0
  312. package/dist/types/providers/llm/adapters/openai-compatible.d.ts +44 -0
  313. package/dist/types/providers/llm/adapters/openrouter/index.d.ts +2 -0
  314. package/dist/types/providers/llm/adapters/openrouter/openrouter.d.ts +60 -0
  315. package/dist/types/providers/llm/index.d.ts +5 -2
  316. package/dist/types/providers/llm/toolmode/index.d.ts +2 -0
  317. package/dist/types/providers/llm/toolmode/toolmode.d.ts +99 -0
  318. package/dist/types/providers/llm/types.d.ts +65 -0
  319. package/dist/types/providers/stt/adapters/deepgram/deepgram.d.ts +4 -1
  320. package/dist/types/providers/stt/adapters/index.d.ts +1 -0
  321. package/dist/types/providers/stt/adapters/openrouter/index.d.ts +2 -0
  322. package/dist/types/providers/stt/adapters/openrouter/openrouter.d.ts +196 -0
  323. package/dist/types/providers/tts/adapters/index.d.ts +1 -0
  324. package/dist/types/providers/tts/adapters/kokoro/kokoro.d.ts +18 -3
  325. package/dist/types/providers/tts/adapters/openrouter/index.d.ts +2 -0
  326. package/dist/types/providers/tts/adapters/openrouter/openrouter.d.ts +64 -0
  327. package/dist/types/transport/streamobject/field-stream/field-stream.d.ts +85 -0
  328. package/dist/types/transport/streamobject/field-stream/index.d.ts +2 -0
  329. package/dist/types/transport/streamobject/index.d.ts +8 -0
  330. package/dist/types/transport/streamobject/json-adapter/index.d.ts +2 -0
  331. package/dist/types/transport/streamobject/json-adapter/json-adapter.d.ts +65 -0
  332. package/dist/types/transport/streamobject/reference/index.d.ts +2 -0
  333. package/dist/types/transport/streamobject/reference/reference.d.ts +127 -0
  334. package/dist/types/transport/streamobject/stream-object/index.d.ts +2 -0
  335. package/dist/types/transport/streamobject/stream-object/stream-object.d.ts +31 -0
  336. package/package.json +29 -2
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/llm/adapters/openrouter/openrouter.ts"],
4
+ "sourcesContent": ["import type {\n FavoriteModel,\n LLM,\n LLMEvent,\n LLMRequest,\n LLMStreamTimingCallback,\n LLMUsageCallback,\n ToolMode,\n} from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\nimport { openAICompatibleStream } from \"../openai-compatible\";\n\nexport interface OpenRouterOptions {\n apiKey: string;\n /**\n * Any model hosted on OpenRouter, e.g. `anthropic/claude-sonnet-4`.\n * Defaults to `openrouter/auto`, which routes the request to the best\n * available model for its shape. The measured `FAVORITE_MODELS`\n * autocomplete; any model id is accepted.\n */\n model?: FavoriteModel;\n /** Defaults to `https://openrouter.ai/api/v1`. */\n baseUrl?: string;\n /**\n * The site that credits the app on OpenRouter's leaderboard via\n * `HTTP-Referer`. Defaults to `https://moureau.dev`.\n */\n appUrl?: string;\n /** Injectable fetch implementation, mainly for tests. */\n fetch?: FetchLike;\n /** Provider-timeline hook: request-start / headers / first-chunk. */\n onTiming?: LLMStreamTimingCallback;\n /**\n * Abort a stream that delivers no data for this long (default 8000ms) \u2014\n * protects against provider connections that go silent after the model\n * already produced its output. Raise it for providers with slow TTFT.\n */\n idleTimeoutMs?: number;\n /** Called with the provider-reported token usage (when included). */\n onUsage?: LLMUsageCallback;\n /**\n * Default tool encoding for requests that don't set `toolMode` (the\n * request's `toolMode` wins). For models whose endpoints lack native tool\n * calling \u2014 e.g. `sao10k/l3-lunaris-8b` -> `\"envelope\"`.\n */\n toolMode?: ToolMode;\n}\n\n/**\n * OpenRouter LLM adapter (OpenAI-compatible chat completions over SSE).\n *\n * OpenRouter is a gateway: one API key and one wire format for hundreds of\n * models from different vendors, so this adapter is a thin wrapper around\n * the shared OpenAI-compatible streaming engine.\n */\nexport class OpenRouterLLM implements LLM {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string;\n private readonly appUrl: string;\n private readonly fetchImpl: FetchLike;\n private readonly onTiming: LLMStreamTimingCallback | undefined;\n private readonly idleTimeoutMs: number;\n private readonly onUsage: LLMUsageCallback | undefined;\n private readonly toolMode: ToolMode | undefined;\n private readonly streams = new Set<AbortController>();\n\n constructor(options: OpenRouterOptions) {\n if (!options.apiKey) {\n throw new Error(\"OpenRouterLLM requires an apiKey\");\n }\n this.apiKey = options.apiKey;\n this.model = options.model ?? \"openrouter/auto\";\n this.baseUrl = (options.baseUrl ?? \"https://openrouter.ai/api/v1\").replace(/\\/+$/, \"\");\n this.appUrl = options.appUrl ?? \"https://moureau.dev\";\n this.fetchImpl = options.fetch ?? fetch;\n this.onTiming = options.onTiming;\n this.idleTimeoutMs = options.idleTimeoutMs ?? 8_000;\n this.onUsage = options.onUsage;\n this.toolMode = options.toolMode;\n }\n\n /** Cancel every in-flight stream (parallel sub-generations included). */\n stop(): void {\n for (const controller of this.streams) controller.abort();\n }\n\n async *stream(request: LLMRequest): AsyncGenerator<LLMEvent> {\n // Multiple generations can stream concurrently (e.g. delegated\n // sub-agents); each gets its own controller, and stop() aborts them all.\n const controller = new AbortController();\n this.streams.add(controller);\n\n try {\n // OpenRouter credits app usage on its leaderboard from HTTP-Referer\n // (defaulting to moureau.dev) and X-Title (always `pipeflow`).\n yield* openAICompatibleStream({\n baseUrl: this.baseUrl,\n apiKey: this.apiKey,\n model: this.model,\n fetchImpl: this.fetchImpl,\n request,\n signal: controller.signal,\n extraHeaders: {\n \"http-referer\": this.appUrl,\n \"x-title\": \"pipeflow\",\n },\n label: \"OpenRouter\",\n onTiming: this.onTiming,\n idleTimeoutMs: this.idleTimeoutMs,\n onUsage: this.onUsage,\n toolMode: this.toolMode,\n });\n } finally {\n this.streams.delete(controller);\n }\n }\n}\n"],
5
+ "mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mBAAAE,IAAA,eAAAC,EAAAH,GAUA,IAAAI,EAAuC,gCA6ChC,MAAMC,CAA6B,CAvD1C,MAuD0C,CAAAC,EAAA,sBACvB,OACA,MACA,QACA,OACA,UACA,SACA,cACA,QACA,SACA,QAAU,IAAI,IAE/B,YAAYC,EAA4B,CACtC,GAAI,CAACA,EAAQ,OACX,MAAM,IAAI,MAAM,kCAAkC,EAEpD,KAAK,OAASA,EAAQ,OACtB,KAAK,MAAQA,EAAQ,OAAS,kBAC9B,KAAK,SAAWA,EAAQ,SAAW,gCAAgC,QAAQ,OAAQ,EAAE,EACrF,KAAK,OAASA,EAAQ,QAAU,sBAChC,KAAK,UAAYA,EAAQ,OAAS,MAClC,KAAK,SAAWA,EAAQ,SACxB,KAAK,cAAgBA,EAAQ,eAAiB,IAC9C,KAAK,QAAUA,EAAQ,QACvB,KAAK,SAAWA,EAAQ,QAC1B,CAGA,MAAa,CACX,UAAWC,KAAc,KAAK,QAASA,EAAW,MAAM,CAC1D,CAEA,MAAO,OAAOC,EAA+C,CAG3D,MAAMD,EAAa,IAAI,gBACvB,KAAK,QAAQ,IAAIA,CAAU,EAE3B,GAAI,CAGF,SAAO,0BAAuB,CAC5B,QAAS,KAAK,QACd,OAAQ,KAAK,OACb,MAAO,KAAK,MACZ,UAAW,KAAK,UAChB,QAAAC,EACA,OAAQD,EAAW,OACnB,aAAc,CACZ,eAAgB,KAAK,OACrB,UAAW,UACb,EACA,MAAO,aACP,SAAU,KAAK,SACf,cAAe,KAAK,cACpB,QAAS,KAAK,QACd,SAAU,KAAK,QACjB,CAAC,CACH,QAAE,CACA,KAAK,QAAQ,OAAOA,CAAU,CAChC,CACF,CACF",
6
+ "names": ["openrouter_exports", "__export", "OpenRouterLLM", "__toCommonJS", "import_openai_compatible", "OpenRouterLLM", "__name", "options", "controller", "request"]
7
+ }
@@ -1,2 +1,2 @@
1
- "use strict";var n=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var s=(t,e)=>n(t,"name",{value:e,configurable:!0});var c=(t,e)=>{for(var o in e)n(t,o,{get:e[o],enumerable:!0})},l=(t,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let L of a(e))!f.call(t,L)&&L!==o&&n(t,L,{get:()=>e[L],enumerable:!(r=p(e,L))||r.enumerable});return t};var y=t=>l(n({},"__esModule",{value:!0}),t);var u={};c(u,{DeepSeekLLM:()=>i.DeepSeekLLM,complete:()=>m,streamText:()=>M});module.exports=y(u);var i=require("./adapters/index.js");async function*M(t,e){for await(const o of t.stream(e))if(o.type==="delta")yield o.content;else if(o.type==="error")throw o.error}s(M,"streamText");async function m(t,e){let o="";for await(const r of M(t,e))o+=r;return o}s(m,"complete");
1
+ "use strict";var M=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var p=(o,e)=>M(o,"name",{value:e,configurable:!0});var c=(o,e)=>{for(var t in e)M(o,t,{get:e[t],enumerable:!0})},u=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let L of m(e))!f.call(o,L)&&L!==t&&M(o,L,{get:()=>e[L],enumerable:!(n=a(e,L))||n.enumerable});return o};var y=o=>u(M({},"__esModule",{value:!0}),o);var T={};c(T,{ClaudeLLM:()=>r.ClaudeLLM,DeepSeekLLM:()=>r.DeepSeekLLM,FAVORITE_MODELS:()=>l.FAVORITE_MODELS,OpenAILLM:()=>r.OpenAILLM,OpenRouterLLM:()=>r.OpenRouterLLM,ToolModeBenchmark:()=>i.ToolModeBenchmark,complete:()=>d,streamText:()=>s});module.exports=y(T);var l=require("./types.js"),r=require("./adapters/index.js"),i=require("./toolmode.js");async function*s(o,e){for await(const t of o.stream(e))if(t.type==="delta")yield t.content;else if(t.type==="error")throw t.error}p(s,"streamText");async function d(o,e){let t="";for await(const n of s(o,e))t+=n;return t}p(d,"complete");
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/providers/llm/index.ts"],
4
- "sourcesContent": ["import type { LLM, LLMRequest } from \"./types\";\n\nexport type {\n LLM,\n LLMEvent,\n LLMMessage,\n LLMRole,\n LLMToolCall,\n LLMToolDefinition,\n LLMRequest,\n} from \"./types\";\n\n/**\n * Stream only the text deltas of an LLM generation, rethrowing any\n * streamed error.\n */\nexport async function* streamText(\n llm: LLM,\n request: LLMRequest,\n): AsyncGenerator<string> {\n for await (const event of llm.stream(request)) {\n if (event.type === \"delta\") {\n yield event.content;\n } else if (event.type === \"error\") {\n throw event.error;\n }\n }\n}\n\n/** Collect an entire LLM generation into a single string. */\nexport async function complete(llm: LLM, request: LLMRequest): Promise<string> {\n let text = \"\";\n for await (const chunk of streamText(llm, request)) {\n text += chunk;\n }\n return text;\n}\n\nexport { DeepSeekLLM } from \"./adapters/index\";\n"],
5
- "mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4CAAAE,EAAA,eAAAC,IAAA,eAAAC,EAAAJ,GAsCA,IAAAK,EAA4B,4BAtB5B,eAAuBC,EACrBC,EACAC,EACwB,CACxB,gBAAiBC,KAASF,EAAI,OAAOC,CAAO,EAC1C,GAAIC,EAAM,OAAS,QACjB,MAAMA,EAAM,gBACHA,EAAM,OAAS,QACxB,MAAMA,EAAM,KAGlB,CAXuBC,EAAAJ,EAAA,cAcvB,eAAsBK,EAASJ,EAAUC,EAAsC,CAC7E,IAAII,EAAO,GACX,gBAAiBC,KAASP,EAAWC,EAAKC,CAAO,EAC/CI,GAAQC,EAEV,OAAOD,CACT,CANsBF,EAAAC,EAAA",
6
- "names": ["llm_exports", "__export", "complete", "streamText", "__toCommonJS", "import_adapters", "streamText", "llm", "request", "event", "__name", "complete", "text", "chunk"]
4
+ "sourcesContent": ["import type { LLM, LLMRequest } from \"./types\";\n\nexport type {\n FavoriteModel,\n LLM,\n LLMEvent,\n LLMMessage,\n LLMRole,\n LLMToolCall,\n LLMToolDefinition,\n LLMRequest,\n StringOr,\n} from \"./types\";\n\nexport { FAVORITE_MODELS } from \"./types\";\n\n/**\n * Stream only the text deltas of an LLM generation, rethrowing any\n * streamed error.\n */\nexport async function* streamText(\n llm: LLM,\n request: LLMRequest,\n): AsyncGenerator<string> {\n for await (const event of llm.stream(request)) {\n if (event.type === \"delta\") {\n yield event.content;\n } else if (event.type === \"error\") {\n throw event.error;\n }\n }\n}\n\n/** Collect an entire LLM generation into a single string. */\nexport async function complete(llm: LLM, request: LLMRequest): Promise<string> {\n let text = \"\";\n for await (const chunk of streamText(llm, request)) {\n text += chunk;\n }\n return text;\n}\n\nexport { ClaudeLLM, DeepSeekLLM, OpenAILLM, OpenRouterLLM } from \"./adapters/index\";\nexport { ToolModeBenchmark } from \"./toolmode\";\nexport type {\n ToolModeBenchmarkOptions,\n ToolModeBenchmarkResult,\n ToolModeBenchmarkRun,\n ToolModeReportEntry,\n ToolModeTiming,\n} from \"./toolmode\";\n"],
5
+ "mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kNAAAE,EAAA,eAAAC,IAAA,eAAAC,EAAAJ,GAcA,IAAAK,EAAgC,mBA4BhCC,EAAiE,4BACjEC,EAAkC,sBAvBlC,eAAuBC,EACrBC,EACAC,EACwB,CACxB,gBAAiBC,KAASF,EAAI,OAAOC,CAAO,EAC1C,GAAIC,EAAM,OAAS,QACjB,MAAMA,EAAM,gBACHA,EAAM,OAAS,QACxB,MAAMA,EAAM,KAGlB,CAXuBC,EAAAJ,EAAA,cAcvB,eAAsBK,EAASJ,EAAUC,EAAsC,CAC7E,IAAII,EAAO,GACX,gBAAiBC,KAASP,EAAWC,EAAKC,CAAO,EAC/CI,GAAQC,EAEV,OAAOD,CACT,CANsBF,EAAAC,EAAA",
6
+ "names": ["llm_exports", "__export", "complete", "streamText", "__toCommonJS", "import_types", "import_adapters", "import_toolmode", "streamText", "llm", "request", "event", "__name", "complete", "text", "chunk"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ "use strict";var m=Object.defineProperty;var d=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var M=Object.prototype.hasOwnProperty;var p=(e,o)=>{for(var l in o)m(e,l,{get:o[l],enumerable:!0})},a=(e,o,l,n)=>{if(o&&typeof o=="object"||typeof o=="function")for(let r of T(o))!M.call(e,r)&&r!==l&&m(e,r,{get:()=>o[r],enumerable:!(n=d(o,r))||n.enumerable});return e};var c=e=>a(m({},"__esModule",{value:!0}),e);var h={};p(h,{ToolModeBenchmark:()=>t.ToolModeBenchmark});module.exports=c(h);var t=require("./toolmode.js");
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/providers/llm/toolmode/index.ts"],
4
+ "sourcesContent": ["export { ToolModeBenchmark } from \"./toolmode\";\nexport type {\n ToolModeBenchmarkOptions,\n ToolModeBenchmarkResult,\n ToolModeBenchmarkRun,\n ToolModeReportEntry,\n ToolModeTiming,\n} from \"./toolmode\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4DAAAE,EAAAF,GAAA,IAAAG,EAAkC",
6
+ "names": ["toolmode_exports", "__export", "__toCommonJS", "import_toolmode"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var h=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var E=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var l=(n,e)=>h(n,"name",{value:e,configurable:!0});var C=(n,e)=>{for(var o in e)h(n,o,{get:e[o],enumerable:!0})},A=(n,e,o,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of E(e))!S.call(n,i)&&i!==o&&h(n,i,{get:()=>e[i],enumerable:!(r=v(e,i))||r.enumerable});return n};var B=n=>A(h({},"__esModule",{value:!0}),n);var O={};C(O,{ToolModeBenchmark:()=>N});module.exports=B(O);var w=require("../adapters/openrouter/openrouter.js"),M=require("zod");const I={name:"get_weather",description:"Get the current weather for a city.",parameters:{type:"object",properties:{city:{type:"string"}},required:["city"]}},x=[{role:"system",content:"You are a weather assistant. Always use the get_weather tool to answer, calling it once per city."},{role:"user",content:"What is the weather in Paris and Tokyo?"}],U=M.z.object({city:M.z.string().min(1)});function b(n,e){if(n.length===0)return Number.NaN;const o=Math.min(n.length-1,Math.floor(e*(n.length-1)));return n[o]}l(b,"percentile");function _(n){if(n.length===0)return;const e=n.slice().sort((o,r)=>o-r);return e[Math.floor(e.length/2)]}l(_,"median");async function K(n,e){try{const i=(await(await e("https://openrouter.ai/api/v1/models")).json()).data.find(a=>a.id===n),m=Number(i?.pricing?.prompt??0),s=Number(i?.pricing?.completion??0);return{in:m,out:s}}catch{return}}l(K,"fetchPricing");class N{static{l(this,"ToolModeBenchmark")}apiKey;model;baseUrl;runs;messages;tools;correctnessSchema;fetchImpl;constructor(e){this.apiKey=e.apiKey,this.model=e.model,this.baseUrl=e.baseUrl,this.runs=e.runs??3,this.messages=e.messages??x,this.tools=e.tools??[I],this.correctnessSchema=e.correctnessSchema??U,this.fetchImpl=e.fetch??fetch}async run(){const e=await K(this.model,this.fetchImpl),o={};for(const s of["native","envelope","prompted"])o[s]=await this.benchmarkMode(s,e);const r=["native","envelope","prompted"].filter(s=>(o[s].toolCalls??0)>0).map(s=>({mode:s,entry:o[s]})),i=[...r].sort((s,a)=>(s.entry.time?.p50??1/0)-(a.entry.time?.p50??1/0)),m=[...r].sort((s,a)=>(s.entry.cost??1/0)-(a.entry.cost??1/0));return{model:this.model,pricing:e,report:o,fastest:i[0]?.mode??null,cheapest:m[0]?.mode??null}}async benchmarkMode(e,o){const r=[];for(let t=0;t<this.runs;t++){let d={};const R=new w.OpenRouterLLM({apiKey:this.apiKey,model:this.model,baseUrl:this.baseUrl,fetch:this.fetchImpl,onUsage:l(c=>{d={prompt:c.promptTokens,completion:c.completionTokens}},"onUsage")}),T=performance.now();let u,f;const g=[];try{for await(const c of R.stream({messages:this.messages,tools:this.tools,temperature:0,toolMode:e})){if(c.type==="tool_call"&&u===void 0&&(u=performance.now()-T),c.type==="tool_call"&&g.push(c.arguments),c.type==="error")throw c.error;if(c.type==="done")break}}catch(c){f=c instanceof Error?c.message.slice(0,120):String(c)}let L;f===void 0&&g.length>0&&this.correctnessSchema!==void 0&&(L=g.every(c=>{try{return this.correctnessSchema.safeParse(JSON.parse(c)).success}catch{return!1}})),r.push({latencyMs:u??(f===void 0?performance.now()-T:void 0),toolCall:u!==void 0,correct:L,prompt:d.prompt,completion:d.completion,error:f})}const i=r.filter(t=>t.toolCall&&t.latencyMs!==void 0).map(t=>t.latencyMs).sort((t,d)=>t-d),m=r.filter(t=>t.prompt!==void 0&&t.completion!==void 0&&o!==void 0).map(t=>t.prompt*o.in+t.completion*o.out),s=r.filter(t=>t.error!==void 0),a=r.filter(t=>t.correct===!0).length,p=this.correctnessSchema!==void 0?a:void 0,y=_(m),k=p!==void 0&&p>0&&y!==void 0?y/(p/r.length):void 0;return{cost:y,time:i.length>0?{p50:b(i,.5),p95:b(i,.95),p99:b(i,.99)}:void 0,toolCalls:r.filter(t=>t.toolCall).length,correct:p,effectiveCost:k,errors:s.length,error:s[0]?.error,runs:r}}}
2
+ //# sourceMappingURL=toolmode.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/providers/llm/toolmode/toolmode.ts"],
4
+ "sourcesContent": ["/**\n * Tool-mode benchmark: measures availability, latency (percentiles), and cost\n * of each tool-call encoding (`native` | `envelope` | `prompted`) for a\n * model, through the real adapter path (`llm.stream({ toolMode })`). A\n * diagnostic utility for onboarding models \u2014 the runtime never calls it.\n *\n * ```ts\n * const bench = new ToolModeBenchmark({ apiKey, model: \"...\" });\n * const { fastest, cheapest, report } = await bench.run();\n * console.log(report.envelope.time.p50);\n * ```\n */\n\nimport { OpenRouterLLM } from \"../adapters/openrouter/openrouter\";\nimport { z } from \"zod\";\nimport type { FetchLike } from \"../../shared\";\nimport type { LLMMessage, LLMToolDefinition, ToolMode } from \"../types\";\n\nexport interface ToolModeBenchmarkOptions {\n apiKey: string;\n model: string;\n /** Override the OpenRouter base URL. */\n baseUrl?: string;\n /** Runs per mode (default 3). */\n runs?: number;\n /** Probe messages (default: a weather question). */\n messages?: LLMMessage[];\n /** Probe tool (default: get_weather). */\n tools?: LLMToolDefinition[];\n /**\n * Validates each emitted tool call's arguments to count `correct` runs \u2014 a\n * call that succeeds with garbage arguments (e.g. `{ \"city\": \"?\" }`) is a\n * failed decision. Defaults to the weather probe's schema.\n */\n correctnessSchema?: z.ZodType;\n /** Injectable fetch implementation, mainly for tests. */\n fetch?: FetchLike;\n}\n\nexport interface ToolModeBenchmarkRun {\n /** ms to the tool call (decision latency), or to stream end when none. */\n latencyMs?: number;\n toolCall: boolean;\n /** Every emitted call's arguments parsed cleanly and validated. */\n correct?: boolean;\n prompt?: number;\n completion?: number;\n error?: string;\n}\n\n/** Latency percentiles over the runs that produced a tool call. */\nexport interface ToolModeTiming {\n p50: number;\n p95: number;\n p99: number;\n}\n\nexport interface ToolModeReportEntry {\n /** Median cost ($) per decision over runs with provider usage. */\n cost?: number;\n /** Decision-latency percentiles; absent when no run emitted a tool call. */\n time?: ToolModeTiming;\n /** How many runs produced a tool call. */\n toolCalls: number;\n /**\n * How many runs emitted only schema-valid calls (undefined when no\n * `correctnessSchema` was provided).\n */\n correct: number | undefined;\n /** Cost per decision that was actually correct: `cost / (correct / runs)`. */\n effectiveCost?: number;\n /** How many runs failed outright (transport, envelope parse, \u2026). */\n errors: number;\n /** The first run's error, when every run failed. */\n error?: string;\n /** Raw per-run rows, for diagnostics. */\n runs: ToolModeBenchmarkRun[];\n}\n\nexport interface ToolModeBenchmarkResult {\n model: string;\n /** Prompt/completion prices ($/token) from the registry, when available. */\n pricing: { in: number; out: number } | undefined;\n report: Record<ToolMode, ToolModeReportEntry>;\n /** Fastest mode that actually emitted a tool call. */\n fastest: ToolMode | null;\n /** Cheapest mode that actually emitted a tool call. */\n cheapest: ToolMode | null;\n}\n\nconst WEATHER_TOOL: LLMToolDefinition = {\n name: \"get_weather\",\n description: \"Get the current weather for a city.\",\n parameters: { type: \"object\", properties: { city: { type: \"string\" } }, required: [\"city\"] },\n};\n\nconst WEATHER_MESSAGES: LLMMessage[] = [\n {\n role: \"system\",\n content:\n \"You are a weather assistant. Always use the get_weather tool to answer, calling it once per city.\",\n },\n { role: \"user\", content: \"What is the weather in Paris and Tokyo?\" },\n];\n\nconst WEATHER_ARGS = z.object({ city: z.string().min(1) });\n\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return Number.NaN;\n const index = Math.min(sorted.length - 1, Math.floor(p * (sorted.length - 1)));\n return sorted[index]!;\n}\n\nfunction median(values: number[]): number | undefined {\n if (values.length === 0) return undefined;\n const sorted = values.slice().sort((a, b) => a - b);\n return sorted[Math.floor(sorted.length / 2)]!;\n}\n\nasync function fetchPricing(\n model: string,\n fetchImpl: FetchLike,\n): Promise<{ in: number; out: number } | undefined> {\n try {\n const response = await fetchImpl(\"https://openrouter.ai/api/v1/models\");\n const data = (await response.json()) as {\n data: Array<{ id: string; pricing?: { prompt?: string; completion?: string } }>;\n };\n const entry = data.data.find((m) => m.id === model);\n const prompt = Number(entry?.pricing?.prompt ?? 0);\n const completion = Number(entry?.pricing?.completion ?? 0);\n return { in: prompt, out: completion };\n } catch {\n return undefined;\n }\n}\n\nexport class ToolModeBenchmark {\n private readonly apiKey: string;\n private readonly model: string;\n private readonly baseUrl: string | undefined;\n private readonly runs: number;\n private readonly messages: LLMMessage[];\n private readonly tools: LLMToolDefinition[];\n private readonly correctnessSchema: z.ZodType | undefined;\n private readonly fetchImpl: FetchLike;\n\n constructor(options: ToolModeBenchmarkOptions) {\n this.apiKey = options.apiKey;\n this.model = options.model;\n this.baseUrl = options.baseUrl;\n this.runs = options.runs ?? 3;\n this.messages = options.messages ?? WEATHER_MESSAGES;\n this.tools = options.tools ?? [WEATHER_TOOL];\n this.correctnessSchema = options.correctnessSchema ?? WEATHER_ARGS;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n /** Benchmark every mode and return the structured report. */\n async run(): Promise<ToolModeBenchmarkResult> {\n const pricing = await fetchPricing(this.model, this.fetchImpl);\n const report = {} as Record<ToolMode, ToolModeReportEntry>;\n for (const mode of [\"native\", \"envelope\", \"prompted\"] as const) {\n report[mode] = await this.benchmarkMode(mode, pricing);\n }\n\n const withCalls = ([\"native\", \"envelope\", \"prompted\"] as const)\n .filter((mode) => (report[mode].toolCalls ?? 0) > 0)\n .map((mode) => ({ mode, entry: report[mode]! }));\n const byLatency = [...withCalls].sort(\n (a, b) => (a.entry.time?.p50 ?? Infinity) - (b.entry.time?.p50 ?? Infinity),\n );\n const byCost = [...withCalls].sort(\n (a, b) => (a.entry.cost ?? Infinity) - (b.entry.cost ?? Infinity),\n );\n\n return {\n model: this.model,\n pricing,\n report,\n fastest: byLatency[0]?.mode ?? null,\n cheapest: byCost[0]?.mode ?? null,\n };\n }\n\n private async benchmarkMode(\n mode: ToolMode,\n pricing: { in: number; out: number } | undefined,\n ): Promise<ToolModeReportEntry> {\n const runRows: ToolModeBenchmarkRun[] = [];\n\n for (let i = 0; i < this.runs; i++) {\n let usage: { prompt?: number; completion?: number } = {};\n const llm = new OpenRouterLLM({\n apiKey: this.apiKey,\n model: this.model,\n baseUrl: this.baseUrl,\n fetch: this.fetchImpl,\n onUsage: (u) => {\n usage = { prompt: u.promptTokens, completion: u.completionTokens };\n },\n });\n const start = performance.now();\n let toolCallAt: number | undefined;\n let error: string | undefined;\n const callArguments: string[] = [];\n try {\n for await (const event of llm.stream({\n messages: this.messages,\n tools: this.tools,\n temperature: 0,\n toolMode: mode,\n })) {\n if (event.type === \"tool_call\" && toolCallAt === undefined) {\n toolCallAt = performance.now() - start;\n }\n if (event.type === \"tool_call\") {\n callArguments.push(event.arguments);\n }\n if (event.type === \"error\") throw event.error;\n if (event.type === \"done\") break;\n }\n } catch (e) {\n error = e instanceof Error ? e.message.slice(0, 120) : String(e);\n }\n // A run is correct when every emitted call's arguments parse and\n // validate \u2014 a call that succeeds with garbage args is a failed decision.\n let correct: boolean | undefined;\n if (error === undefined && callArguments.length > 0 && this.correctnessSchema !== undefined) {\n correct = callArguments.every((raw) => {\n try {\n return this.correctnessSchema!.safeParse(JSON.parse(raw)).success;\n } catch {\n return false;\n }\n });\n }\n runRows.push({\n latencyMs:\n toolCallAt ?? (error === undefined ? performance.now() - start : undefined),\n toolCall: toolCallAt !== undefined,\n correct,\n prompt: usage.prompt,\n completion: usage.completion,\n error,\n });\n }\n\n const decisionLatencies = runRows\n .filter((r) => r.toolCall && r.latencyMs !== undefined)\n .map((r) => r.latencyMs!)\n .sort((a, b) => a - b);\n const costs = runRows\n .filter((r) => r.prompt !== undefined && r.completion !== undefined && pricing !== undefined)\n .map((r) => r.prompt! * pricing!.in + r.completion! * pricing!.out);\n const errors = runRows.filter((r) => r.error !== undefined);\n const correctRuns = runRows.filter((r) => r.correct === true).length;\n const correct = this.correctnessSchema !== undefined ? correctRuns : undefined;\n const cost = median(costs);\n // Price per decision that was actually usable: the median decision cost\n // divided by the correct rate.\n const effectiveCost =\n correct !== undefined && correct > 0 && cost !== undefined\n ? cost / (correct / runRows.length)\n : undefined;\n\n return {\n cost,\n time:\n decisionLatencies.length > 0\n ? {\n p50: percentile(decisionLatencies, 0.5),\n p95: percentile(decisionLatencies, 0.95),\n p99: percentile(decisionLatencies, 0.99),\n }\n : undefined,\n toolCalls: runRows.filter((r) => r.toolCall).length,\n correct,\n effectiveCost,\n errors: errors.length,\n error: errors[0]?.error,\n runs: runRows,\n };\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["toolmode_exports", "__export", "ToolModeBenchmark", "__toCommonJS", "import_openrouter", "import_zod", "WEATHER_TOOL", "WEATHER_MESSAGES", "WEATHER_ARGS", "percentile", "sorted", "p", "index", "__name", "median", "values", "a", "b", "fetchPricing", "model", "fetchImpl", "entry", "m", "prompt", "completion", "ToolModeBenchmark", "options", "pricing", "report", "mode", "withCalls", "byLatency", "byCost", "runRows", "i", "usage", "llm", "u", "start", "toolCallAt", "error", "callArguments", "event", "e", "correct", "raw", "decisionLatencies", "r", "costs", "errors", "correctRuns", "cost", "effectiveCost"]
7
+ }
@@ -1,2 +1,2 @@
1
- "use strict";var o=Object.defineProperty;var i=Object.getOwnPropertyDescriptor;var a=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var l=(t,e,s,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of a(e))!L.call(t,r)&&r!==s&&o(t,r,{get:()=>e[r],enumerable:!(n=i(e,r))||n.enumerable});return t};var g=t=>l(o({},"__esModule",{value:!0}),t);var p={};module.exports=g(p);
1
+ "use strict";var n=Object.defineProperty;var a=Object.getOwnPropertyDescriptor;var i=Object.getOwnPropertyNames;var l=Object.prototype.hasOwnProperty;var p=(t,e)=>{for(var r in e)n(t,r,{get:e[r],enumerable:!0})},L=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of i(e))!l.call(t,o)&&o!==r&&n(t,o,{get:()=>e[o],enumerable:!(s=a(e,o))||s.enumerable});return t};var g=t=>L(n({},"__esModule",{value:!0}),t);var M={};p(M,{FAVORITE_MODELS:()=>m});module.exports=g(M);const m=["meta-llama/llama-4-scout","google/gemini-2.5-flash-lite","amazon/nova-micro-v1","amazon/nova-lite-v1","inclusionai/ling-3.0-flash","sao10k/l3-lunaris-8b","openai/gpt-oss-20b"];
2
2
  //# sourceMappingURL=types.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/providers/llm/types.ts"],
4
- "sourcesContent": ["export type LLMRole = \"system\" | \"user\" | \"assistant\" | \"tool\";\n\nexport interface LLMToolCall {\n id: string;\n name: string;\n /** JSON-encoded arguments for the tool. */\n arguments: string;\n}\n\nexport interface LLMMessage {\n role: LLMRole;\n content: string;\n /** Tool name, for `tool` role messages. */\n name?: string;\n /** Tool call id this message answers, for `tool` role messages. */\n toolCallId?: string;\n /** Tool calls announced by the assistant, for `assistant` role messages. */\n toolCalls?: LLMToolCall[];\n}\n\nexport interface LLMToolDefinition {\n name: string;\n description: string;\n /** JSON schema describing the tool arguments. */\n parameters: Record<string, unknown>;\n}\n\nexport interface LLMRequest {\n messages: LLMMessage[];\n tools?: LLMToolDefinition[];\n temperature?: number;\n maxTokens?: number;\n}\n\nexport type LLMEvent =\n | { type: \"delta\"; content: string }\n | { type: \"tool_call\"; id: string; name: string; arguments: string }\n | { type: \"done\" }\n | { type: \"error\"; error: Error };\n\n/**\n * Vendor-independent language model interface.\n *\n * `stream()` produces deltas as they arrive plus optional tool calls, and\n * terminates with `done`. Transport-level failures (network, HTTP status)\n * are thrown from the generator rather than emitted.\n */\nexport interface LLM {\n stream(request: LLMRequest): AsyncGenerator<LLMEvent>;\n /** Cancel the currently in-flight generation, if any. */\n stop(): void;\n}\n"],
5
- "mappings": "+WAAA,IAAAA,EAAA,kBAAAC,EAAAD",
6
- "names": ["types_exports", "__toCommonJS"]
4
+ "sourcesContent": ["export type LLMRole = \"system\" | \"user\" | \"assistant\" | \"tool\";\n\nexport interface LLMToolCall {\n id: string;\n name: string;\n /** JSON-encoded arguments for the tool. */\n arguments: string;\n}\n\nexport interface LLMMessage {\n role: LLMRole;\n content: string;\n /** Tool name, for `tool` role messages. */\n name?: string;\n /** Tool call id this message answers, for `tool` role messages. */\n toolCallId?: string;\n /** Tool calls announced by the assistant, for `assistant` role messages. */\n toolCalls?: LLMToolCall[];\n}\n\nexport interface LLMToolDefinition {\n name: string;\n description: string;\n /** JSON schema describing the tool arguments. */\n parameters: Record<string, unknown>;\n}\n\n/**\n * How tool calls are encoded on the wire. The semantic contract is the same\n * in every mode \u2014 callers still pass `tools` and consume `tool_call` events \u2014\n * only the encoding differs. The right mode is a property of the model's\n * endpoints, not the caller: use `ToolModeBenchmark` to measure a model and\n * set the mode once at adapter construction.\n *\n * - `native` (default) \u2014 the provider's tool-calling contract: `tools` in\n * the request, `tool_calls` in the stream. Streaming deltas and\n * provider-enforced argument schemas, at the price of wire overhead: the\n * provider expands the schema into its native tool format, which bills\n * measurably more prompt tokens (often 5-10x the cost of the modes below).\n * - `envelope` \u2014 no `tools`; `response_format` forces the model to emit a\n * JSON envelope (`{ answer?, calls: [{ name, arguments }] }`) that the\n * adapter translates back into `tool_call` events. Endpoint-guaranteed\n * JSON and a lean prompt (dramatically cheaper per decision), but nothing\n * is actionable until the whole envelope arrives \u2014 no streaming deltas.\n * Only for endpoints that support structured outputs.\n * - `prompted` \u2014 no `tools`; the same envelope is requested by appending an\n * instruction to the last user message. The universal fallback: works on\n * any chat model, at the cost of extraction/repair/retry, higher token\n * use, and tail-latency risk. Because nothing constrains the output, a\n * model that ignores the envelope and replies in plain prose is answered\n * directly \u2014 its text is treated as the reply rather than failing the\n * generation.\n */\nexport type ToolMode = \"native\" | \"envelope\" | \"prompted\";\n\n/**\n * A string that may also be one of the branded literal values \u2014 e.g. any\n * model id, with the favorites offered for autocomplete. The `& {}` on the\n * string side stops TypeScript from absorbing the literals into `string`\n * (the reverse arrangement \u2014 `string | (T & {})` \u2014 collapses and loses the\n * literals).\n */\nexport type StringOr<T extends string> = T | (string & {});\n\n/** Models measured as usable; offered for autocomplete where a model id is taken. */\nexport const FAVORITE_MODELS = [\n \"meta-llama/llama-4-scout\",\n \"google/gemini-2.5-flash-lite\",\n \"amazon/nova-micro-v1\",\n \"amazon/nova-lite-v1\",\n \"inclusionai/ling-3.0-flash\",\n \"sao10k/l3-lunaris-8b\",\n \"openai/gpt-oss-20b\",\n] as const;\n\n/** Any model id, with the favorites offered for autocomplete. */\nexport type FavoriteModel = StringOr<(typeof FAVORITE_MODELS)[number]>;\n\nexport interface LLMRequest {\n messages: LLMMessage[];\n tools?: LLMToolDefinition[];\n temperature?: number;\n maxTokens?: number;\n /** How tools are encoded on the wire. Default `native`. */\n toolMode?: ToolMode;\n}\n\n/**\n * Moments in the provider round trip that separate application delay from\n * network/queue delay from model latency. The callback is invoked\n * synchronously at each point, so `performance.now()` taken inside it is the\n * event time.\n *\n * - `request-start` \u2014 immediately before the HTTP request is issued.\n * - `headers` \u2014 the response headers arrived (2xx).\n * - `first-chunk` \u2014 the first SSE chunk was parsed.\n */\nexport type LLMStreamTimingPoint = \"request-start\" | \"headers\" | \"first-chunk\";\n\nexport type LLMStreamTimingCallback = (point: LLMStreamTimingPoint) => void;\n\n/** Token usage reported by the provider (when it includes usage in the stream). */\nexport interface LLMUsage {\n promptTokens: number;\n completionTokens: number;\n}\n\nexport type LLMUsageCallback = (usage: LLMUsage) => void;\n\nexport type LLMEvent =\n | {\n type: \"delta\";\n content: string;\n /**\n * Reasoning/thinking tokens, when the provider streams them separately\n * from content (OpenRouter `reasoning`, DeepSeek `reasoning_content`).\n * Omitted when the model does not emit reasoning.\n */\n reasoning?: string;\n }\n | { type: \"tool_call\"; id: string; name: string; arguments: string }\n | { type: \"done\" }\n | { type: \"error\"; error: Error };\n\n/**\n * Vendor-independent language model interface.\n *\n * `stream()` produces deltas as they arrive plus optional tool calls, and\n * terminates with `done`. Transport-level failures (network, HTTP status)\n * are thrown from the generator rather than emitted.\n */\nexport interface LLM {\n stream(request: LLMRequest): AsyncGenerator<LLMEvent>;\n /** Cancel the currently in-flight generation, if any. */\n stop(): void;\n}\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,qBAAAE,IAAA,eAAAC,EAAAH,GAiEO,MAAME,EAAkB,CAC7B,2BACA,+BACA,uBACA,sBACA,6BACA,uBACA,oBACF",
6
+ "names": ["types_exports", "__export", "FAVORITE_MODELS", "__toCommonJS"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var a=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var u=Object.prototype.hasOwnProperty;var i=(t,e)=>a(t,"name",{value:e,configurable:!0});var f=(t,e)=>{for(var r in e)a(t,r,{get:e[r],enumerable:!0})},g=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of v(e))!u.call(t,s)&&s!==r&&a(t,s,{get:()=>e[s],enumerable:!(n=c(e,s))||n.enumerable});return t};var m=t=>g(a({},"__esModule",{value:!0}),t);var T={};f(T,{DeepgramSTT:()=>h,DeepgramSession:()=>l});module.exports=m(T);class h{static{i(this,"DeepgramSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("DeepgramSTT requires an apiKey");this.options=e}start(e={}){const r={...this.options,...e},n=S(r),s=r.createSocket??(p=>new WebSocket(p)),o=new l(s(n));return this.sessions.add(o),o.on("close",()=>{this.sessions.delete(o)}),o}cancel(){for(const e of[...this.sessions])e.abort()}}class l{static{i(this,"DeepgramSession")}socket;listeners=new Map;opened=!1;closed=!1;ended=!1;pendingClose={flag:!1};queued=[];endPromise;resolveEnd;constructor(e){this.socket=e,this.endPromise=new Promise(r=>{this.resolveEnd=r}),e.addEventListener("open",()=>{this.opened=!0;for(const r of this.queued)this.socket.send(d(r));this.queued.length=0,this.pendingClose.flag&&this.sendCloseStream()}),e.addEventListener("message",r=>{const n=y(r.data);if(n!==null)try{this.handleResult(JSON.parse(n))}catch{}}),e.addEventListener("error",()=>{this.emit("error",new Error("Deepgram socket error"))}),e.addEventListener("close",()=>{this.closed=!0,this.resolveEnd(),this.emit("close")})}write(e){if(this.closed)throw new Error("Deepgram session is closed");if(!this.opened){this.queued.push(e);return}this.socket.send(d(e))}async end(){if(!this.closed)return this.ended?this.endPromise:(this.ended=!0,this.opened?(this.sendCloseStream(),this.endPromise):(this.pendingClose.flag=!0,this.endPromise))}abort(){this.closed||this.socket.close()}on(e,r){let n=this.listeners.get(e);n||(n=new Set,this.listeners.set(e,n)),n.add(r)}handleResult(e){if(e.type!=="Results")return;const n=e.channel?.alternatives?.[0]?.transcript??"";n&&(e.is_final===!0?this.emit("final",n):this.emit("partial",n))}sendCloseStream(){this.socket.send(JSON.stringify({type:"CloseStream"}))}emit(e,...r){const n=this.listeners.get(e);if(n)for(const s of[...n])s(...r)}}function S(t){const e=new URLSearchParams;return e.set("model",t.model??"nova-3"),e.set("interim_results",t.interimResults??!0?"true":"false"),e.set("encoding",t.encoding??"linear16"),e.set("sample_rate",String(t.sampleRate??16e3)),e.set("punctuate","true"),t.language&&e.set("language",t.language),e.set("token",t.apiKey),`${(t.endpoint??"wss://api.deepgram.com/v1/listen").replace(/\/+$/,"")}?${e.toString()}`}i(S,"buildListenUrl");function d(t){const e=new Uint8Array(t.byteLength);return e.set(t),e.buffer}i(d,"toArrayBuffer");function y(t){return typeof t=="string"?t:t instanceof ArrayBuffer?new TextDecoder().decode(t):ArrayBuffer.isView(t)?new TextDecoder().decode(new Uint8Array(t.buffer,t.byteOffset,t.byteLength)):null}i(y,"decodeMessage");
1
+ "use strict";var a=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var i=(t,e)=>a(t,"name",{value:e,configurable:!0});var f=(t,e)=>{for(var r in e)a(t,r,{get:e[r],enumerable:!0})},g=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of u(e))!v.call(t,n)&&n!==r&&a(t,n,{get:()=>e[n],enumerable:!(s=c(e,n))||s.enumerable});return t};var m=t=>g(a({},"__esModule",{value:!0}),t);var T={};f(T,{DeepgramSTT:()=>h,DeepgramSession:()=>l});module.exports=m(T);class h{static{i(this,"DeepgramSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("DeepgramSTT requires an apiKey");this.options=e}start(e={}){const r={...this.options,...e},s=S(r),n=r.createSocket??(p=>new WebSocket(p)),o=new l(n(s));return this.sessions.add(o),o.on("close",()=>{this.sessions.delete(o)}),o}cancel(){for(const e of[...this.sessions])e.abort()}}class l{static{i(this,"DeepgramSession")}socket;listeners=new Map;opened=!1;closed=!1;ended=!1;pendingClose={flag:!1};queued=[];endPromise;resolveEnd;constructor(e){this.socket=e,this.endPromise=new Promise(r=>{this.resolveEnd=r}),e.addEventListener("open",()=>{this.opened=!0;for(const r of this.queued)this.socket.send(d(r));this.queued.length=0,this.pendingClose.flag&&this.sendCloseStream()}),e.addEventListener("message",r=>{const s=y(r.data);if(s!==null)try{this.handleResult(JSON.parse(s))}catch{}}),e.addEventListener("error",()=>{this.emit("error",new Error("Deepgram socket error"))}),e.addEventListener("close",()=>{this.closed=!0,this.resolveEnd(),this.emit("close")})}write(e){if(this.closed)throw new Error("Deepgram session is closed");if(!this.opened){this.queued.push(e);return}this.socket.send(d(e))}async end(){if(!this.closed)return this.ended?this.endPromise:(this.ended=!0,this.opened?(this.sendCloseStream(),this.endPromise):(this.pendingClose.flag=!0,this.endPromise))}abort(){this.closed||this.socket.close()}on(e,r){let s=this.listeners.get(e);s||(s=new Set,this.listeners.set(e,s)),s.add(r)}handleResult(e){if(e.type!=="Results")return;const s=e.channel?.alternatives?.[0]?.transcript??"";s&&(e.is_final===!0?this.emit("final",s):this.emit("partial",s))}sendCloseStream(){this.socket.send(JSON.stringify({type:"CloseStream"}))}emit(e,...r){const s=this.listeners.get(e);if(s)for(const n of[...s])n(...r)}}function S(t){const e=new URLSearchParams,r=t.model??"flux-general-en";e.set("model",r),e.set("interim_results",t.interimResults??!0?"true":"false"),e.set("encoding",t.encoding??"linear16"),e.set("sample_rate",String(t.sampleRate??16e3)),e.set("punctuate","true"),t.language&&e.set("language",t.language),e.set("token",t.apiKey);const s=r.startsWith("flux")?"wss://api.deepgram.com/v2/listen":"wss://api.deepgram.com/v1/listen";return`${(t.endpoint??s).replace(/\/+$/,"")}?${e.toString()}`}i(S,"buildListenUrl");function d(t){const e=new Uint8Array(t.byteLength);return e.set(t),e.buffer}i(d,"toArrayBuffer");function y(t){return typeof t=="string"?t:t instanceof ArrayBuffer?new TextDecoder().decode(t):ArrayBuffer.isView(t)?new TextDecoder().decode(new Uint8Array(t.buffer,t.byteOffset,t.byteLength)):null}i(y,"decodeMessage");
2
2
  //# sourceMappingURL=deepgram.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/providers/stt/adapters/deepgram/deepgram.ts"],
4
- "sourcesContent": ["import type { STT, STTOptions, STTSession } from \"../../types\";\n\n/**\n * The minimal WebSocket surface used by the Deepgram adapter. Both the global\n * `WebSocket` (Bun) and test fakes satisfy this shape.\n */\nexport interface STTSocket {\n send(data: string | ArrayBuffer | ArrayBufferView): void;\n close(code?: number, reason?: string): void;\n addEventListener(\n type: string,\n listener: (event: { data?: unknown; error?: unknown }) => void,\n ): void;\n}\n\nexport interface DeepgramOptions extends STTOptions {\n apiKey: string;\n /** Defaults to `wss://api.deepgram.com/v1/listen`. */\n endpoint?: string;\n /** Injectable socket factory, mainly for tests. */\n createSocket?: (url: string) => STTSocket;\n}\n\ntype DeepgramEvent = \"partial\" | \"final\" | \"error\" | \"close\";\n\n/**\n * Deepgram streaming STT adapter (WebSocket).\n *\n * Audio written before the socket opens is queued and flushed on connect.\n * `end()` sends a `CloseStream` frame and resolves once the server closes\n * the socket.\n */\nexport class DeepgramSTT implements STT {\n private readonly options: DeepgramOptions;\n private readonly sessions = new Set<DeepgramSession>();\n\n constructor(options: DeepgramOptions) {\n if (!options.apiKey) {\n throw new Error(\"DeepgramSTT requires an apiKey\");\n }\n this.options = options;\n }\n\n start(options: STTOptions = {}): STTSession {\n const merged: DeepgramOptions = { ...this.options, ...options };\n const url = buildListenUrl(merged);\n const createSocket = merged.createSocket ?? ((target: string) => new WebSocket(target) as unknown as STTSocket);\n const session = new DeepgramSession(createSocket(url));\n this.sessions.add(session);\n session.on(\"close\", () => {\n this.sessions.delete(session);\n });\n return session;\n }\n\n cancel(): void {\n for (const session of [...this.sessions]) {\n session.abort();\n }\n }\n}\n\nexport class DeepgramSession implements STTSession {\n private readonly socket: STTSocket;\n private readonly listeners = new Map<DeepgramEvent, Set<(...args: any[]) => void>>();\n private opened = false;\n private closed = false;\n private ended = false;\n private readonly pendingClose = { flag: false };\n private readonly queued: Uint8Array[] = [];\n private readonly endPromise: Promise<void>;\n private resolveEnd!: () => void;\n\n constructor(socket: STTSocket) {\n this.socket = socket;\n this.endPromise = new Promise((resolve) => {\n this.resolveEnd = resolve;\n });\n\n socket.addEventListener(\"open\", () => {\n this.opened = true;\n for (const audio of this.queued) {\n this.socket.send(toArrayBuffer(audio));\n }\n this.queued.length = 0;\n if (this.pendingClose.flag) {\n this.sendCloseStream();\n }\n });\n\n socket.addEventListener(\"message\", (event) => {\n const text = decodeMessage(event.data);\n if (text === null) return;\n try {\n this.handleResult(JSON.parse(text));\n } catch {\n // Ignore malformed frames.\n }\n });\n\n socket.addEventListener(\"error\", () => {\n this.emit(\"error\", new Error(\"Deepgram socket error\"));\n });\n\n socket.addEventListener(\"close\", () => {\n this.closed = true;\n this.resolveEnd();\n this.emit(\"close\");\n });\n }\n\n write(audio: Uint8Array): void {\n if (this.closed) {\n throw new Error(\"Deepgram session is closed\");\n }\n if (!this.opened) {\n this.queued.push(audio);\n return;\n }\n this.socket.send(toArrayBuffer(audio));\n }\n\n async end(): Promise<void> {\n if (this.closed) return;\n if (this.ended) return this.endPromise;\n this.ended = true;\n if (!this.opened) {\n this.pendingClose.flag = true;\n return this.endPromise;\n }\n this.sendCloseStream();\n return this.endPromise;\n }\n\n /** Force-close the session without waiting for the server. */\n abort(): void {\n if (this.closed) return;\n this.socket.close();\n }\n\n on(event: \"partial\", listener: (transcript: string) => void): void;\n on(event: \"final\", listener: (transcript: string) => void): void;\n on(event: \"error\", listener: (error: Error) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: DeepgramEvent, listener: (...args: any[]) => void): void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n }\n\n private handleResult(message: Record<string, unknown>): void {\n if (message.type !== \"Results\") return;\n const alternatives = (message.channel as { alternatives?: { transcript?: string }[] })\n ?.alternatives;\n const text = alternatives?.[0]?.transcript ?? \"\";\n if (!text) return;\n if (message.is_final === true) {\n this.emit(\"final\", text);\n } else {\n this.emit(\"partial\", text);\n }\n }\n\n private sendCloseStream(): void {\n this.socket.send(JSON.stringify({ type: \"CloseStream\" }));\n }\n\n private emit(event: \"partial\", arg: string): void;\n private emit(event: \"final\", arg: string): void;\n private emit(event: \"error\", arg: Error): void;\n private emit(event: \"close\"): void;\n private emit(event: DeepgramEvent, ...args: any[]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of [...set]) {\n listener(...args);\n }\n }\n}\n\nfunction buildListenUrl(options: DeepgramOptions): string {\n const params = new URLSearchParams();\n params.set(\"model\", options.model ?? \"nova-3\");\n params.set(\"interim_results\", (options.interimResults ?? true) ? \"true\" : \"false\");\n params.set(\"encoding\", options.encoding ?? \"linear16\");\n params.set(\"sample_rate\", String(options.sampleRate ?? 16000));\n params.set(\"punctuate\", \"true\");\n if (options.language) params.set(\"language\", options.language);\n params.set(\"token\", options.apiKey);\n\n const base = (options.endpoint ?? \"wss://api.deepgram.com/v1/listen\").replace(/\\/+$/, \"\");\n return `${base}?${params.toString()}`;\n}\n\nfunction toArrayBuffer(audio: Uint8Array): ArrayBuffer {\n // Copy so the view's backing buffer (possibly larger or shared) is not\n // sent over the socket.\n const copy = new Uint8Array(audio.byteLength);\n copy.set(audio);\n return copy.buffer;\n}\n\nfunction decodeMessage(data: unknown): string | null {\n if (typeof data === \"string\") return data;\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(\n new Uint8Array(data.buffer, data.byteOffset, data.byteLength),\n );\n }\n return null;\n}\n"],
5
- "mappings": "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",
6
- "names": ["deepgram_exports", "__export", "DeepgramSTT", "DeepgramSession", "__toCommonJS", "DeepgramSTT", "__name", "options", "merged", "url", "buildListenUrl", "createSocket", "target", "session", "DeepgramSession", "socket", "resolve", "audio", "toArrayBuffer", "event", "text", "decodeMessage", "listener", "set", "message", "args", "params", "copy", "data"]
4
+ "sourcesContent": ["import type { STT, STTOptions, STTSession } from \"../../types\";\n\n/**\n * The minimal WebSocket surface used by the Deepgram adapter. Both the global\n * `WebSocket` (Bun) and test fakes satisfy this shape.\n */\nexport interface STTSocket {\n send(data: string | ArrayBuffer | ArrayBufferView): void;\n close(code?: number, reason?: string): void;\n addEventListener(\n type: string,\n listener: (event: { data?: unknown; error?: unknown }) => void,\n ): void;\n}\n\nexport interface DeepgramOptions extends STTOptions {\n apiKey: string;\n /**\n * Overrides the listen endpoint. Defaults to `wss://api.deepgram.com/v2/listen`\n * for Flux models (`flux-*`) and `wss://api.deepgram.com/v1/listen` otherwise.\n */\n endpoint?: string;\n /** Injectable socket factory, mainly for tests. */\n createSocket?: (url: string) => STTSocket;\n}\n\ntype DeepgramEvent = \"partial\" | \"final\" | \"error\" | \"close\";\n\n/**\n * Deepgram streaming STT adapter (WebSocket).\n *\n * Audio written before the socket opens is queued and flushed on connect.\n * `end()` sends a `CloseStream` frame and resolves once the server closes\n * the socket.\n */\nexport class DeepgramSTT implements STT {\n private readonly options: DeepgramOptions;\n private readonly sessions = new Set<DeepgramSession>();\n\n constructor(options: DeepgramOptions) {\n if (!options.apiKey) {\n throw new Error(\"DeepgramSTT requires an apiKey\");\n }\n this.options = options;\n }\n\n start(options: STTOptions = {}): STTSession {\n const merged: DeepgramOptions = { ...this.options, ...options };\n const url = buildListenUrl(merged);\n const createSocket = merged.createSocket ?? ((target: string) => new WebSocket(target) as unknown as STTSocket);\n const session = new DeepgramSession(createSocket(url));\n this.sessions.add(session);\n session.on(\"close\", () => {\n this.sessions.delete(session);\n });\n return session;\n }\n\n cancel(): void {\n for (const session of [...this.sessions]) {\n session.abort();\n }\n }\n}\n\nexport class DeepgramSession implements STTSession {\n private readonly socket: STTSocket;\n private readonly listeners = new Map<DeepgramEvent, Set<(...args: any[]) => void>>();\n private opened = false;\n private closed = false;\n private ended = false;\n private readonly pendingClose = { flag: false };\n private readonly queued: Uint8Array[] = [];\n private readonly endPromise: Promise<void>;\n private resolveEnd!: () => void;\n\n constructor(socket: STTSocket) {\n this.socket = socket;\n this.endPromise = new Promise((resolve) => {\n this.resolveEnd = resolve;\n });\n\n socket.addEventListener(\"open\", () => {\n this.opened = true;\n for (const audio of this.queued) {\n this.socket.send(toArrayBuffer(audio));\n }\n this.queued.length = 0;\n if (this.pendingClose.flag) {\n this.sendCloseStream();\n }\n });\n\n socket.addEventListener(\"message\", (event) => {\n const text = decodeMessage(event.data);\n if (text === null) return;\n try {\n this.handleResult(JSON.parse(text));\n } catch {\n // Ignore malformed frames.\n }\n });\n\n socket.addEventListener(\"error\", () => {\n this.emit(\"error\", new Error(\"Deepgram socket error\"));\n });\n\n socket.addEventListener(\"close\", () => {\n this.closed = true;\n this.resolveEnd();\n this.emit(\"close\");\n });\n }\n\n write(audio: Uint8Array): void {\n if (this.closed) {\n throw new Error(\"Deepgram session is closed\");\n }\n if (!this.opened) {\n this.queued.push(audio);\n return;\n }\n this.socket.send(toArrayBuffer(audio));\n }\n\n async end(): Promise<void> {\n if (this.closed) return;\n if (this.ended) return this.endPromise;\n this.ended = true;\n if (!this.opened) {\n this.pendingClose.flag = true;\n return this.endPromise;\n }\n this.sendCloseStream();\n return this.endPromise;\n }\n\n /** Force-close the session without waiting for the server. */\n abort(): void {\n if (this.closed) return;\n this.socket.close();\n }\n\n on(event: \"partial\", listener: (transcript: string) => void): void;\n on(event: \"final\", listener: (transcript: string) => void): void;\n on(event: \"error\", listener: (error: Error) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: DeepgramEvent, listener: (...args: any[]) => void): void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n }\n\n private handleResult(message: Record<string, unknown>): void {\n if (message.type !== \"Results\") return;\n const alternatives = (message.channel as { alternatives?: { transcript?: string }[] })\n ?.alternatives;\n const text = alternatives?.[0]?.transcript ?? \"\";\n if (!text) return;\n if (message.is_final === true) {\n this.emit(\"final\", text);\n } else {\n this.emit(\"partial\", text);\n }\n }\n\n private sendCloseStream(): void {\n this.socket.send(JSON.stringify({ type: \"CloseStream\" }));\n }\n\n private emit(event: \"partial\", arg: string): void;\n private emit(event: \"final\", arg: string): void;\n private emit(event: \"error\", arg: Error): void;\n private emit(event: \"close\"): void;\n private emit(event: DeepgramEvent, ...args: any[]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of [...set]) {\n listener(...args);\n }\n }\n}\n\nfunction buildListenUrl(options: DeepgramOptions): string {\n const params = new URLSearchParams();\n const model = options.model ?? \"flux-general-en\";\n params.set(\"model\", model);\n params.set(\"interim_results\", (options.interimResults ?? true) ? \"true\" : \"false\");\n params.set(\"encoding\", options.encoding ?? \"linear16\");\n params.set(\"sample_rate\", String(options.sampleRate ?? 16000));\n params.set(\"punctuate\", \"true\");\n if (options.language) params.set(\"language\", options.language);\n params.set(\"token\", options.apiKey);\n\n // Flux runs on the v2 listen endpoint; classic models (nova-*) use v1.\n const defaultEndpoint = model.startsWith(\"flux\")\n ? \"wss://api.deepgram.com/v2/listen\"\n : \"wss://api.deepgram.com/v1/listen\";\n const base = (options.endpoint ?? defaultEndpoint).replace(/\\/+$/, \"\");\n return `${base}?${params.toString()}`;\n}\n\nfunction toArrayBuffer(audio: Uint8Array): ArrayBuffer {\n // Copy so the view's backing buffer (possibly larger or shared) is not\n // sent over the socket.\n const copy = new Uint8Array(audio.byteLength);\n copy.set(audio);\n return copy.buffer;\n}\n\nfunction decodeMessage(data: unknown): string | null {\n if (typeof data === \"string\") return data;\n if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(\n new Uint8Array(data.buffer, data.byteOffset, data.byteLength),\n );\n }\n return null;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["deepgram_exports", "__export", "DeepgramSTT", "DeepgramSession", "__toCommonJS", "DeepgramSTT", "__name", "options", "merged", "url", "buildListenUrl", "createSocket", "target", "session", "DeepgramSession", "socket", "resolve", "audio", "toArrayBuffer", "event", "text", "decodeMessage", "listener", "set", "message", "args", "params", "model", "defaultEndpoint", "copy", "data"]
7
7
  }
@@ -1,2 +1,2 @@
1
- "use strict";var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var g=Object.getOwnPropertyNames;var t=Object.prototype.hasOwnProperty;var x=(r,e)=>{for(var o in e)p(r,o,{get:e[o],enumerable:!0})},D=(r,e,o,T)=>{if(e&&typeof e=="object"||typeof e=="function")for(let m of g(e))!t.call(r,m)&&m!==o&&p(r,m,{get:()=>e[m],enumerable:!(T=f(e,m))||T.enumerable});return r};var S=r=>D(p({},"__esModule",{value:!0}),r);var b={};x(b,{DeepgramSTT:()=>a.DeepgramSTT});module.exports=S(b);var a=require("./deepgram/deepgram.js");
1
+ "use strict";var T=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var a=Object.prototype.hasOwnProperty;var g=(r,e)=>{for(var p in e)T(r,p,{get:e[p],enumerable:!0})},n=(r,e,p,m)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of S(e))!a.call(r,o)&&o!==p&&T(r,o,{get:()=>e[o],enumerable:!(m=x(e,o))||m.enumerable});return r};var u=r=>n(T({},"__esModule",{value:!0}),r);var D={};g(D,{DeepgramSTT:()=>t.DeepgramSTT,OpenRouterSTT:()=>f.OpenRouterSTT});module.exports=u(D);var t=require("./deepgram/deepgram.js"),f=require("./openrouter/openrouter.js");
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/providers/stt/adapters/index.ts"],
4
- "sourcesContent": ["export { DeepgramSTT } from \"./deepgram/deepgram\";\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gDAAAE,EAAAF,GAAA,IAAAG,EAA4B",
6
- "names": ["adapters_exports", "__export", "__toCommonJS", "import_deepgram"]
4
+ "sourcesContent": ["export { DeepgramSTT } from \"./deepgram/deepgram\";\nexport { OpenRouterSTT } from \"./openrouter/openrouter\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kFAAAE,EAAAF,GAAA,IAAAG,EAA4B,+BAC5BC,EAA8B",
6
+ "names": ["adapters_exports", "__export", "__toCommonJS", "import_deepgram", "import_openrouter"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ "use strict";var n=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var i=(e,o)=>{for(var p in o)n(e,p,{get:o[p],enumerable:!0})},m=(e,o,p,u)=>{if(o&&typeof o=="object"||typeof o=="function")for(let t of R(o))!T.call(e,t)&&t!==p&&n(e,t,{get:()=>o[t],enumerable:!(u=O(o,t))||u.enumerable});return e};var s=e=>m(n({},"__esModule",{value:!0}),e);var S={};i(S,{OpenRouterSTT:()=>r.OpenRouterSTT,OpenRouterSession:()=>r.OpenRouterSession,toWav:()=>r.toWav});module.exports=s(S);var r=require("./openrouter.js");
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/stt/adapters/openrouter/index.ts"],
4
+ "sourcesContent": ["export { OpenRouterSTT, OpenRouterSession, toWav } from \"./openrouter\";\nexport type { OpenRouterAudioFormat, OpenRouterSTTOptions } from \"./openrouter\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,gHAAAE,EAAAF,GAAA,IAAAG,EAAwD",
6
+ "names": ["openrouter_exports", "__export", "__toCommonJS", "import_openrouter"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var d=Object.defineProperty;var g=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var u=(t,e)=>d(t,"name",{value:e,configurable:!0});var y=(t,e)=>{for(var r in e)d(t,r,{get:e[r],enumerable:!0})},T=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of v(e))!b.call(t,i)&&i!==r&&d(t,i,{get:()=>e[i],enumerable:!(n=g(e,i))||n.enumerable});return t};var O=t=>T(d({},"__esModule",{value:!0}),t);var k={};y(k,{OpenRouterSTT:()=>w,OpenRouterSession:()=>c,cleanTranscript:()=>f,pcmRms:()=>h,toWav:()=>m});module.exports=O(k);class w{static{u(this,"OpenRouterSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("OpenRouterSTT requires an apiKey");this.options=e}start(e={}){const r={...this.options,...e},n=new c(r,this.options);return this.sessions.add(n),n.on("close",()=>{this.sessions.delete(n)}),n}set minClipRms(e){this.options.minClipRms=e}get minClipRms(){return this.options.minClipRms}cancel(){for(const e of[...this.sessions])e.abort()}}class c{static{u(this,"OpenRouterSession")}listeners=new Map;apiKey;baseUrl;model;language;sampleRate;silenceMs;audioFormat;filterHallucinations;fillerPhrases;onClipEnergy;transcriptionTimeoutMs;temperature;providerOptions;fetchImpl;liveOptions;get minClipRms(){return this.liveOptions.minClipRms}chunks=[];timer=null;chain=Promise.resolve();controllers=new Set;ended=!1;aborted=!1;constructor(e,r){this.apiKey=e.apiKey,this.baseUrl=(e.baseUrl??"https://openrouter.ai/api/v1").replace(/\/+$/,""),this.model=e.model??"openai/whisper-large-v3-turbo",this.language=e.language==="auto"?void 0:e.language,this.sampleRate=e.sampleRate??16e3,this.silenceMs=e.silenceMs??800,this.audioFormat=e.audioFormat??"pcm",this.filterHallucinations=e.filterHallucinations??!0,this.fillerPhrases=e.fillerPhrases,this.onClipEnergy=e.onClipEnergy,this.transcriptionTimeoutMs=e.transcriptionTimeoutMs??3e4,this.temperature=e.temperature,this.providerOptions=e.providerOptions,this.fetchImpl=e.fetch??fetch,this.liveOptions=r}write(e){if(this.ended||this.aborted)throw new Error("OpenRouter session is closed");this.chunks.push(e),this.timer&&clearTimeout(this.timer),this.timer=setTimeout(()=>this.flush(),this.silenceMs)}async end(){this.ended||(this.ended=!0,this.timer&&(clearTimeout(this.timer),this.timer=null),this.flush(),await this.chain,this.emit("close"))}abort(){if(!this.aborted){this.aborted=!0,this.timer&&(clearTimeout(this.timer),this.timer=null),this.chunks=[];for(const e of this.controllers)e.abort();this.emit("close")}}on(e,r){let n=this.listeners.get(e);n||(n=new Set,this.listeners.set(e,n)),n.add(r)}flush(){if(this.aborted)return;const e=this.takeBuffer();e.byteLength!==0&&(this.chain=this.chain.then(async()=>{this.aborted||await this.transcribe(e)}))}takeBuffer(){const e=this.chunks;this.chunks=[];let r=0;for(const s of e)r+=s.byteLength;const n=new Uint8Array(r);let i=0;for(const s of e)n.set(s,i),i+=s.byteLength;return n}async transcribe(e){if(this.audioFormat==="pcm"&&(this.minClipRms!==void 0||this.onClipEnergy)){const i=h(e),s=this.minClipRms===void 0||i>=this.minClipRms;if(this.onClipEnergy?.(i,s),!s)return}const r=new AbortController;this.controllers.add(r);let n=!1;try{const i=await x(S({apiKey:this.apiKey,baseUrl:this.baseUrl,model:this.model,language:this.language,sampleRate:this.sampleRate,audioFormat:this.audioFormat,temperature:this.temperature,providerOptions:this.providerOptions,audio:e,signal:r.signal,fetchImpl:this.fetchImpl}),this.transcriptionTimeoutMs,()=>{n=!0,r.abort()}),s=this.filterHallucinations?f(i,this.fillerPhrases):i;!this.aborted&&s&&this.emit("final",s)}catch(i){if(!this.aborted){const s=n?`OpenRouter transcription timed out after ${this.transcriptionTimeoutMs}ms`:i instanceof Error?i.message:String(i);this.emit("error",new Error(s))}}finally{this.controllers.delete(r)}}emit(e,...r){const n=this.listeners.get(e);if(n)for(const i of[...n])i(...r)}}const R={mp3:"audio/mpeg",flac:"audio/flac",m4a:"audio/mp4",ogg:"audio/ogg",webm:"audio/webm",aac:"audio/aac"};async function S(t){const e=new FormData;e.append("model",t.model);let r,n,i;t.audioFormat==="pcm"?(r=m(t.audio,t.sampleRate),n="audio.wav",i="audio/wav"):(r=t.audio,n=`audio.${t.audioFormat}`,i=R[t.audioFormat]),e.append("file",new Blob([r],{type:i}),n),t.language&&e.append("language",t.language),t.temperature!==void 0&&e.append("temperature",String(t.temperature)),t.providerOptions&&e.append("provider",JSON.stringify(t.providerOptions));const s=await t.fetchImpl(`${t.baseUrl}/audio/transcriptions`,{method:"POST",headers:{authorization:`Bearer ${t.apiKey}`},body:e,signal:t.signal});if(!s.ok){const o=await s.text().catch(()=>"");throw new Error(`OpenRouter transcription failed (${s.status})${o?`: ${o}`:""}`)}return((await s.json()).text??"").trim()}u(S,"transcribeClip");function x(t,e,r){return new Promise((n,i)=>{const s=setTimeout(()=>{r(),i(new Error(`timed out after ${e}ms`))},e);t.then(a=>{clearTimeout(s),n(a)},a=>{clearTimeout(s),i(a)})})}u(x,"withTimeout");function m(t,e){const i=e*1*2,s=2,a=t.byteLength,o=new Uint8Array(44+a),l=new DataView(o.buffer);return o.set([82,73,70,70],0),l.setUint32(4,36+a,!0),o.set([87,65,86,69],8),o.set([102,109,116,32],12),l.setUint32(16,16,!0),l.setUint16(20,1,!0),l.setUint16(22,1,!0),l.setUint32(24,e,!0),l.setUint32(28,i,!0),l.setUint16(32,s,!0),l.setUint16(34,16,!0),o.set([100,97,116,97],36),l.setUint32(40,a,!0),o.set(t,44),o}u(m,"toWav");function h(t){const e=Math.floor(t.byteLength/2);if(e===0)return 0;let r=0;for(let n=0;n<e;n++){const i=t[n*2],s=t[n*2+1],a=i|s<<8,o=a>=32768?a-65536:a;r+=o*o}return Math.sqrt(r/e)/32768}u(h,"pcmRms");const p=["thank you","thanks","thank you for watching","thanks for watching","please subscribe","bye bye","goodbye","bye","good night","i love you","love you","you're welcome","no problem","have a great day","see you later","see you","okay","ok","e a\xED","e ai","oi","ol\xE1","ola","tudo bem","obrigado","obrigada","t\xE1 bom","ta bom","tchau","adeus","bom dia","boa noite","entendi","ah bom","\xE9 isso","hola","qu\xE9 tal","que tal","vale","adi\xF3s","adios","bonjour","merci","d'accord","au revoir","hallo","danke","tsch\xFCss","tschuss"];function U(t,e){const r=t.toLowerCase().replace(/[.,!?;:]+/g," ").replace(/\s+/g," ").trim();if(r.length===0)return!1;const n=[...e].sort((a,o)=>o.length-a.length);let i=r,s=!1;for(;;){const a=n.find(o=>i.startsWith(o));if(!a||(s=!0,i=i.slice(a.length).replace(/^\s+/,""),i.length===0))break}return s&&i.length===0}u(U,"isFillerOnly");function f(t,e=[]){let r=t.replace(/\*[^*]*\*/g," ").replace(/\s+/g," ").trim();if(r.length===0)return"";const n=r.split(/(?<=[.!?])\s+/).filter(o=>o.length>0),i=[];for(const o of n){const l=i.at(-1);l!==void 0&&l.toLowerCase()===o.toLowerCase()||i.push(o)}if(r=i.join(" ").trim(),r.length===0)return"";const s=[...p,...e.map(o=>o.toLowerCase())],a=r.toLowerCase().replace(/[.!?]+$/g,"");return p.includes(a)||U(r,s)?"":r}u(f,"cleanTranscript");
2
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/stt/adapters/openrouter/openrouter.ts"],
4
+ "sourcesContent": ["import type { STT, STTOptions, STTSession } from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\n\n/** Audio formats the OpenRouter transcription endpoint accepts. */\nexport type OpenRouterAudioFormat =\n | \"pcm\"\n | \"mp3\"\n | \"flac\"\n | \"m4a\"\n | \"ogg\"\n | \"webm\"\n | \"aac\";\n\nexport interface OpenRouterSTTOptions extends STTOptions {\n apiKey: string;\n /** Defaults to `https://openrouter.ai/api/v1`. */\n baseUrl?: string;\n /** The STT model. Defaults to `openai/whisper-large-v3-turbo`. */\n model?: string;\n /**\n * Silence (ms) that ends an utterance. OpenRouter's transcription endpoint\n * is batch-only (one request per audio clip), so the adapter segments the\n * incoming stream client-side and transcribes a clip once this much silence\n * has elapsed since the last audio. Default 800.\n */\n silenceMs?: number;\n /**\n * Format of the audio written to the session. `\"pcm\"` (the default)\n * assumes raw linear16 samples at `sampleRate` and wraps each clip in a\n * WAV container. Pass the actual encoding (e.g. `\"mp3\"`, `\"ogg\"`, \u2026) when\n * feeding pre-encoded audio \u2014 those formats are self-describing and are\n * sent as-is.\n */\n audioFormat?: OpenRouterAudioFormat;\n /**\n * Strip common whisper hallucinations from transcripts: asterisk stage\n * directions (\"*Dramatic music*\"), repeated filler phrases (\"Thank you.\n * Thank you.\"), and pure-filler clips whisper emits on near-silence\n * (\"Thank you.\", \"E a\u00ED.\", \"Bye.\", \u2026). Set `false` to pass transcripts\n * through untouched. Default `true`.\n */\n filterHallucinations?: boolean;\n /**\n * Extra phrases (lowercased) treated as hallucinations on top of the\n * built-in multilingual filler list \u2014 for fillers whisper \"hears\" in the\n * languages you deploy. A transcript that is *entirely* built from known\n * fillers is dropped. Default none.\n */\n fillerPhrases?: string[];\n /**\n * Energy floor (0\u20131 RMS, on the raw sample range) for transcribing a\n * buffered `pcm` clip. Near-silence clips \u2014 the ones whisper hallucinates\n * \"E a\u00ED.\" / \"Thank you.\" / *stage directions* on \u2014 are skipped entirely\n * (no transcription request, no `final`), instead of being filtered after\n * the fact. Real speech sits far above any reasonable floor; start around\n * `0.01`\u2013`0.02` and raise it if artifacts persist. Only applies to\n * `audioFormat: \"pcm\"` (the only format whose samples are measurable).\n * Default: no floor (transcribe everything the client sends).\n */\n minClipRms?: number;\n /**\n * Called for every buffered `pcm` clip with its measured mean RMS and\n * whether it was transcribed \u2014 for logging the real distribution and\n * tuning `minClipRms` against it (speech clips sit far above artifact\n * clips). Fires when this callback or `minClipRms` is set.\n */\n onClipEnergy?: (rms: number, transcribed: boolean) => void;\n /**\n * Abort a transcription request that produces no response within this\n * long (default 30000ms). Transcriptions are serialized on one session\n * chain, so a hung provider request would otherwise block every later\n * clip (each turn silently waits) until the server restarts. Raise it for\n * very long clips or slow providers.\n */\n transcriptionTimeoutMs?: number;\n /**\n * Sampling temperature (0\u20131) for transcription. Lower is more\n * deterministic; whisper's API default is already 0, so this is rarely the\n * lever for hallucinations.\n */\n temperature?: number;\n /**\n * Provider-specific passthrough, serialized as the `provider` multipart\n * field. OpenRouter ignores the top-level `prompt` field (\"accepted but\n * ignored\"), so the only way to reach whisper's prompt \u2014 vocabulary /\n * context steering \u2014 is per-provider:\n *\n * ```ts\n * providerOptions: { options: { groq: { prompt: \"Transcribe exactly what is said.\" } } }\n * ```\n *\n * Only the options for the provider that actually serves the request are\n * forwarded, so the key must match the serving provider (openai, groq,\n * together, \u2026).\n */\n providerOptions?: Record<string, unknown>;\n /** Injectable fetch, mainly for tests. */\n fetch?: FetchLike;\n}\n\ntype OpenRouterEvent = \"partial\" | \"final\" | \"error\" | \"close\";\n\n/**\n * OpenRouter batch STT adapter (`POST /api/v1/audio/transcriptions`).\n *\n * OpenRouter does not stream transcripts \u2014 it transcribes a complete audio\n * clip and returns the text. The adapter therefore buffers the raw audio\n * written by `write()`, assumes linear16 PCM (mono, `sampleRate`, default\n * 16 kHz \u2014 matching the Deepgram adapter's defaults), wraps each utterance\n * in a WAV header, and posts it after `silenceMs` of silence. Interim\n * results are unavailable, so no `partial` events are ever emitted: a turn\n * arrives whole at the `final` event once its clip has been transcribed.\n *\n * `language` accepts an ISO-639-1 code to force a language (which whisper\n * docs say improves accuracy and latency); omitting it \u2014 or passing `\"auto\"`,\n * the whisper convention, which is normalized to \"omit\" \u2014 leaves detection to\n * the provider, matching OpenRouter's \"auto-detected if omitted\" contract.\n *\n * By default the written audio is treated as raw linear16 PCM and wrapped in\n * a WAV container; set `audioFormat` to the real encoding (e.g. `\"mp3\"`) to\n * send pre-encoded audio as-is instead.\n *\n * `end()` transcribes any remaining buffered audio; `cancel()` drops\n * buffered audio and aborts in-flight requests.\n */\nexport class OpenRouterSTT implements STT {\n private readonly options: OpenRouterSTTOptions;\n private readonly sessions = new Set<OpenRouterSession>();\n\n constructor(options: OpenRouterSTTOptions) {\n if (!options.apiKey) {\n throw new Error(\"OpenRouterSTT requires an apiKey\");\n }\n this.options = options;\n }\n\n start(options: STTOptions = {}): STTSession {\n const merged: OpenRouterSTTOptions = { ...this.options, ...options };\n // Pass the adapter's own options object as the live source: the session\n // snapshots the per-session overrides but reads live-tunable options\n // (minClipRms) through the adapter, so runtime changes apply to every\n // session without recreating it.\n const session = new OpenRouterSession(merged, this.options);\n this.sessions.add(session);\n session.on(\"close\", () => {\n this.sessions.delete(session);\n });\n return session;\n }\n\n /**\n * Live-tunable energy floor (0\u20131 RMS) for `pcm` clips: raise it to skip\n * more near-silence clips (the whisper hallucinations), lower it to keep\n * quieter speech. Applies to all sessions immediately.\n */\n set minClipRms(value: number | undefined) {\n this.options.minClipRms = value;\n }\n\n get minClipRms(): number | undefined {\n return this.options.minClipRms;\n }\n\n cancel(): void {\n for (const session of [...this.sessions]) {\n session.abort();\n }\n }\n}\n\nexport class OpenRouterSession implements STTSession {\n private readonly listeners = new Map<OpenRouterEvent, Set<(...args: any[]) => void>>();\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly model: string;\n private readonly language: string | undefined;\n private readonly sampleRate: number;\n private readonly silenceMs: number;\n private readonly audioFormat: OpenRouterAudioFormat;\n private readonly filterHallucinations: boolean;\n private readonly fillerPhrases: string[] | undefined;\n private readonly onClipEnergy: ((rms: number, transcribed: boolean) => void) | undefined;\n private readonly transcriptionTimeoutMs: number;\n private readonly temperature: number | undefined;\n private readonly providerOptions: Record<string, unknown> | undefined;\n private readonly fetchImpl: FetchLike;\n /** The adapter's live options object; live-tunable fields are read here. */\n private readonly liveOptions: OpenRouterSTTOptions;\n\n /** Live-tunable energy floor, read through the adapter (not a snapshot). */\n private get minClipRms(): number | undefined {\n return this.liveOptions.minClipRms;\n }\n\n private chunks: Uint8Array[] = [];\n private timer: ReturnType<typeof setTimeout> | null = null;\n private chain: Promise<void> = Promise.resolve();\n private readonly controllers = new Set<AbortController>();\n private ended = false;\n private aborted = false;\n\n constructor(options: OpenRouterSTTOptions, liveOptions: OpenRouterSTTOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? \"https://openrouter.ai/api/v1\").replace(/\\/+$/, \"\");\n this.model = options.model ?? \"openai/whisper-large-v3-turbo\";\n // `\"auto\"` is the whisper convention for \"let the provider detect\";\n // OpenRouter detects when the field is omitted, so normalize it away\n // rather than forwarding a string some providers may reject.\n this.language = options.language === \"auto\" ? undefined : options.language;\n this.sampleRate = options.sampleRate ?? 16_000;\n this.silenceMs = options.silenceMs ?? 800;\n this.audioFormat = options.audioFormat ?? \"pcm\";\n this.filterHallucinations = options.filterHallucinations ?? true;\n this.fillerPhrases = options.fillerPhrases;\n this.onClipEnergy = options.onClipEnergy;\n this.transcriptionTimeoutMs = options.transcriptionTimeoutMs ?? 30_000;\n this.temperature = options.temperature;\n this.providerOptions = options.providerOptions;\n this.fetchImpl = options.fetch ?? fetch;\n this.liveOptions = liveOptions;\n }\n\n write(audio: Uint8Array): void {\n if (this.ended || this.aborted) {\n throw new Error(\"OpenRouter session is closed\");\n }\n this.chunks.push(audio);\n // Restart the silence clock: a clip is finalized once no audio has\n // arrived for `silenceMs`.\n if (this.timer) clearTimeout(this.timer);\n this.timer = setTimeout(() => this.flush(), this.silenceMs);\n }\n\n async end(): Promise<void> {\n if (this.ended) return;\n this.ended = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n // Transcribe any trailing audio, then wait for every queued clip.\n this.flush();\n await this.chain;\n this.emit(\"close\");\n }\n\n /** Drop buffered audio and abort in-flight requests (cancel). */\n abort(): void {\n if (this.aborted) return;\n this.aborted = true;\n if (this.timer) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n this.chunks = [];\n for (const controller of this.controllers) controller.abort();\n this.emit(\"close\");\n }\n\n on(event: \"partial\", listener: (transcript: string) => void): void;\n on(event: \"final\", listener: (transcript: string) => void): void;\n on(event: \"error\", listener: (error: Error) => void): void;\n on(event: \"close\", listener: () => void): void;\n on(event: OpenRouterEvent, listener: (...args: any[]) => void): void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(listener);\n }\n\n /** Queue a transcription of whatever audio is buffered (serialized). */\n /**\n * Queue a transcription of whatever audio is buffered (serialized). The\n * clip boundary is frozen *now* \u2014 the moment the trailing silence was\n * detected \u2014 so audio that arrives while this transcription is queued or\n * in flight starts the next clip instead of being swept into this one.\n */\n private flush(): void {\n if (this.aborted) return;\n const audio = this.takeBuffer();\n if (audio.byteLength === 0) return;\n this.chain = this.chain.then(async () => {\n if (this.aborted) return;\n await this.transcribe(audio);\n });\n }\n\n private takeBuffer(): Uint8Array {\n const chunks = this.chunks;\n this.chunks = [];\n let total = 0;\n for (const chunk of chunks) total += chunk.byteLength;\n const out = new Uint8Array(total);\n let offset = 0;\n for (const chunk of chunks) {\n out.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return out;\n }\n\n private async transcribe(audio: Uint8Array): Promise<void> {\n // Near-silence clips (the ones whisper hallucinates on) are dropped at\n // the source: below the energy floor the clip is never sent, saving the\n // transcription round trip and preventing the fabricated turn entirely.\n if (this.audioFormat === \"pcm\" && (this.minClipRms !== undefined || this.onClipEnergy)) {\n const rms = pcmRms(audio);\n const transcribed = this.minClipRms === undefined || rms >= this.minClipRms;\n this.onClipEnergy?.(rms, transcribed);\n if (!transcribed) return;\n }\n const controller = new AbortController();\n this.controllers.add(controller);\n // A transcription request that never responds would otherwise block this\n // session's serialized chain forever \u2014 every later clip silently waits.\n // Bound it: on timeout the request is aborted and surfaces as an error,\n // and the chain moves on to the next clip.\n let timedOut = false;\n try {\n const raw = await withTimeout(\n transcribeClip({\n apiKey: this.apiKey,\n baseUrl: this.baseUrl,\n model: this.model,\n language: this.language,\n sampleRate: this.sampleRate,\n audioFormat: this.audioFormat,\n temperature: this.temperature,\n providerOptions: this.providerOptions,\n audio,\n signal: controller.signal,\n fetchImpl: this.fetchImpl,\n }),\n this.transcriptionTimeoutMs,\n () => {\n timedOut = true;\n controller.abort();\n },\n );\n const text = this.filterHallucinations\n ? cleanTranscript(raw, this.fillerPhrases)\n : raw;\n if (!this.aborted && text) this.emit(\"final\", text);\n } catch (error) {\n if (!this.aborted) {\n const message = timedOut\n ? `OpenRouter transcription timed out after ${this.transcriptionTimeoutMs}ms`\n : error instanceof Error\n ? error.message\n : String(error);\n this.emit(\"error\", new Error(message));\n }\n } finally {\n this.controllers.delete(controller);\n }\n }\n\n private emit(event: \"partial\", arg: string): void;\n private emit(event: \"final\", arg: string): void;\n private emit(event: \"error\", arg: Error): void;\n private emit(event: \"close\"): void;\n private emit(event: OpenRouterEvent, ...args: any[]): void {\n const set = this.listeners.get(event);\n if (!set) return;\n for (const listener of [...set]) {\n listener(...args);\n }\n }\n}\n\ninterface TranscribeClipOptions {\n apiKey: string;\n baseUrl: string;\n model: string;\n language: string | undefined;\n sampleRate: number;\n audioFormat: OpenRouterAudioFormat;\n temperature: number | undefined;\n providerOptions: Record<string, unknown> | undefined;\n audio: Uint8Array;\n signal: AbortSignal;\n fetchImpl: FetchLike;\n}\n\nconst AUDIO_MIME: Record<Exclude<OpenRouterAudioFormat, \"pcm\">, string> = {\n mp3: \"audio/mpeg\",\n flac: \"audio/flac\",\n m4a: \"audio/mp4\",\n ogg: \"audio/ogg\",\n webm: \"audio/webm\",\n aac: \"audio/aac\",\n};\n\nasync function transcribeClip(options: TranscribeClipOptions): Promise<string> {\n const form = new FormData();\n form.append(\"model\", options.model);\n // OpenRouter accepts OpenAI-style multipart uploads. Raw linear16 PCM is\n // wrapped in a WAV container so whisper can read it; pre-encoded formats\n // (mp3, ogg, \u2026) are self-describing and sent as-is.\n let bytes: Uint8Array;\n let name: string;\n let type: string;\n if (options.audioFormat === \"pcm\") {\n bytes = toWav(options.audio, options.sampleRate);\n name = \"audio.wav\";\n type = \"audio/wav\";\n } else {\n bytes = options.audio;\n name = `audio.${options.audioFormat}`;\n type = AUDIO_MIME[options.audioFormat];\n }\n form.append(\"file\", new Blob([bytes], { type }), name);\n if (options.language) form.append(\"language\", options.language);\n if (options.temperature !== undefined) {\n form.append(\"temperature\", String(options.temperature));\n }\n // The only way to reach whisper's `prompt` through OpenRouter (the\n // top-level field is accepted but ignored) is per-provider passthrough.\n if (options.providerOptions) {\n form.append(\"provider\", JSON.stringify(options.providerOptions));\n }\n\n const response = await options.fetchImpl(`${options.baseUrl}/audio/transcriptions`, {\n method: \"POST\",\n headers: { authorization: `Bearer ${options.apiKey}` },\n body: form,\n signal: options.signal,\n });\n if (!response.ok) {\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `OpenRouter transcription failed (${response.status})${detail ? `: ${detail}` : \"\"}`,\n );\n }\n const data = (await response.json()) as { text?: string };\n return (data.text ?? \"\").trim();\n}\n\n/**\n * Race `promise` against a timeout: reject after `ms` and fire `onTimeout`\n * (which should abort the underlying request) so the hanging operation is\n * released rather than left pending forever.\n */\nfunction withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n onTimeout: () => void,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n onTimeout();\n reject(new Error(`timed out after ${ms}ms`));\n }, ms);\n promise.then(\n (value) => {\n clearTimeout(timer);\n resolve(value);\n },\n (error) => {\n clearTimeout(timer);\n reject(error);\n },\n );\n });\n}\n\n/**\n * Wrap linear16 PCM (mono) in a minimal WAV container so whisper can read it.\n * The header is the standard 44-byte RIFF/WAVE layout.\n */\nexport function toWav(pcm: Uint8Array, sampleRate: number): Uint8Array {\n const bytesPerSample = 2; // linear16\n const channels = 1;\n const byteRate = sampleRate * channels * bytesPerSample;\n const blockAlign = channels * bytesPerSample;\n const dataSize = pcm.byteLength;\n\n const wav = new Uint8Array(44 + dataSize);\n const view = new DataView(wav.buffer);\n wav.set([0x52, 0x49, 0x46, 0x46], 0); // \"RIFF\"\n view.setUint32(4, 36 + dataSize, true);\n wav.set([0x57, 0x41, 0x56, 0x45], 8); // \"WAVE\"\n wav.set([0x66, 0x6d, 0x74, 0x20], 12); // \"fmt \"\n view.setUint32(16, 16, true); // fmt chunk size\n view.setUint16(20, 1, true); // PCM\n view.setUint16(22, channels, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, byteRate, true);\n view.setUint16(32, blockAlign, true);\n view.setUint16(34, bytesPerSample * 8, true);\n wav.set([0x64, 0x61, 0x74, 0x61], 36); // \"data\"\n view.setUint32(40, dataSize, true);\n wav.set(pcm, 44);\n return wav;\n}\n\n/**\n * Mean RMS of linear16 PCM (mono), in the 0\u20131 sample range. Used as the\n * clip-level energy floor: near-silence clips sit far below real speech.\n */\nexport function pcmRms(pcm: Uint8Array): number {\n const sampleCount = Math.floor(pcm.byteLength / 2);\n if (sampleCount === 0) return 0;\n let sum = 0;\n for (let i = 0; i < sampleCount; i++) {\n const lo = pcm[i * 2]!;\n const hi = pcm[i * 2 + 1]!;\n const sample = lo | (hi << 8);\n const signed = sample >= 0x8000 ? sample - 0x10000 : sample;\n sum += signed * signed;\n }\n return Math.sqrt(sum / sampleCount) / 32768;\n}\n\n/**\n * Conversational fillers whisper loves to \"hear\" when a clip is mostly\n * silence (a trailing gap, a cough, the speaker's own voice echoing back).\n * Only a transcript that is *entirely* built from these is dropped, so a\n * genuine \"thank you\" still passes through when it is part of a longer\n * utterance. The list is multilingual because whisper fills near-silence\n * with the language it thinks it heard (\"E a\u00ED\" for Portuguese speech, \u2026).\n */\nconst FILLER_PHRASES = [\n // English\n \"thank you\",\n \"thanks\",\n \"thank you for watching\",\n \"thanks for watching\",\n \"please subscribe\",\n \"bye bye\",\n \"goodbye\",\n \"bye\",\n \"good night\",\n \"i love you\",\n \"love you\",\n \"you're welcome\",\n \"no problem\",\n \"have a great day\",\n \"see you later\",\n \"see you\",\n \"okay\",\n \"ok\",\n // Portuguese\n \"e a\u00ED\",\n \"e ai\",\n \"oi\",\n \"ol\u00E1\",\n \"ola\",\n \"tudo bem\",\n \"obrigado\",\n \"obrigada\",\n \"t\u00E1 bom\",\n \"ta bom\",\n \"tchau\",\n \"adeus\",\n \"bom dia\",\n \"boa noite\",\n \"entendi\",\n \"ah bom\",\n \"\u00E9 isso\",\n // Spanish\n \"hola\",\n \"qu\u00E9 tal\",\n \"que tal\",\n \"vale\",\n \"adi\u00F3s\",\n \"adios\",\n // French\n \"bonjour\",\n \"merci\",\n \"d'accord\",\n \"au revoir\",\n // German\n \"hallo\",\n \"danke\",\n \"tsch\u00FCss\",\n \"tschuss\",\n];\n\n/**\n * True when `text` is entirely a concatenation of known filler phrases\n * (\"E a\u00ED.\", \"E a\u00ED E a\u00ED\", \"ok ok thank you\"), with punctuation and extra\n * whitespace ignored. Repeated fillers are the classic near-silence\n * artifact \u2014 whisper often emits them with no sentence boundary at all.\n */\nfunction isFillerOnly(text: string, phrases: string[]): boolean {\n const normalized = text\n .toLowerCase()\n .replace(/[.,!?;:]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n if (normalized.length === 0) return false;\n const sorted = [...phrases].sort((a, b) => b.length - a.length);\n let rest = normalized;\n let matched = false;\n for (;;) {\n const found = sorted.find((phrase) => rest.startsWith(phrase));\n if (!found) break;\n matched = true;\n rest = rest.slice(found.length).replace(/^\\s+/, \"\");\n if (rest.length === 0) break;\n }\n return matched && rest.length === 0;\n}\n\n/**\n * Clean a whisper transcript of its most common hallucinations, returning\n * \"\" when nothing worth speaking remains:\n *\n * 1. Asterisk stage directions (\"*Dramatic music*\", \"*laughs*\") are\n * dropped \u2014 whisper transcribes background noise as stage notes.\n * 2. Consecutive repeated sentences (\"Thank you. Thank you.\") collapse to\n * one \u2014 a doubled filler is a classic near-silence artifact.\n * 3. A transcript built entirely from filler phrases (the built-in\n * multilingual list plus `extraFillers`) is dropped.\n */\nexport function cleanTranscript(text: string, extraFillers: string[] = []): string {\n let out = text\n .replace(/\\*[^*]*\\*/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n if (out.length === 0) return \"\";\n\n const sentences = out.split(/(?<=[.!?])\\s+/).filter((sentence) => sentence.length > 0);\n const collapsed: string[] = [];\n for (const sentence of sentences) {\n const last = collapsed.at(-1);\n if (last !== undefined && last.toLowerCase() === sentence.toLowerCase()) continue;\n collapsed.push(sentence);\n }\n out = collapsed.join(\" \").trim();\n if (out.length === 0) return \"\";\n\n const phrases = [...FILLER_PHRASES, ...extraFillers.map((phrase) => phrase.toLowerCase())];\n const normalized = out.toLowerCase().replace(/[.!?]+$/g, \"\");\n return FILLER_PHRASES.includes(normalized) || isFillerOnly(out, phrases) ? \"\" : out;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["openrouter_exports", "__export", "OpenRouterSTT", "OpenRouterSession", "cleanTranscript", "pcmRms", "toWav", "__toCommonJS", "OpenRouterSTT", "__name", "options", "merged", "session", "OpenRouterSession", "value", "liveOptions", "audio", "controller", "event", "listener", "set", "chunks", "total", "chunk", "out", "offset", "rms", "pcmRms", "transcribed", "timedOut", "raw", "withTimeout", "transcribeClip", "text", "cleanTranscript", "error", "message", "args", "AUDIO_MIME", "form", "bytes", "name", "type", "toWav", "response", "detail", "promise", "ms", "onTimeout", "resolve", "reject", "timer", "pcm", "sampleRate", "byteRate", "blockAlign", "dataSize", "wav", "view", "sampleCount", "sum", "i", "lo", "hi", "sample", "signed", "FILLER_PHRASES", "isFillerOnly", "phrases", "normalized", "sorted", "b", "rest", "matched", "found", "phrase", "extraFillers", "sentences", "sentence", "collapsed", "last"]
7
+ }
@@ -1,2 +1,2 @@
1
- "use strict";var f=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var t=Object.getOwnPropertyNames;var x=Object.prototype.hasOwnProperty;var K=(r,o)=>{for(var e in o)f(r,e,{get:o[e],enumerable:!0})},S=(r,o,e,k)=>{if(o&&typeof o=="object"||typeof o=="function")for(let T of t(o))!x.call(r,T)&&T!==e&&f(r,T,{get:()=>o[T],enumerable:!(k=p(o,T))||k.enumerable});return r};var a=r=>S(f({},"__esModule",{value:!0}),r);var b={};K(b,{KokoroTTS:()=>m.KokoroTTS});module.exports=a(b);var m=require("./kokoro/kokoro.js");
1
+ "use strict";var p=Object.defineProperty;var x=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var k=Object.prototype.hasOwnProperty;var n=(r,o)=>{for(var T in o)p(r,T,{get:o[T],enumerable:!0})},u=(r,o,T,t)=>{if(o&&typeof o=="object"||typeof o=="function")for(let e of S(o))!k.call(r,e)&&e!==T&&p(r,e,{get:()=>o[e],enumerable:!(t=x(o,e))||t.enumerable});return r};var K=r=>u(p({},"__esModule",{value:!0}),r);var O={};n(O,{KokoroTTS:()=>f.KokoroTTS,OpenRouterTTS:()=>m.OpenRouterTTS});module.exports=K(O);var f=require("./kokoro/kokoro.js"),m=require("./openrouter/openrouter.js");
2
2
  //# sourceMappingURL=index.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/providers/tts/adapters/index.ts"],
4
- "sourcesContent": ["export { KokoroTTS } from \"./kokoro/kokoro\";\n"],
5
- "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,4CAAAE,EAAAF,GAAA,IAAAG,EAA0B",
6
- "names": ["adapters_exports", "__export", "__toCommonJS", "import_kokoro"]
4
+ "sourcesContent": ["export { KokoroTTS } from \"./kokoro/kokoro\";\nexport { OpenRouterTTS } from \"./openrouter/openrouter\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,8EAAAE,EAAAF,GAAA,IAAAG,EAA0B,2BAC1BC,EAA8B",
6
+ "names": ["adapters_exports", "__export", "__toCommonJS", "import_kokoro", "import_openrouter"]
7
7
  }
@@ -1,2 +1,5 @@
1
- "use strict";var n=Object.defineProperty;var c=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var l=(t,e)=>n(t,"name",{value:e,configurable:!0});var p=(t,e)=>{for(var r in e)n(t,r,{get:e[r],enumerable:!0})},f=(t,e,r,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of y(e))!d.call(t,a)&&a!==r&&n(t,a,{get:()=>e[a],enumerable:!(i=c(e,a))||i.enumerable});return t};var u=t=>f(n({},"__esModule",{value:!0}),t);var m={};p(m,{KokoroTTS:()=>b});module.exports=u(m);class b{static{l(this,"KokoroTTS")}baseUrl;voice;model;apiKey;chunkSize;fetchImpl;abort=null;constructor(e={}){this.baseUrl=(e.baseUrl??"http://localhost:8880").replace(/\/+$/,""),this.voice=e.voice??"af_heart",this.model=e.model??"kokoro",this.apiKey=e.apiKey,this.chunkSize=e.chunkSize??8192,this.fetchImpl=e.fetch??fetch}stop(){this.abort?.abort()}async*stream(e){if(!e.text)throw new Error("KokoroTTS requires request.text");this.abort?.abort();const r=new AbortController;this.abort=r;try{const i=await this.fetchImpl(`${this.baseUrl}/v1/audio/speech`,{method:"POST",headers:{"content-type":"application/json",...this.apiKey?{authorization:`Bearer ${this.apiKey}`}:{}},body:JSON.stringify({model:this.model,input:e.text,voice:e.voice??this.voice,response_format:e.format,speed:e.speed}),signal:r.signal});if(!i.ok){const s=await i.text();throw new Error(`Kokoro request failed (${i.status}): ${s}`)}if(!i.body)throw new Error("Kokoro returned an empty body");const a=i.body.getReader();let o=new Uint8Array(0);for(;;){if(r.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:s,value:h}=await a.read();if(s)break;for(o=k(o,h);o.length>=this.chunkSize;)yield o.slice(0,this.chunkSize),o=o.slice(this.chunkSize)}o.length>0&&(yield o)}finally{this.abort===r&&(this.abort=null)}}}function k(t,e){const r=new Uint8Array(t.length+e.length);return r.set(t),r.set(e,t.length),r}l(k,"concat");
1
+ "use strict";var f=Object.defineProperty;var v=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var T=Object.prototype.hasOwnProperty;var s=(r,e)=>f(r,"name",{value:e,configurable:!0});var U=(r,e)=>{for(var t in e)f(r,t,{get:e[t],enumerable:!0})},S=(r,e,t,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of k(e))!T.call(r,n)&&n!==t&&f(r,n,{get:()=>e[n],enumerable:!(o=v(e,n))||o.enumerable});return r};var B=r=>S(f({},"__esModule",{value:!0}),r);var E={};U(E,{KokoroTTS:()=>K});module.exports=B(E);class K{static{s(this,"KokoroTTS")}baseUrl;voice;model;apiKey;chunkSize;streaming;fetchImpl;streams=new Set;constructor(e={}){this.baseUrl=(e.baseUrl??"http://localhost:8880").replace(/\/+$/,""),this.voice=e.voice??"af_heart",this.model=e.model??"kokoro",this.apiKey=e.apiKey,this.chunkSize=e.chunkSize??8192,this.streaming=e.stream??!1,this.fetchImpl=e.fetch??fetch}stop(){for(const e of this.streams)e.abort()}async*stream(e){if(!e.text)throw new Error("KokoroTTS requires request.text");const t=new AbortController;this.streams.add(t);try{const o={model:this.model,input:e.text,voice:e.voice??this.voice,speed:e.speed};this.streaming?(o.stream=!0,o.response_format=e.format===void 0||e.format==="pcm"?"raw":e.format):e.format!==void 0&&(o.response_format=e.format);const n=await this.fetchImpl(`${this.baseUrl}/v1/audio/speech`,{method:"POST",headers:{"content-type":"application/json",...this.apiKey?{authorization:`Bearer ${this.apiKey}`}:{}},body:JSON.stringify(o),signal:t.signal});if(!n.ok){const i=await n.text();throw new Error(`Kokoro request failed (${n.status}): ${i}`)}if(!n.body)throw new Error("Kokoro returned an empty body");const d=n.body.getReader(),b=(n.headers.get("content-type")??"").includes("text/event-stream");let a=new Uint8Array(0);if(b){let i="",l=!1;for(;!l;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:g,value:w}=await d.read();if(g)break;i+=new TextDecoder().decode(w);const h=i.split(`
2
+
3
+ `);i=h.pop()??"";for(const A of h)for(const y of A.split(`
4
+ `)){if(!y.startsWith("data:"))continue;const p=y.slice(5).trim();if(p==="[DONE]"){l=!0;break}try{const c=JSON.parse(p);c.type==="conversation.item.audio_output.delta"&&typeof c.delta=="string"&&(a=yield*m(u(a,x(c.delta)),this.chunkSize))}catch{}}}}else for(;;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:i,value:l}=await d.read();if(i)break;a=yield*m(u(a,l),this.chunkSize)}a.length>0&&(yield a)}finally{this.streams.delete(t)}}}function u(r,e){const t=new Uint8Array(r.length+e.length);return t.set(r),t.set(e,r.length),t}s(u,"concat");function*m(r,e){let t=r;for(;t.length>=e;)yield t.slice(0,e),t=t.slice(e);return t}s(m,"sliceChunks");function x(r){const e=atob(r),t=new Uint8Array(e.length);for(let o=0;o<e.length;o++)t[o]=e.charCodeAt(o);return t}s(x,"base64ToBytes");
2
5
  //# sourceMappingURL=kokoro.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/providers/tts/adapters/kokoro/kokoro.ts"],
4
- "sourcesContent": ["import type { TTS, TTSRequest } from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\n\nexport interface KokoroOptions {\n /**\n * Base URL of a Kokoro TTS server exposing the OpenAI-compatible\n * `/v1/audio/speech` endpoint. Defaults to a local kokoro-fastapi server.\n */\n baseUrl?: string;\n /** Default voice, e.g. `af_heart`. */\n voice?: string;\n model?: string;\n apiKey?: string;\n /** Size of the audio chunks yielded from the response stream. */\n chunkSize?: number;\n /** Injectable fetch implementation, mainly for tests. */\n fetch?: FetchLike;\n}\n\n/**\n * Kokoro TTS adapter. Consumes a Kokoro server's OpenAI-compatible speech\n * endpoint and re-chunks the returned audio so it can be played as it\n * arrives.\n */\nexport class KokoroTTS implements TTS {\n private readonly baseUrl: string;\n private readonly voice: string;\n private readonly model: string;\n private readonly apiKey: string | undefined;\n private readonly chunkSize: number;\n private readonly fetchImpl: FetchLike;\n private abort: AbortController | null = null;\n\n constructor(options: KokoroOptions = {}) {\n this.baseUrl = (options.baseUrl ?? \"http://localhost:8880\").replace(/\\/+$/, \"\");\n this.voice = options.voice ?? \"af_heart\";\n this.model = options.model ?? \"kokoro\";\n this.apiKey = options.apiKey;\n this.chunkSize = options.chunkSize ?? 8192;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n stop(): void {\n this.abort?.abort();\n }\n\n async *stream(request: TTSRequest): AsyncGenerator<Uint8Array> {\n if (!request.text) {\n throw new Error(\"KokoroTTS requires request.text\");\n }\n\n this.abort?.abort();\n const controller = new AbortController();\n this.abort = controller;\n\n try {\n const response = await this.fetchImpl(`${this.baseUrl}/v1/audio/speech`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {}),\n },\n body: JSON.stringify({\n model: this.model,\n input: request.text,\n voice: request.voice ?? this.voice,\n response_format: request.format,\n speed: request.speed,\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Kokoro request failed (${response.status}): ${body}`);\n }\n if (!response.body) {\n throw new Error(\"Kokoro returned an empty body\");\n }\n\n const reader = response.body.getReader();\n let buffer: Uint8Array<ArrayBuffer> = new Uint8Array(0);\n\n while (true) {\n if (controller.signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) break;\n buffer = concat(buffer, value);\n while (buffer.length >= this.chunkSize) {\n yield buffer.slice(0, this.chunkSize);\n buffer = buffer.slice(this.chunkSize);\n }\n }\n\n if (buffer.length > 0) {\n yield buffer;\n }\n } finally {\n if (this.abort === controller) {\n this.abort = null;\n }\n }\n }\n}\n\nfunction concat(\n a: Uint8Array<ArrayBufferLike>,\n b: Uint8Array<ArrayBufferLike>,\n): Uint8Array<ArrayBuffer> {\n const out = new Uint8Array(a.length + b.length);\n out.set(a);\n out.set(b, a.length);\n return out;\n}\n"],
5
- "mappings": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAwBO,MAAMI,CAAyB,CAxBtC,MAwBsC,CAAAC,EAAA,kBACnB,QACA,MACA,MACA,OACA,UACA,UACT,MAAgC,KAExC,YAAYC,EAAyB,CAAC,EAAG,CACvC,KAAK,SAAWA,EAAQ,SAAW,yBAAyB,QAAQ,OAAQ,EAAE,EAC9E,KAAK,MAAQA,EAAQ,OAAS,WAC9B,KAAK,MAAQA,EAAQ,OAAS,SAC9B,KAAK,OAASA,EAAQ,OACtB,KAAK,UAAYA,EAAQ,WAAa,KACtC,KAAK,UAAYA,EAAQ,OAAS,KACpC,CAEA,MAAa,CACX,KAAK,OAAO,MAAM,CACpB,CAEA,MAAO,OAAOC,EAAiD,CAC7D,GAAI,CAACA,EAAQ,KACX,MAAM,IAAI,MAAM,iCAAiC,EAGnD,KAAK,OAAO,MAAM,EAClB,MAAMC,EAAa,IAAI,gBACvB,KAAK,MAAQA,EAEb,GAAI,CACF,MAAMC,EAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,mBAAoB,CACvE,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,GAAI,KAAK,OAAS,CAAE,cAAe,UAAU,KAAK,MAAM,EAAG,EAAI,CAAC,CAClE,EACA,KAAM,KAAK,UAAU,CACnB,MAAO,KAAK,MACZ,MAAOF,EAAQ,KACf,MAAOA,EAAQ,OAAS,KAAK,MAC7B,gBAAiBA,EAAQ,OACzB,MAAOA,EAAQ,KACjB,CAAC,EACD,OAAQC,EAAW,MACrB,CAAC,EAED,GAAI,CAACC,EAAS,GAAI,CAChB,MAAMC,EAAO,MAAMD,EAAS,KAAK,EACjC,MAAM,IAAI,MAAM,0BAA0BA,EAAS,MAAM,MAAMC,CAAI,EAAE,CACvE,CACA,GAAI,CAACD,EAAS,KACZ,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAME,EAASF,EAAS,KAAK,UAAU,EACvC,IAAIG,EAAkC,IAAI,WAAW,CAAC,EAEtD,OAAa,CACX,GAAIJ,EAAW,OAAO,QACpB,MAAM,IAAI,aAAa,6BAA8B,YAAY,EAEnE,KAAM,CAAE,KAAAK,EAAM,MAAAC,CAAM,EAAI,MAAMH,EAAO,KAAK,EAC1C,GAAIE,EAAM,MAEV,IADAD,EAASG,EAAOH,EAAQE,CAAK,EACtBF,EAAO,QAAU,KAAK,WAC3B,MAAMA,EAAO,MAAM,EAAG,KAAK,SAAS,EACpCA,EAASA,EAAO,MAAM,KAAK,SAAS,CAExC,CAEIA,EAAO,OAAS,IAClB,MAAMA,EAEV,QAAE,CACI,KAAK,QAAUJ,IACjB,KAAK,MAAQ,KAEjB,CACF,CACF,CAEA,SAASO,EACPC,EACAC,EACyB,CACzB,MAAMC,EAAM,IAAI,WAAWF,EAAE,OAASC,EAAE,MAAM,EAC9C,OAAAC,EAAI,IAAIF,CAAC,EACTE,EAAI,IAAID,EAAGD,EAAE,MAAM,EACZE,CACT,CARSb,EAAAU,EAAA",
6
- "names": ["kokoro_exports", "__export", "KokoroTTS", "__toCommonJS", "KokoroTTS", "__name", "options", "request", "controller", "response", "body", "reader", "buffer", "done", "value", "concat", "a", "b", "out"]
4
+ "sourcesContent": ["import type { TTS, TTSRequest } from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\n\nexport interface KokoroOptions {\n /**\n * Base URL of a Kokoro TTS server exposing the OpenAI-compatible\n * `/v1/audio/speech` endpoint. Defaults to a local kokoro-fastapi server.\n * For Together AI use `https://api.together.ai`.\n */\n baseUrl?: string;\n /** Default voice, e.g. `af_heart`. */\n voice?: string;\n /**\n * Model name. Defaults to `kokoro` (local kokoro-fastapi); Together AI\n * serves the model as `hexgrad/Kokoro-82M`.\n */\n model?: string;\n apiKey?: string;\n /** Size of the audio chunks yielded from the response stream. */\n chunkSize?: number;\n /**\n * Ask the server for progressive (streaming) synthesis. Local\n * kokoro-fastapi streams by default; hosted endpoints such as Together AI\n * require this flag. Together AI responds with server-sent events carrying\n * base64-encoded audio deltas (pcm_s16le, 24 kHz for Kokoro) terminated by\n * `data: [DONE]`; the adapter decodes them into raw audio chunks. wav/mp3\n * formats are not streamable.\n */\n stream?: boolean;\n /** Injectable fetch implementation, mainly for tests. */\n fetch?: FetchLike;\n}\n\n/**\n * Kokoro TTS adapter. Consumes a Kokoro server's OpenAI-compatible speech\n * endpoint (local kokoro-fastapi or a hosted service such as Together AI)\n * and re-chunks the returned audio so it can be played as it arrives.\n */\nexport class KokoroTTS implements TTS {\n private readonly baseUrl: string;\n private readonly voice: string;\n private readonly model: string;\n private readonly apiKey: string | undefined;\n private readonly chunkSize: number;\n private readonly streaming: boolean;\n private readonly fetchImpl: FetchLike;\n private readonly streams = new Set<AbortController>();\n\n constructor(options: KokoroOptions = {}) {\n this.baseUrl = (options.baseUrl ?? \"http://localhost:8880\").replace(/\\/+$/, \"\");\n this.voice = options.voice ?? \"af_heart\";\n this.model = options.model ?? \"kokoro\";\n this.apiKey = options.apiKey;\n this.chunkSize = options.chunkSize ?? 8192;\n this.streaming = options.stream ?? false;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n stop(): void {\n // Abort every in-flight synthesis. Streams are tracked individually so\n // concurrent requests (the speech pipeline pre-starts the next sentence\n // while the current one is still streaming) do not cancel each other.\n for (const controller of this.streams) controller.abort();\n }\n\n async *stream(request: TTSRequest): AsyncGenerator<Uint8Array> {\n if (!request.text) {\n throw new Error(\"KokoroTTS requires request.text\");\n }\n\n const controller = new AbortController();\n this.streams.add(controller);\n\n try {\n const body: Record<string, unknown> = {\n model: this.model,\n input: request.text,\n voice: request.voice ?? this.voice,\n speed: request.speed,\n };\n if (this.streaming) {\n // Streaming synthesis: hosted endpoints (Together AI) only accept\n // raw audio; `pcm` requests map to it. wav/mp3 pass through and are\n // rejected server-side when not streamable.\n body.stream = true;\n body.response_format =\n request.format === undefined || request.format === \"pcm\"\n ? \"raw\"\n : request.format;\n } else if (request.format !== undefined) {\n body.response_format = request.format;\n }\n\n const response = await this.fetchImpl(`${this.baseUrl}/v1/audio/speech`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(this.apiKey ? { authorization: `Bearer ${this.apiKey}` } : {}),\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Kokoro request failed (${response.status}): ${body}`);\n }\n if (!response.body) {\n throw new Error(\"Kokoro returned an empty body\");\n }\n\n const reader = response.body.getReader();\n const isSse = (response.headers.get(\"content-type\") ?? \"\").includes(\n \"text/event-stream\",\n );\n let audioBuffer: Uint8Array<ArrayBuffer> = new Uint8Array(0);\n\n if (isSse) {\n // Together AI streams server-sent events with base64-encoded audio\n // deltas; the stream ends with `data: [DONE]`.\n let textBuffer = \"\";\n let doneEvent = false;\n while (!doneEvent) {\n if (controller.signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) break;\n textBuffer += new TextDecoder().decode(value);\n const events = textBuffer.split(\"\\n\\n\");\n textBuffer = events.pop() ?? \"\";\n for (const event of events) {\n for (const line of event.split(\"\\n\")) {\n if (!line.startsWith(\"data:\")) continue;\n const data = line.slice(5).trim();\n if (data === \"[DONE]\") {\n doneEvent = true;\n break;\n }\n try {\n const parsed = JSON.parse(data) as {\n type?: string;\n delta?: string;\n };\n if (\n parsed.type === \"conversation.item.audio_output.delta\" &&\n typeof parsed.delta === \"string\"\n ) {\n audioBuffer = yield* sliceChunks(\n concat(audioBuffer, base64ToBytes(parsed.delta)),\n this.chunkSize,\n );\n }\n } catch {\n // Ignore malformed events.\n }\n }\n }\n }\n } else {\n while (true) {\n if (controller.signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) break;\n audioBuffer = yield* sliceChunks(concat(audioBuffer, value), this.chunkSize);\n }\n }\n\n if (audioBuffer.length > 0) {\n yield audioBuffer;\n }\n } finally {\n this.streams.delete(controller);\n }\n }\n}\n\nfunction concat(\n a: Uint8Array<ArrayBufferLike>,\n b: Uint8Array<ArrayBufferLike>,\n): Uint8Array<ArrayBuffer> {\n const out = new Uint8Array(a.length + b.length);\n out.set(a);\n out.set(b, a.length);\n return out;\n}\n\n/**\n * Yield up to `chunkSize` bytes from `audio`, returning the remainder for\n * the caller to keep buffering. Providers deliver arbitrary chunk\n * boundaries, so audio is re-sliced into uniform pieces.\n */\nfunction* sliceChunks(\n audio: Uint8Array<ArrayBuffer>,\n chunkSize: number,\n): Generator<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>, undefined> {\n let buffer = audio;\n while (buffer.length >= chunkSize) {\n yield buffer.slice(0, chunkSize);\n buffer = buffer.slice(chunkSize);\n }\n return buffer;\n}\n\nfunction base64ToBytes(base64: string): Uint8Array<ArrayBuffer> {\n const binary = atob(base64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n out[i] = binary.charCodeAt(i);\n }\n return out;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["kokoro_exports", "__export", "KokoroTTS", "__toCommonJS", "KokoroTTS", "__name", "options", "controller", "request", "body", "response", "reader", "isSse", "audioBuffer", "textBuffer", "doneEvent", "done", "value", "events", "event", "line", "data", "parsed", "sliceChunks", "concat", "base64ToBytes", "a", "b", "out", "audio", "chunkSize", "buffer", "base64", "binary", "i"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ "use strict";var t=Object.defineProperty;var u=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var m=(o,e)=>{for(var p in e)t(o,p,{get:e[p],enumerable:!0})},x=(o,e,p,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of O(e))!f.call(o,r)&&r!==p&&t(o,r,{get:()=>e[r],enumerable:!(n=u(e,r))||n.enumerable});return o};var R=o=>x(t({},"__esModule",{value:!0}),o);var S={};m(S,{OpenRouterTTS:()=>T.OpenRouterTTS});module.exports=R(S);var T=require("./openrouter.js");
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/tts/adapters/openrouter/index.ts"],
4
+ "sourcesContent": ["export { OpenRouterTTS } from \"./openrouter\";\nexport type { OpenRouterTTSOptions } from \"./openrouter\";\n"],
5
+ "mappings": "yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oDAAAE,EAAAF,GAAA,IAAAG,EAA8B",
6
+ "names": ["openrouter_exports", "__export", "__toCommonJS", "import_openrouter"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var d=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var s=(t,e)=>d(t,"name",{value:e,configurable:!0});var y=(t,e)=>{for(var r in e)d(t,r,{get:e[r],enumerable:!0})},T=(t,e,r,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of p(e))!f.call(t,i)&&i!==r&&d(t,i,{get:()=>e[i],enumerable:!(o=m(e,i))||o.enumerable});return t};var w=t=>T(d({},"__esModule",{value:!0}),t);var A={};y(A,{OpenRouterTTS:()=>b});module.exports=w(A);class b{static{s(this,"OpenRouterTTS")}apiKey;baseUrl;model;voice;format;chunkSize;idleTimeoutMs;fetchImpl;streams=new Set;constructor(e){if(!e.apiKey)throw new Error("OpenRouterTTS requires an apiKey");this.apiKey=e.apiKey,this.baseUrl=(e.baseUrl??"https://openrouter.ai/api/v1").replace(/\/+$/,""),this.model=e.model??"fish-audio/s2.1-pro-free:free",this.voice=e.voice??"",this.format=e.format??"pcm",this.chunkSize=e.chunkSize??8192,this.idleTimeoutMs=e.idleTimeoutMs??15e3,this.fetchImpl=e.fetch??fetch}stop(){for(const e of this.streams)e.abort()}async*stream(e){if(!e.text)throw new Error("OpenRouterTTS requires request.text");const r=new AbortController;this.streams.add(r);let o=!1;try{const i={model:this.model,input:e.text,...e.voice??this.voice?{voice:e.voice??this.voice}:{},response_format:(e.format??this.format)==="mp3"?"mp3":"pcm"};e.speed!==void 0&&(i.speed=e.speed);const n=await c(this.fetchImpl(`${this.baseUrl}/audio/speech`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(i),signal:r.signal}),this.idleTimeoutMs,()=>{o=!0,r.abort()});if(!n.ok){const u=await n.text().catch(()=>"");throw new Error(`OpenRouter TTS failed (${n.status})${u?`: ${u}`:""}`)}if(!n.body)throw new Error("OpenRouter returned an empty body");const l=n.body.getReader();let a=new Uint8Array(0);for(;;){if(r.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:u,value:h}=await c(l.read(),this.idleTimeoutMs,()=>{o=!0,r.abort()});if(u)break;a=yield*g(v(a,h),this.chunkSize)}a.length>0&&(yield a)}catch(i){throw o?new Error(`OpenRouter TTS produced no audio for ${this.idleTimeoutMs}ms`):i}finally{this.streams.delete(r)}}}function c(t,e,r){return new Promise((o,i)=>{let n;const l=s(()=>clearTimeout(n),"cleanup");n=setTimeout(()=>{l(),r(),i(new Error(`timed out after ${e}ms`))},e),t.then(a=>{l(),o(a)},a=>{l(),i(a)})})}s(c,"withTimeout");function v(t,e){const r=new Uint8Array(t.length+e.length);return r.set(t),r.set(e,t.length),r}s(v,"concat");function*g(t,e){let r=t;for(;r.length>=e;)yield r.slice(0,e),r=r.slice(e);return r}s(g,"sliceChunks");
2
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/tts/adapters/openrouter/openrouter.ts"],
4
+ "sourcesContent": ["import type { TTS, TTSRequest } from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\n\nexport interface OpenRouterTTSOptions {\n apiKey: string;\n /** Defaults to `https://openrouter.ai/api/v1`. */\n baseUrl?: string;\n /**\n * TTS model. Defaults to the free `fish-audio/s2.1-pro-free:free` variant \u2014\n * fast, per-character priced. Pass `fish-audio/s2.1-pro` for the paid tier\n * (no free-variant rate limits). Note the free variant is intermittently\n * unavailable on OpenRouter (404 \"No endpoints found\").\n */\n model?: string;\n /**\n * Default voice, sent when the request carries none. Set it for a\n * consistent voice: the default fish model accepts exactly `\"alloy\"` and\n * rejects other names, and omitting `voice` lets the free variant vary the\n * voice per request. Models like OpenAI TTS require a voice. Voice support\n * varies by model and provider.\n */\n voice?: string;\n /**\n * Output format used when the request doesn't specify one. `\"pcm\"` (the\n * default) is the lowest-latency realtime format, but its sample rate is\n * provider-defined and opaque \u2014 clients must know it. `\"mp3\"` is\n * self-describing: decoders read the real rate from the stream.\n */\n format?: \"pcm\" | \"mp3\";\n /** Size of the audio chunks yielded from the response stream. Default 8192. */\n chunkSize?: number;\n /**\n * Abort a synthesis that delivers no audio bytes for this long (default\n * 15000ms). A provider connection that goes silent \u2014 or never responds \u2014\n * would otherwise wedge the speech pipeline forever, since the pipeline\n * holds one synthesis slot and its delivery chain until the stream ends.\n * Any byte resets the clock; a genuinely slow synthesis just needs a\n * response within this window.\n */\n idleTimeoutMs?: number;\n /** Injectable fetch, mainly for tests. */\n fetch?: FetchLike;\n}\n\n/**\n * OpenRouter TTS adapter (`POST /api/v1/audio/speech`, OpenAI-compatible).\n *\n * The endpoint returns raw audio bytes (not JSON) in `mp3` or `pcm` format.\n * The adapter re-chunks the response stream so audio can be played as it\n * arrives; `stop()` aborts the in-flight synthesis. `pcm` is the default\n * output (OpenRouter's own recommendation for realtime pipelines).\n */\nexport class OpenRouterTTS implements TTS {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly model: string;\n private readonly voice: string;\n private readonly format: \"pcm\" | \"mp3\";\n private readonly chunkSize: number;\n private readonly idleTimeoutMs: number;\n private readonly fetchImpl: FetchLike;\n private readonly streams = new Set<AbortController>();\n\n constructor(options: OpenRouterTTSOptions) {\n if (!options.apiKey) {\n throw new Error(\"OpenRouterTTS requires an apiKey\");\n }\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? \"https://openrouter.ai/api/v1\").replace(/\\/+$/, \"\");\n this.model = options.model ?? \"fish-audio/s2.1-pro-free:free\";\n this.voice = options.voice ?? \"\";\n this.format = options.format ?? \"pcm\";\n this.chunkSize = options.chunkSize ?? 8192;\n this.idleTimeoutMs = options.idleTimeoutMs ?? 15_000;\n this.fetchImpl = options.fetch ?? fetch;\n }\n\n stop(): void {\n // Abort every in-flight synthesis. Streams are tracked individually so\n // concurrent requests (the speech pipeline pre-starts the next sentence\n // while the current one is still streaming) do not cancel each other.\n for (const controller of this.streams) controller.abort();\n }\n\n async *stream(request: TTSRequest): AsyncGenerator<Uint8Array> {\n if (!request.text) {\n throw new Error(\"OpenRouterTTS requires request.text\");\n }\n\n const controller = new AbortController();\n this.streams.add(controller);\n // Every await is bounded by an idle clock: a request that never responds,\n // or a connection that goes silent mid-stream, aborts with a clear error\n // instead of holding the speech pipeline's synthesis slot and delivery\n // chain open forever (the reply would otherwise wait for the next input).\n let timedOut = false;\n\n try {\n const body: Record<string, unknown> = {\n model: this.model,\n input: request.text,\n // Voice is only sent when configured: fish-audio (the default model)\n // rejects an explicit voice with a 400, while models like OpenAI TTS\n // require one.\n ...(request.voice ?? this.voice ? { voice: request.voice ?? this.voice } : {}),\n // OpenRouter supports mp3 and pcm only; anything else (or\n // unspecified) maps to pcm, the lower-latency realtime format.\n response_format: (request.format ?? this.format) === \"mp3\" ? \"mp3\" : \"pcm\",\n };\n if (request.speed !== undefined) body.speed = request.speed;\n\n const response = await withTimeout(\n this.fetchImpl(`${this.baseUrl}/audio/speech`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n }),\n this.idleTimeoutMs,\n () => {\n timedOut = true;\n controller.abort();\n },\n );\n\n if (!response.ok) {\n // Non-200 responses carry a JSON error body, not audio.\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `OpenRouter TTS failed (${response.status})${detail ? `: ${detail}` : \"\"}`,\n );\n }\n if (!response.body) {\n throw new Error(\"OpenRouter returned an empty body\");\n }\n\n const reader = response.body.getReader();\n let buffer = new Uint8Array(0);\n while (true) {\n if (controller.signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await withTimeout(\n reader.read(),\n this.idleTimeoutMs,\n () => {\n timedOut = true;\n controller.abort();\n },\n );\n if (done) break;\n buffer = yield* sliceChunks(concat(buffer, value), this.chunkSize);\n }\n if (buffer.length > 0) {\n yield buffer;\n }\n } catch (error) {\n // Translate the watchdog's abort into a clear failure. Real aborts\n // (`stop()`, interrupts) keep their AbortError so callers can tell the\n // difference.\n if (timedOut) {\n throw new Error(`OpenRouter TTS produced no audio for ${this.idleTimeoutMs}ms`);\n }\n throw error;\n } finally {\n this.streams.delete(controller);\n }\n }\n}\n\n/**\n * Race `promise` against a timeout: reject after `ms` and fire `onTimeout`\n * (which should abort the underlying request) so a hanging operation is\n * released rather than left pending forever.\n */\nfunction withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n onTimeout: () => void,\n): Promise<T> {\n return new Promise((resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const cleanup = () => clearTimeout(timer);\n timer = setTimeout(() => {\n cleanup();\n onTimeout();\n reject(new Error(`timed out after ${ms}ms`));\n }, ms);\n promise.then(\n (value) => {\n cleanup();\n resolve(value);\n },\n (error) => {\n cleanup();\n reject(error);\n },\n );\n });\n}\n\nfunction concat(\n a: Uint8Array<ArrayBufferLike>,\n b: Uint8Array<ArrayBufferLike>,\n): Uint8Array<ArrayBuffer> {\n const out = new Uint8Array(a.length + b.length);\n out.set(a);\n out.set(b, a.length);\n return out;\n}\n\n/**\n * Yield up to `chunkSize` bytes from `audio`, returning the remainder for\n * the caller to keep buffering. Providers deliver arbitrary chunk\n * boundaries, so audio is re-sliced into uniform pieces.\n */\nfunction* sliceChunks(\n audio: Uint8Array<ArrayBuffer>,\n chunkSize: number,\n): Generator<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>, undefined> {\n let buffer = audio;\n while (buffer.length >= chunkSize) {\n yield buffer.slice(0, chunkSize);\n buffer = buffer.slice(chunkSize);\n }\n return buffer;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["openrouter_exports", "__export", "OpenRouterTTS", "__toCommonJS", "OpenRouterTTS", "__name", "options", "controller", "request", "timedOut", "body", "response", "withTimeout", "detail", "reader", "buffer", "done", "value", "sliceChunks", "concat", "error", "promise", "ms", "onTimeout", "resolve", "reject", "timer", "cleanup", "a", "b", "out", "audio", "chunkSize"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var a=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var h=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var o=(i,e)=>a(i,"name",{value:e,configurable:!0});var p=(i,e)=>{for(var t in e)a(i,t,{get:e[t],enumerable:!0})},m=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of h(e))!f.call(i,n)&&n!==t&&a(i,n,{get:()=>e[n],enumerable:!(r=l(e,n))||r.enumerable});return i};var u=i=>m(a({},"__esModule",{value:!0}),i);var j={};p(j,{FieldStream:()=>b});module.exports=u(j);var s=require("../reference/reference.js");class b{static{o(this,"FieldStream")}schema;fieldHandlers;itemHandlers;objectHandlers=new Set;errorHandlers=new Set;onCancel;cancelled=!1;objectState="streaming";constructor(e,t={}){this.schema=e,this.onCancel=t.onCancel,this.fieldHandlers=new Array(e.length),this.itemHandlers=new Array(e.length)}when(e,t){const r=this.resolveIndex(e);return(this.fieldHandlers[r]??=new Set).add(t),this}whenItem(e,t){const r=this.resolveIndex(e),n=this.schema[r];if(n.mode!=="array")throw new TypeError(`whenItem: field "${n.path.join(".")}" is not an array`);return(this.itemHandlers[r]??=new Set).add(t),this}whenObjectDone(e){return this.objectHandlers.add(e),this}onError(e){return this.errorHandlers.add(e),this}cancel(){this.cancelled||(this.cancelled=!0,this.cancelObject(),this.onCancel?.())}beginObject(){this.objectState="streaming"}emitField(e,t){if(this.objectState!=="streaming")return;const r=this.fieldHandlers[e];if(r!==void 0)for(const n of r)this.fire(n,[t])}emitItem(e,t,r){if(this.objectState!=="streaming")return;const n=this.itemHandlers[e];if(n!==void 0)for(const c of n)this.fire(c,[r,t])}emitObject(e){if(this.objectState!=="streaming")return;this.objectState="done";const t=(0,s.materializeObject)(this.schema,e);for(const r of this.objectHandlers)this.fire(r,[t])}cancelObject(){this.objectState==="streaming"&&(this.objectState="cancelled")}fail(e){this.objectState==="streaming"&&(this.objectState="failed"),this.dispatchError(d(e))}get objectCompleted(){return this.objectState==="done"}resolveIndex(e){const t=Array.isArray(e)?e.join("."):e,r=this.schema.findIndex(n=>n.path.join(".")===t);if(r===-1)throw new TypeError(`unknown field path: "${t}"`);return r}fire(e,t){try{const r=e(...t);r instanceof Promise&&r.catch(n=>this.dispatchError(d(n)))}catch(r){this.dispatchError(d(r))}}dispatchError(e){if(this.errorHandlers.size===0)throw e;for(const t of this.errorHandlers)try{t(e)}catch{}}}function d(i){return i instanceof Error?i:new Error(String(i))}o(d,"toError");
2
+ //# sourceMappingURL=field-stream.js.map