@fudrouter/fsrouter 0.6.62 → 0.6.63

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 (572) hide show
  1. package/bin/copyJs.js +0 -4
  2. package/dist/lib/oauth/constants/oauth.js +2 -2
  3. package/dist/lib/oauth/providers.js +1 -1
  4. package/dist/routes/providers/[id]/test/testUtils.js +3 -3
  5. package/dist/shared/constants/models.js +2 -2
  6. package/dist/sse/handlers/chat.js +10 -10
  7. package/dist/sse/handlers/embeddings.js +3 -3
  8. package/dist/sse/handlers/fetch.js +4 -4
  9. package/dist/sse/handlers/imageGeneration.js +5 -5
  10. package/dist/sse/handlers/search.js +4 -4
  11. package/dist/sse/handlers/stt.js +3 -3
  12. package/dist/sse/handlers/tts.js +4 -4
  13. package/dist/sse/handlers/videoGeneration.js +4 -4
  14. package/dist/sse/services/auth.js +2 -2
  15. package/dist/sse/services/model.js +1 -1
  16. package/dist/sse/services/tokenRefresh.js +3 -3
  17. package/open-sse/AGENTS.md +39 -0
  18. package/open-sse/config/anthropicHeaders.js +87 -0
  19. package/open-sse/config/appConstants.js +238 -0
  20. package/open-sse/config/audioRegistry.js +506 -0
  21. package/open-sse/config/cliFingerprints.js +304 -0
  22. package/open-sse/config/codexInstructions.js +119 -0
  23. package/open-sse/config/codexQuotaScopes.js +42 -0
  24. package/open-sse/config/constants.js +4 -0
  25. package/open-sse/config/contextEditing.js +68 -0
  26. package/open-sse/config/credentialLoader.js +83 -0
  27. package/open-sse/config/defaultThinkingSignature.js +12 -0
  28. package/open-sse/config/embeddingRegistry.js +264 -0
  29. package/open-sse/config/errorConfig.js +96 -0
  30. package/open-sse/config/freeModelCatalog.data.js +121 -0
  31. package/open-sse/config/freeModelCatalog.js +90 -0
  32. package/open-sse/config/freeTierCatalog.js +81 -0
  33. package/open-sse/config/googleTtsLanguages.js +62 -0
  34. package/open-sse/config/imageParamDefs.js +360 -0
  35. package/open-sse/config/imageRegistry.js +493 -0
  36. package/open-sse/config/kiroConstants.js +268 -0
  37. package/open-sse/config/mediaConfig.js +27 -0
  38. package/open-sse/config/mediaServiceKinds.js +71 -0
  39. package/open-sse/config/models.js +13 -0
  40. package/open-sse/config/moderationRegistry.js +71 -0
  41. package/open-sse/config/musicRegistry.js +99 -0
  42. package/open-sse/config/ocrRegistry.js +61 -0
  43. package/open-sse/config/ollamaModels.js +19 -0
  44. package/open-sse/config/providerErrorRules.js +114 -0
  45. package/open-sse/config/providerFieldStrips.js +35 -0
  46. package/open-sse/config/providerHeaderProfiles.js +191 -0
  47. package/open-sse/config/providerModels.js +106 -0
  48. package/open-sse/config/providerRegistry.js +16 -0
  49. package/open-sse/config/providers.js +19 -0
  50. package/open-sse/config/registryUtils.js +106 -0
  51. package/open-sse/config/rerankRegistry.js +177 -0
  52. package/open-sse/config/runtimeConfig.js +96 -0
  53. package/open-sse/config/runway.js +39 -0
  54. package/open-sse/config/searchRegistry.js +153 -0
  55. package/open-sse/config/toolCloaking.js +199 -0
  56. package/open-sse/config/ttsModels.js +129 -0
  57. package/open-sse/config/videoRegistry.js +207 -0
  58. package/open-sse/executors/adapta-web.js +435 -0
  59. package/open-sse/executors/antigravity.js +534 -0
  60. package/open-sse/executors/antigravityUpstreamError.js +13 -0
  61. package/open-sse/executors/auggie.js +437 -0
  62. package/open-sse/executors/azure-openai.js +32 -0
  63. package/open-sse/executors/azure.js +57 -0
  64. package/open-sse/executors/base.js +181 -0
  65. package/open-sse/executors/bedrock.js +633 -0
  66. package/open-sse/executors/blackbox-web.js +578 -0
  67. package/open-sse/executors/chatgpt-web.js +2042 -0
  68. package/open-sse/executors/chipotle.js +357 -0
  69. package/open-sse/executors/claude-web-with-auto-refresh.js +73 -0
  70. package/open-sse/executors/claude-web.js +586 -0
  71. package/open-sse/executors/cliproxyapi.js +296 -0
  72. package/open-sse/executors/cloudflare-ai.js +46 -0
  73. package/open-sse/executors/codebuddy-cn.js +40 -0
  74. package/open-sse/executors/codex.js +1324 -0
  75. package/open-sse/executors/commandCode.js +558 -0
  76. package/open-sse/executors/commandcode.js +88 -0
  77. package/open-sse/executors/copilot-m365-connection.js +176 -0
  78. package/open-sse/executors/copilot-m365-frames.js +192 -0
  79. package/open-sse/executors/copilot-m365-web.js +299 -0
  80. package/open-sse/executors/copilot-web.js +580 -0
  81. package/open-sse/executors/cursor.js +795 -0
  82. package/open-sse/executors/deepseek-web-with-auto-refresh.js +150 -0
  83. package/open-sse/executors/deepseek-web.js +875 -0
  84. package/open-sse/executors/default.js +430 -0
  85. package/open-sse/executors/devin-cli.js +351 -0
  86. package/open-sse/executors/doubao-web.js +550 -0
  87. package/open-sse/executors/duckduckgo-web.js +629 -0
  88. package/open-sse/executors/forceResponsesUpstream.js +33 -0
  89. package/open-sse/executors/gemini-business.js +320 -0
  90. package/open-sse/executors/gemini-cli.js +89 -0
  91. package/open-sse/executors/gemini-web.js +297 -0
  92. package/open-sse/executors/github.js +379 -0
  93. package/open-sse/executors/gitlab.js +569 -0
  94. package/open-sse/executors/glm.js +361 -0
  95. package/open-sse/executors/grok-cli.js +119 -0
  96. package/open-sse/executors/grok-web.js +345 -0
  97. package/open-sse/executors/huggingchat.js +467 -0
  98. package/open-sse/executors/iflow.js +108 -0
  99. package/open-sse/executors/index.js +78 -0
  100. package/open-sse/executors/inner-ai.js +527 -0
  101. package/open-sse/executors/kimchi.js +123 -0
  102. package/open-sse/executors/kimi-web.js +325 -0
  103. package/open-sse/executors/kimi.js +97 -0
  104. package/open-sse/executors/kiro.js +503 -0
  105. package/open-sse/executors/kiroThinking.js +44 -0
  106. package/open-sse/executors/lmarena.js +395 -0
  107. package/open-sse/executors/mimo-free.js +167 -0
  108. package/open-sse/executors/mimocode.js +392 -0
  109. package/open-sse/executors/ninerouter.js +155 -0
  110. package/open-sse/executors/nlpcloud.js +438 -0
  111. package/open-sse/executors/ollama-local.js +14 -0
  112. package/open-sse/executors/opencode-go.js +41 -0
  113. package/open-sse/executors/opencode.js +32 -0
  114. package/open-sse/executors/perplexity-web.js +581 -0
  115. package/open-sse/executors/poe-web.js +133 -0
  116. package/open-sse/executors/pollinations.js +95 -0
  117. package/open-sse/executors/puter.js +31 -0
  118. package/open-sse/executors/qoder.js +448 -0
  119. package/open-sse/executors/qwen-web.js +372 -0
  120. package/open-sse/executors/qwen.js +129 -0
  121. package/open-sse/executors/t3-chat-web.js +417 -0
  122. package/open-sse/executors/theoldllm.js +290 -0
  123. package/open-sse/executors/trae.js +418 -0
  124. package/open-sse/executors/v0-vercel-web.js +141 -0
  125. package/open-sse/executors/venice-web.js +142 -0
  126. package/open-sse/executors/vertex.js +131 -0
  127. package/open-sse/executors/vertexMedia.js +236 -0
  128. package/open-sse/executors/windsurf.js +560 -0
  129. package/open-sse/executors/xai.js +46 -0
  130. package/open-sse/executors/xiaomi-tokenplan.js +19 -0
  131. package/open-sse/executors/yuanbao-web.js +385 -0
  132. package/open-sse/executors/zed-hosted.js +281 -0
  133. package/open-sse/executors/zenmux-free.js +247 -0
  134. package/open-sse/handlers/audioSpeech.js +759 -0
  135. package/open-sse/handlers/audioTranscription.js +395 -0
  136. package/open-sse/handlers/audioTranslation.js +181 -0
  137. package/open-sse/handlers/chatCore/nonStreamingHandler.js +300 -0
  138. package/open-sse/handlers/chatCore/requestDetail.js +102 -0
  139. package/open-sse/handlers/chatCore/sseToJsonHandler.js +231 -0
  140. package/open-sse/handlers/chatCore/streamingHandler.js +95 -0
  141. package/open-sse/handlers/chatCore.js +404 -0
  142. package/open-sse/handlers/embeddingProviders/_base.js +4 -0
  143. package/open-sse/handlers/embeddingProviders/gemini.js +54 -0
  144. package/open-sse/handlers/embeddingProviders/index.js +23 -0
  145. package/open-sse/handlers/embeddingProviders/openai.js +39 -0
  146. package/open-sse/handlers/embeddingProviders/openaiCompatNode.js +13 -0
  147. package/open-sse/handlers/embeddingsCore.js +126 -0
  148. package/open-sse/handlers/fetch/index.js +237 -0
  149. package/open-sse/handlers/imageGenerationCore.js +230 -0
  150. package/open-sse/handlers/imageProviders/_base.js +31 -0
  151. package/open-sse/handlers/imageProviders/antigravity.js +73 -0
  152. package/open-sse/handlers/imageProviders/blackForestLabs.js +63 -0
  153. package/open-sse/handlers/imageProviders/cloudflareAi.js +207 -0
  154. package/open-sse/handlers/imageProviders/codex.js +217 -0
  155. package/open-sse/handlers/imageProviders/comfyui.js +25 -0
  156. package/open-sse/handlers/imageProviders/falAi.js +62 -0
  157. package/open-sse/handlers/imageProviders/gemini.js +40 -0
  158. package/open-sse/handlers/imageProviders/huggingface.js +38 -0
  159. package/open-sse/handlers/imageProviders/index.js +44 -0
  160. package/open-sse/handlers/imageProviders/leonardo.js +435 -0
  161. package/open-sse/handlers/imageProviders/nanobanana.js +79 -0
  162. package/open-sse/handlers/imageProviders/openai.js +61 -0
  163. package/open-sse/handlers/imageProviders/runwayml.js +72 -0
  164. package/open-sse/handlers/imageProviders/sdwebui.js +36 -0
  165. package/open-sse/handlers/imageProviders/stabilityAi.js +59 -0
  166. package/open-sse/handlers/imageProviders/weavy.js +222 -0
  167. package/open-sse/handlers/moderations.js +92 -0
  168. package/open-sse/handlers/ocr.js +89 -0
  169. package/open-sse/handlers/rerank.js +223 -0
  170. package/open-sse/handlers/responseSanitizer/reasoning.js +144 -0
  171. package/open-sse/handlers/responsesHandler.js +99 -0
  172. package/open-sse/handlers/search/callers.js +371 -0
  173. package/open-sse/handlers/search/chatSearch.js +409 -0
  174. package/open-sse/handlers/search/index.js +201 -0
  175. package/open-sse/handlers/search/normalizers.js +223 -0
  176. package/open-sse/handlers/search.js +1056 -0
  177. package/open-sse/handlers/sseParser.js +469 -0
  178. package/open-sse/handlers/sttCore.js +193 -0
  179. package/open-sse/handlers/ttsCore.js +74 -0
  180. package/open-sse/handlers/ttsProviders/_base.js +39 -0
  181. package/open-sse/handlers/ttsProviders/edgeTts.js +89 -0
  182. package/open-sse/handlers/ttsProviders/elevenlabs.js +48 -0
  183. package/open-sse/handlers/ttsProviders/gemini.js +117 -0
  184. package/open-sse/handlers/ttsProviders/genericFormats.js +169 -0
  185. package/open-sse/handlers/ttsProviders/googleTts.js +54 -0
  186. package/open-sse/handlers/ttsProviders/index.js +50 -0
  187. package/open-sse/handlers/ttsProviders/localDevice.js +87 -0
  188. package/open-sse/handlers/ttsProviders/minimax.js +59 -0
  189. package/open-sse/handlers/ttsProviders/openai.js +30 -0
  190. package/open-sse/handlers/ttsProviders/openrouter.js +70 -0
  191. package/open-sse/handlers/usageExtractor.js +93 -0
  192. package/open-sse/handlers/videoGenerationCore.js +119 -0
  193. package/open-sse/handlers/videoProviders/index.js +22 -0
  194. package/open-sse/handlers/videoProviders/leonardo.js +511 -0
  195. package/open-sse/handlers/videoProviders/weavy.js +198 -0
  196. package/open-sse/handlers/webFetch.js +200 -0
  197. package/open-sse/index.js +79 -0
  198. package/open-sse/mcp-server/audit.js +283 -0
  199. package/open-sse/mcp-server/catalog.js +184 -0
  200. package/open-sse/mcp-server/descriptionCompressor.js +175 -0
  201. package/open-sse/mcp-server/httpAuthContext.js +32 -0
  202. package/open-sse/mcp-server/httpTransport.js +229 -0
  203. package/open-sse/mcp-server/index.js +33 -0
  204. package/open-sse/mcp-server/mcpCallerIdentity.js +21 -0
  205. package/open-sse/mcp-server/runtimeHeartbeat.js +110 -0
  206. package/open-sse/mcp-server/schemas/a2a.js +153 -0
  207. package/open-sse/mcp-server/schemas/audit.js +17 -0
  208. package/open-sse/mcp-server/schemas/index.js +166 -0
  209. package/open-sse/mcp-server/schemas/pickFastestModel.js +90 -0
  210. package/open-sse/mcp-server/schemas/toolDefinition.js +0 -0
  211. package/open-sse/mcp-server/schemas/toolSearch.js +32 -0
  212. package/open-sse/mcp-server/schemas/tools.js +1219 -0
  213. package/open-sse/mcp-server/scopeEnforcement.js +80 -0
  214. package/open-sse/mcp-server/server.js +1168 -0
  215. package/open-sse/mcp-server/toolCardinality.js +78 -0
  216. package/open-sse/mcp-server/toolSearch/catalog.js +66 -0
  217. package/open-sse/mcp-server/toolSearch/handler.js +20 -0
  218. package/open-sse/mcp-server/toolSearch/index.js +10 -0
  219. package/open-sse/mcp-server/toolSearch/register.js +21 -0
  220. package/open-sse/mcp-server/toolSearch/search.js +55 -0
  221. package/open-sse/mcp-server/toolSearch/signature.js +67 -0
  222. package/open-sse/mcp-server/tools/advancedTools.js +893 -0
  223. package/open-sse/mcp-server/tools/agentSkillTools.js +69 -0
  224. package/open-sse/mcp-server/tools/compressionTools.js +309 -0
  225. package/open-sse/mcp-server/tools/gamificationTools.js +134 -0
  226. package/open-sse/mcp-server/tools/githubSkillTools.js +102 -0
  227. package/open-sse/mcp-server/tools/memoryTools.js +118 -0
  228. package/open-sse/mcp-server/tools/notionTools.js +101 -0
  229. package/open-sse/mcp-server/tools/obsidianTools.js +307 -0
  230. package/open-sse/mcp-server/tools/pickFastestModel.js +223 -0
  231. package/open-sse/mcp-server/tools/pluginTools.js +181 -0
  232. package/open-sse/mcp-server/tools/poolTools.js +139 -0
  233. package/open-sse/mcp-server/tools/skillTools.js +113 -0
  234. package/open-sse/package.json +10 -0
  235. package/open-sse/providers/REGISTRY_TEMPLATE.js +98 -0
  236. package/open-sse/providers/capabilities.js +293 -0
  237. package/open-sse/providers/index.js +51 -0
  238. package/open-sse/providers/models/helpers.js +20 -0
  239. package/open-sse/providers/models/namePatterns.js +33 -0
  240. package/open-sse/providers/models/schema.js +40 -0
  241. package/open-sse/providers/pricing.js +308 -0
  242. package/open-sse/providers/registry/alicode-intl.js +30 -0
  243. package/open-sse/providers/registry/alicode.js +31 -0
  244. package/open-sse/providers/registry/anthropic.js +37 -0
  245. package/open-sse/providers/registry/antigravity.js +81 -0
  246. package/open-sse/providers/registry/assemblyai.js +38 -0
  247. package/open-sse/providers/registry/aws-polly.js +45 -0
  248. package/open-sse/providers/registry/azure.js +21 -0
  249. package/open-sse/providers/registry/black-forest-labs.js +32 -0
  250. package/open-sse/providers/registry/blackbox.js +41 -0
  251. package/open-sse/providers/registry/brave-search.js +35 -0
  252. package/open-sse/providers/registry/byteplus.js +35 -0
  253. package/open-sse/providers/registry/cartesia.js +36 -0
  254. package/open-sse/providers/registry/cerebras.js +31 -0
  255. package/open-sse/providers/registry/chutes.js +24 -0
  256. package/open-sse/providers/registry/claude.js +87 -0
  257. package/open-sse/providers/registry/cline.js +51 -0
  258. package/open-sse/providers/registry/clinepass.js +57 -0
  259. package/open-sse/providers/registry/cloudflare-ai.js +55 -0
  260. package/open-sse/providers/registry/codebuddy-cn.js +77 -0
  261. package/open-sse/providers/registry/codex.js +100 -0
  262. package/open-sse/providers/registry/cohere.js +27 -0
  263. package/open-sse/providers/registry/comfyui.js +20 -0
  264. package/open-sse/providers/registry/commandcode.js +43 -0
  265. package/open-sse/providers/registry/coqui.js +30 -0
  266. package/open-sse/providers/registry/cursor.js +58 -0
  267. package/open-sse/providers/registry/deepgram.js +33 -0
  268. package/open-sse/providers/registry/deepseek.js +51 -0
  269. package/open-sse/providers/registry/digitalocean.js +92 -0
  270. package/open-sse/providers/registry/edge-tts.js +24 -0
  271. package/open-sse/providers/registry/elevenlabs.js +35 -0
  272. package/open-sse/providers/registry/exa.js +50 -0
  273. package/open-sse/providers/registry/fal-ai.js +34 -0
  274. package/open-sse/providers/registry/featherless.js +34 -0
  275. package/open-sse/providers/registry/firecrawl.js +34 -0
  276. package/open-sse/providers/registry/fireworks.js +29 -0
  277. package/open-sse/providers/registry/gemini-cli.js +58 -0
  278. package/open-sse/providers/registry/gemini.js +83 -0
  279. package/open-sse/providers/registry/github.js +88 -0
  280. package/open-sse/providers/registry/gitlab.js +32 -0
  281. package/open-sse/providers/registry/glm-cn.js +35 -0
  282. package/open-sse/providers/registry/glm.js +58 -0
  283. package/open-sse/providers/registry/google-pse.js +35 -0
  284. package/open-sse/providers/registry/google-tts.js +24 -0
  285. package/open-sse/providers/registry/grok-cli.js +86 -0
  286. package/open-sse/providers/registry/grok-web.js +39 -0
  287. package/open-sse/providers/registry/groq.js +37 -0
  288. package/open-sse/providers/registry/huggingface.js +34 -0
  289. package/open-sse/providers/registry/hyperbolic.js +35 -0
  290. package/open-sse/providers/registry/ibm-bob.js +27 -0
  291. package/open-sse/providers/registry/iflow.js +52 -0
  292. package/open-sse/providers/registry/index.js +210 -0
  293. package/open-sse/providers/registry/inworld.js +36 -0
  294. package/open-sse/providers/registry/jina-ai.js +42 -0
  295. package/open-sse/providers/registry/jina-reader.js +34 -0
  296. package/open-sse/providers/registry/kilocode.js +51 -0
  297. package/open-sse/providers/registry/kimchi.js +49 -0
  298. package/open-sse/providers/registry/kimi-coding.js +65 -0
  299. package/open-sse/providers/registry/kimi.js +56 -0
  300. package/open-sse/providers/registry/kiro.js +115 -0
  301. package/open-sse/providers/registry/linkup.js +34 -0
  302. package/open-sse/providers/registry/local-device.js +24 -0
  303. package/open-sse/providers/registry/mimo-free.js +24 -0
  304. package/open-sse/providers/registry/minimax-cn.js +76 -0
  305. package/open-sse/providers/registry/minimax.js +83 -0
  306. package/open-sse/providers/registry/mistral.js +34 -0
  307. package/open-sse/providers/registry/mmf.js +19 -0
  308. package/open-sse/providers/registry/nanobanana.js +35 -0
  309. package/open-sse/providers/registry/nebius.js +27 -0
  310. package/open-sse/providers/registry/nvidia.js +43 -0
  311. package/open-sse/providers/registry/ollama-local.js +19 -0
  312. package/open-sse/providers/registry/ollama.js +36 -0
  313. package/open-sse/providers/registry/openai.js +84 -0
  314. package/open-sse/providers/registry/opencode-go.js +41 -0
  315. package/open-sse/providers/registry/opencode.js +25 -0
  316. package/open-sse/providers/registry/openrouter.js +60 -0
  317. package/open-sse/providers/registry/perplexity-agent.js +49 -0
  318. package/open-sse/providers/registry/perplexity-web.js +33 -0
  319. package/open-sse/providers/registry/perplexity.js +37 -0
  320. package/open-sse/providers/registry/playht.js +36 -0
  321. package/open-sse/providers/registry/qoder.js +54 -0
  322. package/open-sse/providers/registry/qwen.js +33 -0
  323. package/open-sse/providers/registry/recraft.js +24 -0
  324. package/open-sse/providers/registry/runwayml.js +30 -0
  325. package/open-sse/providers/registry/sdwebui.js +20 -0
  326. package/open-sse/providers/registry/searchapi.js +35 -0
  327. package/open-sse/providers/registry/searxng.js +35 -0
  328. package/open-sse/providers/registry/serper.js +35 -0
  329. package/open-sse/providers/registry/siliconflow.js +39 -0
  330. package/open-sse/providers/registry/stability-ai.js +31 -0
  331. package/open-sse/providers/registry/tavily.js +50 -0
  332. package/open-sse/providers/registry/together.js +31 -0
  333. package/open-sse/providers/registry/topaz.js +19 -0
  334. package/open-sse/providers/registry/tortoise.js +30 -0
  335. package/open-sse/providers/registry/venice.js +56 -0
  336. package/open-sse/providers/registry/vercel-ai-gateway.js +41 -0
  337. package/open-sse/providers/registry/vertex-partner.js +29 -0
  338. package/open-sse/providers/registry/vertex.js +32 -0
  339. package/open-sse/providers/registry/volcengine-ark.js +35 -0
  340. package/open-sse/providers/registry/voyage-ai.js +30 -0
  341. package/open-sse/providers/registry/xai.js +45 -0
  342. package/open-sse/providers/registry/xiaomi-mimo.js +46 -0
  343. package/open-sse/providers/registry/xiaomi-tokenplan.js +63 -0
  344. package/open-sse/providers/registry/youcom.js +35 -0
  345. package/open-sse/providers/schema.js +76 -0
  346. package/open-sse/providers/shared.js +74 -0
  347. package/open-sse/providers/thinkingLevels.js +48 -0
  348. package/open-sse/rtk/applyFilter.js +15 -0
  349. package/open-sse/rtk/autodetect.js +111 -0
  350. package/open-sse/rtk/caveman.js +100 -0
  351. package/open-sse/rtk/cavemanPrompts.js +78 -0
  352. package/open-sse/rtk/constants.js +55 -0
  353. package/open-sse/rtk/filters/buildOutput.js +127 -0
  354. package/open-sse/rtk/filters/dedupLog.js +44 -0
  355. package/open-sse/rtk/filters/find.js +50 -0
  356. package/open-sse/rtk/filters/gitDiff.js +92 -0
  357. package/open-sse/rtk/filters/gitLog.js +99 -0
  358. package/open-sse/rtk/filters/gitStatus.js +117 -0
  359. package/open-sse/rtk/filters/grep.js +48 -0
  360. package/open-sse/rtk/filters/ls.js +79 -0
  361. package/open-sse/rtk/filters/readNumbered.js +27 -0
  362. package/open-sse/rtk/filters/searchList.js +52 -0
  363. package/open-sse/rtk/filters/smartTruncate.js +15 -0
  364. package/open-sse/rtk/filters/tree.js +32 -0
  365. package/open-sse/rtk/headroom.js +215 -0
  366. package/open-sse/rtk/index.js +155 -0
  367. package/open-sse/rtk/paleo.js +47 -0
  368. package/open-sse/rtk/ponytail.js +9 -0
  369. package/open-sse/rtk/ponytailPrompt.js +52 -0
  370. package/open-sse/rtk/ponytailPrompts.js +76 -0
  371. package/open-sse/rtk/pxpipe.js +104 -0
  372. package/open-sse/rtk/registry.js +38 -0
  373. package/open-sse/rtk/systemInject.js +98 -0
  374. package/open-sse/services/accountFallback.js +323 -0
  375. package/open-sse/services/antigravityHeaders.js +91 -0
  376. package/open-sse/services/antigravityVersion.js +154 -0
  377. package/open-sse/services/clinepassModels.js +63 -0
  378. package/open-sse/services/codexQuotaFetcher.js +312 -0
  379. package/open-sse/services/codexVerbosity.js +30 -0
  380. package/open-sse/services/combo/context.js +12 -0
  381. package/open-sse/services/combo/fingerprintExpansion.js +51 -0
  382. package/open-sse/services/combo/headroomRanking.js +28 -0
  383. package/open-sse/services/combo/quotaExhaustionCutoff.js +87 -0
  384. package/open-sse/services/combo/quotaScoring.js +226 -0
  385. package/open-sse/services/combo/quotaShareConcurrency.js +25 -0
  386. package/open-sse/services/combo/quotaShareInflight.js +50 -0
  387. package/open-sse/services/combo/quotaShareStrategy.js +130 -0
  388. package/open-sse/services/combo/resolveAutoStrategy.js +231 -0
  389. package/open-sse/services/combo/responseValidation.js +140 -0
  390. package/open-sse/services/combo/runtimeUnits.js +195 -0
  391. package/open-sse/services/combo/sessionStickiness.js +128 -0
  392. package/open-sse/services/combo/shadowRouting.js +131 -0
  393. package/open-sse/services/combo/targetExhaustion.js +75 -0
  394. package/open-sse/services/combo/targetSorters.js +126 -0
  395. package/open-sse/services/combo/targetTimeoutRunner.js +65 -0
  396. package/open-sse/services/combo/validateQuality.js +271 -0
  397. package/open-sse/services/combo.js +571 -0
  398. package/open-sse/services/comboAgentMiddleware.js +91 -0
  399. package/open-sse/services/comboConfig.js +153 -0
  400. package/open-sse/services/comboManifestMetrics.js +10 -0
  401. package/open-sse/services/comboMetrics.js +278 -0
  402. package/open-sse/services/compact.js +71 -0
  403. package/open-sse/services/copilotModels.js +155 -0
  404. package/open-sse/services/emergencyFallback.js +109 -0
  405. package/open-sse/services/errorClassifier.js +190 -0
  406. package/open-sse/services/evalRouting.js +201 -0
  407. package/open-sse/services/freeWebSearch.js +4 -0
  408. package/open-sse/services/fusion.js +223 -0
  409. package/open-sse/services/intentClassifier.js +631 -0
  410. package/open-sse/services/kimchiModels.js +176 -0
  411. package/open-sse/services/kiroModels.js +332 -0
  412. package/open-sse/services/model.js +136 -0
  413. package/open-sse/services/modelCapabilities.js +5 -0
  414. package/open-sse/services/modelDeprecation.js +101 -0
  415. package/open-sse/services/modelFamilyFallback.js +166 -0
  416. package/open-sse/services/oauthCredentialManager.js +156 -0
  417. package/open-sse/services/pipeline.js +107 -0
  418. package/open-sse/services/projectId.js +306 -0
  419. package/open-sse/services/provider.js +169 -0
  420. package/open-sse/services/providerCooldownTracker.js +94 -0
  421. package/open-sse/services/providerDefaultRateLimit.js +11 -0
  422. package/open-sse/services/proxyAutoSelector.js +24 -0
  423. package/open-sse/services/qoderModels.js +214 -0
  424. package/open-sse/services/quotaFetchThrottle.js +103 -0
  425. package/open-sse/services/quotaMonitor.js +310 -0
  426. package/open-sse/services/quotaPreflight.js +266 -0
  427. package/open-sse/services/rateLimitManager/headers.js +56 -0
  428. package/open-sse/services/rateLimitManager.js +616 -0
  429. package/open-sse/services/rateLimitSemaphore.js +112 -0
  430. package/open-sse/services/responsesInputSanitizer.js +99 -0
  431. package/open-sse/services/slidingWindowLimiter.js +44 -0
  432. package/open-sse/services/streamRecovery.js +355 -0
  433. package/open-sse/services/taskAwareRouter.js +227 -0
  434. package/open-sse/services/taskAwareRouting.js +332 -0
  435. package/open-sse/services/thinkingBudget.js +247 -0
  436. package/open-sse/services/tokenRefresh/dedup.js +31 -0
  437. package/open-sse/services/tokenRefresh/providers.js +624 -0
  438. package/open-sse/services/tokenRefresh.js +254 -0
  439. package/open-sse/services/usage/claude.js +147 -0
  440. package/open-sse/services/usage/codebuddy-cn.js +138 -0
  441. package/open-sse/services/usage/codex.js +267 -0
  442. package/open-sse/services/usage/github.js +100 -0
  443. package/open-sse/services/usage/google.js +241 -0
  444. package/open-sse/services/usage/grok-cli.js +274 -0
  445. package/open-sse/services/usage/kiro.js +188 -0
  446. package/open-sse/services/usage/minimax.js +234 -0
  447. package/open-sse/services/usage/misc.js +288 -0
  448. package/open-sse/services/usage/shared.js +70 -0
  449. package/open-sse/services/usage.js +62 -0
  450. package/open-sse/services/usageDb.js +2 -0
  451. package/open-sse/services/webSearchRouting.js +25 -0
  452. package/open-sse/services/wildcardRouter.js +59 -0
  453. package/open-sse/shared/clineAuth.js +37 -0
  454. package/open-sse/shared/machineId.js +19 -0
  455. package/open-sse/shared/qoder/constants.js +64 -0
  456. package/open-sse/shared/qoder/cosy.js +175 -0
  457. package/open-sse/shared/qoder/encoding.js +55 -0
  458. package/open-sse/transformer/responsesTransformer.js +439 -0
  459. package/open-sse/transformer/streamToJsonConverter.js +103 -0
  460. package/open-sse/translator/concerns/chunk.js +11 -0
  461. package/open-sse/translator/concerns/finishReason.js +63 -0
  462. package/open-sse/translator/concerns/image.js +124 -0
  463. package/open-sse/translator/concerns/json.js +5 -0
  464. package/open-sse/translator/concerns/message.js +7 -0
  465. package/open-sse/translator/concerns/modality.js +155 -0
  466. package/open-sse/translator/concerns/paramSupport.js +75 -0
  467. package/open-sse/translator/concerns/prefetch.js +96 -0
  468. package/open-sse/translator/concerns/reasoning.js +24 -0
  469. package/open-sse/translator/concerns/thinking.js +53 -0
  470. package/open-sse/translator/concerns/thinkingUnified.js +329 -0
  471. package/open-sse/translator/concerns/toolCall.js +153 -0
  472. package/open-sse/translator/concerns/usage.js +69 -0
  473. package/open-sse/translator/formats/claude.js +368 -0
  474. package/open-sse/translator/formats/gemini.js +380 -0
  475. package/open-sse/translator/formats/maxTokens.js +34 -0
  476. package/open-sse/translator/formats/openai.js +134 -0
  477. package/open-sse/translator/formats/responsesApi.js +141 -0
  478. package/open-sse/translator/formats.js +36 -0
  479. package/open-sse/translator/helpers/claudeHelper.js +216 -0
  480. package/open-sse/translator/helpers/geminiHelper.js +372 -0
  481. package/open-sse/translator/helpers/imageHelper.js +34 -0
  482. package/open-sse/translator/helpers/maxTokensHelper.js +27 -0
  483. package/open-sse/translator/helpers/openaiHelper.js +130 -0
  484. package/open-sse/translator/helpers/responsesApiHelper.js +139 -0
  485. package/open-sse/translator/helpers/toolCallHelper.js +148 -0
  486. package/open-sse/translator/index.js +252 -0
  487. package/open-sse/translator/request/antigravity-to-openai.js +242 -0
  488. package/open-sse/translator/request/claude-to-kiro.js +478 -0
  489. package/open-sse/translator/request/claude-to-openai.js +263 -0
  490. package/open-sse/translator/request/gemini-to-openai.js +152 -0
  491. package/open-sse/translator/request/openai-responses.js +385 -0
  492. package/open-sse/translator/request/openai-to-claude.js +383 -0
  493. package/open-sse/translator/request/openai-to-commandcode.js +176 -0
  494. package/open-sse/translator/request/openai-to-cursor.js +185 -0
  495. package/open-sse/translator/request/openai-to-gemini.js +442 -0
  496. package/open-sse/translator/request/openai-to-kiro.js +607 -0
  497. package/open-sse/translator/request/openai-to-kiro.old.js +278 -0
  498. package/open-sse/translator/request/openai-to-ollama.js +195 -0
  499. package/open-sse/translator/request/openai-to-vertex.js +42 -0
  500. package/open-sse/translator/response/claude-to-openai.js +195 -0
  501. package/open-sse/translator/response/commandcode-to-openai.js +188 -0
  502. package/open-sse/translator/response/cursor-to-openai.js +30 -0
  503. package/open-sse/translator/response/gemini-to-openai.js +147 -0
  504. package/open-sse/translator/response/kiro-to-claude.js +261 -0
  505. package/open-sse/translator/response/kiro-to-openai.js +160 -0
  506. package/open-sse/translator/response/ollama-to-openai.js +134 -0
  507. package/open-sse/translator/response/openai-responses.js +535 -0
  508. package/open-sse/translator/response/openai-to-antigravity.js +123 -0
  509. package/open-sse/translator/response/openai-to-claude.js +265 -0
  510. package/open-sse/translator/schema/blocks.js +43 -0
  511. package/open-sse/translator/schema/defaults.js +7 -0
  512. package/open-sse/translator/schema/finishReasons.js +27 -0
  513. package/open-sse/translator/schema/index.js +8 -0
  514. package/open-sse/translator/schema/roles.js +16 -0
  515. package/open-sse/utils/agentGoalPolicy.js +85 -0
  516. package/open-sse/utils/aiSdkCompat.js +75 -0
  517. package/open-sse/utils/bypassHandler.js +298 -0
  518. package/open-sse/utils/cacheControlPolicy.js +189 -0
  519. package/open-sse/utils/claudeCloaking.js +155 -0
  520. package/open-sse/utils/claudeHeaderCache.js +70 -0
  521. package/open-sse/utils/claudeSignature.js +41 -0
  522. package/open-sse/utils/clientDetector.js +63 -0
  523. package/open-sse/utils/codebuddySanitizer.js +96 -0
  524. package/open-sse/utils/composerToolCalls.js +158 -0
  525. package/open-sse/utils/cors.js +13 -0
  526. package/open-sse/utils/cursorChecksum.js +149 -0
  527. package/open-sse/utils/cursorImages.js +224 -0
  528. package/open-sse/utils/cursorProtobuf.js +904 -0
  529. package/open-sse/utils/cursorVersionDetector.js +55 -0
  530. package/open-sse/utils/debugLog.js +14 -0
  531. package/open-sse/utils/diagnostics.js +143 -0
  532. package/open-sse/utils/error.js +164 -0
  533. package/open-sse/utils/estimateSize.js +33 -0
  534. package/open-sse/utils/finishReason.js +32 -0
  535. package/open-sse/utils/flattenToolHistory.js +68 -0
  536. package/open-sse/utils/headers.js +36 -0
  537. package/open-sse/utils/heapPressure.js +35 -0
  538. package/open-sse/utils/jsonToSse.js +125 -0
  539. package/open-sse/utils/keepaliveThreshold.js +40 -0
  540. package/open-sse/utils/logger.js +104 -0
  541. package/open-sse/utils/noThinkingAlias.js +74 -0
  542. package/open-sse/utils/number.js +7 -0
  543. package/open-sse/utils/ollamaTransform.js +85 -0
  544. package/open-sse/utils/opencodeHeaders.js +47 -0
  545. package/open-sse/utils/progressTracker.js +81 -0
  546. package/open-sse/utils/providerRequestLogging.js +219 -0
  547. package/open-sse/utils/proxyFetch.js +369 -0
  548. package/open-sse/utils/reasoningContentInjector.js +79 -0
  549. package/open-sse/utils/reasoningFields.js +70 -0
  550. package/open-sse/utils/requestLogger.js +260 -0
  551. package/open-sse/utils/responsesInputNormalization.js +92 -0
  552. package/open-sse/utils/responsesStatePolicy.js +52 -0
  553. package/open-sse/utils/responsesStreamHelpers.js +49 -0
  554. package/open-sse/utils/sessionManager.js +240 -0
  555. package/open-sse/utils/sleep.js +6 -0
  556. package/open-sse/utils/sse.js +14 -0
  557. package/open-sse/utils/sseConstants.js +23 -0
  558. package/open-sse/utils/sseHeartbeat.js +104 -0
  559. package/open-sse/utils/stream.js +517 -0
  560. package/open-sse/utils/streamFailureFinalization.js +134 -0
  561. package/open-sse/utils/streamHandler.js +250 -0
  562. package/open-sse/utils/streamHelpers.js +137 -0
  563. package/open-sse/utils/textualToolCall.js +104 -0
  564. package/open-sse/utils/thinkCloseMarker.js +72 -0
  565. package/open-sse/utils/thinkTagParser.js +146 -0
  566. package/open-sse/utils/toolCallArguments.js +12 -0
  567. package/open-sse/utils/toolDeduper.js +49 -0
  568. package/open-sse/utils/toolSources.js +31 -0
  569. package/open-sse/utils/upstreamResponseHeaders.js +25 -0
  570. package/open-sse/utils/urlSanitize.js +15 -0
  571. package/open-sse/utils/usageTracking.js +347 -0
  572. package/package.json +1 -1
