@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,2 @@
1
+ var k=Object.defineProperty;var l=(i,e)=>k(i,"name",{value:e,configurable:!0});import{OpenRouterLLM as R}from"../adapters/openrouter/openrouter.js";import{z as T}from"zod";const v={name:"get_weather",description:"Get the current weather for a city.",parameters:{type:"object",properties:{city:{type:"string"}},required:["city"]}},E=[{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?"}],S=T.object({city:T.string().min(1)});function g(i,e){if(i.length===0)return Number.NaN;const s=Math.min(i.length-1,Math.floor(e*(i.length-1)));return i[s]}l(g,"percentile");function C(i){if(i.length===0)return;const e=i.slice().sort((s,r)=>s-r);return e[Math.floor(e.length/2)]}l(C,"median");async function A(i,e){try{const c=(await(await e("https://openrouter.ai/api/v1/models")).json()).data.find(a=>a.id===i),m=Number(c?.pricing?.prompt??0),n=Number(c?.pricing?.completion??0);return{in:m,out:n}}catch{return}}l(A,"fetchPricing");class U{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??E,this.tools=e.tools??[v],this.correctnessSchema=e.correctnessSchema??S,this.fetchImpl=e.fetch??fetch}async run(){const e=await A(this.model,this.fetchImpl),s={};for(const n of["native","envelope","prompted"])s[n]=await this.benchmarkMode(n,e);const r=["native","envelope","prompted"].filter(n=>(s[n].toolCalls??0)>0).map(n=>({mode:n,entry:s[n]})),c=[...r].sort((n,a)=>(n.entry.time?.p50??1/0)-(a.entry.time?.p50??1/0)),m=[...r].sort((n,a)=>(n.entry.cost??1/0)-(a.entry.cost??1/0));return{model:this.model,pricing:e,report:s,fastest:c[0]?.mode??null,cheapest:m[0]?.mode??null}}async benchmarkMode(e,s){const r=[];for(let t=0;t<this.runs;t++){let d={};const w=new R({apiKey:this.apiKey,model:this.model,baseUrl:this.baseUrl,fetch:this.fetchImpl,onUsage:l(o=>{d={prompt:o.promptTokens,completion:o.completionTokens}},"onUsage")}),b=performance.now();let u,f;const y=[];try{for await(const o of w.stream({messages:this.messages,tools:this.tools,temperature:0,toolMode:e})){if(o.type==="tool_call"&&u===void 0&&(u=performance.now()-b),o.type==="tool_call"&&y.push(o.arguments),o.type==="error")throw o.error;if(o.type==="done")break}}catch(o){f=o instanceof Error?o.message.slice(0,120):String(o)}let M;f===void 0&&y.length>0&&this.correctnessSchema!==void 0&&(M=y.every(o=>{try{return this.correctnessSchema.safeParse(JSON.parse(o)).success}catch{return!1}})),r.push({latencyMs:u??(f===void 0?performance.now()-b:void 0),toolCall:u!==void 0,correct:M,prompt:d.prompt,completion:d.completion,error:f})}const c=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&&s!==void 0).map(t=>t.prompt*s.in+t.completion*s.out),n=r.filter(t=>t.error!==void 0),a=r.filter(t=>t.correct===!0).length,p=this.correctnessSchema!==void 0?a:void 0,h=C(m),L=p!==void 0&&p>0&&h!==void 0?h/(p/r.length):void 0;return{cost:h,time:c.length>0?{p50:g(c,.5),p95:g(c,.95),p99:g(c,.99)}:void 0,toolCalls:r.filter(t=>t.toolCall).length,correct:p,effectiveCost:L,errors:n.length,error:n[0]?.error,runs:r}}}export{U as ToolModeBenchmark};
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": ["OpenRouterLLM", "z", "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 +1,2 @@
1
+ const e=["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"];export{e as FAVORITE_MODELS};
1
2
  //# sourceMappingURL=types.js.map
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": [],
4
- "sourcesContent": [],
5
- "mappings": "",
6
- "names": []
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\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": "AA8DO,MAAMA,EAAkB,CAC7B,2BACA,+BACA,uBACA,sBACA,6BACA,uBACA,oBACF",
6
+ "names": ["FAVORITE_MODELS"]
7
7
  }
@@ -1,2 +1,2 @@
1
- var l=Object.defineProperty;var s=(t,e)=>l(t,"name",{value:e,configurable:!0});class f{static{s(this,"DeepgramSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("DeepgramSTT requires an apiKey");this.options=e}start(e={}){const n={...this.options,...e},r=c(n),o=n.createSocket??(d=>new WebSocket(d)),i=new p(o(r));return this.sessions.add(i),i.on("close",()=>{this.sessions.delete(i)}),i}cancel(){for(const e of[...this.sessions])e.abort()}}class p{static{s(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(n=>{this.resolveEnd=n}),e.addEventListener("open",()=>{this.opened=!0;for(const n of this.queued)this.socket.send(a(n));this.queued.length=0,this.pendingClose.flag&&this.sendCloseStream()}),e.addEventListener("message",n=>{const r=v(n.data);if(r!==null)try{this.handleResult(JSON.parse(r))}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(a(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,n){let r=this.listeners.get(e);r||(r=new Set,this.listeners.set(e,r)),r.add(n)}handleResult(e){if(e.type!=="Results")return;const r=e.channel?.alternatives?.[0]?.transcript??"";r&&(e.is_final===!0?this.emit("final",r):this.emit("partial",r))}sendCloseStream(){this.socket.send(JSON.stringify({type:"CloseStream"}))}emit(e,...n){const r=this.listeners.get(e);if(r)for(const o of[...r])o(...n)}}function c(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()}`}s(c,"buildListenUrl");function a(t){const e=new Uint8Array(t.byteLength);return e.set(t),e.buffer}s(a,"toArrayBuffer");function v(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}s(v,"decodeMessage");export{f as DeepgramSTT,p as DeepgramSession};
1
+ var l=Object.defineProperty;var n=(t,e)=>l(t,"name",{value:e,configurable:!0});class f{static{n(this,"DeepgramSTT")}options;sessions=new Set;constructor(e){if(!e.apiKey)throw new Error("DeepgramSTT requires an apiKey");this.options=e}start(e={}){const s={...this.options,...e},r=c(s),i=s.createSocket??(d=>new WebSocket(d)),o=new p(i(r));return this.sessions.add(o),o.on("close",()=>{this.sessions.delete(o)}),o}cancel(){for(const e of[...this.sessions])e.abort()}}class p{static{n(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(s=>{this.resolveEnd=s}),e.addEventListener("open",()=>{this.opened=!0;for(const s of this.queued)this.socket.send(a(s));this.queued.length=0,this.pendingClose.flag&&this.sendCloseStream()}),e.addEventListener("message",s=>{const r=u(s.data);if(r!==null)try{this.handleResult(JSON.parse(r))}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(a(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,s){let r=this.listeners.get(e);r||(r=new Set,this.listeners.set(e,r)),r.add(s)}handleResult(e){if(e.type!=="Results")return;const r=e.channel?.alternatives?.[0]?.transcript??"";r&&(e.is_final===!0?this.emit("final",r):this.emit("partial",r))}sendCloseStream(){this.socket.send(JSON.stringify({type:"CloseStream"}))}emit(e,...s){const r=this.listeners.get(e);if(r)for(const i of[...r])i(...s)}}function c(t){const e=new URLSearchParams,s=t.model??"flux-general-en";e.set("model",s),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 r=s.startsWith("flux")?"wss://api.deepgram.com/v2/listen":"wss://api.deepgram.com/v1/listen";return`${(t.endpoint??r).replace(/\/+$/,"")}?${e.toString()}`}n(c,"buildListenUrl");function a(t){const e=new Uint8Array(t.byteLength);return e.set(t),e.buffer}n(a,"toArrayBuffer");function u(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}n(u,"decodeMessage");export{f as DeepgramSTT,p as DeepgramSession};
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": ["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": "+EAmCO,MAAMA,CAA2B,CAnCxC,MAmCwC,CAAAC,EAAA,oBACrB,QACA,SAAW,IAAI,IAEhC,YAAYC,EAA0B,CACpC,GAAI,CAACA,EAAQ,OACX,MAAM,IAAI,MAAM,gCAAgC,EAElD,KAAK,QAAUA,CACjB,CAEA,MAAMA,EAAsB,CAAC,EAAe,CAC1C,MAAMC,EAA0B,CAAE,GAAG,KAAK,QAAS,GAAGD,CAAQ,EACxDE,EAAMC,EAAeF,CAAM,EAC3BG,EAAeH,EAAO,eAAkBI,GAAmB,IAAI,UAAUA,CAAM,GAC/EC,EAAU,IAAIC,EAAgBH,EAAaF,CAAG,CAAC,EACrD,YAAK,SAAS,IAAII,CAAO,EACzBA,EAAQ,GAAG,QAAS,IAAM,CACxB,KAAK,SAAS,OAAOA,CAAO,CAC9B,CAAC,EACMA,CACT,CAEA,QAAe,CACb,UAAWA,IAAW,CAAC,GAAG,KAAK,QAAQ,EACrCA,EAAQ,MAAM,CAElB,CACF,CAEO,MAAMC,CAAsC,CAjEnD,MAiEmD,CAAAR,EAAA,wBAChC,OACA,UAAY,IAAI,IACzB,OAAS,GACT,OAAS,GACT,MAAQ,GACC,aAAe,CAAE,KAAM,EAAM,EAC7B,OAAuB,CAAC,EACxB,WACT,WAER,YAAYS,EAAmB,CAC7B,KAAK,OAASA,EACd,KAAK,WAAa,IAAI,QAASC,GAAY,CACzC,KAAK,WAAaA,CACpB,CAAC,EAEDD,EAAO,iBAAiB,OAAQ,IAAM,CACpC,KAAK,OAAS,GACd,UAAWE,KAAS,KAAK,OACvB,KAAK,OAAO,KAAKC,EAAcD,CAAK,CAAC,EAEvC,KAAK,OAAO,OAAS,EACjB,KAAK,aAAa,MACpB,KAAK,gBAAgB,CAEzB,CAAC,EAEDF,EAAO,iBAAiB,UAAYI,GAAU,CAC5C,MAAMC,EAAOC,EAAcF,EAAM,IAAI,EACrC,GAAIC,IAAS,KACb,GAAI,CACF,KAAK,aAAa,KAAK,MAAMA,CAAI,CAAC,CACpC,MAAQ,CAER,CACF,CAAC,EAEDL,EAAO,iBAAiB,QAAS,IAAM,CACrC,KAAK,KAAK,QAAS,IAAI,MAAM,uBAAuB,CAAC,CACvD,CAAC,EAEDA,EAAO,iBAAiB,QAAS,IAAM,CACrC,KAAK,OAAS,GACd,KAAK,WAAW,EAChB,KAAK,KAAK,OAAO,CACnB,CAAC,CACH,CAEA,MAAME,EAAyB,CAC7B,GAAI,KAAK,OACP,MAAM,IAAI,MAAM,4BAA4B,EAE9C,GAAI,CAAC,KAAK,OAAQ,CAChB,KAAK,OAAO,KAAKA,CAAK,EACtB,MACF,CACA,KAAK,OAAO,KAAKC,EAAcD,CAAK,CAAC,CACvC,CAEA,MAAM,KAAqB,CACzB,GAAI,MAAK,OACT,OAAI,KAAK,MAAc,KAAK,YAC5B,KAAK,MAAQ,GACR,KAAK,QAIV,KAAK,gBAAgB,EACd,KAAK,aAJV,KAAK,aAAa,KAAO,GAClB,KAAK,YAIhB,CAGA,OAAc,CACR,KAAK,QACT,KAAK,OAAO,MAAM,CACpB,CAMA,GAAGE,EAAsBG,EAA0C,CACjE,IAAIC,EAAM,KAAK,UAAU,IAAIJ,CAAK,EAC7BI,IACHA,EAAM,IAAI,IACV,KAAK,UAAU,IAAIJ,EAAOI,CAAG,GAE/BA,EAAI,IAAID,CAAQ,CAClB,CAEQ,aAAaE,EAAwC,CAC3D,GAAIA,EAAQ,OAAS,UAAW,OAGhC,MAAMJ,EAFgBI,EAAQ,SAC1B,eACwB,CAAC,GAAG,YAAc,GACzCJ,IACDI,EAAQ,WAAa,GACvB,KAAK,KAAK,QAASJ,CAAI,EAEvB,KAAK,KAAK,UAAWA,CAAI,EAE7B,CAEQ,iBAAwB,CAC9B,KAAK,OAAO,KAAK,KAAK,UAAU,CAAE,KAAM,aAAc,CAAC,CAAC,CAC1D,CAMQ,KAAKD,KAAyBM,EAAmB,CACvD,MAAMF,EAAM,KAAK,UAAU,IAAIJ,CAAK,EACpC,GAAKI,EACL,UAAWD,IAAY,CAAC,GAAGC,CAAG,EAC5BD,EAAS,GAAGG,CAAI,CAEpB,CACF,CAEA,SAASf,EAAeH,EAAkC,CACxD,MAAMmB,EAAS,IAAI,gBACbC,EAAQpB,EAAQ,OAAS,kBAC/BmB,EAAO,IAAI,QAASC,CAAK,EACzBD,EAAO,IAAI,kBAAoBnB,EAAQ,gBAAkB,GAAQ,OAAS,OAAO,EACjFmB,EAAO,IAAI,WAAYnB,EAAQ,UAAY,UAAU,EACrDmB,EAAO,IAAI,cAAe,OAAOnB,EAAQ,YAAc,IAAK,CAAC,EAC7DmB,EAAO,IAAI,YAAa,MAAM,EAC1BnB,EAAQ,UAAUmB,EAAO,IAAI,WAAYnB,EAAQ,QAAQ,EAC7DmB,EAAO,IAAI,QAASnB,EAAQ,MAAM,EAGlC,MAAMqB,EAAkBD,EAAM,WAAW,MAAM,EAC3C,mCACA,mCAEJ,MAAO,IADOpB,EAAQ,UAAYqB,GAAiB,QAAQ,OAAQ,EAAE,CACvD,IAAIF,EAAO,SAAS,CAAC,EACrC,CAjBSpB,EAAAI,EAAA,kBAmBT,SAASQ,EAAcD,EAAgC,CAGrD,MAAMY,EAAO,IAAI,WAAWZ,EAAM,UAAU,EAC5C,OAAAY,EAAK,IAAIZ,CAAK,EACPY,EAAK,MACd,CANSvB,EAAAY,EAAA,iBAQT,SAASG,EAAcS,EAA8B,CACnD,OAAI,OAAOA,GAAS,SAAiBA,EACjCA,aAAgB,YAAoB,IAAI,YAAY,EAAE,OAAOA,CAAI,EACjE,YAAY,OAAOA,CAAI,EAClB,IAAI,YAAY,EAAE,OACvB,IAAI,WAAWA,EAAK,OAAQA,EAAK,WAAYA,EAAK,UAAU,CAC9D,EAEK,IACT,CATSxB,EAAAe,EAAA",
6
+ "names": ["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
- import{DeepgramSTT as m}from"./deepgram/deepgram.js";export{m as DeepgramSTT};
1
+ import{DeepgramSTT as o}from"./deepgram/deepgram.js";import{OpenRouterSTT as T}from"./openrouter/openrouter.js";export{o as DeepgramSTT,T as OpenRouterSTT};
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": "AAAA,OAAS,eAAAA,MAAmB",
6
- "names": ["DeepgramSTT"]
4
+ "sourcesContent": ["export { DeepgramSTT } from \"./deepgram/deepgram\";\nexport { OpenRouterSTT } from \"./openrouter/openrouter\";\n"],
5
+ "mappings": "AAAA,OAAS,eAAAA,MAAmB,sBAC5B,OAAS,iBAAAC,MAAqB",
6
+ "names": ["DeepgramSTT", "OpenRouterSTT"]
7
7
  }
@@ -0,0 +1,2 @@
1
+ import{OpenRouterSTT as r,OpenRouterSession as t,toWav as p}from"./openrouter.js";export{r as OpenRouterSTT,t as OpenRouterSession,p as toWav};
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": "AAAA,OAAS,iBAAAA,EAAe,qBAAAC,EAAmB,SAAAC,MAAa",
6
+ "names": ["OpenRouterSTT", "OpenRouterSession", "toWav"]
7
+ }
@@ -0,0 +1,2 @@
1
+ var d=Object.defineProperty;var l=(r,e)=>d(r,"name",{value:e,configurable:!0});class g{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 s of e)i+=s.byteLength;const t=new Uint8Array(i);let a=0;for(const s of e)t.set(s,a),a+=s.byteLength;return t}async transcribe(e){const i=new AbortController;this.controllers.add(i);try{const t=await h({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 a of[...t])a(...i)}}const p={mp3:"audio/mpeg",flac:"audio/flac",m4a:"audio/mp4",ogg:"audio/ogg",webm:"audio/webm",aac:"audio/aac"};async function h(r){const e=new FormData;e.append("model",r.model);let i,t,a;r.audioFormat==="pcm"?(i=m(r.audio,r.sampleRate),t="audio.wav",a="audio/wav"):(i=r.audio,t=`audio.${r.audioFormat}`,a=p[r.audioFormat]),e.append("file",new Blob([i],{type:a}),t),r.language&&e.append("language",r.language);const s=await r.fetchImpl(`${r.baseUrl}/audio/transcriptions`,{method:"POST",headers:{authorization:`Bearer ${r.apiKey}`},body:e,signal:r.signal});if(!s.ok){const n=await s.text().catch(()=>"");throw new Error(`OpenRouter transcription failed (${s.status})${n?`: ${n}`:""}`)}return((await s.json()).text??"").trim()}l(h,"transcribeClip");function m(r,e){const a=e*1*2,s=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,a,!0),o.setUint16(32,s,!0),o.setUint16(34,16,!0),n.set([100,97,116,97],36),o.setUint32(40,u,!0),n.set(r,44),n}l(m,"toWav");export{g as OpenRouterSTT,c as OpenRouterSession,m as 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": ["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
- import{KokoroTTS as T}from"./kokoro/kokoro.js";export{T as KokoroTTS};
1
+ import{KokoroTTS as e}from"./kokoro/kokoro.js";import{OpenRouterTTS as p}from"./openrouter/openrouter.js";export{e as KokoroTTS,p as OpenRouterTTS};
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": "AAAA,OAAS,aAAAA,MAAiB",
6
- "names": ["KokoroTTS"]
4
+ "sourcesContent": ["export { KokoroTTS } from \"./kokoro/kokoro\";\nexport { OpenRouterTTS } from \"./openrouter/openrouter\";\n"],
5
+ "mappings": "AAAA,OAAS,aAAAA,MAAiB,kBAC1B,OAAS,iBAAAC,MAAqB",
6
+ "names": ["KokoroTTS", "OpenRouterTTS"]
7
7
  }
@@ -1,2 +1,5 @@
1
- var h=Object.defineProperty;var n=(i,e)=>h(i,"name",{value:e,configurable:!0});class d{static{n(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 t=new AbortController;this.abort=t;try{const o=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:t.signal});if(!o.ok){const a=await o.text();throw new Error(`Kokoro request failed (${o.status}): ${a}`)}if(!o.body)throw new Error("Kokoro returned an empty body");const s=o.body.getReader();let r=new Uint8Array(0);for(;;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:a,value:l}=await s.read();if(a)break;for(r=c(r,l);r.length>=this.chunkSize;)yield r.slice(0,this.chunkSize),r=r.slice(this.chunkSize)}r.length>0&&(yield r)}finally{this.abort===t&&(this.abort=null)}}}function c(i,e){const t=new Uint8Array(i.length+e.length);return t.set(i),t.set(e,i.length),t}n(c,"concat");export{d as KokoroTTS};
1
+ var A=Object.defineProperty;var s=(r,e)=>A(r,"name",{value:e,configurable:!0});class T{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 n=await this.fetchImpl(`${this.baseUrl}/v1/audio/speech`,{method:"POST",headers:{"content-type":"application/json",...this.apiKey?{authorization:`Bearer ${this.apiKey}`}:{}},body:JSON.stringify(o),signal:t.signal});if(!n.ok){const i=await n.text();throw new Error(`Kokoro request failed (${n.status}): ${i}`)}if(!n.body)throw new Error("Kokoro returned an empty body");const c=n.body.getReader(),b=(n.headers.get("content-type")??"").includes("text/event-stream");let a=new Uint8Array(0);if(b){let i="",l=!1;for(;!l;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:m,value:g}=await c.read();if(m)break;i+=new TextDecoder().decode(g);const d=i.split(`
2
+
3
+ `);i=d.pop()??"";for(const w of d)for(const h of w.split(`
4
+ `)){if(!h.startsWith("data:"))continue;const y=h.slice(5).trim();if(y==="[DONE]"){l=!0;break}try{const f=JSON.parse(y);f.type==="conversation.item.audio_output.delta"&&typeof f.delta=="string"&&(a=yield*u(p(a,v(f.delta)),this.chunkSize))}catch{}}}}else for(;;){if(t.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:i,value:l}=await c.read();if(i)break;a=yield*u(p(a,l),this.chunkSize)}a.length>0&&(yield a)}finally{this.abort===t&&(this.abort=null)}}}function p(r,e){const t=new Uint8Array(r.length+e.length);return t.set(r),t.set(e,r.length),t}s(p,"concat");function*u(r,e){let t=r;for(;t.length>=e;)yield t.slice(0,e),t=t.slice(e);return t}s(u,"sliceChunks");function v(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(v,"base64ToBytes");export{T as KokoroTTS};
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": "+EAwBO,MAAMA,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": ["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": "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",
6
+ "names": ["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
+ import{OpenRouterTTS as r}from"./openrouter.js";export{r as OpenRouterTTS};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/tts/adapters/openrouter/index.ts"],
4
+ "sourcesContent": ["export { OpenRouterTTS } from \"./openrouter\";\nexport type { OpenRouterTTSOptions } from \"./openrouter\";\n"],
5
+ "mappings": "AAAA,OAAS,iBAAAA,MAAqB",
6
+ "names": ["OpenRouterTTS"]
7
+ }
@@ -0,0 +1,2 @@
1
+ var c=Object.defineProperty;var a=(t,e)=>c(t,"name",{value:e,configurable:!0});class d{static{a(this,"OpenRouterTTS")}apiKey;baseUrl;model;voice;chunkSize;fetchImpl;abort=null;constructor(e){if(!e.apiKey)throw new Error("OpenRouterTTS requires an apiKey");this.apiKey=e.apiKey,this.baseUrl=(e.baseUrl??"https://openrouter.ai/api/v1").replace(/\/+$/,""),this.model=e.model??"fish-audio/s2.1-pro-free:free",this.voice=e.voice??"",this.chunkSize=e.chunkSize??8192,this.fetchImpl=e.fetch??fetch}stop(){this.abort?.abort()}async*stream(e){if(!e.text)throw new Error("OpenRouterTTS requires request.text");this.abort?.abort();const r=new AbortController;this.abort=r;try{const s={model:this.model,input:e.text,...e.voice??this.voice?{voice:e.voice??this.voice}:{},response_format:e.format==="mp3"?"mp3":"pcm"};e.speed!==void 0&&(s.speed=e.speed);const i=await this.fetchImpl(`${this.baseUrl}/audio/speech`,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${this.apiKey}`},body:JSON.stringify(s),signal:r.signal});if(!i.ok){const o=await i.text().catch(()=>"");throw new Error(`OpenRouter TTS failed (${i.status})${o?`: ${o}`:""}`)}if(!i.body)throw new Error("OpenRouter returned an empty body");const l=i.body.getReader();let n=new Uint8Array(0);for(;;){if(r.signal.aborted)throw new DOMException("The operation was aborted.","AbortError");const{done:o,value:h}=await l.read();if(o)break;n=yield*f(p(n,h),this.chunkSize)}n.length>0&&(yield n)}finally{this.abort===r&&(this.abort=null)}}}function p(t,e){const r=new Uint8Array(t.length+e.length);return r.set(t),r.set(e,t.length),r}a(p,"concat");function*f(t,e){let r=t;for(;r.length>=e;)yield r.slice(0,e),r=r.slice(e);return r}a(f,"sliceChunks");export{d as OpenRouterTTS};
2
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/providers/tts/adapters/openrouter/openrouter.ts"],
4
+ "sourcesContent": ["import type { TTS, TTSRequest } from \"../../types\";\nimport type { FetchLike } from \"../../../shared\";\n\nexport interface OpenRouterTTSOptions {\n apiKey: string;\n /** Defaults to `https://openrouter.ai/api/v1`. */\n baseUrl?: string;\n /**\n * TTS model. Defaults to the free `fish-audio/s2.1-pro-free:free` variant \u2014\n * fast, per-character priced. Pass `fish-audio/s2.1-pro` for the paid tier\n * (no free-variant rate limits).\n */\n model?: string;\n /**\n * Default voice, sent when the request carries none. Omitted entirely when\n * unset: providers with a built-in default voice (e.g. fish-audio) accept\n * omission, and some reject an explicit `voice` outright \u2014 while providers\n * that require one (e.g. OpenAI TTS) need it set. Voice support varies by\n * model and provider.\n */\n voice?: string;\n /** Size of the audio chunks yielded from the response stream. Default 8192. */\n chunkSize?: number;\n /** Injectable fetch, mainly for tests. */\n fetch?: FetchLike;\n}\n\n/**\n * OpenRouter TTS adapter (`POST /api/v1/audio/speech`, OpenAI-compatible).\n *\n * The endpoint returns raw audio bytes (not JSON) in `mp3` or `pcm` format.\n * The adapter re-chunks the response stream so audio can be played as it\n * arrives; `stop()` aborts the in-flight synthesis. `pcm` is the default\n * output (OpenRouter's own recommendation for realtime pipelines).\n */\nexport class OpenRouterTTS implements TTS {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly model: string;\n private readonly voice: string;\n private readonly chunkSize: number;\n private readonly fetchImpl: FetchLike;\n private abort: AbortController | null = null;\n\n constructor(options: OpenRouterTTSOptions) {\n if (!options.apiKey) {\n throw new Error(\"OpenRouterTTS requires an apiKey\");\n }\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? \"https://openrouter.ai/api/v1\").replace(/\\/+$/, \"\");\n this.model = options.model ?? \"fish-audio/s2.1-pro-free:free\";\n this.voice = options.voice ?? \"\";\n this.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(\"OpenRouterTTS 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 is only sent when configured: fish-audio (the default model)\n // rejects an explicit voice with a 400, while models like OpenAI TTS\n // require one.\n ...(request.voice ?? this.voice ? { voice: request.voice ?? this.voice } : {}),\n // OpenRouter supports mp3 and pcm only; anything else (or\n // unspecified) maps to pcm, the lower-latency realtime format.\n response_format: request.format === \"mp3\" ? \"mp3\" : \"pcm\",\n };\n if (request.speed !== undefined) body.speed = request.speed;\n\n const response = await this.fetchImpl(`${this.baseUrl}/audio/speech`, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n // Non-200 responses carry a JSON error body, not audio.\n const detail = await response.text().catch(() => \"\");\n throw new Error(\n `OpenRouter TTS failed (${response.status})${detail ? `: ${detail}` : \"\"}`,\n );\n }\n if (!response.body) {\n throw new Error(\"OpenRouter returned an empty body\");\n }\n\n const reader = response.body.getReader();\n let buffer = new Uint8Array(0);\n while (true) {\n if (controller.signal.aborted) {\n throw new DOMException(\"The operation was aborted.\", \"AbortError\");\n }\n const { done, value } = await reader.read();\n if (done) break;\n buffer = yield* sliceChunks(concat(buffer, value), this.chunkSize);\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\n/**\n * Yield up to `chunkSize` bytes from `audio`, returning the remainder for\n * the caller to keep buffering. Providers deliver arbitrary chunk\n * boundaries, so audio is re-sliced into uniform pieces.\n */\nfunction* sliceChunks(\n audio: Uint8Array<ArrayBuffer>,\n chunkSize: number,\n): Generator<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>, undefined> {\n let buffer = audio;\n while (buffer.length >= chunkSize) {\n yield buffer.slice(0, chunkSize);\n buffer = buffer.slice(chunkSize);\n }\n return buffer;\n}\n"],
5
+ "mappings": "+EAmCO,MAAMA,CAA6B,CAnC1C,MAmC0C,CAAAC,EAAA,sBACvB,OACA,QACA,MACA,MACA,UACA,UACT,MAAgC,KAExC,YAAYC,EAA+B,CACzC,GAAI,CAACA,EAAQ,OACX,MAAM,IAAI,MAAM,kCAAkC,EAEpD,KAAK,OAASA,EAAQ,OACtB,KAAK,SAAWA,EAAQ,SAAW,gCAAgC,QAAQ,OAAQ,EAAE,EACrF,KAAK,MAAQA,EAAQ,OAAS,gCAC9B,KAAK,MAAQA,EAAQ,OAAS,GAC9B,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,qCAAqC,EAGvD,KAAK,OAAO,MAAM,EAClB,MAAMC,EAAa,IAAI,gBACvB,KAAK,MAAQA,EAEb,GAAI,CACF,MAAMC,EAAgC,CACpC,MAAO,KAAK,MACZ,MAAOF,EAAQ,KAIf,GAAIA,EAAQ,OAAS,KAAK,MAAQ,CAAE,MAAOA,EAAQ,OAAS,KAAK,KAAM,EAAI,CAAC,EAG5E,gBAAiBA,EAAQ,SAAW,MAAQ,MAAQ,KACtD,EACIA,EAAQ,QAAU,SAAWE,EAAK,MAAQF,EAAQ,OAEtD,MAAMG,EAAW,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,gBAAiB,CACpE,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAe,UAAU,KAAK,MAAM,EACtC,EACA,KAAM,KAAK,UAAUD,CAAI,EACzB,OAAQD,EAAW,MACrB,CAAC,EAED,GAAI,CAACE,EAAS,GAAI,CAEhB,MAAMC,EAAS,MAAMD,EAAS,KAAK,EAAE,MAAM,IAAM,EAAE,EACnD,MAAM,IAAI,MACR,0BAA0BA,EAAS,MAAM,IAAIC,EAAS,KAAKA,CAAM,GAAK,EAAE,EAC1E,CACF,CACA,GAAI,CAACD,EAAS,KACZ,MAAM,IAAI,MAAM,mCAAmC,EAGrD,MAAME,EAASF,EAAS,KAAK,UAAU,EACvC,IAAIG,EAAS,IAAI,WAAW,CAAC,EAC7B,OAAa,CACX,GAAIL,EAAW,OAAO,QACpB,MAAM,IAAI,aAAa,6BAA8B,YAAY,EAEnE,KAAM,CAAE,KAAAM,EAAM,MAAAC,CAAM,EAAI,MAAMH,EAAO,KAAK,EAC1C,GAAIE,EAAM,MACVD,EAAS,MAAOG,EAAYC,EAAOJ,EAAQE,CAAK,EAAG,KAAK,SAAS,CACnE,CACIF,EAAO,OAAS,IAClB,MAAMA,EAEV,QAAE,CACI,KAAK,QAAUL,IACjB,KAAK,MAAQ,KAEjB,CACF,CACF,CAEA,SAASS,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,CARSf,EAAAY,EAAA,UAeT,SAAUD,EACRK,EACAC,EACwE,CACxE,IAAIT,EAASQ,EACb,KAAOR,EAAO,QAAUS,GACtB,MAAMT,EAAO,MAAM,EAAGS,CAAS,EAC/BT,EAASA,EAAO,MAAMS,CAAS,EAEjC,OAAOT,CACT,CAVUR,EAAAW,EAAA",
6
+ "names": ["OpenRouterTTS", "__name", "options", "request", "controller", "body", "response", "detail", "reader", "buffer", "done", "value", "sliceChunks", "concat", "a", "b", "out", "audio", "chunkSize"]
7
+ }
@@ -0,0 +1,2 @@
1
+ var s=Object.defineProperty;var a=(i,e)=>s(i,"name",{value:e,configurable:!0});import{materializeObject as c}from"../reference/reference.js";class f{static{a(this,"FieldStream")}schema;fieldHandlers;itemHandlers;objectHandlers=new Set;errorHandlers=new Set;onCancel;cancelled=!1;objectState="streaming";constructor(e,r={}){this.schema=e,this.onCancel=r.onCancel,this.fieldHandlers=new Array(e.length),this.itemHandlers=new Array(e.length)}when(e,r){const t=this.resolveIndex(e);return(this.fieldHandlers[t]??=new Set).add(r),this}whenItem(e,r){const t=this.resolveIndex(e),n=this.schema[t];if(n.mode!=="array")throw new TypeError(`whenItem: field "${n.path.join(".")}" is not an array`);return(this.itemHandlers[t]??=new Set).add(r),this}whenObjectDone(e){return this.objectHandlers.add(e),this}onError(e){return this.errorHandlers.add(e),this}cancel(){this.cancelled||(this.cancelled=!0,this.cancelObject(),this.onCancel?.())}beginObject(){this.objectState="streaming"}emitField(e,r){if(this.objectState!=="streaming")return;const t=this.fieldHandlers[e];if(t!==void 0)for(const n of t)this.fire(n,[r])}emitItem(e,r,t){if(this.objectState!=="streaming")return;const n=this.itemHandlers[e];if(n!==void 0)for(const d of n)this.fire(d,[t,r])}emitObject(e){if(this.objectState!=="streaming")return;this.objectState="done";const r=c(this.schema,e);for(const t of this.objectHandlers)this.fire(t,[r])}cancelObject(){this.objectState==="streaming"&&(this.objectState="cancelled")}fail(e){this.objectState==="streaming"&&(this.objectState="failed"),this.dispatchError(o(e))}get objectCompleted(){return this.objectState==="done"}resolveIndex(e){const r=Array.isArray(e)?e.join("."):e,t=this.schema.findIndex(n=>n.path.join(".")===r);if(t===-1)throw new TypeError(`unknown field path: "${r}"`);return t}fire(e,r){try{const t=e(...r);t instanceof Promise&&t.catch(n=>this.dispatchError(o(n)))}catch(t){this.dispatchError(o(t))}}dispatchError(e){if(this.errorHandlers.size===0)throw e;for(const r of this.errorHandlers)try{r(e)}catch{}}}function o(i){return i instanceof Error?i:new Error(String(i))}a(o,"toError");export{f as FieldStream};
2
+ //# sourceMappingURL=field-stream.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/transport/streamobject/field-stream/field-stream.ts"],
4
+ "sourcesContent": ["/**\n * FieldStream \u2014 the semantic completion core shared by every source adapter.\n *\n * A FieldStream registers completion handlers (`when`/`whenItem`/`whenObjectDone`)\n * and dispatches completion events to them. It knows nothing about JSON, wire\n * records, SSE, or any particular producer: subclasses turn their source into\n * completion events via the protected emit helpers.\n *\n * Implementations:\n * - StreamObject \u2014 consumes an AsyncIterable of text chunks (JSON source)\n * - ConversationStream \u2014 consumes conversation events (provider-native source)\n *\n * Lifecycle, per object:\n *\n * STREAMING \u2500\u2500emitObject\u2500\u2500\u25B6 DONE\n * \u2502\n * \u251C\u2500\u2500cancel()\u2500\u2500\u25B6 CANCELLED\n * \u2514\u2500\u2500fail()\u2500\u2500\u2500\u2500\u25B6 FAILED\n *\n * Exactly-once: within an object, completion handlers fire at most once per\n * boundary and never after a terminal state (events after DONE/CANCELLED/\n * FAILED are dropped). cancel() is idempotent. Multi-object sources call\n * beginObject() to start each object.\n *\n * `when` means completion, not mutation: handlers never see chunks or source\n * format. Handlers are invoked synchronously at the completion boundary and\n * never block the producer; async handlers run concurrently and their\n * rejections are routed to onError.\n *\n * Errors: delivered to onError handlers when registered; otherwise the\n * subclass decides (StreamObject rejects start(); callers of `fail()` get the\n * throw).\n */\n\nimport type { Event, FieldValue, ScalarValue, Schema } from \"../reference/reference\";\nimport { materializeObject } from \"../reference/reference\";\n\nexport type FieldHandler = (value: FieldValue) => void;\nexport type ItemHandler = (value: ScalarValue, index: number) => void;\nexport type ObjectDoneHandler = (object: Record<string, unknown>) => void;\nexport type ErrorHandler = (error: Error) => void;\n\nexport interface FieldStreamOptions {\n /** Invoked by cancel() \u2014 e.g. to abort the producer's request. */\n onCancel?: () => void;\n}\n\ntype ObjectState = \"streaming\" | \"done\" | \"cancelled\" | \"failed\";\n\nexport abstract class FieldStream {\n protected readonly schema: Schema;\n private readonly fieldHandlers: Array<Set<FieldHandler> | undefined>;\n private readonly itemHandlers: Array<Set<ItemHandler> | undefined>;\n private readonly objectHandlers = new Set<ObjectDoneHandler>();\n private readonly errorHandlers = new Set<ErrorHandler>();\n private readonly onCancel: (() => void) | undefined;\n /** Stream-level: set by cancel(); consumed by source-driven subclasses. */\n protected cancelled = false;\n private objectState: ObjectState = \"streaming\";\n\n constructor(schema: Schema, options: FieldStreamOptions = {}) {\n this.schema = schema;\n this.onCancel = options.onCancel;\n this.fieldHandlers = new Array(schema.length);\n this.itemHandlers = new Array(schema.length);\n }\n\n when(field: string | string[], handler: FieldHandler): this {\n const index = this.resolveIndex(field);\n (this.fieldHandlers[index] ??= new Set()).add(handler);\n return this;\n }\n\n whenItem(field: string | string[], handler: ItemHandler): this {\n const index = this.resolveIndex(field);\n const def = this.schema[index]!;\n if (def.mode !== \"array\") {\n throw new TypeError(`whenItem: field \"${def.path.join(\".\")}\" is not an array`);\n }\n (this.itemHandlers[index] ??= new Set()).add(handler);\n return this;\n }\n\n whenObjectDone(handler: ObjectDoneHandler): this {\n this.objectHandlers.add(handler);\n return this;\n }\n\n onError(handler: ErrorHandler): this {\n this.errorHandlers.add(handler);\n return this;\n }\n\n /**\n * Abort the current object and stop the producer (idempotent). The object\n * will not complete; already-delivered items remain valid partial state.\n */\n cancel(): void {\n if (this.cancelled) return; // idempotent\n this.cancelled = true;\n this.cancelObject();\n this.onCancel?.();\n }\n\n /** Start a new object's lifecycle (multi-object sources call this per object). */\n protected beginObject(): void {\n this.objectState = \"streaming\";\n }\n\n /** Emit a completed field value (subclasses call this at the completion boundary). */\n protected emitField(field: number, value: FieldValue): void {\n if (this.objectState !== \"streaming\") return;\n const handlers = this.fieldHandlers[field];\n if (handlers === undefined) return;\n for (const handler of handlers) this.fire(handler, [value]);\n }\n\n /** Emit a completed array item. */\n protected emitItem(field: number, index: number, value: ScalarValue): void {\n if (this.objectState !== \"streaming\") return;\n const handlers = this.itemHandlers[field];\n if (handlers === undefined) return;\n for (const handler of handlers) this.fire(handler, [value, index]);\n }\n\n /**\n * Complete the current object: materializes and fires whenObjectDone exactly\n * once (a second call, or a call after cancel/fail, is ignored).\n */\n protected emitObject(events: Event[]): void {\n if (this.objectState !== \"streaming\") return;\n this.objectState = \"done\";\n const object = materializeObject(this.schema, events);\n for (const handler of this.objectHandlers) this.fire(handler, [object]);\n }\n\n /** Abort the current object without completing it (idempotent). */\n protected cancelObject(): void {\n if (this.objectState === \"streaming\") this.objectState = \"cancelled\";\n }\n\n /**\n * Route a source/adapter failure to onError (or rethrow when unhandled) and\n * mark the current object failed.\n */\n protected fail(error: unknown): void {\n if (this.objectState === \"streaming\") this.objectState = \"failed\";\n this.dispatchError(toError(error));\n }\n\n protected get objectCompleted(): boolean {\n return this.objectState === \"done\";\n }\n\n protected resolveIndex(field: string | string[]): number {\n const key = Array.isArray(field) ? field.join(\".\") : field;\n const index = this.schema.findIndex((def) => def.path.join(\".\") === key);\n if (index === -1) {\n throw new TypeError(`unknown field path: \"${key}\"`);\n }\n return index;\n }\n\n private fire<T extends unknown[]>(handler: (...args: T) => unknown, args: T): void {\n try {\n const result = handler(...args);\n if (result instanceof Promise) {\n void result.catch((error) => this.dispatchError(toError(error)));\n }\n } catch (error) {\n this.dispatchError(toError(error));\n }\n }\n\n private dispatchError(error: Error): void {\n if (this.errorHandlers.size === 0) throw error;\n for (const handler of this.errorHandlers) {\n try {\n handler(error);\n } catch {\n // a failing error handler is not re-dispatched\n }\n }\n }\n}\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["materializeObject", "FieldStream", "__name", "schema", "options", "field", "handler", "index", "def", "value", "handlers", "events", "object", "error", "toError", "key", "args", "result"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{FieldStream as t}from"./field-stream.js";export{t as FieldStream};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/transport/streamobject/field-stream/index.ts"],
4
+ "sourcesContent": ["export { FieldStream } from \"./field-stream\";\nexport type {\n ErrorHandler,\n FieldHandler,\n FieldStreamOptions,\n ItemHandler,\n ObjectDoneHandler,\n} from \"./field-stream\";\n"],
5
+ "mappings": "AAAA,OAAS,eAAAA,MAAmB",
6
+ "names": ["FieldStream"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{FieldStream as a}from"./field-stream/index.js";import{StreamObject as t}from"./stream-object/index.js";import{IncrementalJsonParser as l,JsonToStreamObjectAdapter as n}from"./json-adapter/index.js";import{ReferenceDecoder as p,Receiver as c,decode as i,encodeText as s,executeRecords as x,materializeObject as f,parseBooleanPayload as y,parseIntegerPayload as b,parseNumberPayload as S}from"./reference/index.js";export{a as FieldStream,l as IncrementalJsonParser,n as JsonToStreamObjectAdapter,c as Receiver,p as ReferenceDecoder,t as StreamObject,i as decode,s as encodeText,x as executeRecords,f as materializeObject,y as parseBooleanPayload,b as parseIntegerPayload,S as parseNumberPayload};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/transport/streamobject/index.ts"],
4
+ "sourcesContent": ["// StreamObject \u2014 a schema-bound streaming semantic protocol.\n//\n// Turns a text-chunk source (LLM deltas, SSE, wire records, conversation\n// events) into completion events: consumers act when the value they depend on\n// completes, without waiting for the enclosing object. See README.md.\nexport { FieldStream } from \"./field-stream/index\";\nexport type {\n ErrorHandler,\n FieldHandler,\n FieldStreamOptions,\n ItemHandler,\n ObjectDoneHandler,\n} from \"./field-stream/index\";\nexport { StreamObject } from \"./stream-object/index\";\nexport type { StreamObjectOptions } from \"./stream-object/index\";\nexport { IncrementalJsonParser, JsonToStreamObjectAdapter } from \"./json-adapter/index\";\nexport type { JsonAdapterCallbacks, JsonValue } from \"./json-adapter/index\";\nexport {\n ReferenceDecoder,\n Receiver,\n decode,\n encodeText,\n executeRecords,\n materializeObject,\n parseBooleanPayload,\n parseIntegerPayload,\n parseNumberPayload,\n} from \"./reference/index\";\nexport type {\n Decoder,\n Event,\n FieldDef,\n FieldType,\n FieldValue,\n Payload,\n ScalarValue,\n Schema,\n StreamRecord,\n} from \"./reference/index\";\n"],
5
+ "mappings": "AAKA,OAAS,eAAAA,MAAmB,uBAQ5B,OAAS,gBAAAC,MAAoB,wBAE7B,OAAS,yBAAAC,EAAuB,6BAAAC,MAAiC,uBAEjE,OACE,oBAAAC,EACA,YAAAC,EACA,UAAAC,EACA,cAAAC,EACA,kBAAAC,EACA,qBAAAC,EACA,uBAAAC,EACA,uBAAAC,EACA,sBAAAC,MACK",
6
+ "names": ["FieldStream", "StreamObject", "IncrementalJsonParser", "JsonToStreamObjectAdapter", "ReferenceDecoder", "Receiver", "decode", "encodeText", "executeRecords", "materializeObject", "parseBooleanPayload", "parseIntegerPayload", "parseNumberPayload"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{IncrementalJsonParser as a,JsonToStreamObjectAdapter as o}from"./json-adapter.js";export{a as IncrementalJsonParser,o as JsonToStreamObjectAdapter};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/transport/streamobject/json-adapter/index.ts"],
4
+ "sourcesContent": ["export { IncrementalJsonParser, JsonToStreamObjectAdapter } from \"./json-adapter\";\nexport type { JsonAdapterCallbacks, JsonValue } from \"./json-adapter\";\n"],
5
+ "mappings": "AAAA,OAAS,yBAAAA,EAAuB,6BAAAC,MAAiC",
6
+ "names": ["IncrementalJsonParser", "JsonToStreamObjectAdapter"]
7
+ }
@@ -0,0 +1,4 @@
1
+ var y=Object.defineProperty;var s=(n,e)=>y(n,"name",{value:e,configurable:!0});class m{constructor(e){this.onValueDone=e}onValueDone;static{s(this,"IncrementalJsonParser")}stack=[];value=null;started=!1;done=!1;push(e){for(let t=0;t<e.length;t++)this.pushChar(e[t])}pushChar(e){if(this.done)return;if(!this.started){e==="{"&&(this.started=!0,this.stack.push({kind:"object",path:[],map:{},key:null,phase:"key"}));return}let t=e;for(;;){if(this.value!==null){if(this.feedValue(t))return;continue}this.dispatch(t);return}}dispatch(e){if(h(e))return;const t=this.stack[this.stack.length-1];if(t.kind==="object"){if(t.phase==="key"){if(e==='"'){this.value={kind:"string",path:t.path,raw:"",escaped:!1};return}if(e==="}"){this.closeContainer(t);return}if(e===",")return;throw new Error(`JSON: expected key or '}' at path [${t.path.join(".")}], got ${JSON.stringify(e)}`)}if(t.phase==="colon"){if(e===":"){t.phase="value";return}throw new Error(`JSON: expected ':' after key ${JSON.stringify(t.key)}`)}this.startValue(e,t,t.path.concat(t.key??"?"));return}if(e==="]"){this.closeContainer(t);return}e!==","&&this.startValue(e,t,t.path.concat(String(t.items.length)))}startValue(e,t,r){if(e==='"'){this.value={kind:"string",path:r,raw:"",escaped:!1};return}if(e==="{"){this.stack.push({kind:"object",path:r,map:{},key:null,phase:"key"});return}if(e==="["){this.stack.push({kind:"array",path:r,items:[]});return}if(e==="-"||p(e)){this.value={kind:"number",path:r,raw:e};return}if(e==="t"||e==="f"||e==="n"){this.value={kind:"literal",path:r,raw:e};return}throw new Error(`JSON: unexpected value start ${JSON.stringify(e)} at path [${r.join(".")}]`)}feedValue(e){const t=this.value;switch(t.kind){case"string":return e==="\\"?(t.raw+=e,t.escaped=!t.escaped,!0):e==='"'&&!t.escaped?(this.value=null,this.completeValue(t.path,b(t.raw)),!0):(t.raw+=e,t.escaped=!1,!0);case"number":{if(p(e)||e==="-"||e==="+"||e==="."||e==="e"||e==="E")return t.raw+=e,!0;const r=Number(t.raw);if(Number.isNaN(r))throw new Error(`JSON: invalid number ${JSON.stringify(t.raw)}`);return this.value=null,this.completeValue(t.path,r),!1}case"literal":{if(k(e)&&v(t.raw)){const i=t.raw==="true"?!0:t.raw==="false"?!1:null;return this.value=null,this.completeValue(t.path,i),!1}const r=t.raw+e;if(w(r))return t.raw=r,!0;throw new Error(`JSON: invalid literal ${JSON.stringify(r)}`)}}}completeValue(e,t){const r=this.stack[this.stack.length-1];if(r.kind==="object"&&r.phase==="key"){if(typeof t!="string")throw new Error("JSON: object key must be a string");r.key=t,r.phase="colon";return}if(this.onValueDone(e,t),r.kind==="object"){if(r.phase!=="value"||r.key===null)throw new Error(`JSON: unexpected value position at path [${e.join(".")}]`);r.map[r.key]=t,r.key=null,r.phase="key"}else r.items.push(t)}closeContainer(e){const t=e.kind==="object"?e.map:e.items,r=e.path;if(this.stack.pop(),this.stack.length===0){this.done=!0,this.onValueDone(r,t);return}this.completeValue(r,t)}}function b(n){let e="";for(let t=0;t<n.length;t++){const r=n[t];if(r!=="\\"){e+=r;continue}t++;const i=n[t];if(i===void 0)throw new Error("JSON: unterminated escape");switch(i){case'"':e+='"';break;case"\\":e+="\\";break;case"/":e+="/";break;case"b":e+="\b";break;case"f":e+="\f";break;case"n":e+=`
2
+ `;break;case"r":e+="\r";break;case"t":e+=" ";break;case"u":{const a=n.slice(t+1,t+5);if(!/^[0-9a-fA-F]{4}$/.test(a))throw new Error("JSON: invalid \\u escape");const o=parseInt(a,16);if(o>=55296&&o<=56319){const u=/^\\u([0-9a-fA-F]{4})/.exec(n.slice(t+5)),l=u?parseInt(u[1],16):-1;if(l>=56320&&l<=57343){e+=String.fromCharCode(o,l),t+=10;break}}e+=String.fromCharCode(o),t+=4;break}default:throw new Error(`JSON: invalid escape \\${i}`)}}return e}s(b,"decodeJsonString");function h(n){return n===" "||n===" "||n===`
3
+ `||n==="\r"}s(h,"isWhitespace");function p(n){return n>="0"&&n<="9"}s(p,"isDigit");function k(n){return h(n)||n===","||n==="}"||n==="]"}s(k,"isTerminator");function v(n){return n==="true"||n==="false"||n==="null"}s(v,"isCompleteLiteral");function w(n){return"true".startsWith(n)||"false".startsWith(n)||"null".startsWith(n)}s(w,"isLiteralPrefix");class S{static{s(this,"JsonToStreamObjectAdapter")}fields;parser;events=[];onFieldDone;onItemDone;onObjectDone;constructor(e,t={}){this.fields=e.map((r,i)=>({def:r,pathKey:r.path.join(".")})),this.onFieldDone=t.onFieldDone,this.onItemDone=t.onItemDone,this.onObjectDone=t.onObjectDone,this.parser=new m((r,i)=>this.handleValue(r,i))}push(e){this.parser.push(e)}getEvents(){return this.events}handleValue(e,t){const r=e.join(".");if(r===""){this.events.push({kind:"object-complete"}),this.onObjectDone?.();return}const i=this.fields.findIndex(d=>d.pathKey===r);if(i!==-1){const d=J(this.fields[i].def,t),g={kind:"field",field:i,value:d};this.events.push(g),this.onFieldDone?.(i,d);return}if(Array.isArray(t)||typeof t=="object"&&t!==null)return;const a=this.matchArrayItem(e);if(a===-1)return;const o=this.fields[a].def,u=f(o.type,t,o.path.join(".")),l=Number(e[e.length-1]),c={kind:"item",field:a,index:l,value:u};this.events.push(c),this.onItemDone?.(a,l,u)}matchArrayItem(e){if(e.length<2)return-1;const t=e[e.length-1];if(!/^\d+$/.test(t))return-1;const r=e.slice(0,-1).join("."),i=this.fields.findIndex(a=>a.pathKey===r);return i===-1||this.fields[i].def.mode!=="array"?-1:i}}function J(n,e){if(n.mode==="array"){if(!Array.isArray(e))throw new Error(`field ${n.path.join(".")}: expected array, got ${JSON.stringify(e)}`);return e.map(t=>f(n.type,t,n.path.join(".")))}return f(n.type,e,n.path.join("."))}s(J,"coerceToField");function f(n,e,t){switch(n){case"string":if(typeof e!="string")throw new Error(`field ${t}: expected string, got ${JSON.stringify(e)}`);return e;case"integer":if(typeof e!="number"||!Number.isSafeInteger(e))throw new Error(`field ${t}: expected integer, got ${JSON.stringify(e)}`);return e;case"number":if(typeof e!="number"||!Number.isFinite(e))throw new Error(`field ${t}: expected number, got ${JSON.stringify(e)}`);return e;case"boolean":if(typeof e!="boolean")throw new Error(`field ${t}: expected boolean, got ${JSON.stringify(e)}`);return e}}s(f,"coerceScalar");export{m as IncrementalJsonParser,S as JsonToStreamObjectAdapter};
4
+ //# sourceMappingURL=json-adapter.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/transport/streamobject/json-adapter/json-adapter.ts"],
4
+ "sourcesContent": ["/**\n * Incremental JSON \u2192 StreamObject adapter.\n *\n * Consumes a JSON token stream (e.g. LLM output deltas) and emits StreamObject\n * field-completion events as soon as each schema field's value is complete \u2014\n * without waiting for the rest of the object. This is the designed production\n * shape of the protocol: the model emits ordinary output, deterministic\n * software does the parsing, and consumers react at field boundaries.\n *\n * The wire encoding is irrelevant here; only the Event model matters.\n */\n\nimport type { Event, FieldDef, FieldType, FieldValue, ScalarValue, Schema } from \"../reference/reference\";\n\nexport type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };\n\ninterface ObjectCtx {\n kind: \"object\";\n path: string[];\n map: Record<string, JsonValue>;\n key: string | null; // the key currently awaiting its value\n phase: \"key\" | \"colon\" | \"value\";\n}\n\ninterface ArrayCtx {\n kind: \"array\";\n path: string[];\n items: JsonValue[];\n}\n\ntype ValueState =\n | { kind: \"string\"; path: string[]; raw: string; escaped: boolean }\n | { kind: \"number\"; path: string[]; raw: string }\n | { kind: \"literal\"; path: string[]; raw: string };\n\n/** Minimal incremental JSON parser. Emits every completed value with its path. */\nexport class IncrementalJsonParser {\n private readonly stack: Array<ObjectCtx | ArrayCtx> = [];\n private value: ValueState | null = null;\n private started = false;\n private done = false;\n\n constructor(private readonly onValueDone: (path: string[], value: JsonValue) => void) {}\n\n push(chunk: string): void {\n for (let i = 0; i < chunk.length; i++) {\n this.pushChar(chunk[i]!);\n }\n }\n\n private pushChar(c: string): void {\n if (this.done) return;\n if (!this.started) {\n // Preamble (markdown fences, prose) is ignored until the object starts.\n if (c === \"{\") {\n this.started = true;\n this.stack.push({ kind: \"object\", path: [], map: {}, key: null, phase: \"key\" });\n }\n return;\n }\n let ch = c;\n for (;;) {\n if (this.value !== null) {\n if (this.feedValue(ch)) return;\n continue; // the value completed on ch as a terminator; reprocess it\n }\n this.dispatch(ch);\n return;\n }\n }\n\n private dispatch(c: string): void {\n if (isWhitespace(c)) return;\n const top = this.stack[this.stack.length - 1]!;\n if (top.kind === \"object\") {\n if (top.phase === \"key\") {\n if (c === '\"') {\n this.value = { kind: \"string\", path: top.path, raw: \"\", escaped: false };\n return;\n }\n if (c === \"}\") {\n this.closeContainer(top);\n return;\n }\n if (c === \",\") return; // tolerate trailing commas in model output\n throw new Error(\n `JSON: expected key or '}' at path [${top.path.join(\".\")}], got ${JSON.stringify(c)}`,\n );\n }\n if (top.phase === \"colon\") {\n if (c === \":\") {\n top.phase = \"value\";\n return;\n }\n throw new Error(`JSON: expected ':' after key ${JSON.stringify(top.key)}`);\n }\n this.startValue(c, top, top.path.concat(top.key ?? \"?\"));\n return;\n }\n if (c === \"]\") {\n this.closeContainer(top);\n return;\n }\n if (c === \",\") return; // tolerate trailing commas\n this.startValue(c, top, top.path.concat(String(top.items.length)));\n }\n\n private startValue(c: string, _top: ObjectCtx | ArrayCtx, path: string[]): void {\n if (c === '\"') {\n this.value = { kind: \"string\", path, raw: \"\", escaped: false };\n return;\n }\n if (c === \"{\") {\n this.stack.push({ kind: \"object\", path, map: {}, key: null, phase: \"key\" });\n return;\n }\n if (c === \"[\") {\n this.stack.push({ kind: \"array\", path, items: [] });\n return;\n }\n if (c === \"-\" || isDigit(c)) {\n this.value = { kind: \"number\", path, raw: c };\n return;\n }\n if (c === \"t\" || c === \"f\" || c === \"n\") {\n this.value = { kind: \"literal\", path, raw: c };\n return;\n }\n throw new Error(`JSON: unexpected value start ${JSON.stringify(c)} at path [${path.join(\".\")}]`);\n }\n\n private feedValue(c: string): boolean {\n const v = this.value!;\n switch (v.kind) {\n case \"string\": {\n if (c === \"\\\\\") {\n v.raw += c;\n v.escaped = !v.escaped;\n return true;\n }\n if (c === '\"' && !v.escaped) {\n this.value = null;\n this.completeValue(v.path, decodeJsonString(v.raw));\n return true;\n }\n v.raw += c;\n v.escaped = false;\n return true;\n }\n case \"number\": {\n if (isDigit(c) || c === \"-\" || c === \"+\" || c === \".\" || c === \"e\" || c === \"E\") {\n v.raw += c;\n return true;\n }\n const n = Number(v.raw);\n if (Number.isNaN(n)) throw new Error(`JSON: invalid number ${JSON.stringify(v.raw)}`);\n this.value = null;\n this.completeValue(v.path, n);\n return false; // reprocess the terminator\n }\n case \"literal\": {\n if (isTerminator(c) && isCompleteLiteral(v.raw)) {\n const value = v.raw === \"true\" ? true : v.raw === \"false\" ? false : null;\n this.value = null;\n this.completeValue(v.path, value);\n return false; // reprocess the terminator\n }\n const text = v.raw + c;\n if (isLiteralPrefix(text)) {\n v.raw = text;\n return true;\n }\n throw new Error(`JSON: invalid literal ${JSON.stringify(text)}`);\n }\n }\n }\n\n private completeValue(path: string[], value: JsonValue): void {\n const top = this.stack[this.stack.length - 1]!;\n if (top.kind === \"object\" && top.phase === \"key\") {\n // The completed string was an object key, not a value.\n if (typeof value !== \"string\") throw new Error(\"JSON: object key must be a string\");\n top.key = value;\n top.phase = \"colon\";\n return;\n }\n this.onValueDone(path, value);\n if (top.kind === \"object\") {\n if (top.phase !== \"value\" || top.key === null) {\n throw new Error(`JSON: unexpected value position at path [${path.join(\".\")}]`);\n }\n top.map[top.key] = value;\n top.key = null;\n top.phase = \"key\";\n } else {\n top.items.push(value);\n }\n }\n\n private closeContainer(ctx: ObjectCtx | ArrayCtx): void {\n const value: JsonValue = ctx.kind === \"object\" ? ctx.map : ctx.items;\n const path = ctx.path;\n this.stack.pop();\n if (this.stack.length === 0) {\n this.done = true;\n this.onValueDone(path, value);\n return;\n }\n this.completeValue(path, value);\n }\n}\n\n/** Decode the raw content of a JSON string literal (escapes and \\uXXXX). */\nfunction decodeJsonString(raw: string): string {\n let out = \"\";\n for (let i = 0; i < raw.length; i++) {\n const c = raw[i]!;\n if (c !== \"\\\\\") {\n out += c;\n continue;\n }\n i++;\n const esc = raw[i];\n if (esc === undefined) throw new Error(\"JSON: unterminated escape\");\n switch (esc) {\n case '\"': out += '\"'; break;\n case \"\\\\\": out += \"\\\\\"; break;\n case \"/\": out += \"/\"; break;\n case \"b\": out += \"\\b\"; break;\n case \"f\": out += \"\\f\"; break;\n case \"n\": out += \"\\n\"; break;\n case \"r\": out += \"\\r\"; break;\n case \"t\": out += \"\\t\"; break;\n case \"u\": {\n const hex = raw.slice(i + 1, i + 5);\n if (!/^[0-9a-fA-F]{4}$/.test(hex)) throw new Error(\"JSON: invalid \\\\u escape\");\n const code = parseInt(hex, 16);\n // Combine surrogate pairs (\\uD83D\\uDE00 \u2192 \uD83D\uDE00).\n if (code >= 0xd800 && code <= 0xdbff) {\n const low = /^\\\\u([0-9a-fA-F]{4})/.exec(raw.slice(i + 5));\n const lowCode = low ? parseInt(low[1]!, 16) : -1;\n if (lowCode >= 0xdc00 && lowCode <= 0xdfff) {\n out += String.fromCharCode(code, lowCode);\n i += 10;\n break;\n }\n }\n out += String.fromCharCode(code);\n i += 4;\n break;\n }\n default:\n throw new Error(`JSON: invalid escape \\\\${esc}`);\n }\n }\n return out;\n}\n\nfunction isWhitespace(c: string): boolean {\n return c === \" \" || c === \"\\t\" || c === \"\\n\" || c === \"\\r\";\n}\n\nfunction isDigit(c: string): boolean {\n return c >= \"0\" && c <= \"9\";\n}\n\nfunction isTerminator(c: string): boolean {\n return isWhitespace(c) || c === \",\" || c === \"}\" || c === \"]\";\n}\n\nfunction isCompleteLiteral(text: string): boolean {\n return text === \"true\" || text === \"false\" || text === \"null\";\n}\n\nfunction isLiteralPrefix(text: string): boolean {\n return (\n \"true\".startsWith(text) || \"false\".startsWith(text) || \"null\".startsWith(text)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Schema-aware adapter: JSON values \u2192 StreamObject field-completion events\n// ---------------------------------------------------------------------------\n\nexport interface JsonAdapterCallbacks {\n onFieldDone?: (field: number, value: FieldValue) => void;\n /** Fires for each completed array element, before the array field completes. */\n onItemDone?: (field: number, index: number, value: ScalarValue) => void;\n /** Fires when the root object closes. */\n onObjectDone?: () => void;\n}\n\n/**\n * Feeds a JSON token stream into the reference Event model and emits a\n * completion hierarchy as soon as each piece is known:\n *\n * VALUE_COMPLETE \u2192 every parsed value (parser level, per path)\n * ITEM_COMPLETE \u2192 each array element of an array-typed schema field\n * FIELD_COMPLETE \u2192 a schema field's whole value (incl. a closed array)\n * OBJECT_COMPLETE \u2192 the object closed\n *\n * The v0.1 wire decoder only exposes FIELD/OBJECT; item completion is a\n * semantic projection of array boundaries that every source already carries\n * (each JSON array element, each array append), not a wire record. Strictly\n * typed against the schema \u2014 a value that violates its field type throws.\n */\nexport class JsonToStreamObjectAdapter {\n private readonly fields: Array<{ def: FieldDef; pathKey: string }>;\n private readonly parser: IncrementalJsonParser;\n private readonly events: Event[] = [];\n private readonly onFieldDone: ((field: number, value: FieldValue) => void) | undefined;\n private readonly onItemDone: ((field: number, index: number, value: ScalarValue) => void) | undefined;\n private readonly onObjectDone: (() => void) | undefined;\n\n constructor(schema: Schema, callbacks: JsonAdapterCallbacks = {}) {\n this.fields = schema.map((def, field) => ({ def, pathKey: def.path.join(\".\") }));\n this.onFieldDone = callbacks.onFieldDone;\n this.onItemDone = callbacks.onItemDone;\n this.onObjectDone = callbacks.onObjectDone;\n this.parser = new IncrementalJsonParser((path, value) => this.handleValue(path, value));\n }\n\n push(chunk: string): void {\n this.parser.push(chunk);\n }\n\n getEvents(): Event[] {\n return this.events;\n }\n\n private handleValue(path: string[], value: JsonValue): void {\n const pathKey = path.join(\".\");\n if (pathKey === \"\") {\n // The root object closed.\n this.events.push({ kind: \"object-complete\" });\n this.onObjectDone?.();\n return;\n }\n const index = this.fields.findIndex((f) => f.pathKey === pathKey);\n if (index !== -1) {\n // Whole field value (a scalar, or the completed array).\n const fieldValue = coerceToField(this.fields[index]!.def, value);\n const event: Event = { kind: \"field\", field: index, value: fieldValue };\n this.events.push(event);\n this.onFieldDone?.(index, fieldValue);\n return;\n }\n // Array element: path = fieldPath + numeric segment, field is array mode.\n if (Array.isArray(value) || (typeof value === \"object\" && value !== null)) return;\n const itemField = this.matchArrayItem(path);\n if (itemField === -1) return;\n const def = this.fields[itemField]!.def;\n const item = coerceScalar(def.type, value, def.path.join(\".\"));\n const itemIndex = Number(path[path.length - 1]);\n const itemEvent: Event = { kind: \"item\", field: itemField, index: itemIndex, value: item };\n this.events.push(itemEvent);\n this.onItemDone?.(itemField, itemIndex, item);\n }\n\n private matchArrayItem(path: string[]): number {\n if (path.length < 2) return -1;\n const last = path[path.length - 1]!;\n if (!/^\\d+$/.test(last)) return -1;\n const parentKey = path.slice(0, -1).join(\".\");\n const index = this.fields.findIndex((f) => f.pathKey === parentKey);\n if (index === -1 || this.fields[index]!.def.mode !== \"array\") return -1;\n return index;\n }\n}\n\nfunction coerceToField(def: FieldDef, value: JsonValue): FieldValue {\n if (def.mode === \"array\") {\n if (!Array.isArray(value)) {\n throw new Error(`field ${def.path.join(\".\")}: expected array, got ${JSON.stringify(value)}`);\n }\n return value.map((item) => coerceScalar(def.type, item, def.path.join(\".\")));\n }\n return coerceScalar(def.type, value, def.path.join(\".\"));\n}\n\nfunction coerceScalar(type: FieldType, value: JsonValue, label: string): ScalarValue {\n switch (type) {\n case \"string\":\n if (typeof value !== \"string\") {\n throw new Error(`field ${label}: expected string, got ${JSON.stringify(value)}`);\n }\n return value;\n case \"integer\":\n if (typeof value !== \"number\" || !Number.isSafeInteger(value)) {\n throw new Error(`field ${label}: expected integer, got ${JSON.stringify(value)}`);\n }\n return value;\n case \"number\":\n if (typeof value !== \"number\" || !Number.isFinite(value)) {\n throw new Error(`field ${label}: expected number, got ${JSON.stringify(value)}`);\n }\n return value;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n throw new Error(`field ${label}: expected boolean, got ${JSON.stringify(value)}`);\n }\n return value;\n }\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["IncrementalJsonParser", "onValueDone", "__name", "chunk", "i", "c", "ch", "isWhitespace", "top", "_top", "path", "isDigit", "v", "decodeJsonString", "n", "isTerminator", "isCompleteLiteral", "value", "text", "isLiteralPrefix", "ctx", "raw", "out", "esc", "hex", "code", "low", "lowCode", "JsonToStreamObjectAdapter", "schema", "callbacks", "def", "field", "pathKey", "index", "f", "fieldValue", "coerceToField", "event", "itemField", "item", "coerceScalar", "itemIndex", "itemEvent", "last", "parentKey", "type", "label"]
7
+ }
@@ -0,0 +1,2 @@
1
+ import{ReferenceDecoder as r,Receiver as o,decode as d,encodeText as c,executeRecords as l,materializeObject as t,parseBooleanPayload as p,parseIntegerPayload as i,parseNumberPayload as m}from"./reference.js";export{o as Receiver,r as ReferenceDecoder,d as decode,c as encodeText,l as executeRecords,t as materializeObject,p as parseBooleanPayload,i as parseIntegerPayload,m as parseNumberPayload};
2
+ //# sourceMappingURL=index.js.map