@moureau/pipeflow 0.0.1 → 0.0.2

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 (311) hide show
  1. package/README.md +245 -117
  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/conversations/conversation/conversation.js +1 -1
  7. package/dist/cjs/conversations/conversation/conversation.js.map +3 -3
  8. package/dist/cjs/conversations/conversations.js +1 -1
  9. package/dist/cjs/conversations/conversations.js.map +3 -3
  10. package/dist/cjs/conversations/index.js +1 -1
  11. package/dist/cjs/conversations/index.js.map +3 -3
  12. package/dist/cjs/conversations/orchestration/coordination/coordination.js +10 -0
  13. package/dist/cjs/conversations/orchestration/coordination/coordination.js.map +7 -0
  14. package/dist/cjs/conversations/orchestration/coordination/index.js +2 -0
  15. package/dist/cjs/conversations/orchestration/coordination/index.js.map +7 -0
  16. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js +4 -0
  17. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js.map +7 -0
  18. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/index.js +2 -0
  19. package/dist/cjs/conversations/orchestration/orchestrator/coordination-runner/index.js.map +7 -0
  20. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js +2 -0
  21. package/dist/cjs/conversations/orchestration/orchestrator/generation/generation.js.map +7 -0
  22. package/dist/cjs/conversations/orchestration/orchestrator/generation/index.js +2 -0
  23. package/dist/cjs/conversations/orchestration/orchestrator/generation/index.js.map +7 -0
  24. package/dist/cjs/conversations/orchestration/orchestrator/history/history.js +4 -0
  25. package/dist/cjs/conversations/orchestration/orchestrator/history/history.js.map +7 -0
  26. package/dist/cjs/conversations/orchestration/orchestrator/history/index.js +2 -0
  27. package/dist/cjs/conversations/orchestration/orchestrator/history/index.js.map +7 -0
  28. package/dist/cjs/conversations/orchestration/orchestrator/index.js +1 -1
  29. package/dist/cjs/conversations/orchestration/orchestrator/index.js.map +3 -3
  30. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js +1 -2
  31. package/dist/cjs/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  32. package/dist/cjs/conversations/orchestration/orchestrator/routing/index.js +2 -0
  33. package/dist/cjs/conversations/orchestration/orchestrator/routing/index.js.map +7 -0
  34. package/dist/cjs/conversations/orchestration/orchestrator/routing/routing.js +23 -0
  35. package/dist/cjs/conversations/orchestration/orchestrator/routing/routing.js.map +7 -0
  36. package/dist/cjs/conversations/orchestration/orchestrator/speech/index.js +2 -0
  37. package/dist/cjs/conversations/orchestration/orchestrator/speech/index.js.map +7 -0
  38. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js +2 -0
  39. package/dist/cjs/conversations/orchestration/orchestrator/speech/speech.js.map +7 -0
  40. package/dist/cjs/conversations/orchestration/orchestrator/tools/index.js +2 -0
  41. package/dist/cjs/conversations/orchestration/orchestrator/tools/index.js.map +7 -0
  42. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js +2 -0
  43. package/dist/cjs/conversations/orchestration/orchestrator/tools/tools.js.map +7 -0
  44. package/dist/cjs/conversations/orchestration/test-harness.js +2 -0
  45. package/dist/cjs/conversations/orchestration/test-harness.js.map +7 -0
  46. package/dist/cjs/conversations/orchestration/text-chunker/index.js +2 -0
  47. package/dist/cjs/conversations/orchestration/text-chunker/index.js.map +7 -0
  48. package/dist/cjs/conversations/orchestration/text-chunker/text-chunker.js +3 -0
  49. package/dist/cjs/conversations/orchestration/text-chunker/text-chunker.js.map +7 -0
  50. package/dist/cjs/conversations/stream/conversation-stream.js +2 -0
  51. package/dist/cjs/conversations/stream/conversation-stream.js.map +7 -0
  52. package/dist/cjs/conversations/stream/index.js +2 -0
  53. package/dist/cjs/conversations/stream/index.js.map +7 -0
  54. package/dist/cjs/conversations/types.js +1 -1
  55. package/dist/cjs/conversations/types.js.map +2 -2
  56. package/dist/cjs/index.js +1 -1
  57. package/dist/cjs/index.js.map +3 -3
  58. package/dist/cjs/persistence/adapters/sqlite/sqlite.js +18 -14
  59. package/dist/cjs/persistence/adapters/sqlite/sqlite.js.map +3 -3
  60. package/dist/cjs/pipeflow/index.js +2 -0
  61. package/dist/cjs/pipeflow/index.js.map +7 -0
  62. package/dist/cjs/pipeflow/pipeflow.js +2 -0
  63. package/dist/cjs/pipeflow/pipeflow.js.map +7 -0
  64. package/dist/cjs/providers/index.js +1 -1
  65. package/dist/cjs/providers/index.js.map +2 -2
  66. package/dist/cjs/providers/llm/adapters/claude/claude.js +5 -0
  67. package/dist/cjs/providers/llm/adapters/claude/claude.js.map +7 -0
  68. package/dist/cjs/providers/llm/adapters/claude/index.js +2 -0
  69. package/dist/cjs/providers/llm/adapters/claude/index.js.map +7 -0
  70. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js +1 -1
  71. package/dist/cjs/providers/llm/adapters/deepseek/deepseek.js.map +3 -3
  72. package/dist/cjs/providers/llm/adapters/index.js +1 -1
  73. package/dist/cjs/providers/llm/adapters/index.js.map +3 -3
  74. package/dist/cjs/providers/llm/adapters/openai/index.js +2 -0
  75. package/dist/cjs/providers/llm/adapters/openai/index.js.map +7 -0
  76. package/dist/cjs/providers/llm/adapters/openai/openai.js +2 -0
  77. package/dist/cjs/providers/llm/adapters/openai/openai.js.map +7 -0
  78. package/dist/cjs/providers/llm/adapters/openai-compatible.js +10 -0
  79. package/dist/cjs/providers/llm/adapters/openai-compatible.js.map +7 -0
  80. package/dist/cjs/providers/llm/adapters/openrouter/index.js +2 -0
  81. package/dist/cjs/providers/llm/adapters/openrouter/index.js.map +7 -0
  82. package/dist/cjs/providers/llm/adapters/openrouter/openrouter.js +2 -0
  83. package/dist/cjs/providers/llm/adapters/openrouter/openrouter.js.map +7 -0
  84. package/dist/cjs/providers/llm/index.js +1 -1
  85. package/dist/cjs/providers/llm/index.js.map +3 -3
  86. package/dist/cjs/providers/llm/toolmode/index.js +2 -0
  87. package/dist/cjs/providers/llm/toolmode/index.js.map +7 -0
  88. package/dist/cjs/providers/llm/toolmode/toolmode.js +2 -0
  89. package/dist/cjs/providers/llm/toolmode/toolmode.js.map +7 -0
  90. package/dist/cjs/providers/llm/types.js +1 -1
  91. package/dist/cjs/providers/llm/types.js.map +3 -3
  92. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js +1 -1
  93. package/dist/cjs/providers/stt/adapters/deepgram/deepgram.js.map +3 -3
  94. package/dist/cjs/providers/stt/adapters/index.js +1 -1
  95. package/dist/cjs/providers/stt/adapters/index.js.map +3 -3
  96. package/dist/cjs/providers/stt/adapters/openrouter/index.js +2 -0
  97. package/dist/cjs/providers/stt/adapters/openrouter/index.js.map +7 -0
  98. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js +2 -0
  99. package/dist/cjs/providers/stt/adapters/openrouter/openrouter.js.map +7 -0
  100. package/dist/cjs/providers/tts/adapters/index.js +1 -1
  101. package/dist/cjs/providers/tts/adapters/index.js.map +3 -3
  102. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js +4 -1
  103. package/dist/cjs/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  104. package/dist/cjs/providers/tts/adapters/openrouter/index.js +2 -0
  105. package/dist/cjs/providers/tts/adapters/openrouter/index.js.map +7 -0
  106. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js +2 -0
  107. package/dist/cjs/providers/tts/adapters/openrouter/openrouter.js.map +7 -0
  108. package/dist/cjs/transport/streamobject/field-stream/field-stream.js +2 -0
  109. package/dist/cjs/transport/streamobject/field-stream/field-stream.js.map +7 -0
  110. package/dist/cjs/transport/streamobject/field-stream/index.js +2 -0
  111. package/dist/cjs/transport/streamobject/field-stream/index.js.map +7 -0
  112. package/dist/cjs/transport/streamobject/index.js +2 -0
  113. package/dist/cjs/transport/streamobject/index.js.map +7 -0
  114. package/dist/cjs/transport/streamobject/json-adapter/index.js +2 -0
  115. package/dist/cjs/transport/streamobject/json-adapter/index.js.map +7 -0
  116. package/dist/cjs/transport/streamobject/json-adapter/json-adapter.js +4 -0
  117. package/dist/cjs/transport/streamobject/json-adapter/json-adapter.js.map +7 -0
  118. package/dist/cjs/transport/streamobject/reference/index.js +2 -0
  119. package/dist/cjs/transport/streamobject/reference/index.js.map +7 -0
  120. package/dist/cjs/transport/streamobject/reference/reference.js +2 -0
  121. package/dist/cjs/transport/streamobject/reference/reference.js.map +7 -0
  122. package/dist/cjs/transport/streamobject/stream-object/index.js +2 -0
  123. package/dist/cjs/transport/streamobject/stream-object/index.js.map +7 -0
  124. package/dist/cjs/transport/streamobject/stream-object/stream-object.js +2 -0
  125. package/dist/cjs/transport/streamobject/stream-object/stream-object.js.map +7 -0
  126. package/dist/esm/agents/agent.js +1 -1
  127. package/dist/esm/agents/agent.js.map +3 -3
  128. package/dist/esm/agents/tools/tools.js +1 -1
  129. package/dist/esm/agents/tools/tools.js.map +3 -3
  130. package/dist/esm/conversations/conversation/conversation.js +1 -1
  131. package/dist/esm/conversations/conversation/conversation.js.map +3 -3
  132. package/dist/esm/conversations/conversations.js +1 -1
  133. package/dist/esm/conversations/conversations.js.map +3 -3
  134. package/dist/esm/conversations/index.js +1 -1
  135. package/dist/esm/conversations/index.js.map +3 -3
  136. package/dist/esm/conversations/orchestration/coordination/coordination.js +10 -0
  137. package/dist/esm/conversations/orchestration/coordination/coordination.js.map +7 -0
  138. package/dist/esm/conversations/orchestration/coordination/index.js +2 -0
  139. package/dist/esm/conversations/orchestration/coordination/index.js.map +7 -0
  140. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js +4 -0
  141. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.js.map +7 -0
  142. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/index.js +2 -0
  143. package/dist/esm/conversations/orchestration/orchestrator/coordination-runner/index.js.map +7 -0
  144. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js +2 -0
  145. package/dist/esm/conversations/orchestration/orchestrator/generation/generation.js.map +7 -0
  146. package/dist/esm/conversations/orchestration/orchestrator/generation/index.js +2 -0
  147. package/dist/esm/conversations/orchestration/orchestrator/generation/index.js.map +7 -0
  148. package/dist/esm/conversations/orchestration/orchestrator/history/history.js +4 -0
  149. package/dist/esm/conversations/orchestration/orchestrator/history/history.js.map +7 -0
  150. package/dist/esm/conversations/orchestration/orchestrator/history/index.js +2 -0
  151. package/dist/esm/conversations/orchestration/orchestrator/history/index.js.map +7 -0
  152. package/dist/esm/conversations/orchestration/orchestrator/index.js +1 -1
  153. package/dist/esm/conversations/orchestration/orchestrator/index.js.map +3 -3
  154. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js +1 -2
  155. package/dist/esm/conversations/orchestration/orchestrator/orchestrator.js.map +3 -3
  156. package/dist/esm/conversations/orchestration/orchestrator/routing/index.js +2 -0
  157. package/dist/esm/conversations/orchestration/orchestrator/routing/index.js.map +7 -0
  158. package/dist/esm/conversations/orchestration/orchestrator/routing/routing.js +23 -0
  159. package/dist/esm/conversations/orchestration/orchestrator/routing/routing.js.map +7 -0
  160. package/dist/esm/conversations/orchestration/orchestrator/speech/index.js +2 -0
  161. package/dist/esm/conversations/orchestration/orchestrator/speech/index.js.map +7 -0
  162. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js +2 -0
  163. package/dist/esm/conversations/orchestration/orchestrator/speech/speech.js.map +7 -0
  164. package/dist/esm/conversations/orchestration/orchestrator/tools/index.js +2 -0
  165. package/dist/esm/conversations/orchestration/orchestrator/tools/index.js.map +7 -0
  166. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js +2 -0
  167. package/dist/esm/conversations/orchestration/orchestrator/tools/tools.js.map +7 -0
  168. package/dist/esm/conversations/orchestration/test-harness.js +2 -0
  169. package/dist/esm/conversations/orchestration/test-harness.js.map +7 -0
  170. package/dist/esm/conversations/orchestration/text-chunker/index.js +2 -0
  171. package/dist/esm/conversations/orchestration/text-chunker/index.js.map +7 -0
  172. package/dist/esm/conversations/orchestration/text-chunker/text-chunker.js +3 -0
  173. package/dist/esm/conversations/orchestration/text-chunker/text-chunker.js.map +7 -0
  174. package/dist/esm/conversations/stream/conversation-stream.js +2 -0
  175. package/dist/esm/conversations/stream/conversation-stream.js.map +7 -0
  176. package/dist/esm/conversations/stream/index.js +2 -0
  177. package/dist/esm/conversations/stream/index.js.map +7 -0
  178. package/dist/esm/conversations/types.js +1 -1
  179. package/dist/esm/conversations/types.js.map +2 -2
  180. package/dist/esm/index.js +1 -1
  181. package/dist/esm/index.js.map +3 -3
  182. package/dist/esm/persistence/adapters/sqlite/sqlite.js +18 -14
  183. package/dist/esm/persistence/adapters/sqlite/sqlite.js.map +3 -3
  184. package/dist/esm/pipeflow/index.js +2 -0
  185. package/dist/esm/pipeflow/index.js.map +7 -0
  186. package/dist/esm/pipeflow/pipeflow.js +2 -0
  187. package/dist/esm/pipeflow/pipeflow.js.map +7 -0
  188. package/dist/esm/providers/index.js +1 -1
  189. package/dist/esm/providers/index.js.map +3 -3
  190. package/dist/esm/providers/llm/adapters/claude/claude.js +5 -0
  191. package/dist/esm/providers/llm/adapters/claude/claude.js.map +7 -0
  192. package/dist/esm/providers/llm/adapters/claude/index.js +2 -0
  193. package/dist/esm/providers/llm/adapters/claude/index.js.map +7 -0
  194. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js +1 -1
  195. package/dist/esm/providers/llm/adapters/deepseek/deepseek.js.map +3 -3
  196. package/dist/esm/providers/llm/adapters/index.js +1 -1
  197. package/dist/esm/providers/llm/adapters/index.js.map +3 -3
  198. package/dist/esm/providers/llm/adapters/openai/index.js +2 -0
  199. package/dist/esm/providers/llm/adapters/openai/index.js.map +7 -0
  200. package/dist/esm/providers/llm/adapters/openai/openai.js +2 -0
  201. package/dist/esm/providers/llm/adapters/openai/openai.js.map +7 -0
  202. package/dist/esm/providers/llm/adapters/openai-compatible.js +10 -0
  203. package/dist/esm/providers/llm/adapters/openai-compatible.js.map +7 -0
  204. package/dist/esm/providers/llm/adapters/openrouter/index.js +2 -0
  205. package/dist/esm/providers/llm/adapters/openrouter/index.js.map +7 -0
  206. package/dist/esm/providers/llm/adapters/openrouter/openrouter.js +2 -0
  207. package/dist/esm/providers/llm/adapters/openrouter/openrouter.js.map +7 -0
  208. package/dist/esm/providers/llm/index.js +1 -1
  209. package/dist/esm/providers/llm/index.js.map +3 -3
  210. package/dist/esm/providers/llm/toolmode/index.js +2 -0
  211. package/dist/esm/providers/llm/toolmode/index.js.map +7 -0
  212. package/dist/esm/providers/llm/toolmode/toolmode.js +2 -0
  213. package/dist/esm/providers/llm/toolmode/toolmode.js.map +7 -0
  214. package/dist/esm/providers/llm/types.js +1 -0
  215. package/dist/esm/providers/llm/types.js.map +4 -4
  216. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js +1 -1
  217. package/dist/esm/providers/stt/adapters/deepgram/deepgram.js.map +3 -3
  218. package/dist/esm/providers/stt/adapters/index.js +1 -1
  219. package/dist/esm/providers/stt/adapters/index.js.map +3 -3
  220. package/dist/esm/providers/stt/adapters/openrouter/index.js +2 -0
  221. package/dist/esm/providers/stt/adapters/openrouter/index.js.map +7 -0
  222. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js +2 -0
  223. package/dist/esm/providers/stt/adapters/openrouter/openrouter.js.map +7 -0
  224. package/dist/esm/providers/tts/adapters/index.js +1 -1
  225. package/dist/esm/providers/tts/adapters/index.js.map +3 -3
  226. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js +4 -1
  227. package/dist/esm/providers/tts/adapters/kokoro/kokoro.js.map +3 -3
  228. package/dist/esm/providers/tts/adapters/openrouter/index.js +2 -0
  229. package/dist/esm/providers/tts/adapters/openrouter/index.js.map +7 -0
  230. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js +2 -0
  231. package/dist/esm/providers/tts/adapters/openrouter/openrouter.js.map +7 -0
  232. package/dist/esm/transport/streamobject/field-stream/field-stream.js +2 -0
  233. package/dist/esm/transport/streamobject/field-stream/field-stream.js.map +7 -0
  234. package/dist/esm/transport/streamobject/field-stream/index.js +2 -0
  235. package/dist/esm/transport/streamobject/field-stream/index.js.map +7 -0
  236. package/dist/esm/transport/streamobject/index.js +2 -0
  237. package/dist/esm/transport/streamobject/index.js.map +7 -0
  238. package/dist/esm/transport/streamobject/json-adapter/index.js +2 -0
  239. package/dist/esm/transport/streamobject/json-adapter/index.js.map +7 -0
  240. package/dist/esm/transport/streamobject/json-adapter/json-adapter.js +4 -0
  241. package/dist/esm/transport/streamobject/json-adapter/json-adapter.js.map +7 -0
  242. package/dist/esm/transport/streamobject/reference/index.js +2 -0
  243. package/dist/esm/transport/streamobject/reference/index.js.map +7 -0
  244. package/dist/esm/transport/streamobject/reference/reference.js +2 -0
  245. package/dist/esm/transport/streamobject/reference/reference.js.map +7 -0
  246. package/dist/esm/transport/streamobject/stream-object/index.js +2 -0
  247. package/dist/esm/transport/streamobject/stream-object/index.js.map +7 -0
  248. package/dist/esm/transport/streamobject/stream-object/stream-object.js +2 -0
  249. package/dist/esm/transport/streamobject/stream-object/stream-object.js.map +7 -0
  250. package/dist/types/agents/agent.d.ts +3 -0
  251. package/dist/types/agents/tools/tools.d.ts +28 -6
  252. package/dist/types/conversations/conversation/conversation.d.ts +85 -6
  253. package/dist/types/conversations/conversations.d.ts +11 -1
  254. package/dist/types/conversations/index.d.ts +2 -1
  255. package/dist/types/conversations/orchestration/coordination/coordination.d.ts +192 -0
  256. package/dist/types/conversations/orchestration/coordination/index.d.ts +2 -0
  257. package/dist/types/conversations/orchestration/orchestrator/coordination-runner/coordination-runner.d.ts +108 -0
  258. package/dist/types/conversations/orchestration/orchestrator/coordination-runner/index.d.ts +2 -0
  259. package/dist/types/conversations/orchestration/orchestrator/generation/generation.d.ts +43 -0
  260. package/dist/types/conversations/orchestration/orchestrator/generation/index.d.ts +2 -0
  261. package/dist/types/conversations/orchestration/orchestrator/history/history.d.ts +62 -0
  262. package/dist/types/conversations/orchestration/orchestrator/history/index.d.ts +1 -0
  263. package/dist/types/conversations/orchestration/orchestrator/index.d.ts +10 -0
  264. package/dist/types/conversations/orchestration/orchestrator/orchestrator.d.ts +45 -27
  265. package/dist/types/conversations/orchestration/orchestrator/routing/index.d.ts +1 -0
  266. package/dist/types/conversations/orchestration/orchestrator/routing/routing.d.ts +19 -0
  267. package/dist/types/conversations/orchestration/orchestrator/speech/index.d.ts +2 -0
  268. package/dist/types/conversations/orchestration/orchestrator/speech/speech.d.ts +49 -0
  269. package/dist/types/conversations/orchestration/orchestrator/tools/index.d.ts +2 -0
  270. package/dist/types/conversations/orchestration/orchestrator/tools/tools.d.ts +31 -0
  271. package/dist/types/conversations/orchestration/test-harness.d.ts +96 -0
  272. package/dist/types/conversations/orchestration/text-chunker/index.d.ts +1 -0
  273. package/dist/types/conversations/orchestration/text-chunker/text-chunker.d.ts +39 -0
  274. package/dist/types/conversations/stream/conversation-stream.d.ts +42 -0
  275. package/dist/types/conversations/stream/index.d.ts +1 -0
  276. package/dist/types/conversations/types.d.ts +32 -0
  277. package/dist/types/index.d.ts +5 -38
  278. package/dist/types/pipeflow/index.d.ts +2 -0
  279. package/dist/types/pipeflow/pipeflow.d.ts +35 -0
  280. package/dist/types/providers/index.d.ts +1 -1
  281. package/dist/types/providers/llm/adapters/claude/claude.d.ts +45 -0
  282. package/dist/types/providers/llm/adapters/claude/index.d.ts +2 -0
  283. package/dist/types/providers/llm/adapters/deepseek/deepseek.d.ts +23 -2
  284. package/dist/types/providers/llm/adapters/index.d.ts +3 -0
  285. package/dist/types/providers/llm/adapters/openai/index.d.ts +2 -0
  286. package/dist/types/providers/llm/adapters/openai/openai.d.ts +45 -0
  287. package/dist/types/providers/llm/adapters/openai-compatible.d.ts +44 -0
  288. package/dist/types/providers/llm/adapters/openrouter/index.d.ts +2 -0
  289. package/dist/types/providers/llm/adapters/openrouter/openrouter.d.ts +60 -0
  290. package/dist/types/providers/llm/index.d.ts +5 -2
  291. package/dist/types/providers/llm/toolmode/index.d.ts +2 -0
  292. package/dist/types/providers/llm/toolmode/toolmode.d.ts +99 -0
  293. package/dist/types/providers/llm/types.d.ts +62 -0
  294. package/dist/types/providers/stt/adapters/deepgram/deepgram.d.ts +4 -1
  295. package/dist/types/providers/stt/adapters/index.d.ts +1 -0
  296. package/dist/types/providers/stt/adapters/openrouter/index.d.ts +2 -0
  297. package/dist/types/providers/stt/adapters/openrouter/openrouter.d.ts +94 -0
  298. package/dist/types/providers/tts/adapters/index.d.ts +1 -0
  299. package/dist/types/providers/tts/adapters/kokoro/kokoro.d.ts +17 -2
  300. package/dist/types/providers/tts/adapters/openrouter/index.d.ts +2 -0
  301. package/dist/types/providers/tts/adapters/openrouter/openrouter.d.ts +45 -0
  302. package/dist/types/transport/streamobject/field-stream/field-stream.d.ts +85 -0
  303. package/dist/types/transport/streamobject/field-stream/index.d.ts +2 -0
  304. package/dist/types/transport/streamobject/index.d.ts +8 -0
  305. package/dist/types/transport/streamobject/json-adapter/index.d.ts +2 -0
  306. package/dist/types/transport/streamobject/json-adapter/json-adapter.d.ts +65 -0
  307. package/dist/types/transport/streamobject/reference/index.d.ts +2 -0
  308. package/dist/types/transport/streamobject/reference/reference.d.ts +127 -0
  309. package/dist/types/transport/streamobject/stream-object/index.d.ts +2 -0
  310. package/dist/types/transport/streamobject/stream-object/stream-object.d.ts +31 -0
  311. package/package.json +23 -2