@@ -0,0 +1,395 @@
1
+ /**
2
+ * Audio Transcription Handler
3
+ *
4
+ * Handles POST /v1/audio/transcriptions (Whisper API format).
5
+ * Proxies multipart/form-data to upstream providers.
6
+ *
7
+ * Supported provider formats:
8
+ * - OpenAI/Groq/Qwen3: standard multipart form-data proxy
9
+ * - Deepgram: raw binary audio POST with model via query param
10
+ * - AssemblyAI: async workflow (upload → submit → poll)
11
+ * - Nvidia NIM: multipart POST, transform response to { text }
12
+ * - HuggingFace Inference: POST raw binary to /models/{model_id}
13
+ *
14
+ * Converted from OmniRoute TypeScript.
15
+ */
16
+
17
+ import { Buffer } from "node:buffer";
18
+ import { errorResponse } from "../utils/error.js";
19
+
20
+ const CORS_HEADERS = {
21
+ "Access-Control-Allow-Origin": "*",
22
+ "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
23
+ "Access-Control-Allow-Headers": "Content-Type, Authorization",
24
+ };
25
+
26
+ // ── Registry stubs ──────────────────────────────────────────────────
27
+ function parseTranscriptionModel(model) {
28
+ if (model.includes("/")) {
29
+ const [provider, ...rest] = model.split("/");
30
+ return { provider, model: rest.join("/") };
31
+ }
32
+ return { provider: "openai", model };
33
+ }
34
+
35
+ const TRANSCRIPTION_PROVIDERS = {
36
+ openai: { id: "openai", baseUrl: "https://api.openai.com/v1/audio/transcriptions", authType: "bearer", authHeader: "bearer", format: "openai" },
37
+ groq: { id: "groq", baseUrl: "https://api.groq.com/openai/v1/audio/transcriptions", authType: "bearer", authHeader: "bearer", format: "openai" },
38
+ deepgram: { id: "deepgram", baseUrl: "https://api.deepgram.com/v1/listen", authType: "token", authHeader: "token", format: "deepgram" },
39
+ assemblyai: { id: "assemblyai", baseUrl: "https://api.assemblyai.com/v2/transcript", authType: "bearer", authHeader: "bearer", format: "assemblyai" },
40
+ nvidia: { id: "nvidia", baseUrl: "https://integrate.api.nvidia.com/v1/audio/transcriptions", authType: "bearer", authHeader: "bearer", format: "nvidia-asr" },
41
+ huggingface: { id: "huggingface", baseUrl: "https://api-inference.huggingface.co/models", authType: "bearer", authHeader: "bearer", format: "huggingface-asr" },
42
+ };
43
+
44
+ function getTranscriptionProvider(providerId) {
45
+ return TRANSCRIPTION_PROVIDERS[providerId] || null;
46
+ }
47
+
48
+ function buildAuthHeaders(providerConfig, token) {
49
+ if (!token) return {};
50
+ if (providerConfig.authHeader === "bearer") return { Authorization: `Bearer ${token}` };
51
+ if (providerConfig.authHeader === "token") return { Authorization: `Token ${token}` };
52
+ return { Authorization: `Bearer ${token}` };
53
+ }
54
+
55
+ // ── Helpers ──────────────────────────────────────────────────────────
56
+
57
+ function upstreamErrorResponse(res, errText) {
58
+ // Always return JSON so the client can parse the error reliably
59
+ let errorMessage;
60
+ try {
61
+ const parsed = JSON.parse(errText);
62
+ const raw =
63
+ parsed?.err_msg ||
64
+ parsed?.error?.message ||
65
+ (typeof parsed?.error === "string" ? parsed.error : null) ||
66
+ parsed?.message ||
67
+ (typeof parsed?.detail === "string" ? parsed.detail : parsed?.detail?.message) ||
68
+ null;
69
+ errorMessage = raw ? String(raw) : errText || `Upstream error (${res.status})`;
70
+ } catch {
71
+ errorMessage = errText || `Upstream error (${res.status})`;
72
+ }
73
+
74
+ return Response.json(
75
+ { error: { message: errorMessage, code: res.status } },
76
+ {
77
+ status: res.status,
78
+ headers: { ...CORS_HEADERS },
79
+ }
80
+ );
81
+ }
82
+
83
+ function isValidPathSegment(segment) {
84
+ return !segment.includes("..") && !segment.includes("//");
85
+ }
86
+
87
+ function getUploadedFileName(file) {
88
+ return typeof file.name === "string" && file.name.length > 0 ? file.name : "audio.wav";
89
+ }
90
+
91
+ export async function buildMultipartBody(file, fields) {
92
+ const boundary = "----OmniRouteAudioBoundary" + Date.now().toString(36);
93
+ const parts = [];
94
+ const encoder = new TextEncoder();
95
+
96
+ for (const [name, value] of Object.entries(fields)) {
97
+ parts.push(
98
+ encoder.encode(
99
+ `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`
100
+ )
101
+ );
102
+ }
103
+
104
+ const fileName = getUploadedFileName(file)
105
+ .replace(/["]/g, "_")
106
+ .replace(/[\r\n]/g, "_");
107
+ const fileBytes = new Uint8Array(await file.arrayBuffer());
108
+ parts.push(
109
+ encoder.encode(
110
+ `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${fileName}"\r\nContent-Type: ${file.type || "application/octet-stream"}\r\n\r\n`
111
+ )
112
+ );
113
+ parts.push(fileBytes);
114
+ parts.push(encoder.encode(`\r\n--${boundary}--\r\n`));
115
+
116
+ const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
117
+ const body = new Uint8Array(totalLength);
118
+ let offset = 0;
119
+ for (const part of parts) {
120
+ body.set(part, offset);
121
+ offset += part.length;
122
+ }
123
+
124
+ return { body, contentType: "multipart/form-data; boundary=" + boundary };
125
+ }
126
+
127
+ function resolveAudioContentType(file) {
128
+ const browserType = (file.type || "").toLowerCase();
129
+ const fileName = typeof file.name === "string" ? file.name.toLowerCase() : "";
130
+
131
+ if (browserType.startsWith("audio/")) return browserType;
132
+
133
+ const ext = fileName.includes(".") ? fileName.split(".").pop() : "";
134
+ const EXT_TO_MIME = {
135
+ mp3: "audio/mpeg",
136
+ mp4: "audio/mp4",
137
+ m4a: "audio/mp4",
138
+ wav: "audio/wav",
139
+ ogg: "audio/ogg",
140
+ flac: "audio/flac",
141
+ webm: "audio/webm",
142
+ aac: "audio/aac",
143
+ wma: "audio/x-ms-wma",
144
+ opus: "audio/opus",
145
+ };
146
+ if (ext && EXT_TO_MIME[ext]) return EXT_TO_MIME[ext];
147
+
148
+ return "application/octet-stream";
149
+ }
150
+
151
+ // ── Provider-specific handlers ───────────────────────────────────────
152
+
153
+ async function handleDeepgramTranscription(providerConfig, file, modelId, token, formData) {
154
+ const url = new URL(providerConfig.baseUrl);
155
+ url.searchParams.set("model", modelId);
156
+ url.searchParams.set("smart_format", "true");
157
+ url.searchParams.set("punctuate", "true");
158
+
159
+ const langParam = formData?.get("language");
160
+ if (typeof langParam === "string" && langParam.trim()) {
161
+ url.searchParams.set("language", langParam.trim());
162
+ } else {
163
+ url.searchParams.set("detect_language", "true");
164
+ }
165
+
166
+ const arrayBuffer = await file.arrayBuffer();
167
+
168
+ const res = await fetch(url.toString(), {
169
+ method: "POST",
170
+ headers: {
171
+ ...buildAuthHeaders(providerConfig, token),
172
+ "Content-Type": resolveAudioContentType(file),
173
+ },
174
+ body: arrayBuffer,
175
+ });
176
+
177
+ if (!res.ok) {
178
+ return upstreamErrorResponse(res, await res.text());
179
+ }
180
+
181
+ const data = await res.json();
182
+ const text = data.results?.channels?.[0]?.alternatives?.[0]?.transcript ?? null;
183
+
184
+ return Response.json(
185
+ { text: text ?? "", noSpeechDetected: text === null || text === "" },
186
+ { headers: { ...CORS_HEADERS } }
187
+ );
188
+ }
189
+
190
+ async function handleAssemblyAITranscription(providerConfig, file, modelId, token) {
191
+ const authHeaders = buildAuthHeaders(providerConfig, token);
192
+
193
+ const arrayBuffer = await file.arrayBuffer();
194
+ const uploadRes = await fetch("https://api.assemblyai.com/v2/upload", {
195
+ method: "POST",
196
+ headers: { ...authHeaders, "Content-Type": "application/octet-stream" },
197
+ body: arrayBuffer,
198
+ });
199
+
200
+ if (!uploadRes.ok) {
201
+ return upstreamErrorResponse(uploadRes, await uploadRes.text());
202
+ }
203
+
204
+ const { upload_url } = await uploadRes.json();
205
+
206
+ const submitRes = await fetch(providerConfig.baseUrl, {
207
+ method: "POST",
208
+ headers: { ...authHeaders, "Content-Type": "application/json" },
209
+ body: JSON.stringify({
210
+ audio_url: upload_url,
211
+ speech_models: [modelId],
212
+ language_detection: true,
213
+ }),
214
+ });
215
+
216
+ if (!submitRes.ok) {
217
+ return upstreamErrorResponse(submitRes, await submitRes.text());
218
+ }
219
+
220
+ const { id: transcriptId } = await submitRes.json();
221
+
222
+ const pollUrl = `${providerConfig.baseUrl}/${transcriptId}`;
223
+ const maxWait = 120_000;
224
+ const start = Date.now();
225
+
226
+ while (Date.now() - start < maxWait) {
227
+ await new Promise((r) => setTimeout(r, 2000));
228
+
229
+ const pollRes = await fetch(pollUrl, { headers: authHeaders });
230
+ if (!pollRes.ok) continue;
231
+
232
+ const result = await pollRes.json();
233
+
234
+ if (result.status === "completed") {
235
+ return Response.json({ text: result.text || "" }, { headers: { ...CORS_HEADERS } });
236
+ }
237
+
238
+ if (result.status === "error") {
239
+ return errorResponse(500, result.error || "AssemblyAI transcription failed");
240
+ }
241
+ }
242
+
243
+ return errorResponse(504, "AssemblyAI transcription timed out after 120s");
244
+ }
245
+
246
+ async function handleNvidiaTranscription(providerConfig, file, modelId, token) {
247
+ const { body, contentType } = await buildMultipartBody(file, { model: modelId });
248
+
249
+ const res = await fetch(providerConfig.baseUrl, {
250
+ method: "POST",
251
+ headers: { ...buildAuthHeaders(providerConfig, token), "Content-Type": contentType },
252
+ body,
253
+ });
254
+
255
+ if (!res.ok) {
256
+ return upstreamErrorResponse(res, await res.text());
257
+ }
258
+
259
+ const data = await res.json();
260
+ const text = data.text || data.transcript || "";
261
+
262
+ return Response.json({ text }, { headers: { ...CORS_HEADERS } });
263
+ }
264
+
265
+ async function handleHuggingFaceTranscription(providerConfig, file, modelId, token) {
266
+ if (!isValidPathSegment(modelId)) {
267
+ return errorResponse(400, "Invalid model ID");
268
+ }
269
+ const url = `${providerConfig.baseUrl}/${modelId}`;
270
+ const arrayBuffer = await file.arrayBuffer();
271
+
272
+ const res = await fetch(url, {
273
+ method: "POST",
274
+ headers: {
275
+ ...buildAuthHeaders(providerConfig, token),
276
+ "Content-Type": resolveAudioContentType(file),
277
+ },
278
+ body: arrayBuffer,
279
+ });
280
+
281
+ if (!res.ok) {
282
+ return upstreamErrorResponse(res, await res.text());
283
+ }
284
+
285
+ const data = await res.json();
286
+ const text = data.text || "";
287
+
288
+ return Response.json({ text }, { headers: { ...CORS_HEADERS } });
289
+ }
290
+
291
+ // ── Main handler ─────────────────────────────────────────────────────
292
+
293
+ /**
294
+ * Handle audio transcription request
295
+ */
296
+ export async function handleAudioTranscription({
297
+ formData,
298
+ credentials,
299
+ resolvedProvider = null,
300
+ resolvedModel = null,
301
+ }) {
302
+ const model = formData.get("model");
303
+ if (typeof model !== "string" || !model) {
304
+ return errorResponse(400, "model is required");
305
+ }
306
+
307
+ const fileEntry = formData.get("file");
308
+ if (!(fileEntry instanceof Blob)) {
309
+ return errorResponse(400, "file is required");
310
+ }
311
+ const file = fileEntry;
312
+
313
+ // Use pre-resolved provider/model from route handler if available.
314
+ let providerConfig = resolvedProvider;
315
+ let modelId = resolvedModel;
316
+ if (!providerConfig) {
317
+ const parsed = parseTranscriptionModel(model);
318
+ providerConfig = parsed.provider ? getTranscriptionProvider(parsed.provider) : null;
319
+ modelId = parsed.model;
320
+ }
321
+
322
+ if (!providerConfig) {
323
+ return errorResponse(
324
+ 400,
325
+ `No transcription provider found for model "${model}". Available: openai, groq, deepgram, assemblyai, nvidia, huggingface, qwen`
326
+ );
327
+ }
328
+
329
+ // Skip credential check for local providers (authType: "none")
330
+ const token =
331
+ providerConfig.authType === "none" ? null : credentials?.apiKey || credentials?.accessToken;
332
+ if (providerConfig.authType !== "none" && !token) {
333
+ return errorResponse(401, `No credentials for transcription provider: ${providerConfig.id}`);
334
+ }
335
+
336
+ // Route to provider-specific handler
337
+ if (providerConfig.format === "deepgram") {
338
+ return handleDeepgramTranscription(providerConfig, file, modelId, token, formData);
339
+ }
340
+
341
+ if (providerConfig.format === "assemblyai") {
342
+ return handleAssemblyAITranscription(providerConfig, file, modelId, token);
343
+ }
344
+
345
+ if (providerConfig.format === "nvidia-asr") {
346
+ return handleNvidiaTranscription(providerConfig, file, modelId, token);
347
+ }
348
+
349
+ if (providerConfig.format === "huggingface-asr") {
350
+ return handleHuggingFaceTranscription(providerConfig, file, modelId, token);
351
+ }
352
+
353
+ // Default: OpenAI/Groq/Qwen3-compatible multipart proxy
354
+ const extraFields = {};
355
+ for (const key of [
356
+ "language",
357
+ "prompt",
358
+ "response_format",
359
+ "temperature",
360
+ "timestamp_granularities[]",
361
+ ]) {
362
+ const val = formData.get(key);
363
+ if (val !== null && val !== undefined) {
364
+ extraFields[key] = String(val);
365
+ }
366
+ }
367
+
368
+ const { body: multipartBody, contentType: multipartCT } = await buildMultipartBody(file, {
369
+ model: modelId,
370
+ ...extraFields,
371
+ });
372
+
373
+ try {
374
+ const res = await fetch(providerConfig.baseUrl, {
375
+ method: "POST",
376
+ headers: { ...buildAuthHeaders(providerConfig, token), "Content-Type": multipartCT },
377
+ body: multipartBody,
378
+ });
379
+
380
+ if (!res.ok) {
381
+ return upstreamErrorResponse(res, await res.text());
382
+ }
383
+
384
+ const data = await res.text();
385
+ const respContentType = res.headers.get("content-type") || "application/json";
386
+
387
+ return new Response(data, {
388
+ status: 200,
389
+ headers: { "Content-Type": respContentType },
390
+ });
391
+ } catch (err) {
392
+ const error = err instanceof Error ? err : new Error(String(err));
393
+ return errorResponse(500, `Transcription request failed: ${error.message}`);
394
+ }
395
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Audio Translation Handler
3
+ *
4
+ * Handles POST /v1/audio/translations (Whisper translate-to-English API
5
+ * format). Proxies multipart/form-data to upstream providers that expose an
6
+ * OpenAI-Whisper-compatible /audio/translations endpoint.
7
+ *
8
+ * Unlike /v1/audio/transcriptions, translation always outputs English text
9
+ * regardless of the source audio language, so there is no `language` input
10
+ * field — only `model`, `file`, `prompt`, `response_format`, and
11
+ * `temperature` are forwarded upstream.
12
+ *
13
+ * Converted from OmniRoute TypeScript.
14
+ */
15
+
16
+ import { errorResponse } from "../utils/error.js";
17
+
18
+ // ── Registry stubs ──────────────────────────────────────────────────
19
+ function parseTranslationModel(model) {
20
+ if (model.includes("/")) {
21
+ const [provider, ...rest] = model.split("/");
22
+ return { provider, model: rest.join("/") };
23
+ }
24
+ return { provider: "openai", model };
25
+ }
26
+
27
+ const TRANSLATION_PROVIDERS = {
28
+ openai: { id: "openai", baseUrl: "https://api.openai.com/v1/audio/translations", authType: "bearer", authHeader: "bearer" },
29
+ groq: { id: "groq", baseUrl: "https://api.groq.com/openai/v1/audio/translations", authType: "bearer", authHeader: "bearer" },
30
+ };
31
+
32
+ function getTranslationProvider(providerId) {
33
+ return TRANSLATION_PROVIDERS[providerId] || null;
34
+ }
35
+
36
+ function buildAuthHeaders(providerConfig, token) {
37
+ if (!token) return {};
38
+ if (providerConfig.authHeader === "bearer") return { Authorization: `Bearer ${token}` };
39
+ if (providerConfig.authHeader === "token") return { Authorization: `Token ${token}` };
40
+ return { Authorization: `Bearer ${token}` };
41
+ }
42
+
43
+ // ── Multipart builder (from audioTranscription) ──────────────────────
44
+
45
+ function getUploadedFileName(file) {
46
+ return typeof file.name === "string" && file.name.length > 0 ? file.name : "audio.wav";
47
+ }
48
+
49
+ async function buildMultipartBody(file, fields) {
50
+ const boundary = "----OmniRouteAudioBoundary" + Date.now().toString(36);
51
+ const parts = [];
52
+ const encoder = new TextEncoder();
53
+
54
+ for (const [name, value] of Object.entries(fields)) {
55
+ parts.push(
56
+ encoder.encode(
57
+ `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`
58
+ )
59
+ );
60
+ }
61
+
62
+ const fileName = getUploadedFileName(file)
63
+ .replace(/["]/g, "_")
64
+ .replace(/[\r\n]/g, "_");
65
+ const fileBytes = new Uint8Array(await file.arrayBuffer());
66
+ parts.push(
67
+ encoder.encode(
68
+ `--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${fileName}"\r\nContent-Type: ${file.type || "application/octet-stream"}\r\n\r\n`
69
+ )
70
+ );
71
+ parts.push(fileBytes);
72
+ parts.push(encoder.encode(`\r\n--${boundary}--\r\n`));
73
+
74
+ const totalLength = parts.reduce((sum, p) => sum + p.length, 0);
75
+ const body = new Uint8Array(totalLength);
76
+ let offset = 0;
77
+ for (const part of parts) {
78
+ body.set(part, offset);
79
+ offset += part.length;
80
+ }
81
+
82
+ return { body, contentType: "multipart/form-data; boundary=" + boundary };
83
+ }
84
+
85
+ // ── Error helper ─────────────────────────────────────────────────────
86
+
87
+ function extractUpstreamErrorMessage(errText, status) {
88
+ try {
89
+ const parsed = JSON.parse(errText);
90
+ const raw =
91
+ parsed?.error?.message ||
92
+ (typeof parsed?.error === "string" ? parsed.error : null) ||
93
+ parsed?.message ||
94
+ null;
95
+ return raw ? String(raw) : errText || `Upstream error (${status})`;
96
+ } catch {
97
+ return errText || `Upstream error (${status})`;
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Handle audio translation request
103
+ */
104
+ export async function handleAudioTranslation({
105
+ formData,
106
+ credentials,
107
+ resolvedProvider = null,
108
+ resolvedModel = null,
109
+ }) {
110
+ const model = formData.get("model");
111
+ if (typeof model !== "string" || !model) {
112
+ return errorResponse(400, "model is required");
113
+ }
114
+
115
+ const fileEntry = formData.get("file");
116
+ if (!(fileEntry instanceof Blob)) {
117
+ return errorResponse(400, "file is required");
118
+ }
119
+ const file = fileEntry;
120
+
121
+ // Use pre-resolved provider/model from route handler if available.
122
+ let providerConfig = resolvedProvider;
123
+ let modelId = resolvedModel;
124
+ if (!providerConfig) {
125
+ const parsed = parseTranslationModel(model);
126
+ providerConfig = parsed.provider ? getTranslationProvider(parsed.provider) : null;
127
+ modelId = parsed.model;
128
+ }
129
+
130
+ if (!providerConfig) {
131
+ return errorResponse(
132
+ 400,
133
+ `No translation provider found for model "${model}". Available: openai, groq`
134
+ );
135
+ }
136
+
137
+ const token =
138
+ providerConfig.authType === "none" ? null : credentials?.apiKey || credentials?.accessToken;
139
+ if (providerConfig.authType !== "none" && !token) {
140
+ return errorResponse(401, `No credentials for translation provider: ${providerConfig.id}`);
141
+ }
142
+
143
+ // OpenAI Whisper translate-to-English params — no `language`, output is
144
+ // always English regardless of the source audio language.
145
+ const extraFields = {};
146
+ for (const key of ["prompt", "response_format", "temperature"]) {
147
+ const val = formData.get(key);
148
+ if (val !== null && val !== undefined) {
149
+ extraFields[key] = String(val);
150
+ }
151
+ }
152
+
153
+ const { body: multipartBody, contentType: multipartCT } = await buildMultipartBody(file, {
154
+ model: modelId,
155
+ ...extraFields,
156
+ });
157
+
158
+ try {
159
+ const res = await fetch(providerConfig.baseUrl, {
160
+ method: "POST",
161
+ headers: { ...buildAuthHeaders(providerConfig, token), "Content-Type": multipartCT },
162
+ body: multipartBody,
163
+ });
164
+
165
+ if (!res.ok) {
166
+ const errText = await res.text();
167
+ return errorResponse(res.status, extractUpstreamErrorMessage(errText, res.status));
168
+ }
169
+
170
+ const data = await res.text();
171
+ const respContentType = res.headers.get("content-type") || "application/json";
172
+
173
+ return new Response(data, {
174
+ status: 200,
175
+ headers: { "Content-Type": respContentType },
176
+ });
177
+ } catch (err) {
178
+ const error = err instanceof Error ? err : new Error(String(err));
179
+ return errorResponse(500, `Translation request failed: ${error.message}`);
180
+ }
181
+ }