@@ -0,0 +1,10 @@
1
+ "use strict";var O=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var u=(n,t)=>O(n,"name",{value:t,configurable:!0});var j=(n,t)=>{for(var s in t)O(n,s,{get:t[s],enumerable:!0})},P=(n,t,s,a)=>{if(t&&typeof t=="object"||typeof t=="function")for(let m of I(t))!U.call(n,m)&&m!==s&&O(n,m,{get:()=>t[m],enumerable:!(a=q(t,m))||a.enumerable});return n};var W=n=>P(O({},"__esModule",{value:!0}),n);var H={};j(H,{openAICompatibleStream:()=>z});module.exports=W(H);function R(n){return{type:"object",properties:{answer:{type:"string",description:"Your spoken reply when no tool call is needed."},calls:{type:"array",description:"The tool calls to execute, when the request needs tools.",items:{type:"object",properties:{name:{type:"string",enum:n.map(t=>t.name)},arguments:{description:"The arguments for the named tool, per its schema.",oneOf:n.map(t=>t.parameters??{type:"object"})}},required:["name","arguments"],additionalProperties:!1}}}}}u(R,"buildEnvelopeSchema");function Y(n){return`Respond with ONLY valid JSON matching this schema, with no prose and no markdown fences:
2
+ `+JSON.stringify(R(n))+`
3
+ The JSON must be either {"answer": "..."} or {"calls": [{"name": "...", "arguments": {...}}]}.`}u(Y,"promptedEnvelopeInstruction");function B(n){const t=n.replace(/```(?:json)?/gi,"").replace(/^[^{]*/,"").replace(/[^}]*$/,"");if(t.length===0)return null;const s=t.indexOf("{"),a=t.lastIndexOf("}");return s===-1||a===-1||a<=s?null:t.slice(s,a+1)}u(B,"extractEnvelope");function F(n){return`You have access to the following tools. When the user's request requires one, respond with ONLY the tool-call JSON envelope shown in the request \u2014 no prose, no markdown, no tool-call syntax. When no tool is needed, respond with the envelope's "answer" field.
4
+
5
+ Available tools:
6
+ `+n.map(s=>`- ${s.name}: ${s.description}`).join(`
7
+ `)}u(F,"buildToolContract");async function*z(n){const{baseUrl:t,apiKey:s,model:a,fetchImpl:m,request:o,signal:_,extraHeaders:b,extraBody:C,label:h,onTiming:p,idleTimeoutMs:k,onUsage:g}=n;p?.("request-start");const l=o.toolMode??n.toolMode??"native",i=l!=="native"&&(o.tools?.length??0)>0,c=i?F(o.tools):null,$=o.messages.findIndex(e=>e.role!=="system"),A=$===-1?o.messages.length:$,v=[];o.messages.forEach((e,r,w)=>{r===A&&c!==null&&v.push({role:"system",content:c});const f={role:e.role,content:e.content};e.name&&(f.name=e.name),e.toolCallId&&(f.tool_call_id=e.toolCallId),e.toolCalls&&(f.tool_calls=e.toolCalls.map(y=>({id:y.id,type:"function",function:{name:y.name,arguments:y.arguments}}))),l==="prompted"&&i&&r===w.length-1&&(f.content=`${e.content}
8
+
9
+ ${Y(o.tools)}`),v.push(f)}),A===o.messages.length&&c!==null&&v.push({role:"system",content:c});const L=await m(`${t}/chat/completions`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${s}`,...b},body:JSON.stringify({model:a,messages:v,...!i&&o.tools&&o.tools.length>0?{tools:o.tools.map(e=>({type:"function",function:e}))}:{},...i&&l==="envelope"?{response_format:{type:"json_schema",json_schema:{name:"tool_envelope",strict:!1,schema:R(o.tools)}}}:{},stream:!0,temperature:o.temperature,max_tokens:o.maxTokens,stream_options:{include_usage:!0},...C}),signal:_});if(!L.ok){const e=await L.text();throw new Error(`${h} request failed (${L.status}): ${e}`)}if(!L.body)throw new Error(`${h} returned an empty body`);p?.("headers");const M=new Map;let x=!1,S=0,N=!1,D=!1,T="";for await(const e of G(L.body,_,p,k,h)){const r=e.choices?.[0];if(e.error){yield{type:"error",error:new Error(`${h} provider aborted the stream: ${e.error.message??JSON.stringify(e.error)}`)};break}if(e.usage&&(e.usage.prompt_tokens??0)>0&&(N=!0,S=0,g?.({promptTokens:e.usage.prompt_tokens??0,completionTokens:e.usage.completion_tokens??0})),!r)continue;const w=r.delta,f=w?.content??"",y=w?.reasoning??w?.reasoning_content??"";i?T+=f:(f.length>0||y.length>0)&&(yield{type:"delta",content:f,...y.length>0?{reasoning:y}:{}});for(const d of w?.tool_calls??[]){const J=d.index??0,E=M.get(J)??{id:"",name:"",arguments:""};d.id&&(E.id=d.id),d.function?.name&&(E.name+=d.function.name),d.function?.arguments&&(E.arguments+=d.function.arguments),M.set(J,E)}if(r.finish_reason==="tool_calls"&&!D){for(const d of M.values())yield{type:"tool_call",id:d.id,name:d.name,arguments:d.arguments};D=!0,x=!0}else if(r.finish_reason==="stop")x=!0;else if(r.finish_reason==="error"||r.finish_reason==="content_filter"){yield{type:"error",error:new Error(`${h} stream finished with "${r.finish_reason}"`+(M.size>0?"":" and produced no output"))};break}if(x&&(N||(S===0&&(S=Date.now()),Date.now()-S>500)))break}if(i){let e;try{e=JSON.parse(T)}catch{const r=B(T);if(r!==null)try{e=JSON.parse(r)}catch{e=void 0}if(e===void 0)try{e=JSON.parse(`{${T}`)}catch{e=void 0}}if(e===void 0)yield{type:"error",error:new Error(`${h} ${l} tool mode: model did not return a JSON envelope (${T.slice(0,120)})`)};else{typeof e.answer=="string"&&e.answer.length>0&&(yield{type:"delta",content:e.answer});for(const r of e.calls??[])typeof r.name=="string"&&(yield{type:"tool_call",id:crypto.randomUUID(),name:r.name,arguments:JSON.stringify(r.arguments??{})})}}yield{type:"done"}}u(z,"openAICompatibleStream");async function*G(n,t,s,a=8e3,m="LLM"){const o=n.getReader(),_=new TextDecoder;let b="",C=!1;const h=u(()=>{let p;return new Promise((k,g)=>{const l=u(()=>{p!==void 0&&clearTimeout(p),t.removeEventListener("abort",i)},"cleanup"),i=u(()=>{l(),g(new DOMException("The operation was aborted.","AbortError"))},"onAbort");o.read().then(c=>{l(),k(c)},c=>{l(),g(c)}),p=setTimeout(()=>{l(),o.cancel().catch(()=>{}),g(new Error(`${m} stream idle for ${a}ms \u2014 no data received; aborting the stalled stream`))},a),t.addEventListener("abort",i,{once:!0})})},"readWithTimeout");for(;;){if(t.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:p,value:k}=await h();if(p)break;b+=_.decode(k,{stream:!0});const g=b.split(/\r?\n\r?\n/);b=g.pop()??"";for(const l of g)for(const i of l.split(/\r?\n/)){if(!i.startsWith("data:"))continue;const c=i.slice(5).trim();if(c==="[DONE]")return;C||(C=!0,s?.("first-chunk"));try{yield JSON.parse(c)}catch{}}}}u(G,"parseSSE");
10
+ //# sourceMappingURL=openai-compatible.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/providers/llm/adapters/openai-compatible.ts"],
4
+ "sourcesContent": ["import type {\n LLMEvent,\n LLMRequest,\n LLMToolDefinition,\n LLMStreamTimingCallback,\n LLMUsageCallback,\n ToolMode,\n} from \"../types\";\nimport type { FetchLike } from \"../../shared\";\n\ninterface DeltaToolCall {\n index?: number;\n id?: string;\n function?: { name?: string; arguments?: string };\n}\n\ninterface ChatCompletionChunk {\n choices?: Array<{\n delta?: {\n content?: string | null;\n tool_calls?: DeltaToolCall[];\n // OpenRouter normalizes reasoning tokens to `reasoning`; DeepSeek uses\n // `reasoning_content`. Both are surfaced on the delta event.\n reasoning?: string | null;\n reasoning_content?: string | null;\n };\n finish_reason?: string | null;\n }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number };\n /** Provider failure delivered as an SSE data chunk (no choices). */\n error?: { code?: number | string; message?: string };\n}\n\nexport interface OpenAICompatibleStreamParams {\n /** Provider base URL, e.g. `https://openrouter.ai/api/v1`. */\n baseUrl: string;\n apiKey: string;\n model: string;\n /** Injectable fetch implementation, mainly for tests. */\n fetchImpl: FetchLike;\n request: LLMRequest;\n signal: AbortSignal;\n /** Extra headers merged into the request (e.g. attribution headers). */\n extraHeaders?: Record<string, string>;\n /** Extra body fields merged after the standard ones. */\n extraBody?: Record<string, unknown>;\n /** Provider name used in error messages. */\n label: string;\n /** Optional provider-timeline hook (see `LLMStreamTimingPoint`). */\n onTiming?: LLMStreamTimingCallback;\n /**\n * Default tool encoding for requests that do not set `toolMode`\n * themselves (the request's `toolMode` wins when present). Useful for\n * models whose endpoints lack native tool calling.\n */\n toolMode?: ToolMode;\n /**\n * Abort the stream if no data arrives for this long (default 8000ms).\n * Protects against providers whose connection goes silent after the model\n * has already produced its output (e.g. a tool call that is never followed\n * by a terminating frame).\n */\n idleTimeoutMs?: number;\n /**\n * Called with the provider-reported token usage when the stream includes\n * it (OpenRouter sends usage in the final chunk).\n */\n onUsage?: LLMUsageCallback;\n}\n\n/**\n * JSON schema for the `envelope`/`prompted` tool modes: the model answers\n * either directly (`answer`) or with a list of tool calls (`calls`). The\n * adapter turns the parsed envelope back into the standard `LLMEvent`\n * surface, so consumers never see the envelope format.\n *\n * The `arguments` of each call carry a per-tool `oneOf` of the tools' own\n * parameter schemas (index-aligned with the `name` enum), so the model sees\n * the same argument contract it would in native mode \u2014 without the native\n * tool-calling endpoint.\n */\nfunction buildEnvelopeSchema(tools: LLMToolDefinition[]): Record<string, unknown> {\n return {\n type: \"object\",\n properties: {\n answer: {\n type: \"string\",\n description: \"Your spoken reply when no tool call is needed.\",\n },\n calls: {\n type: \"array\",\n description: \"The tool calls to execute, when the request needs tools.\",\n items: {\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n enum: tools.map((tool) => tool.name),\n },\n arguments: {\n description: \"The arguments for the named tool, per its schema.\",\n oneOf: tools.map((tool) => tool.parameters ?? { type: \"object\" }),\n },\n },\n required: [\"name\", \"arguments\"],\n additionalProperties: false,\n },\n },\n },\n };\n}\n\n/** Instruction appended to the last user message in `prompted` mode. */\nfunction promptedEnvelopeInstruction(tools: LLMToolDefinition[]): string {\n return (\n \"Respond with ONLY valid JSON matching this schema, with no prose and no markdown fences:\\n\" +\n JSON.stringify(buildEnvelopeSchema(tools)) +\n '\\nThe JSON must be either {\"answer\": \"...\"} or {\"calls\": [{\"name\": \"...\", \"arguments\": {...}}]}.'\n );\n}\n\n/**\n * Fallback extraction for prompted mode: strip markdown fences and keep the\n * outermost JSON object, rescuing models that wrap or preface the envelope\n * despite the instruction (the endpoint guarantees nothing without\n * `response_format`).\n */\nfunction extractEnvelope(raw: string): string | null {\n const stripped = raw\n .replace(/```(?:json)?/gi, \"\")\n .replace(/^[^{]*/, \"\")\n .replace(/[^}]*$/, \"\");\n if (stripped.length === 0) return null;\n const start = stripped.indexOf(\"{\");\n const end = stripped.lastIndexOf(\"}\");\n if (start === -1 || end === -1 || end <= start) return null;\n return stripped.slice(start, end + 1);\n}\n\n/**\n * System-level tool contract for `envelope`/`prompted` modes. The envelope\n * schema itself carries only tool names + argument shapes, so without this\n * the model never sees tool descriptions or the output rule up front \u2014 which\n * invites prose, native tool-call syntax, or waffling instead of the envelope.\n */\nfunction buildToolContract(tools: LLMToolDefinition[]): string {\n const list = tools.map((tool) => `- ${tool.name}: ${tool.description}`).join(\"\\n\");\n return (\n \"You have access to the following tools. When the user's request requires one, \" +\n \"respond with ONLY the tool-call JSON envelope shown in the request \u2014 no prose, \" +\n \"no markdown, no tool-call syntax. When no tool is needed, respond with the \" +\n 'envelope\\'s \"answer\" field.\\n\\nAvailable tools:\\n' +\n list\n );\n}\n\n/**\n * Stream a request through any OpenAI-compatible `/chat/completions`\n * endpoint, reassembling fragmented tool calls and normalizing the SSE\n * stream into `LLMEvent`s. Shared by the DeepSeek and OpenRouter adapters.\n */\nexport async function* openAICompatibleStream(\n params: OpenAICompatibleStreamParams,\n): AsyncGenerator<LLMEvent> {\n const {\n baseUrl,\n apiKey,\n model,\n fetchImpl,\n request,\n signal,\n extraHeaders,\n extraBody,\n label,\n onTiming,\n idleTimeoutMs,\n onUsage,\n } = params;\n\n onTiming?.(\"request-start\");\n // Precedence: the per-request override wins, then the adapter default, then\n // native.\n const toolMode = request.toolMode ?? params.toolMode ?? \"native\";\n const usesEnvelope = toolMode !== \"native\" && (request.tools?.length ?? 0) > 0;\n // In envelope modes the tools are hidden from the provider, so the model\n // never sees their descriptions (or the output rule) unless we say so.\n // Insert the contract after the caller's system messages, where formatting\n // rules belong.\n const toolContract = usesEnvelope ? buildToolContract(request.tools!) : null;\n const contractAt = request.messages.findIndex((message) => message.role !== \"system\");\n const insertAt = contractAt === -1 ? request.messages.length : contractAt;\n const wireMessages: Record<string, unknown>[] = [];\n request.messages.forEach((message, index, all) => {\n if (index === insertAt && toolContract !== null) {\n wireMessages.push({ role: \"system\", content: toolContract });\n }\n const wire: Record<string, unknown> = {\n role: message.role,\n content: message.content,\n };\n if (message.name) wire.name = message.name;\n if (message.toolCallId) wire.tool_call_id = message.toolCallId;\n if (message.toolCalls) {\n wire.tool_calls = message.toolCalls.map((call) => ({\n id: call.id,\n type: \"function\",\n function: { name: call.name, arguments: call.arguments },\n }));\n }\n if (toolMode === \"prompted\" && usesEnvelope && index === all.length - 1) {\n // The only way to constrain output without provider support: ask\n // the model directly, and parse/validate the result ourselves.\n wire.content = `${message.content}\\n\\n${promptedEnvelopeInstruction(request.tools!)}`;\n }\n wireMessages.push(wire);\n });\n if (insertAt === request.messages.length && toolContract !== null) {\n wireMessages.push({ role: \"system\", content: toolContract });\n }\n const response = await fetchImpl(`${baseUrl}/chat/completions`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${apiKey}`,\n ...extraHeaders,\n },\n body: JSON.stringify({\n model,\n // LLMMessage uses camelCase; the wire format is snake_case.\n messages: wireMessages,\n // Envelope modes hide the tools from the provider and encode them in a\n // schema the model must emit as JSON; native mode uses the provider's\n // own tool-calling contract.\n ...(!usesEnvelope && request.tools && request.tools.length > 0\n ? { tools: request.tools.map((tool) => ({ type: \"function\", function: tool })) }\n : {}),\n ...(usesEnvelope && toolMode === \"envelope\"\n ? {\n response_format: {\n type: \"json_schema\",\n json_schema: {\n name: \"tool_envelope\",\n strict: false,\n schema: buildEnvelopeSchema(request.tools!),\n },\n },\n }\n : {}),\n stream: true,\n temperature: request.temperature,\n max_tokens: request.maxTokens,\n // Ask the provider to include usage in the final chunk so token\n // accounting (and latency-vs-tokens curves) are possible.\n stream_options: { include_usage: true },\n ...extraBody,\n }),\n signal,\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`${label} request failed (${response.status}): ${body}`);\n }\n if (!response.body) {\n throw new Error(`${label} returned an empty body`);\n }\n onTiming?.(\"headers\");\n\n // Tool calls arrive fragmented across chunks and are reassembled by\n // index before being emitted once the stream signals `tool_calls`.\n const toolCalls = new Map<number, { id: string; name: string; arguments: string }>();\n // After the content finishes (finish_reason), keep reading briefly so the\n // provider-reported usage chunk \u2014 which arrives right after \u2014 is captured.\n let contentFinished = false;\n let finishAt = 0;\n let sawUsage = false;\n let toolCallsEmitted = false;\n // Envelope modes buffer the model's JSON and translate it into events at\n // the end of the stream (the JSON is not speech and cannot be acted on\n // until it is complete).\n let envelopeContent = \"\";\n\n for await (const chunk of parseSSE(response.body, signal, onTiming, idleTimeoutMs, label)) {\n const choice = chunk.choices?.[0];\n\n if (chunk.error) {\n // Some providers (e.g. Bedrock via OpenRouter) abort mid-stream and\n // deliver the failure as an SSE chunk with no choices. Surface it\n // instead of silently swallowing it into a confusing empty stream.\n yield {\n type: \"error\",\n error: new Error(\n `${label} provider aborted the stream: ${chunk.error.message ?? JSON.stringify(chunk.error)}`,\n ),\n };\n break;\n }\n\n if (chunk.usage && (chunk.usage.prompt_tokens ?? 0) > 0) {\n sawUsage = true;\n finishAt = 0;\n onUsage?.({\n promptTokens: chunk.usage.prompt_tokens ?? 0,\n completionTokens: chunk.usage.completion_tokens ?? 0,\n });\n }\n if (!choice) continue;\n\n const delta = choice.delta;\n const content = delta?.content ?? \"\";\n // Thinking models stream reasoning before content; emit it so consumers\n // can measure thinking time (and optionally surface it). Reasoning-only\n // chunks carry empty content.\n const reasoning = (delta?.reasoning ?? delta?.reasoning_content) ?? \"\";\n if (usesEnvelope) {\n envelopeContent += content;\n } else if (content.length > 0 || reasoning.length > 0) {\n yield {\n type: \"delta\",\n content,\n ...(reasoning.length > 0 ? { reasoning } : {}),\n };\n }\n\n for (const tc of delta?.tool_calls ?? []) {\n const index = tc.index ?? 0;\n const accumulated = toolCalls.get(index) ?? { id: \"\", name: \"\", arguments: \"\" };\n if (tc.id) accumulated.id = tc.id;\n if (tc.function?.name) accumulated.name += tc.function.name;\n if (tc.function?.arguments) accumulated.arguments += tc.function.arguments;\n toolCalls.set(index, accumulated);\n }\n\n if (choice.finish_reason === \"tool_calls\" && !toolCallsEmitted) {\n for (const call of toolCalls.values()) {\n yield {\n type: \"tool_call\",\n id: call.id,\n name: call.name,\n arguments: call.arguments,\n };\n }\n toolCallsEmitted = true;\n contentFinished = true;\n } else if (choice.finish_reason === \"stop\") {\n contentFinished = true;\n } else if (choice.finish_reason === \"error\" || choice.finish_reason === \"content_filter\") {\n // Some providers (e.g. gemini models via OpenRouter) return HTTP 200\n // with an empty body and finish_reason \"error\"/\"content_filter\"\n // instead of a real failure. Surface it as an error event rather than\n // silently completing with an empty generation.\n yield {\n type: \"error\",\n error: new Error(\n `${label} stream finished with \"${choice.finish_reason}\"` +\n (toolCalls.size > 0 ? \"\" : \" and produced no output\"),\n ),\n };\n break;\n }\n\n if (contentFinished) {\n if (sawUsage) break; // usage captured \u2014 no need to wait for [DONE]\n if (finishAt === 0) finishAt = Date.now();\n // Grace for providers that send finish_reason but never [DONE] or\n // usage: proceed after a short wait instead of stalling the run.\n if (Date.now() - finishAt > 500) break;\n }\n }\n\n if (usesEnvelope) {\n let envelope:\n | { answer?: unknown; calls?: Array<{ name?: unknown; arguments?: unknown }> }\n | undefined;\n try {\n envelope = JSON.parse(envelopeContent);\n } catch {\n // Prompted mode has no endpoint guarantee: the model may wrap the JSON\n // in fences or preface it with prose. Retry with extraction before\n // failing the stream.\n const extracted = extractEnvelope(envelopeContent);\n if (extracted !== null) {\n try {\n envelope = JSON.parse(extracted);\n } catch {\n envelope = undefined;\n }\n }\n if (envelope === undefined) {\n // Some providers drop the opening bytes of the stream (observed with\n // llama-4-scout via OpenRouter): the content starts mid-object but\n // closes cleanly. Prepend the brace and retry.\n try {\n envelope = JSON.parse(`{${envelopeContent}`);\n } catch {\n envelope = undefined;\n }\n }\n }\n if (envelope === undefined) {\n yield {\n type: \"error\",\n error: new Error(\n `${label} ${toolMode} tool mode: model did not return a JSON envelope (${envelopeContent.slice(0, 120)})`,\n ),\n };\n } else {\n if (typeof envelope.answer === \"string\" && envelope.answer.length > 0) {\n yield { type: \"delta\", content: envelope.answer };\n }\n for (const call of envelope.calls ?? []) {\n if (typeof call.name !== \"string\") continue;\n // Envelope mode has no provider-assigned call ids; synthesize them so\n // consumers can match results to calls (id uniqueness is the contract).\n yield {\n type: \"tool_call\",\n id: crypto.randomUUID(),\n name: call.name,\n arguments: JSON.stringify(call.arguments ?? {}),\n };\n }\n }\n }\n\n yield { type: \"done\" };\n}\n\n/**\n * Parse an SSE byte stream into JSON events. Supports both LF and CRLF\n * framing, ignores comments and malformed frames, and stops at `[DONE]`.\n * Aborts with an `AbortError` once the signal fires.\n */\nasync function* parseSSE(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n onTiming?: LLMStreamTimingCallback,\n idleTimeoutMs = 8_000,\n label = \"LLM\",\n): AsyncGenerator<ChatCompletionChunk> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n let firstChunkEmitted = false;\n\n // `reader.read()` bounded by an idle timeout: if no bytes arrive within\n // `idleTimeoutMs`, cancel the connection and fail the stream. Only bytes\n // reset the timer \u2014 a connection that stays open but silent still trips it.\n const readWithTimeout = (): Promise<Awaited<ReturnType<typeof reader.read>>> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n return new Promise((resolve, reject) => {\n const cleanup = () => {\n if (timer !== undefined) clearTimeout(timer);\n signal.removeEventListener(\"abort\", onAbort);\n };\n const onAbort = () => {\n cleanup();\n reject(new DOMException(\"The operation was aborted.\", \"AbortError\"));\n };\n reader.read().then(\n (result) => {\n cleanup();\n resolve(result);\n },\n (error) => {\n cleanup();\n reject(error);\n },\n );\n timer = setTimeout(() => {\n cleanup();\n // Cancel the reader so the underlying connection is actually closed.\n void reader.cancel().catch(() => {});\n reject(\n new Error(\n `${label} stream idle for ${idleTimeoutMs}ms \u2014 no data received; aborting the stalled stream`,\n ),\n );\n }, idleTimeoutMs);\n signal.addEventListener(\"abort\", onAbort, { once: true });\n });\n };\n\n while (true) {\n if (signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await readWithTimeout();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n const frames = buffer.split(/\\r?\\n\\r?\\n/);\n buffer = frames.pop() ?? \"\";\n for (const frame of frames) {\n for (const line of frame.split(/\\r?\\n/)) {\n if (!line.startsWith(\"data:\")) continue;\n const data = line.slice(5).trim();\n if (data === \"[DONE]\") return;\n if (!firstChunkEmitted) {\n firstChunkEmitted = true;\n onTiming?.(\"first-chunk\");\n }\n try {\n yield JSON.parse(data) as ChatCompletionChunk;\n } catch {\n // Ignore malformed frames.\n }\n }\n }\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["openai_compatible_exports", "__export", "openAICompatibleStream", "__toCommonJS", "buildEnvelopeSchema", "tools", "tool", "__name", "promptedEnvelopeInstruction", "extractEnvelope", "raw", "stripped", "start", "end", "buildToolContract", "openAICompatibleStream", "params", "baseUrl", "apiKey", "model", "fetchImpl", "request", "signal", "extraHeaders", "extraBody", "label", "onTiming", "idleTimeoutMs", "onUsage", "toolMode", "usesEnvelope", "toolContract", "contractAt", "message", "insertAt", "wireMessages", "index", "all", "wire", "call", "response", "body", "toolCalls", "contentFinished", "finishAt", "sawUsage", "toolCallsEmitted", "envelopeContent", "chunk", "parseSSE", "choice", "delta", "content", "reasoning", "tc", "accumulated", "envelope", "extracted", "reader", "decoder", "buffer", "firstChunkEmitted", "readWithTimeout", "timer", "resolve", "reject", "cleanup", "onAbort", "result", "error", "done", "value", "frames", "frame", "line", "data"]
7
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";var t=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var f=Object.getOwnPropertyNames;var m=Object.prototype.hasOwnProperty;var x=(o,e)=>{for(var p in e)t(o,p,{get:e[p],enumerable:!0})},L=(o,e,p,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of f(e))!m.call(o,r)&&r!==p&&t(o,r,{get:()=>e[r],enumerable:!(n=O(e,r))||n.enumerable});return o};var R=o=>L(t({},"__esModule",{value:!0}),o);var i={};x(i,{OpenRouterLLM:()=>u.OpenRouterLLM});module.exports=R(i);var u=require("./openrouter.js");
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/llm/adapters/openrouter/index.ts"],
4
+ "sourcesContent": ["export { OpenRouterLLM } from \"./openrouter\";\nexport type { OpenRouterOptions } 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 o=Object.defineProperty;var n=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var d=Object.prototype.hasOwnProperty;var l=(t,e)=>o(t,"name",{value:e,configurable:!0});var m=(t,e)=>{for(var r in e)o(t,r,{get:e[r],enumerable:!0})},h=(t,e,r,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of p(e))!d.call(t,i)&&i!==r&&o(t,i,{get:()=>e[i],enumerable:!(a=n(e,i))||a.enumerable});return t};var L=t=>h(o({},"__esModule",{value:!0}),t);var c={};m(c,{OpenRouterLLM:()=>M});module.exports=L(c);var s=require("../openai-compatible.js");class M{static{l(this,"OpenRouterLLM")}apiKey;model;baseUrl;appUrl;fetchImpl;onTiming;idleTimeoutMs;onUsage;toolMode;streams=new Set;constructor(e){if(!e.apiKey)throw new Error("OpenRouterLLM requires an apiKey");this.apiKey=e.apiKey,this.model=e.model??"openrouter/auto",this.baseUrl=(e.baseUrl??"https://openrouter.ai/api/v1").replace(/\/+$/,""),this.appUrl=e.appUrl??"https://moureau.dev",this.fetchImpl=e.fetch??fetch,this.onTiming=e.onTiming,this.idleTimeoutMs=e.idleTimeoutMs??8e3,this.onUsage=e.onUsage,this.toolMode=e.toolMode}stop(){for(const e of this.streams)e.abort()}async*stream(e){const r=new AbortController;this.streams.add(r);try{yield*(0,s.openAICompatibleStream)({baseUrl:this.baseUrl,apiKey:this.apiKey,model:this.model,fetchImpl:this.fetchImpl,request:e,signal:r.signal,extraHeaders:{"http-referer":this.appUrl,"x-title":"pipeflow"},label:"OpenRouter",onTiming:this.onTiming,idleTimeoutMs:this.idleTimeoutMs,onUsage:this.onUsage,toolMode:this.toolMode})}finally{this.streams.delete(r)}}}
2
+ //# sourceMappingURL=openrouter.js.map
@@ -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.\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,GA8DO,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 h=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var f=Object.prototype.hasOwnProperty;var l=(r,e)=>d(r,"name",{value:e,configurable:!0});var g=(r,e)=>{for(var i in e)d(r,i,{get:e[i],enumerable:!0})},v=(r,e,i,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of m(e))!f.call(r,s)&&s!==i&&d(r,s,{get:()=>e[s],enumerable:!(t=h(e,s))||t.enumerable});return r};var y=r=>v(d({},"__esModule",{value:!0}),r);var O={};g(O,{OpenRouterSTT:()=>b,OpenRouterSession:()=>c,toWav:()=>p});module.exports=y(O);class b{static{l(this,"OpenRouterSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("OpenRouterSTT requires an apiKey");this.options=e}start(e={}){const i={...this.options,...e},t=new c(i);return this.sessions.add(t),t.on("close",()=>{this.sessions.delete(t)}),t}cancel(){for(const e of[...this.sessions])e.abort()}}class c{static{l(this,"OpenRouterSession")}listeners=new Map;apiKey;baseUrl;model;language;sampleRate;silenceMs;audioFormat;fetchImpl;chunks=[];timer=null;chain=Promise.resolve();controllers=new Set;ended=!1;aborted=!1;constructor(e){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.fetchImpl=e.fetch??fetch}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,i){let t=this.listeners.get(e);t||(t=new Set,this.listeners.set(e,t)),t.add(i)}flush(){this.chain=this.chain.then(async()=>{if(this.aborted||this.chunks.length===0)return;const e=this.takeBuffer();await this.transcribe(e)})}takeBuffer(){const e=this.chunks;this.chunks=[];let i=0;for(const a of e)i+=a.byteLength;const t=new Uint8Array(i);let s=0;for(const a of e)t.set(a,s),s+=a.byteLength;return t}async transcribe(e){const i=new AbortController;this.controllers.add(i);try{const t=await w({apiKey:this.apiKey,baseUrl:this.baseUrl,model:this.model,language:this.language,sampleRate:this.sampleRate,audioFormat:this.audioFormat,audio:e,signal:i.signal,fetchImpl:this.fetchImpl});!this.aborted&&t&&this.emit("final",t)}catch(t){this.aborted||this.emit("error",t instanceof Error?t:new Error(String(t)))}finally{this.controllers.delete(i)}}emit(e,...i){const t=this.listeners.get(e);if(t)for(const s of[...t])s(...i)}}const T={mp3:"audio/mpeg",flac:"audio/flac",m4a:"audio/mp4",ogg:"audio/ogg",webm:"audio/webm",aac:"audio/aac"};async function w(r){const e=new FormData;e.append("model",r.model);let i,t,s;r.audioFormat==="pcm"?(i=p(r.audio,r.sampleRate),t="audio.wav",s="audio/wav"):(i=r.audio,t=`audio.${r.audioFormat}`,s=T[r.audioFormat]),e.append("file",new Blob([i],{type:s}),t),r.language&&e.append("language",r.language);const a=await r.fetchImpl(`${r.baseUrl}/audio/transcriptions`,{method:"POST",headers:{authorization:`Bearer ${r.apiKey}`},body:e,signal:r.signal});if(!a.ok){const n=await a.text().catch(()=>"");throw new Error(`OpenRouter transcription failed (${a.status})${n?`: ${n}`:""}`)}return((await a.json()).text??"").trim()}l(w,"transcribeClip");function p(r,e){const s=e*1*2,a=2,u=r.byteLength,n=new Uint8Array(44+u),o=new DataView(n.buffer);return n.set([82,73,70,70],0),o.setUint32(4,36+u,!0),n.set([87,65,86,69],8),n.set([102,109,116,32],12),o.setUint32(16,16,!0),o.setUint16(20,1,!0),o.setUint16(22,1,!0),o.setUint32(24,e,!0),o.setUint32(28,s,!0),o.setUint16(32,a,!0),o.setUint16(34,16,!0),n.set([100,97,116,97],36),o.setUint32(40,u,!0),n.set(r,44),n}l(p,"toWav");
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 /** 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 const session = new OpenRouterSession(merged);\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 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 fetchImpl: FetchLike;\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) {\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.fetchImpl = options.fetch ?? fetch;\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 private flush(): void {\n this.chain = this.chain.then(async () => {\n if (this.aborted) return;\n if (this.chunks.length === 0) return;\n const audio = this.takeBuffer();\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 const controller = new AbortController();\n this.controllers.add(controller);\n try {\n const text = await 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 audio,\n signal: controller.signal,\n fetchImpl: this.fetchImpl,\n });\n if (!this.aborted && text) this.emit(\"final\", text);\n } catch (error) {\n if (!this.aborted) {\n this.emit(\"error\", error instanceof Error ? error : new Error(String(error)));\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 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\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 * 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"],
5
+ "mappings": "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",
6
+ "names": ["openrouter_exports", "__export", "OpenRouterSTT", "OpenRouterSession", "toWav", "__toCommonJS", "OpenRouterSTT", "__name", "options", "merged", "session", "OpenRouterSession", "audio", "controller", "event", "listener", "set", "chunks", "total", "chunk", "out", "offset", "text", "transcribeClip", "error", "args", "AUDIO_MIME", "form", "bytes", "name", "type", "toWav", "response", "detail", "pcm", "sampleRate", "byteRate", "blockAlign", "dataSize", "wav", "view"]
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 i of k(e))!T.call(r,i)&&i!==t&&f(r,i,{get:()=>e[i],enumerable:!(o=v(e,i))||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;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.streaming=e.stream??!1,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 t=new AbortController;this.abort=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 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(o),signal:t.signal});if(!i.ok){const n=await i.text();throw new Error(`Kokoro request failed (${i.status}): ${n}`)}if(!i.body)throw new Error("Kokoro returned an empty body");const d=i.body.getReader(),m=(i.headers.get("content-type")??"").includes("text/event-stream");let a=new Uint8Array(0);if(m){let n="",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;n+=new TextDecoder().decode(w);const h=n.split(`
2
+
3
+ `);n=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*b(u(a,x(c.delta)),this.chunkSize))}catch{}}}}else for(;;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:n,value:l}=await d.read();if(n)break;a=yield*b(u(a,l),this.chunkSize)}a.length>0&&(yield a)}finally{this.abort===t&&(this.abort=null)}}}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*b(r,e){let t=r;for(;t.length>=e;)yield t.slice(0,e),t=t.slice(e);return t}s(b,"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 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.streaming = options.stream ?? false;\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 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 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\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": "4dAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAsCO,MAAMI,CAAyB,CAtCtC,MAsCsC,CAAAC,EAAA,kBACnB,QACA,MACA,MACA,OACA,UACA,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,QAAU,GACnC,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,EAAgC,CACpC,MAAO,KAAK,MACZ,MAAOF,EAAQ,KACf,MAAOA,EAAQ,OAAS,KAAK,MAC7B,MAAOA,EAAQ,KACjB,EACI,KAAK,WAIPE,EAAK,OAAS,GACdA,EAAK,gBACHF,EAAQ,SAAW,QAAaA,EAAQ,SAAW,MAC/C,MACAA,EAAQ,QACLA,EAAQ,SAAW,SAC5BE,EAAK,gBAAkBF,EAAQ,QAGjC,MAAMG,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,UAAUD,CAAI,EACzB,OAAQD,EAAW,MACrB,CAAC,EAED,GAAI,CAACE,EAAS,GAAI,CAChB,MAAMD,EAAO,MAAMC,EAAS,KAAK,EACjC,MAAM,IAAI,MAAM,0BAA0BA,EAAS,MAAM,MAAMD,CAAI,EAAE,CACvE,CACA,GAAI,CAACC,EAAS,KACZ,MAAM,IAAI,MAAM,+BAA+B,EAGjD,MAAMC,EAASD,EAAS,KAAK,UAAU,EACjCE,GAASF,EAAS,QAAQ,IAAI,cAAc,GAAK,IAAI,SACzD,mBACF,EACA,IAAIG,EAAuC,IAAI,WAAW,CAAC,EAE3D,GAAID,EAAO,CAGT,IAAIE,EAAa,GACbC,EAAY,GAChB,KAAO,CAACA,GAAW,CACjB,GAAIP,EAAW,OAAO,QACpB,MAAM,IAAI,aAAa,6BAA8B,YAAY,EAEnE,KAAM,CAAE,KAAAQ,EAAM,MAAAC,CAAM,EAAI,MAAMN,EAAO,KAAK,EAC1C,GAAIK,EAAM,MACVF,GAAc,IAAI,YAAY,EAAE,OAAOG,CAAK,EAC5C,MAAMC,EAASJ,EAAW,MAAM;AAAA;AAAA,CAAM,EACtCA,EAAaI,EAAO,IAAI,GAAK,GAC7B,UAAWC,KAASD,EAClB,UAAWE,KAAQD,EAAM,MAAM;AAAA,CAAI,EAAG,CACpC,GAAI,CAACC,EAAK,WAAW,OAAO,EAAG,SAC/B,MAAMC,EAAOD,EAAK,MAAM,CAAC,EAAE,KAAK,EAChC,GAAIC,IAAS,SAAU,CACrBN,EAAY,GACZ,KACF,CACA,GAAI,CACF,MAAMO,EAAS,KAAK,MAAMD,CAAI,EAK5BC,EAAO,OAAS,wCAChB,OAAOA,EAAO,OAAU,WAExBT,EAAc,MAAOU,EACnBC,EAAOX,EAAaY,EAAcH,EAAO,KAAK,CAAC,EAC/C,KAAK,SACP,EAEJ,MAAQ,CAER,CACF,CAEJ,CACF,KACE,QAAa,CACX,GAAId,EAAW,OAAO,QACpB,MAAM,IAAI,aAAa,6BAA8B,YAAY,EAEnE,KAAM,CAAE,KAAAQ,EAAM,MAAAC,CAAM,EAAI,MAAMN,EAAO,KAAK,EAC1C,GAAIK,EAAM,MACVH,EAAc,MAAOU,EAAYC,EAAOX,EAAaI,CAAK,EAAG,KAAK,SAAS,CAC7E,CAGEJ,EAAY,OAAS,IACvB,MAAMA,EAEV,QAAE,CACI,KAAK,QAAUL,IACjB,KAAK,MAAQ,KAEjB,CACF,CACF,CAEA,SAASgB,EACPE,EACAC,EACyB,CACzB,MAAMC,EAAM,IAAI,WAAWF,EAAE,OAASC,EAAE,MAAM,EAC9C,OAAAC,EAAI,IAAIF,CAAC,EACTE,EAAI,IAAID,EAAGD,EAAE,MAAM,EACZE,CACT,CARSvB,EAAAmB,EAAA,UAeT,SAAUD,EACRM,EACAC,EACwE,CACxE,IAAIC,EAASF,EACb,KAAOE,EAAO,QAAUD,GACtB,MAAMC,EAAO,MAAM,EAAGD,CAAS,EAC/BC,EAASA,EAAO,MAAMD,CAAS,EAEjC,OAAOC,CACT,CAVU1B,EAAAkB,EAAA,eAYV,SAASE,EAAcO,EAAyC,CAC9D,MAAMC,EAAS,KAAKD,CAAM,EACpBJ,EAAM,IAAI,WAAWK,EAAO,MAAM,EACxC,QAASC,EAAI,EAAGA,EAAID,EAAO,OAAQC,IACjCN,EAAIM,CAAC,EAAID,EAAO,WAAWC,CAAC,EAE9B,OAAON,CACT,CAPSvB,EAAAoB,EAAA",
6
+ "names": ["kokoro_exports", "__export", "KokoroTTS", "__toCommonJS", "KokoroTTS", "__name", "options", "request", "controller", "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