@srouterhq/server 0.0.0-stage → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1027) hide show
  1. package/LICENSE +21 -0
  2. package/client/dist/assets/am-TQ7Jmdqe.js +1 -0
  3. package/client/dist/assets/ar-Bf5a7yfC.js +1 -0
  4. package/client/dist/assets/az-CrosE1xH.js +1 -0
  5. package/client/dist/assets/bg-BvWnmcUz.js +1 -0
  6. package/client/dist/assets/bn-ams5TsJp.js +1 -0
  7. package/client/dist/assets/cs-Dh67CaJ_.js +1 -0
  8. package/client/dist/assets/da-9WlT8Iq7.js +1 -0
  9. package/client/dist/assets/de-lWhJJzz7.js +1 -0
  10. package/client/dist/assets/el-Cu7_GhNq.js +1 -0
  11. package/client/dist/assets/es-BZuzsBcP.js +1 -0
  12. package/client/dist/assets/fa-Dwdn-jKS.js +1 -0
  13. package/client/dist/assets/fi-BFrFyOTy.js +1 -0
  14. package/client/dist/assets/fr-XhbtmPpj.js +1 -0
  15. package/client/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  16. package/client/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  17. package/client/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  18. package/client/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  19. package/client/dist/assets/geist-mono-cyrillic-ext-wght-normal-I4S5GZfc.woff2 +0 -0
  20. package/client/dist/assets/geist-mono-cyrillic-wght-normal-BmXc_FBt.woff2 +0 -0
  21. package/client/dist/assets/geist-mono-latin-ext-wght-normal-DrnZ1wKl.woff2 +0 -0
  22. package/client/dist/assets/geist-mono-latin-wght-normal-B_7UjwxQ.woff2 +0 -0
  23. package/client/dist/assets/geist-mono-symbols2-wght-normal-GZpp1pK2.woff2 +0 -0
  24. package/client/dist/assets/geist-mono-vietnamese-wght-normal-D8KDMBhC.woff2 +0 -0
  25. package/client/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  26. package/client/dist/assets/gu-D0uhLUB8.js +1 -0
  27. package/client/dist/assets/ha-CVlm9pqp.js +1 -0
  28. package/client/dist/assets/he-CMIFeoWr.js +1 -0
  29. package/client/dist/assets/hi-ENTwhBpn.js +1 -0
  30. package/client/dist/assets/hr-fc03vMy_.js +1 -0
  31. package/client/dist/assets/hu-YBo2BIYt.js +1 -0
  32. package/client/dist/assets/id-BEWXYX69.js +1 -0
  33. package/client/dist/assets/ig-BKLZFKka.js +1 -0
  34. package/client/dist/assets/index-D9I4KtvR.js +145 -0
  35. package/client/dist/assets/index-txhEmCdb.css +2 -0
  36. package/client/dist/assets/it-Bzmei1Y2.js +1 -0
  37. package/client/dist/assets/ja-BfcpuqEl.js +1 -0
  38. package/client/dist/assets/ka-CnWjRgLo.js +1 -0
  39. package/client/dist/assets/km-BAjPR3Qj.js +1 -0
  40. package/client/dist/assets/kn-B15WKU5m.js +1 -0
  41. package/client/dist/assets/ko-C61fAB-w.js +1 -0
  42. package/client/dist/assets/lt-CYTr4_YW.js +1 -0
  43. package/client/dist/assets/ml-CtqxgyH3.js +1 -0
  44. package/client/dist/assets/mr-X5TMY7aR.js +1 -0
  45. package/client/dist/assets/ms-DSg8RqPS.js +1 -0
  46. package/client/dist/assets/my-D7RqR8uE.js +1 -0
  47. package/client/dist/assets/ne-DaflxkDj.js +1 -0
  48. package/client/dist/assets/nl-BOqqtsC8.js +1 -0
  49. package/client/dist/assets/no-BSHp_StB.js +1 -0
  50. package/client/dist/assets/or-BkLR9Vub.js +1 -0
  51. package/client/dist/assets/pa-CarJ2XgI.js +1 -0
  52. package/client/dist/assets/pl-C2n0V-0k.js +1 -0
  53. package/client/dist/assets/pt-BR-CqJdNhg2.js +1 -0
  54. package/client/dist/assets/pt-PT-C_HpMyKz.js +1 -0
  55. package/client/dist/assets/ro-DVRZqSAS.js +1 -0
  56. package/client/dist/assets/ru-DwAJG7VC.js +1 -0
  57. package/client/dist/assets/si-BxUezXm0.js +1 -0
  58. package/client/dist/assets/sk-eI1VcIfY.js +1 -0
  59. package/client/dist/assets/sr-CJF3auCr.js +1 -0
  60. package/client/dist/assets/srouter-logo-C6ZfjGIi.svg +14 -0
  61. package/client/dist/assets/sv-nqvwHAq_.js +1 -0
  62. package/client/dist/assets/sw-DWynE2pW.js +1 -0
  63. package/client/dist/assets/ta-BVM12knC.js +1 -0
  64. package/client/dist/assets/te-oyzEkF7_.js +1 -0
  65. package/client/dist/assets/th-COVeE0ZQ.js +1 -0
  66. package/client/dist/assets/tl-DsJANnQz.js +1 -0
  67. package/client/dist/assets/tr-B-Fl-urO.js +1 -0
  68. package/client/dist/assets/uk-C5qketAO.js +1 -0
  69. package/client/dist/assets/ur-QU9KugfV.js +1 -0
  70. package/client/dist/assets/uz-DL9cG_mY.js +1 -0
  71. package/client/dist/assets/vi-CQiVy3Pu.js +1 -0
  72. package/client/dist/assets/yo-DqazMdrZ.js +1 -0
  73. package/client/dist/assets/zh-CN-DnYL_Kio.js +1 -0
  74. package/client/dist/assets/zh-TW-5bdbosKj.js +1 -0
  75. package/client/dist/favicon.svg +14 -0
  76. package/client/dist/icons.svg +24 -0
  77. package/client/dist/index.html +37 -0
  78. package/dist/app.d.ts +4 -0
  79. package/dist/app.d.ts.map +1 -0
  80. package/dist/app.js +372 -0
  81. package/dist/app.js.map +1 -0
  82. package/dist/db/index.d.ts +49 -0
  83. package/dist/db/index.d.ts.map +1 -0
  84. package/dist/db/index.js +203 -0
  85. package/dist/db/index.js.map +1 -0
  86. package/dist/db/migrate/TEMPLATE.d.ts +4 -0
  87. package/dist/db/migrate/TEMPLATE.d.ts.map +1 -0
  88. package/dist/db/migrate/TEMPLATE.js +18 -0
  89. package/dist/db/migrate/TEMPLATE.js.map +1 -0
  90. package/dist/db/migrate/cli.d.ts +2 -0
  91. package/dist/db/migrate/cli.d.ts.map +1 -0
  92. package/dist/db/migrate/cli.js +177 -0
  93. package/dist/db/migrate/cli.js.map +1 -0
  94. package/dist/db/migrate/defaults.d.ts +52 -0
  95. package/dist/db/migrate/defaults.d.ts.map +1 -0
  96. package/dist/db/migrate/defaults.js +126 -0
  97. package/dist/db/migrate/defaults.js.map +1 -0
  98. package/dist/db/migrate/runner.d.ts +16 -0
  99. package/dist/db/migrate/runner.d.ts.map +1 -0
  100. package/dist/db/migrate/runner.js +178 -0
  101. package/dist/db/migrate/runner.js.map +1 -0
  102. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts +4 -0
  103. package/dist/db/migrations/20260101_000000_legacy_baseline.d.ts.map +1 -0
  104. package/dist/db/migrations/20260101_000000_legacy_baseline.js +2240 -0
  105. package/dist/db/migrations/20260101_000000_legacy_baseline.js.map +1 -0
  106. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts +4 -0
  107. package/dist/db/migrations/20260627_000001_custom_provider_modalities.d.ts.map +1 -0
  108. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js +27 -0
  109. package/dist/db/migrations/20260627_000001_custom_provider_modalities.js.map +1 -0
  110. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts +4 -0
  111. package/dist/db/migrations/20260627_000002_catalog_model_state.d.ts.map +1 -0
  112. package/dist/db/migrations/20260627_000002_catalog_model_state.js +30 -0
  113. package/dist/db/migrations/20260627_000002_catalog_model_state.js.map +1 -0
  114. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts +6 -0
  115. package/dist/db/migrations/20260628_120000_request_aggregates.d.ts.map +1 -0
  116. package/dist/db/migrations/20260628_120000_request_aggregates.js +104 -0
  117. package/dist/db/migrations/20260628_120000_request_aggregates.js.map +1 -0
  118. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts +9 -0
  119. package/dist/db/migrations/20260630_000001_github_gpt41_context.d.ts.map +1 -0
  120. package/dist/db/migrations/20260630_000001_github_gpt41_context.js +22 -0
  121. package/dist/db/migrations/20260630_000001_github_gpt41_context.js.map +1 -0
  122. package/dist/db/migrations/20260706_000001_request_client_info.d.ts +4 -0
  123. package/dist/db/migrations/20260706_000001_request_client_info.d.ts.map +1 -0
  124. package/dist/db/migrations/20260706_000001_request_client_info.js +30 -0
  125. package/dist/db/migrations/20260706_000001_request_client_info.js.map +1 -0
  126. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts +17 -0
  127. package/dist/db/migrations/20260706_000002_custom_model_tool_support.d.ts.map +1 -0
  128. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js +20 -0
  129. package/dist/db/migrations/20260706_000002_custom_model_tool_support.js.map +1 -0
  130. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts +12 -0
  131. package/dist/db/migrations/20260714_000001_profile_chain_backfill.d.ts.map +1 -0
  132. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js +45 -0
  133. package/dist/db/migrations/20260714_000001_profile_chain_backfill.js.map +1 -0
  134. package/dist/db/migrations/20260720_000001_key_health_error.d.ts +5 -0
  135. package/dist/db/migrations/20260720_000001_key_health_error.d.ts.map +1 -0
  136. package/dist/db/migrations/20260720_000001_key_health_error.js +16 -0
  137. package/dist/db/migrations/20260720_000001_key_health_error.js.map +1 -0
  138. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts +4 -0
  139. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.d.ts.map +1 -0
  140. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js +38 -0
  141. package/dist/db/migrations/20260726_000001_cooldown_probe_provenance.js.map +1 -0
  142. package/dist/db/migrations/20260726_000002_request_attempts.d.ts +4 -0
  143. package/dist/db/migrations/20260726_000002_request_attempts.d.ts.map +1 -0
  144. package/dist/db/migrations/20260726_000002_request_attempts.js +43 -0
  145. package/dist/db/migrations/20260726_000002_request_attempts.js.map +1 -0
  146. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts +4 -0
  147. package/dist/db/migrations/20260726_000003_model_source_provenance.d.ts.map +1 -0
  148. package/dist/db/migrations/20260726_000003_model_source_provenance.js +80 -0
  149. package/dist/db/migrations/20260726_000003_model_source_provenance.js.map +1 -0
  150. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts +4 -0
  151. package/dist/db/migrations/20260726_000004_media_model_meta.d.ts.map +1 -0
  152. package/dist/db/migrations/20260726_000004_media_model_meta.js +34 -0
  153. package/dist/db/migrations/20260726_000004_media_model_meta.js.map +1 -0
  154. package/dist/db/migrations/20260726_000005_request_served_model.d.ts +4 -0
  155. package/dist/db/migrations/20260726_000005_request_served_model.d.ts.map +1 -0
  156. package/dist/db/migrations/20260726_000005_request_served_model.js +31 -0
  157. package/dist/db/migrations/20260726_000005_request_served_model.js.map +1 -0
  158. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts +4 -0
  159. package/dist/db/migrations/20260726_000006_attempt_error_summary.d.ts.map +1 -0
  160. package/dist/db/migrations/20260726_000006_attempt_error_summary.js +29 -0
  161. package/dist/db/migrations/20260726_000006_attempt_error_summary.js.map +1 -0
  162. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts +4 -0
  163. package/dist/db/migrations/20260727_000001_agent_compatibility.d.ts.map +1 -0
  164. package/dist/db/migrations/20260727_000001_agent_compatibility.js +41 -0
  165. package/dist/db/migrations/20260727_000001_agent_compatibility.js.map +1 -0
  166. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts +12 -0
  167. package/dist/db/migrations/20260728_000001_tombstone_provenance.d.ts.map +1 -0
  168. package/dist/db/migrations/20260728_000001_tombstone_provenance.js +37 -0
  169. package/dist/db/migrations/20260728_000001_tombstone_provenance.js.map +1 -0
  170. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts +4 -0
  171. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.d.ts.map +1 -0
  172. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js +145 -0
  173. package/dist/db/migrations/20260729_000001_custom_model_endpoint_identity.js.map +1 -0
  174. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts +6 -0
  175. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.d.ts.map +1 -0
  176. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js +52 -0
  177. package/dist/db/migrations/20260802_000001_custom_endpoint_host_labels.js.map +1 -0
  178. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts +6 -0
  179. package/dist/db/migrations/20260805_000001_key_model_scope.d.ts.map +1 -0
  180. package/dist/db/migrations/20260805_000001_key_model_scope.js +17 -0
  181. package/dist/db/migrations/20260805_000001_key_model_scope.js.map +1 -0
  182. package/dist/db/migrations/20260805_000002_client_profiles.d.ts +4 -0
  183. package/dist/db/migrations/20260805_000002_client_profiles.d.ts.map +1 -0
  184. package/dist/db/migrations/20260805_000002_client_profiles.js +33 -0
  185. package/dist/db/migrations/20260805_000002_client_profiles.js.map +1 -0
  186. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts +4 -0
  187. package/dist/db/migrations/20260810_000001_api_key_proxy.d.ts.map +1 -0
  188. package/dist/db/migrations/20260810_000001_api_key_proxy.js +33 -0
  189. package/dist/db/migrations/20260810_000001_api_key_proxy.js.map +1 -0
  190. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts +4 -0
  191. package/dist/db/migrations/20260819_000001_custom_model_tombstones.d.ts.map +1 -0
  192. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js +19 -0
  193. package/dist/db/migrations/20260819_000001_custom_model_tombstones.js.map +1 -0
  194. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts +4 -0
  195. package/dist/db/migrations/20260820_000001_playground_conversations.d.ts.map +1 -0
  196. package/dist/db/migrations/20260820_000001_playground_conversations.js +47 -0
  197. package/dist/db/migrations/20260820_000001_playground_conversations.js.map +1 -0
  198. package/dist/db/migrations/20260823_000001_server_logs.d.ts +4 -0
  199. package/dist/db/migrations/20260823_000001_server_logs.d.ts.map +1 -0
  200. package/dist/db/migrations/20260823_000001_server_logs.js +53 -0
  201. package/dist/db/migrations/20260823_000001_server_logs.js.map +1 -0
  202. package/dist/db/migrations/20260823_000002_backups_table.d.ts +8 -0
  203. package/dist/db/migrations/20260823_000002_backups_table.d.ts.map +1 -0
  204. package/dist/db/migrations/20260823_000002_backups_table.js +22 -0
  205. package/dist/db/migrations/20260823_000002_backups_table.js.map +1 -0
  206. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts +19 -0
  207. package/dist/db/migrations/20260823_000003_attempt_key_label.d.ts.map +1 -0
  208. package/dist/db/migrations/20260823_000003_attempt_key_label.js +28 -0
  209. package/dist/db/migrations/20260823_000003_attempt_key_label.js.map +1 -0
  210. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts +14 -0
  211. package/dist/db/migrations/20260823_000004_profile_auto_include.d.ts.map +1 -0
  212. package/dist/db/migrations/20260823_000004_profile_auto_include.js +25 -0
  213. package/dist/db/migrations/20260823_000004_profile_auto_include.js.map +1 -0
  214. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts +4 -0
  215. package/dist/db/migrations/20260901_000001_idempotency_claims.d.ts.map +1 -0
  216. package/dist/db/migrations/20260901_000001_idempotency_claims.js +46 -0
  217. package/dist/db/migrations/20260901_000001_idempotency_claims.js.map +1 -0
  218. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts +4 -0
  219. package/dist/db/migrations/20260901_000002_quota_observation_lookup.d.ts.map +1 -0
  220. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js +37 -0
  221. package/dist/db/migrations/20260901_000002_quota_observation_lookup.js.map +1 -0
  222. package/dist/db/migrations/20260901_000003_request_caller.d.ts +4 -0
  223. package/dist/db/migrations/20260901_000003_request_caller.d.ts.map +1 -0
  224. package/dist/db/migrations/20260901_000003_request_caller.js +27 -0
  225. package/dist/db/migrations/20260901_000003_request_caller.js.map +1 -0
  226. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts +4 -0
  227. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.d.ts.map +1 -0
  228. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js +35 -0
  229. package/dist/db/migrations/20260902_000001_analytics_latency_percentile_index.js.map +1 -0
  230. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts +4 -0
  231. package/dist/db/migrations/20260903_000001_mcp_enabled_default.d.ts.map +1 -0
  232. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js +37 -0
  233. package/dist/db/migrations/20260903_000001_mcp_enabled_default.js.map +1 -0
  234. package/dist/db/migrations/20260903_000002_response_cache.d.ts +4 -0
  235. package/dist/db/migrations/20260903_000002_response_cache.d.ts.map +1 -0
  236. package/dist/db/migrations/20260903_000002_response_cache.js +53 -0
  237. package/dist/db/migrations/20260903_000002_response_cache.js.map +1 -0
  238. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts +13 -0
  239. package/dist/db/migrations/20260904_000001_key_monthly_budget.d.ts.map +1 -0
  240. package/dist/db/migrations/20260904_000001_key_monthly_budget.js +35 -0
  241. package/dist/db/migrations/20260904_000001_key_monthly_budget.js.map +1 -0
  242. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts +4 -0
  243. package/dist/db/migrations/20260913_000001_request_model_attribution.d.ts.map +1 -0
  244. package/dist/db/migrations/20260913_000001_request_model_attribution.js +68 -0
  245. package/dist/db/migrations/20260913_000001_request_model_attribution.js.map +1 -0
  246. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts +5 -0
  247. package/dist/db/migrations/20260914_000001_key_monthly_usage.d.ts.map +1 -0
  248. package/dist/db/migrations/20260914_000001_key_monthly_usage.js +37 -0
  249. package/dist/db/migrations/20260914_000001_key_monthly_usage.js.map +1 -0
  250. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts +4 -0
  251. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.d.ts.map +1 -0
  252. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js +28 -0
  253. package/dist/db/migrations/20260915_000001_quota_snapshot_freshness.js.map +1 -0
  254. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts +4 -0
  255. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.d.ts.map +1 -0
  256. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js +25 -0
  257. package/dist/db/migrations/20261004_000001_unified_key_sr_prefix.js.map +1 -0
  258. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts +10 -0
  259. package/dist/db/migrations/20261005_000001_key_budget_period.d.ts.map +1 -0
  260. package/dist/db/migrations/20261005_000001_key_budget_period.js +78 -0
  261. package/dist/db/migrations/20261005_000001_key_budget_period.js.map +1 -0
  262. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts +21 -0
  263. package/dist/db/migrations/20261005_000002_key_budget_period_auto.d.ts.map +1 -0
  264. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js +41 -0
  265. package/dist/db/migrations/20261005_000002_key_budget_period_auto.js.map +1 -0
  266. package/dist/db/model-pricing.d.ts +39 -0
  267. package/dist/db/model-pricing.d.ts.map +1 -0
  268. package/dist/db/model-pricing.js +274 -0
  269. package/dist/db/model-pricing.js.map +1 -0
  270. package/dist/db/node-sqlite.d.ts +9 -0
  271. package/dist/db/node-sqlite.d.ts.map +1 -0
  272. package/dist/db/node-sqlite.js +87 -0
  273. package/dist/db/node-sqlite.js.map +1 -0
  274. package/dist/db/types.d.ts +22 -0
  275. package/dist/db/types.d.ts.map +1 -0
  276. package/dist/db/types.js +2 -0
  277. package/dist/db/types.js.map +1 -0
  278. package/dist/docs/docs-page.d.ts +2 -0
  279. package/dist/docs/docs-page.d.ts.map +1 -0
  280. package/dist/docs/docs-page.js +319 -0
  281. package/dist/docs/docs-page.js.map +1 -0
  282. package/dist/docs/openapi.d.ts +1896 -0
  283. package/dist/docs/openapi.d.ts.map +1 -0
  284. package/dist/docs/openapi.js +1096 -0
  285. package/dist/docs/openapi.js.map +1 -0
  286. package/dist/env.d.ts +2 -0
  287. package/dist/env.d.ts.map +1 -0
  288. package/dist/env.js +9 -0
  289. package/dist/env.js.map +1 -0
  290. package/dist/index.d.ts +2 -0
  291. package/dist/index.d.ts.map +1 -0
  292. package/dist/index.js +144 -0
  293. package/dist/index.js.map +1 -0
  294. package/dist/lib/anthropic-documents.d.ts +41 -0
  295. package/dist/lib/anthropic-documents.d.ts.map +1 -0
  296. package/dist/lib/anthropic-documents.js +132 -0
  297. package/dist/lib/anthropic-documents.js.map +1 -0
  298. package/dist/lib/app-version.d.ts +5 -0
  299. package/dist/lib/app-version.d.ts.map +1 -0
  300. package/dist/lib/app-version.js +61 -0
  301. package/dist/lib/app-version.js.map +1 -0
  302. package/dist/lib/attempt-trace.d.ts +23 -0
  303. package/dist/lib/attempt-trace.d.ts.map +1 -0
  304. package/dist/lib/attempt-trace.js +32 -0
  305. package/dist/lib/attempt-trace.js.map +1 -0
  306. package/dist/lib/budget.d.ts +6 -0
  307. package/dist/lib/budget.d.ts.map +1 -0
  308. package/dist/lib/budget.js +40 -0
  309. package/dist/lib/budget.js.map +1 -0
  310. package/dist/lib/client-classifier.d.ts +10 -0
  311. package/dist/lib/client-classifier.d.ts.map +1 -0
  312. package/dist/lib/client-classifier.js +126 -0
  313. package/dist/lib/client-classifier.js.map +1 -0
  314. package/dist/lib/client-context.d.ts +10 -0
  315. package/dist/lib/client-context.d.ts.map +1 -0
  316. package/dist/lib/client-context.js +39 -0
  317. package/dist/lib/client-context.js.map +1 -0
  318. package/dist/lib/config.d.ts +33 -0
  319. package/dist/lib/config.d.ts.map +1 -0
  320. package/dist/lib/config.js +94 -0
  321. package/dist/lib/config.js.map +1 -0
  322. package/dist/lib/content.d.ts +33 -0
  323. package/dist/lib/content.d.ts.map +1 -0
  324. package/dist/lib/content.js +201 -0
  325. package/dist/lib/content.js.map +1 -0
  326. package/dist/lib/credential.d.ts +11 -0
  327. package/dist/lib/credential.d.ts.map +1 -0
  328. package/dist/lib/credential.js +18 -0
  329. package/dist/lib/credential.js.map +1 -0
  330. package/dist/lib/crypto.d.ts +31 -0
  331. package/dist/lib/crypto.d.ts.map +1 -0
  332. package/dist/lib/crypto.js +206 -0
  333. package/dist/lib/crypto.js.map +1 -0
  334. package/dist/lib/custom-provider-cleanup.d.ts +13 -0
  335. package/dist/lib/custom-provider-cleanup.d.ts.map +1 -0
  336. package/dist/lib/custom-provider-cleanup.js +43 -0
  337. package/dist/lib/custom-provider-cleanup.js.map +1 -0
  338. package/dist/lib/db-backup.d.ts +31 -0
  339. package/dist/lib/db-backup.d.ts.map +1 -0
  340. package/dist/lib/db-backup.js +255 -0
  341. package/dist/lib/db-backup.js.map +1 -0
  342. package/dist/lib/endpoint-scope.d.ts +42 -0
  343. package/dist/lib/endpoint-scope.d.ts.map +1 -0
  344. package/dist/lib/endpoint-scope.js +95 -0
  345. package/dist/lib/endpoint-scope.js.map +1 -0
  346. package/dist/lib/env-drift.d.ts +17 -0
  347. package/dist/lib/env-drift.d.ts.map +1 -0
  348. package/dist/lib/env-drift.js +108 -0
  349. package/dist/lib/env-drift.js.map +1 -0
  350. package/dist/lib/error-classify.d.ts +40 -0
  351. package/dist/lib/error-classify.d.ts.map +1 -0
  352. package/dist/lib/error-classify.js +687 -0
  353. package/dist/lib/error-classify.js.map +1 -0
  354. package/dist/lib/error-redaction.d.ts +8 -0
  355. package/dist/lib/error-redaction.d.ts.map +1 -0
  356. package/dist/lib/error-redaction.js +45 -0
  357. package/dist/lib/error-redaction.js.map +1 -0
  358. package/dist/lib/fallback-loop.d.ts +306 -0
  359. package/dist/lib/fallback-loop.d.ts.map +1 -0
  360. package/dist/lib/fallback-loop.js +1336 -0
  361. package/dist/lib/fallback-loop.js.map +1 -0
  362. package/dist/lib/file-permissions.d.ts +98 -0
  363. package/dist/lib/file-permissions.d.ts.map +1 -0
  364. package/dist/lib/file-permissions.js +159 -0
  365. package/dist/lib/file-permissions.js.map +1 -0
  366. package/dist/lib/gemini-wire.d.ts +76 -0
  367. package/dist/lib/gemini-wire.d.ts.map +1 -0
  368. package/dist/lib/gemini-wire.js +392 -0
  369. package/dist/lib/gemini-wire.js.map +1 -0
  370. package/dist/lib/guardrails.d.ts +37 -0
  371. package/dist/lib/guardrails.d.ts.map +1 -0
  372. package/dist/lib/guardrails.js +106 -0
  373. package/dist/lib/guardrails.js.map +1 -0
  374. package/dist/lib/header-value.d.ts +8 -0
  375. package/dist/lib/header-value.d.ts.map +1 -0
  376. package/dist/lib/header-value.js +55 -0
  377. package/dist/lib/header-value.js.map +1 -0
  378. package/dist/lib/image-normalize.d.ts +21 -0
  379. package/dist/lib/image-normalize.d.ts.map +1 -0
  380. package/dist/lib/image-normalize.js +218 -0
  381. package/dist/lib/image-normalize.js.map +1 -0
  382. package/dist/lib/inbound-chat.d.ts +48 -0
  383. package/dist/lib/inbound-chat.d.ts.map +1 -0
  384. package/dist/lib/inbound-chat.js +406 -0
  385. package/dist/lib/inbound-chat.js.map +1 -0
  386. package/dist/lib/key-parser.d.ts +79 -0
  387. package/dist/lib/key-parser.d.ts.map +1 -0
  388. package/dist/lib/key-parser.js +701 -0
  389. package/dist/lib/key-parser.js.map +1 -0
  390. package/dist/lib/key-proxy.d.ts +49 -0
  391. package/dist/lib/key-proxy.d.ts.map +1 -0
  392. package/dist/lib/key-proxy.js +92 -0
  393. package/dist/lib/key-proxy.js.map +1 -0
  394. package/dist/lib/log-redaction.d.ts +28 -0
  395. package/dist/lib/log-redaction.d.ts.map +1 -0
  396. package/dist/lib/log-redaction.js +166 -0
  397. package/dist/lib/log-redaction.js.map +1 -0
  398. package/dist/lib/model-scope.d.ts +9 -0
  399. package/dist/lib/model-scope.d.ts.map +1 -0
  400. package/dist/lib/model-scope.js +29 -0
  401. package/dist/lib/model-scope.js.map +1 -0
  402. package/dist/lib/one-time-code.d.ts +15 -0
  403. package/dist/lib/one-time-code.d.ts.map +1 -0
  404. package/dist/lib/one-time-code.js +41 -0
  405. package/dist/lib/one-time-code.js.map +1 -0
  406. package/dist/lib/output-cap.d.ts +24 -0
  407. package/dist/lib/output-cap.d.ts.map +1 -0
  408. package/dist/lib/output-cap.js +80 -0
  409. package/dist/lib/output-cap.js.map +1 -0
  410. package/dist/lib/password.d.ts +3 -0
  411. package/dist/lib/password.d.ts.map +1 -0
  412. package/dist/lib/password.js +27 -0
  413. package/dist/lib/password.js.map +1 -0
  414. package/dist/lib/process-safety-net.d.ts +32 -0
  415. package/dist/lib/process-safety-net.d.ts.map +1 -0
  416. package/dist/lib/process-safety-net.js +123 -0
  417. package/dist/lib/process-safety-net.js.map +1 -0
  418. package/dist/lib/provider-identity.d.ts +37 -0
  419. package/dist/lib/provider-identity.d.ts.map +1 -0
  420. package/dist/lib/provider-identity.js +110 -0
  421. package/dist/lib/provider-identity.js.map +1 -0
  422. package/dist/lib/provider-size-parser.d.ts +6 -0
  423. package/dist/lib/provider-size-parser.d.ts.map +1 -0
  424. package/dist/lib/provider-size-parser.js +72 -0
  425. package/dist/lib/provider-size-parser.js.map +1 -0
  426. package/dist/lib/provider-timeout.d.ts +17 -0
  427. package/dist/lib/provider-timeout.d.ts.map +1 -0
  428. package/dist/lib/provider-timeout.js +70 -0
  429. package/dist/lib/provider-timeout.js.map +1 -0
  430. package/dist/lib/proxy.d.ts +180 -0
  431. package/dist/lib/proxy.d.ts.map +1 -0
  432. package/dist/lib/proxy.js +1002 -0
  433. package/dist/lib/proxy.js.map +1 -0
  434. package/dist/lib/request-log.d.ts +4 -0
  435. package/dist/lib/request-log.d.ts.map +1 -0
  436. package/dist/lib/request-log.js +161 -0
  437. package/dist/lib/request-log.js.map +1 -0
  438. package/dist/lib/reset-code.d.ts +5 -0
  439. package/dist/lib/reset-code.d.ts.map +1 -0
  440. package/dist/lib/reset-code.js +39 -0
  441. package/dist/lib/reset-code.js.map +1 -0
  442. package/dist/lib/retry-hint.d.ts +14 -0
  443. package/dist/lib/retry-hint.d.ts.map +1 -0
  444. package/dist/lib/retry-hint.js +36 -0
  445. package/dist/lib/retry-hint.js.map +1 -0
  446. package/dist/lib/route-live.d.ts +29 -0
  447. package/dist/lib/route-live.d.ts.map +1 -0
  448. package/dist/lib/route-live.js +97 -0
  449. package/dist/lib/route-live.js.map +1 -0
  450. package/dist/lib/sampling-params.d.ts +183 -0
  451. package/dist/lib/sampling-params.d.ts.map +1 -0
  452. package/dist/lib/sampling-params.js +386 -0
  453. package/dist/lib/sampling-params.js.map +1 -0
  454. package/dist/lib/scheduler.d.ts +13 -0
  455. package/dist/lib/scheduler.d.ts.map +1 -0
  456. package/dist/lib/scheduler.js +11 -0
  457. package/dist/lib/scheduler.js.map +1 -0
  458. package/dist/lib/served-model.d.ts +16 -0
  459. package/dist/lib/served-model.d.ts.map +1 -0
  460. package/dist/lib/served-model.js +0 -0
  461. package/dist/lib/served-model.js.map +1 -0
  462. package/dist/lib/server-logs.d.ts +132 -0
  463. package/dist/lib/server-logs.d.ts.map +1 -0
  464. package/dist/lib/server-logs.js +473 -0
  465. package/dist/lib/server-logs.js.map +1 -0
  466. package/dist/lib/setup-code.d.ts +5 -0
  467. package/dist/lib/setup-code.d.ts.map +1 -0
  468. package/dist/lib/setup-code.js +35 -0
  469. package/dist/lib/setup-code.js.map +1 -0
  470. package/dist/lib/structured-output.d.ts +9 -0
  471. package/dist/lib/structured-output.d.ts.map +1 -0
  472. package/dist/lib/structured-output.js +72 -0
  473. package/dist/lib/structured-output.js.map +1 -0
  474. package/dist/lib/system-prompt.d.ts +30 -0
  475. package/dist/lib/system-prompt.d.ts.map +1 -0
  476. package/dist/lib/system-prompt.js +66 -0
  477. package/dist/lib/system-prompt.js.map +1 -0
  478. package/dist/lib/task-type.d.ts +18 -0
  479. package/dist/lib/task-type.d.ts.map +1 -0
  480. package/dist/lib/task-type.js +92 -0
  481. package/dist/lib/task-type.js.map +1 -0
  482. package/dist/lib/think-tags.d.ts +37 -0
  483. package/dist/lib/think-tags.d.ts.map +1 -0
  484. package/dist/lib/think-tags.js +203 -0
  485. package/dist/lib/think-tags.js.map +1 -0
  486. package/dist/lib/tool-args.d.ts +36 -0
  487. package/dist/lib/tool-args.d.ts.map +1 -0
  488. package/dist/lib/tool-args.js +190 -0
  489. package/dist/lib/tool-args.js.map +1 -0
  490. package/dist/lib/tool-call-rescue.d.ts +62 -0
  491. package/dist/lib/tool-call-rescue.d.ts.map +1 -0
  492. package/dist/lib/tool-call-rescue.js +258 -0
  493. package/dist/lib/tool-call-rescue.js.map +1 -0
  494. package/dist/lib/tool-capability.d.ts +13 -0
  495. package/dist/lib/tool-capability.d.ts.map +1 -0
  496. package/dist/lib/tool-capability.js +70 -0
  497. package/dist/lib/tool-capability.js.map +1 -0
  498. package/dist/lib/tool-validate.d.ts +44 -0
  499. package/dist/lib/tool-validate.d.ts.map +1 -0
  500. package/dist/lib/tool-validate.js +165 -0
  501. package/dist/lib/tool-validate.js.map +1 -0
  502. package/dist/lib/ttfb-budget.d.ts +13 -0
  503. package/dist/lib/ttfb-budget.d.ts.map +1 -0
  504. package/dist/lib/ttfb-budget.js +153 -0
  505. package/dist/lib/ttfb-budget.js.map +1 -0
  506. package/dist/lib/url-guard.d.ts +47 -0
  507. package/dist/lib/url-guard.d.ts.map +1 -0
  508. package/dist/lib/url-guard.js +229 -0
  509. package/dist/lib/url-guard.js.map +1 -0
  510. package/dist/lib/wake-detect.d.ts +12 -0
  511. package/dist/lib/wake-detect.d.ts.map +1 -0
  512. package/dist/lib/wake-detect.js +99 -0
  513. package/dist/lib/wake-detect.js.map +1 -0
  514. package/dist/middleware/errorHandler.d.ts +3 -0
  515. package/dist/middleware/errorHandler.d.ts.map +1 -0
  516. package/dist/middleware/errorHandler.js +45 -0
  517. package/dist/middleware/errorHandler.js.map +1 -0
  518. package/dist/middleware/rateLimit.d.ts +4 -0
  519. package/dist/middleware/rateLimit.d.ts.map +1 -0
  520. package/dist/middleware/rateLimit.js +125 -0
  521. package/dist/middleware/rateLimit.js.map +1 -0
  522. package/dist/middleware/requireAuth.d.ts +3 -0
  523. package/dist/middleware/requireAuth.d.ts.map +1 -0
  524. package/dist/middleware/requireAuth.js +17 -0
  525. package/dist/middleware/requireAuth.js.map +1 -0
  526. package/dist/providers/aclide.d.ts +18 -0
  527. package/dist/providers/aclide.d.ts.map +1 -0
  528. package/dist/providers/aclide.js +174 -0
  529. package/dist/providers/aclide.js.map +1 -0
  530. package/dist/providers/aihorde.d.ts +42 -0
  531. package/dist/providers/aihorde.d.ts.map +1 -0
  532. package/dist/providers/aihorde.js +192 -0
  533. package/dist/providers/aihorde.js.map +1 -0
  534. package/dist/providers/airforce.d.ts +11 -0
  535. package/dist/providers/airforce.d.ts.map +1 -0
  536. package/dist/providers/airforce.js +32 -0
  537. package/dist/providers/airforce.js.map +1 -0
  538. package/dist/providers/base.d.ts +177 -0
  539. package/dist/providers/base.d.ts.map +1 -0
  540. package/dist/providers/base.js +354 -0
  541. package/dist/providers/base.js.map +1 -0
  542. package/dist/providers/blaze.d.ts +11 -0
  543. package/dist/providers/blaze.d.ts.map +1 -0
  544. package/dist/providers/blaze.js +33 -0
  545. package/dist/providers/blaze.js.map +1 -0
  546. package/dist/providers/blockrun.d.ts +14 -0
  547. package/dist/providers/blockrun.d.ts.map +1 -0
  548. package/dist/providers/blockrun.js +36 -0
  549. package/dist/providers/blockrun.js.map +1 -0
  550. package/dist/providers/clod.d.ts +11 -0
  551. package/dist/providers/clod.d.ts.map +1 -0
  552. package/dist/providers/clod.js +39 -0
  553. package/dist/providers/clod.js.map +1 -0
  554. package/dist/providers/cloudflare.d.ts +15 -0
  555. package/dist/providers/cloudflare.d.ts.map +1 -0
  556. package/dist/providers/cloudflare.js +175 -0
  557. package/dist/providers/cloudflare.js.map +1 -0
  558. package/dist/providers/cohere.d.ts +11 -0
  559. package/dist/providers/cohere.d.ts.map +1 -0
  560. package/dist/providers/cohere.js +117 -0
  561. package/dist/providers/cohere.js.map +1 -0
  562. package/dist/providers/dreamprompting.d.ts +11 -0
  563. package/dist/providers/dreamprompting.d.ts.map +1 -0
  564. package/dist/providers/dreamprompting.js +41 -0
  565. package/dist/providers/dreamprompting.js.map +1 -0
  566. package/dist/providers/electronhub.d.ts +10 -0
  567. package/dist/providers/electronhub.d.ts.map +1 -0
  568. package/dist/providers/electronhub.js +56 -0
  569. package/dist/providers/electronhub.js.map +1 -0
  570. package/dist/providers/experiential.d.ts +10 -0
  571. package/dist/providers/experiential.d.ts.map +1 -0
  572. package/dist/providers/experiential.js +33 -0
  573. package/dist/providers/experiential.js.map +1 -0
  574. package/dist/providers/gizmo.d.ts +15 -0
  575. package/dist/providers/gizmo.d.ts.map +1 -0
  576. package/dist/providers/gizmo.js +54 -0
  577. package/dist/providers/gizmo.js.map +1 -0
  578. package/dist/providers/google.d.ts +30 -0
  579. package/dist/providers/google.d.ts.map +1 -0
  580. package/dist/providers/google.js +766 -0
  581. package/dist/providers/google.js.map +1 -0
  582. package/dist/providers/index.d.ts +13 -0
  583. package/dist/providers/index.d.ts.map +1 -0
  584. package/dist/providers/index.js +561 -0
  585. package/dist/providers/index.js.map +1 -0
  586. package/dist/providers/llmtr.d.ts +15 -0
  587. package/dist/providers/llmtr.d.ts.map +1 -0
  588. package/dist/providers/llmtr.js +51 -0
  589. package/dist/providers/llmtr.js.map +1 -0
  590. package/dist/providers/logfare.d.ts +11 -0
  591. package/dist/providers/logfare.d.ts.map +1 -0
  592. package/dist/providers/logfare.js +33 -0
  593. package/dist/providers/logfare.js.map +1 -0
  594. package/dist/providers/lucidity.d.ts +11 -0
  595. package/dist/providers/lucidity.d.ts.map +1 -0
  596. package/dist/providers/lucidity.js +37 -0
  597. package/dist/providers/lucidity.js.map +1 -0
  598. package/dist/providers/modelscope.d.ts +50 -0
  599. package/dist/providers/modelscope.d.ts.map +1 -0
  600. package/dist/providers/modelscope.js +143 -0
  601. package/dist/providers/modelscope.js.map +1 -0
  602. package/dist/providers/moondream.d.ts +17 -0
  603. package/dist/providers/moondream.d.ts.map +1 -0
  604. package/dist/providers/moondream.js +140 -0
  605. package/dist/providers/moondream.js.map +1 -0
  606. package/dist/providers/openai-compat.d.ts +140 -0
  607. package/dist/providers/openai-compat.d.ts.map +1 -0
  608. package/dist/providers/openai-compat.js +537 -0
  609. package/dist/providers/openai-compat.js.map +1 -0
  610. package/dist/providers/opencode-free.d.ts +24 -0
  611. package/dist/providers/opencode-free.d.ts.map +1 -0
  612. package/dist/providers/opencode-free.js +262 -0
  613. package/dist/providers/opencode-free.js.map +1 -0
  614. package/dist/providers/pollinations.d.ts +32 -0
  615. package/dist/providers/pollinations.d.ts.map +1 -0
  616. package/dist/providers/pollinations.js +65 -0
  617. package/dist/providers/pollinations.js.map +1 -0
  618. package/dist/providers/router9.d.ts +16 -0
  619. package/dist/providers/router9.d.ts.map +1 -0
  620. package/dist/providers/router9.js +42 -0
  621. package/dist/providers/router9.js.map +1 -0
  622. package/dist/providers/sail.d.ts +44 -0
  623. package/dist/providers/sail.d.ts.map +1 -0
  624. package/dist/providers/sail.js +302 -0
  625. package/dist/providers/sail.js.map +1 -0
  626. package/dist/providers/septor.d.ts +10 -0
  627. package/dist/providers/septor.d.ts.map +1 -0
  628. package/dist/providers/septor.js +36 -0
  629. package/dist/providers/septor.js.map +1 -0
  630. package/dist/providers/speechify.d.ts +14 -0
  631. package/dist/providers/speechify.d.ts.map +1 -0
  632. package/dist/providers/speechify.js +28 -0
  633. package/dist/providers/speechify.js.map +1 -0
  634. package/dist/providers/speka.d.ts +12 -0
  635. package/dist/providers/speka.d.ts.map +1 -0
  636. package/dist/providers/speka.js +37 -0
  637. package/dist/providers/speka.js.map +1 -0
  638. package/dist/providers/waterfall.d.ts +11 -0
  639. package/dist/providers/waterfall.d.ts.map +1 -0
  640. package/dist/providers/waterfall.js +33 -0
  641. package/dist/providers/waterfall.js.map +1 -0
  642. package/dist/providers/xkiro.d.ts +71 -0
  643. package/dist/providers/xkiro.d.ts.map +1 -0
  644. package/dist/providers/xkiro.js +119 -0
  645. package/dist/providers/xkiro.js.map +1 -0
  646. package/dist/providers/zhipu.d.ts +43 -0
  647. package/dist/providers/zhipu.d.ts.map +1 -0
  648. package/dist/providers/zhipu.js +95 -0
  649. package/dist/providers/zhipu.js.map +1 -0
  650. package/dist/routes/analytics.d.ts +2 -0
  651. package/dist/routes/analytics.d.ts.map +1 -0
  652. package/dist/routes/analytics.js +738 -0
  653. package/dist/routes/analytics.js.map +1 -0
  654. package/dist/routes/anthropic.d.ts +7 -0
  655. package/dist/routes/anthropic.d.ts.map +1 -0
  656. package/dist/routes/anthropic.js +1076 -0
  657. package/dist/routes/anthropic.js.map +1 -0
  658. package/dist/routes/auth.d.ts +2 -0
  659. package/dist/routes/auth.d.ts.map +1 -0
  660. package/dist/routes/auth.js +310 -0
  661. package/dist/routes/auth.js.map +1 -0
  662. package/dist/routes/backups.d.ts +2 -0
  663. package/dist/routes/backups.d.ts.map +1 -0
  664. package/dist/routes/backups.js +127 -0
  665. package/dist/routes/backups.js.map +1 -0
  666. package/dist/routes/cache.d.ts +2 -0
  667. package/dist/routes/cache.d.ts.map +1 -0
  668. package/dist/routes/cache.js +40 -0
  669. package/dist/routes/cache.js.map +1 -0
  670. package/dist/routes/client-profiles.d.ts +2 -0
  671. package/dist/routes/client-profiles.d.ts.map +1 -0
  672. package/dist/routes/client-profiles.js +128 -0
  673. package/dist/routes/client-profiles.js.map +1 -0
  674. package/dist/routes/compression.d.ts +2 -0
  675. package/dist/routes/compression.d.ts.map +1 -0
  676. package/dist/routes/compression.js +82 -0
  677. package/dist/routes/compression.js.map +1 -0
  678. package/dist/routes/conversations.d.ts +3 -0
  679. package/dist/routes/conversations.d.ts.map +1 -0
  680. package/dist/routes/conversations.js +215 -0
  681. package/dist/routes/conversations.js.map +1 -0
  682. package/dist/routes/docs.d.ts +2 -0
  683. package/dist/routes/docs.d.ts.map +1 -0
  684. package/dist/routes/docs.js +21 -0
  685. package/dist/routes/docs.js.map +1 -0
  686. package/dist/routes/embeddings.d.ts +2 -0
  687. package/dist/routes/embeddings.d.ts.map +1 -0
  688. package/dist/routes/embeddings.js +255 -0
  689. package/dist/routes/embeddings.js.map +1 -0
  690. package/dist/routes/fallback.d.ts +2 -0
  691. package/dist/routes/fallback.d.ts.map +1 -0
  692. package/dist/routes/fallback.js +631 -0
  693. package/dist/routes/fallback.js.map +1 -0
  694. package/dist/routes/free-tier.d.ts +22 -0
  695. package/dist/routes/free-tier.d.ts.map +1 -0
  696. package/dist/routes/free-tier.js +175 -0
  697. package/dist/routes/free-tier.js.map +1 -0
  698. package/dist/routes/gemini.d.ts +2 -0
  699. package/dist/routes/gemini.d.ts.map +1 -0
  700. package/dist/routes/gemini.js +218 -0
  701. package/dist/routes/gemini.js.map +1 -0
  702. package/dist/routes/health.d.ts +2 -0
  703. package/dist/routes/health.d.ts.map +1 -0
  704. package/dist/routes/health.js +72 -0
  705. package/dist/routes/health.js.map +1 -0
  706. package/dist/routes/keys.d.ts +17 -0
  707. package/dist/routes/keys.d.ts.map +1 -0
  708. package/dist/routes/keys.js +1787 -0
  709. package/dist/routes/keys.js.map +1 -0
  710. package/dist/routes/logs.d.ts +2 -0
  711. package/dist/routes/logs.d.ts.map +1 -0
  712. package/dist/routes/logs.js +88 -0
  713. package/dist/routes/logs.js.map +1 -0
  714. package/dist/routes/mcp.d.ts +4 -0
  715. package/dist/routes/mcp.d.ts.map +1 -0
  716. package/dist/routes/mcp.js +563 -0
  717. package/dist/routes/mcp.js.map +1 -0
  718. package/dist/routes/media.d.ts +2 -0
  719. package/dist/routes/media.d.ts.map +1 -0
  720. package/dist/routes/media.js +180 -0
  721. package/dist/routes/media.js.map +1 -0
  722. package/dist/routes/models.d.ts +2 -0
  723. package/dist/routes/models.d.ts.map +1 -0
  724. package/dist/routes/models.js +439 -0
  725. package/dist/routes/models.js.map +1 -0
  726. package/dist/routes/ollama.d.ts +7 -0
  727. package/dist/routes/ollama.d.ts.map +1 -0
  728. package/dist/routes/ollama.js +595 -0
  729. package/dist/routes/ollama.js.map +1 -0
  730. package/dist/routes/profiles.d.ts +3 -0
  731. package/dist/routes/profiles.d.ts.map +1 -0
  732. package/dist/routes/profiles.js +431 -0
  733. package/dist/routes/profiles.js.map +1 -0
  734. package/dist/routes/proxy.d.ts +32 -0
  735. package/dist/routes/proxy.d.ts.map +1 -0
  736. package/dist/routes/proxy.js +2793 -0
  737. package/dist/routes/proxy.js.map +1 -0
  738. package/dist/routes/responses.d.ts +1161 -0
  739. package/dist/routes/responses.d.ts.map +1 -0
  740. package/dist/routes/responses.js +1521 -0
  741. package/dist/routes/responses.js.map +1 -0
  742. package/dist/routes/settings.d.ts +2 -0
  743. package/dist/routes/settings.d.ts.map +1 -0
  744. package/dist/routes/settings.js +549 -0
  745. package/dist/routes/settings.js.map +1 -0
  746. package/dist/routes/status.d.ts +3 -0
  747. package/dist/routes/status.d.ts.map +1 -0
  748. package/dist/routes/status.js +214 -0
  749. package/dist/routes/status.js.map +1 -0
  750. package/dist/routes/update.d.ts +31 -0
  751. package/dist/routes/update.d.ts.map +1 -0
  752. package/dist/routes/update.js +536 -0
  753. package/dist/routes/update.js.map +1 -0
  754. package/dist/routes/url-tokens.d.ts +2 -0
  755. package/dist/routes/url-tokens.d.ts.map +1 -0
  756. package/dist/routes/url-tokens.js +31 -0
  757. package/dist/routes/url-tokens.js.map +1 -0
  758. package/dist/scripts/export-catalog.d.ts +2 -0
  759. package/dist/scripts/export-catalog.d.ts.map +1 -0
  760. package/dist/scripts/export-catalog.js +114 -0
  761. package/dist/scripts/export-catalog.js.map +1 -0
  762. package/dist/scripts/rotate-encryption-key.d.ts +81 -0
  763. package/dist/scripts/rotate-encryption-key.d.ts.map +1 -0
  764. package/dist/scripts/rotate-encryption-key.js +232 -0
  765. package/dist/scripts/rotate-encryption-key.js.map +1 -0
  766. package/dist/scripts/routing-sim.d.ts +2 -0
  767. package/dist/scripts/routing-sim.d.ts.map +1 -0
  768. package/dist/scripts/routing-sim.js +130 -0
  769. package/dist/scripts/routing-sim.js.map +1 -0
  770. package/dist/scripts/test-all-models.d.ts +2 -0
  771. package/dist/scripts/test-all-models.d.ts.map +1 -0
  772. package/dist/scripts/test-all-models.js +56 -0
  773. package/dist/scripts/test-all-models.js.map +1 -0
  774. package/dist/services/anthropic-map.d.ts +39 -0
  775. package/dist/services/anthropic-map.d.ts.map +1 -0
  776. package/dist/services/anthropic-map.js +138 -0
  777. package/dist/services/anthropic-map.js.map +1 -0
  778. package/dist/services/auth.d.ts +30 -0
  779. package/dist/services/auth.d.ts.map +1 -0
  780. package/dist/services/auth.js +124 -0
  781. package/dist/services/auth.js.map +1 -0
  782. package/dist/services/auto-discover.d.ts +77 -0
  783. package/dist/services/auto-discover.d.ts.map +1 -0
  784. package/dist/services/auto-discover.js +1047 -0
  785. package/dist/services/auto-discover.js.map +1 -0
  786. package/dist/services/backups.d.ts +67 -0
  787. package/dist/services/backups.d.ts.map +1 -0
  788. package/dist/services/backups.js +444 -0
  789. package/dist/services/backups.js.map +1 -0
  790. package/dist/services/builtin-model-discovery.d.ts +107 -0
  791. package/dist/services/builtin-model-discovery.d.ts.map +1 -0
  792. package/dist/services/builtin-model-discovery.js +296 -0
  793. package/dist/services/builtin-model-discovery.js.map +1 -0
  794. package/dist/services/cache.d.ts +190 -0
  795. package/dist/services/cache.d.ts.map +1 -0
  796. package/dist/services/cache.js +617 -0
  797. package/dist/services/cache.js.map +1 -0
  798. package/dist/services/catalog-sync.d.ts +257 -0
  799. package/dist/services/catalog-sync.d.ts.map +1 -0
  800. package/dist/services/catalog-sync.js +1041 -0
  801. package/dist/services/catalog-sync.js.map +1 -0
  802. package/dist/services/compression/config.d.ts +33 -0
  803. package/dist/services/compression/config.d.ts.map +1 -0
  804. package/dist/services/compression/config.js +158 -0
  805. package/dist/services/compression/config.js.map +1 -0
  806. package/dist/services/compression/engines/aging.d.ts +2 -0
  807. package/dist/services/compression/engines/aging.d.ts.map +1 -0
  808. package/dist/services/compression/engines/aging.js +53 -0
  809. package/dist/services/compression/engines/aging.js.map +1 -0
  810. package/dist/services/compression/engines/custom-filters.d.ts +4 -0
  811. package/dist/services/compression/engines/custom-filters.d.ts.map +1 -0
  812. package/dist/services/compression/engines/custom-filters.js +49 -0
  813. package/dist/services/compression/engines/custom-filters.js.map +1 -0
  814. package/dist/services/compression/engines/dedup.d.ts +2 -0
  815. package/dist/services/compression/engines/dedup.d.ts.map +1 -0
  816. package/dist/services/compression/engines/dedup.js +61 -0
  817. package/dist/services/compression/engines/dedup.js.map +1 -0
  818. package/dist/services/compression/engines/filter-definitions.d.ts +75 -0
  819. package/dist/services/compression/engines/filter-definitions.d.ts.map +1 -0
  820. package/dist/services/compression/engines/filter-definitions.js +45 -0
  821. package/dist/services/compression/engines/filter-definitions.js.map +1 -0
  822. package/dist/services/compression/engines/hard-budget.d.ts +2 -0
  823. package/dist/services/compression/engines/hard-budget.d.ts.map +1 -0
  824. package/dist/services/compression/engines/hard-budget.js +52 -0
  825. package/dist/services/compression/engines/hard-budget.js.map +1 -0
  826. package/dist/services/compression/engines/index.d.ts +9 -0
  827. package/dist/services/compression/engines/index.d.ts.map +1 -0
  828. package/dist/services/compression/engines/index.js +9 -0
  829. package/dist/services/compression/engines/index.js.map +1 -0
  830. package/dist/services/compression/engines/jsoncompact.d.ts +3 -0
  831. package/dist/services/compression/engines/jsoncompact.d.ts.map +1 -0
  832. package/dist/services/compression/engines/jsoncompact.js +145 -0
  833. package/dist/services/compression/engines/jsoncompact.js.map +1 -0
  834. package/dist/services/compression/engines/lite.d.ts +2 -0
  835. package/dist/services/compression/engines/lite.d.ts.map +1 -0
  836. package/dist/services/compression/engines/lite.js +34 -0
  837. package/dist/services/compression/engines/lite.js.map +1 -0
  838. package/dist/services/compression/engines/read-lifecycle.d.ts +2 -0
  839. package/dist/services/compression/engines/read-lifecycle.d.ts.map +1 -0
  840. package/dist/services/compression/engines/read-lifecycle.js +70 -0
  841. package/dist/services/compression/engines/read-lifecycle.js.map +1 -0
  842. package/dist/services/compression/engines/relevance.d.ts +2 -0
  843. package/dist/services/compression/engines/relevance.d.ts.map +1 -0
  844. package/dist/services/compression/engines/relevance.js +74 -0
  845. package/dist/services/compression/engines/relevance.js.map +1 -0
  846. package/dist/services/compression/engines/toolfilter.d.ts +2 -0
  847. package/dist/services/compression/engines/toolfilter.d.ts.map +1 -0
  848. package/dist/services/compression/engines/toolfilter.js +195 -0
  849. package/dist/services/compression/engines/toolfilter.js.map +1 -0
  850. package/dist/services/compression/fidelity-gate.d.ts +12 -0
  851. package/dist/services/compression/fidelity-gate.d.ts.map +1 -0
  852. package/dist/services/compression/fidelity-gate.js +90 -0
  853. package/dist/services/compression/fidelity-gate.js.map +1 -0
  854. package/dist/services/compression/helpers.d.ts +9 -0
  855. package/dist/services/compression/helpers.d.ts.map +1 -0
  856. package/dist/services/compression/helpers.js +52 -0
  857. package/dist/services/compression/helpers.js.map +1 -0
  858. package/dist/services/compression/pipeline.d.ts +7 -0
  859. package/dist/services/compression/pipeline.d.ts.map +1 -0
  860. package/dist/services/compression/pipeline.js +245 -0
  861. package/dist/services/compression/pipeline.js.map +1 -0
  862. package/dist/services/compression/preservation.d.ts +27 -0
  863. package/dist/services/compression/preservation.d.ts.map +1 -0
  864. package/dist/services/compression/preservation.js +109 -0
  865. package/dist/services/compression/preservation.js.map +1 -0
  866. package/dist/services/compression/registry.d.ts +6 -0
  867. package/dist/services/compression/registry.d.ts.map +1 -0
  868. package/dist/services/compression/registry.js +16 -0
  869. package/dist/services/compression/registry.js.map +1 -0
  870. package/dist/services/compression/stats.d.ts +29 -0
  871. package/dist/services/compression/stats.d.ts.map +1 -0
  872. package/dist/services/compression/stats.js +55 -0
  873. package/dist/services/compression/stats.js.map +1 -0
  874. package/dist/services/compression/types.d.ts +90 -0
  875. package/dist/services/compression/types.d.ts.map +1 -0
  876. package/dist/services/compression/types.js +2 -0
  877. package/dist/services/compression/types.js.map +1 -0
  878. package/dist/services/context-handoff.d.ts +22 -0
  879. package/dist/services/context-handoff.d.ts.map +1 -0
  880. package/dist/services/context-handoff.js +164 -0
  881. package/dist/services/context-handoff.js.map +1 -0
  882. package/dist/services/cooldown-probe.d.ts +24 -0
  883. package/dist/services/cooldown-probe.d.ts.map +1 -0
  884. package/dist/services/cooldown-probe.js +181 -0
  885. package/dist/services/cooldown-probe.js.map +1 -0
  886. package/dist/services/custom-endpoint.d.ts +58 -0
  887. package/dist/services/custom-endpoint.d.ts.map +1 -0
  888. package/dist/services/custom-endpoint.js +167 -0
  889. package/dist/services/custom-endpoint.js.map +1 -0
  890. package/dist/services/custom-media-register.d.ts +22 -0
  891. package/dist/services/custom-media-register.d.ts.map +1 -0
  892. package/dist/services/custom-media-register.js +45 -0
  893. package/dist/services/custom-media-register.js.map +1 -0
  894. package/dist/services/custom-model-register.d.ts +50 -0
  895. package/dist/services/custom-model-register.d.ts.map +1 -0
  896. package/dist/services/custom-model-register.js +103 -0
  897. package/dist/services/custom-model-register.js.map +1 -0
  898. package/dist/services/custom-model-seed.d.ts +18 -0
  899. package/dist/services/custom-model-seed.d.ts.map +1 -0
  900. package/dist/services/custom-model-seed.js +42 -0
  901. package/dist/services/custom-model-seed.js.map +1 -0
  902. package/dist/services/custom-model-sync.d.ts +37 -0
  903. package/dist/services/custom-model-sync.d.ts.map +1 -0
  904. package/dist/services/custom-model-sync.js +118 -0
  905. package/dist/services/custom-model-sync.js.map +1 -0
  906. package/dist/services/custom-model-tombstone.d.ts +5 -0
  907. package/dist/services/custom-model-tombstone.d.ts.map +1 -0
  908. package/dist/services/custom-model-tombstone.js +30 -0
  909. package/dist/services/custom-model-tombstone.js.map +1 -0
  910. package/dist/services/declarative-config.d.ts +356 -0
  911. package/dist/services/declarative-config.d.ts.map +1 -0
  912. package/dist/services/declarative-config.js +427 -0
  913. package/dist/services/declarative-config.js.map +1 -0
  914. package/dist/services/degradation.d.ts +40 -0
  915. package/dist/services/degradation.d.ts.map +1 -0
  916. package/dist/services/degradation.js +119 -0
  917. package/dist/services/degradation.js.map +1 -0
  918. package/dist/services/embeddings.d.ts +76 -0
  919. package/dist/services/embeddings.d.ts.map +1 -0
  920. package/dist/services/embeddings.js +319 -0
  921. package/dist/services/embeddings.js.map +1 -0
  922. package/dist/services/fusion.d.ts +162 -0
  923. package/dist/services/fusion.d.ts.map +1 -0
  924. package/dist/services/fusion.js +789 -0
  925. package/dist/services/fusion.js.map +1 -0
  926. package/dist/services/gemini-map.d.ts +14 -0
  927. package/dist/services/gemini-map.d.ts.map +1 -0
  928. package/dist/services/gemini-map.js +78 -0
  929. package/dist/services/gemini-map.js.map +1 -0
  930. package/dist/services/health.d.ts +52 -0
  931. package/dist/services/health.d.ts.map +1 -0
  932. package/dist/services/health.js +352 -0
  933. package/dist/services/health.js.map +1 -0
  934. package/dist/services/idempotency.d.ts +49 -0
  935. package/dist/services/idempotency.d.ts.map +1 -0
  936. package/dist/services/idempotency.js +140 -0
  937. package/dist/services/idempotency.js.map +1 -0
  938. package/dist/services/key-budget.d.ts +74 -0
  939. package/dist/services/key-budget.d.ts.map +1 -0
  940. package/dist/services/key-budget.js +200 -0
  941. package/dist/services/key-budget.js.map +1 -0
  942. package/dist/services/media.d.ts +123 -0
  943. package/dist/services/media.d.ts.map +1 -0
  944. package/dist/services/media.js +954 -0
  945. package/dist/services/media.js.map +1 -0
  946. package/dist/services/model-discovery.d.ts +126 -0
  947. package/dist/services/model-discovery.d.ts.map +1 -0
  948. package/dist/services/model-discovery.js +662 -0
  949. package/dist/services/model-discovery.js.map +1 -0
  950. package/dist/services/model-groups.d.ts +149 -0
  951. package/dist/services/model-groups.d.ts.map +1 -0
  952. package/dist/services/model-groups.js +314 -0
  953. package/dist/services/model-groups.js.map +1 -0
  954. package/dist/services/model-listing.d.ts +18 -0
  955. package/dist/services/model-listing.d.ts.map +1 -0
  956. package/dist/services/model-listing.js +94 -0
  957. package/dist/services/model-listing.js.map +1 -0
  958. package/dist/services/model-retirement.d.ts +29 -0
  959. package/dist/services/model-retirement.d.ts.map +1 -0
  960. package/dist/services/model-retirement.js +86 -0
  961. package/dist/services/model-retirement.js.map +1 -0
  962. package/dist/services/model-state.d.ts +90 -0
  963. package/dist/services/model-state.d.ts.map +1 -0
  964. package/dist/services/model-state.js +342 -0
  965. package/dist/services/model-state.js.map +1 -0
  966. package/dist/services/model-weight-overrides.d.ts +48 -0
  967. package/dist/services/model-weight-overrides.d.ts.map +1 -0
  968. package/dist/services/model-weight-overrides.js +127 -0
  969. package/dist/services/model-weight-overrides.js.map +1 -0
  970. package/dist/services/notification-emitter.d.ts +50 -0
  971. package/dist/services/notification-emitter.d.ts.map +1 -0
  972. package/dist/services/notification-emitter.js +177 -0
  973. package/dist/services/notification-emitter.js.map +1 -0
  974. package/dist/services/opencode-free-sync.d.ts +29 -0
  975. package/dist/services/opencode-free-sync.d.ts.map +1 -0
  976. package/dist/services/opencode-free-sync.js +206 -0
  977. package/dist/services/opencode-free-sync.js.map +1 -0
  978. package/dist/services/penalty-inspector.d.ts +56 -0
  979. package/dist/services/penalty-inspector.d.ts.map +1 -0
  980. package/dist/services/penalty-inspector.js +167 -0
  981. package/dist/services/penalty-inspector.js.map +1 -0
  982. package/dist/services/profile-models.d.ts +5 -0
  983. package/dist/services/profile-models.d.ts.map +1 -0
  984. package/dist/services/profile-models.js +62 -0
  985. package/dist/services/profile-models.js.map +1 -0
  986. package/dist/services/provider-credential.d.ts +19 -0
  987. package/dist/services/provider-credential.d.ts.map +1 -0
  988. package/dist/services/provider-credential.js +51 -0
  989. package/dist/services/provider-credential.js.map +1 -0
  990. package/dist/services/provider-quota.d.ts +64 -0
  991. package/dist/services/provider-quota.d.ts.map +1 -0
  992. package/dist/services/provider-quota.js +563 -0
  993. package/dist/services/provider-quota.js.map +1 -0
  994. package/dist/services/quirks.d.ts +0 -0
  995. package/dist/services/quirks.d.ts.map +1 -0
  996. package/dist/services/quirks.js +0 -0
  997. package/dist/services/quirks.js.map +1 -0
  998. package/dist/services/quota-forecast.d.ts +50 -0
  999. package/dist/services/quota-forecast.d.ts.map +1 -0
  1000. package/dist/services/quota-forecast.js +150 -0
  1001. package/dist/services/quota-forecast.js.map +1 -0
  1002. package/dist/services/quota-outlook.d.ts +4 -0
  1003. package/dist/services/quota-outlook.d.ts.map +1 -0
  1004. package/dist/services/quota-outlook.js +92 -0
  1005. package/dist/services/quota-outlook.js.map +1 -0
  1006. package/dist/services/ratelimit.d.ts +249 -0
  1007. package/dist/services/ratelimit.d.ts.map +1 -0
  1008. package/dist/services/ratelimit.js +1182 -0
  1009. package/dist/services/ratelimit.js.map +1 -0
  1010. package/dist/services/request-retention.d.ts +60 -0
  1011. package/dist/services/request-retention.d.ts.map +1 -0
  1012. package/dist/services/request-retention.js +229 -0
  1013. package/dist/services/request-retention.js.map +1 -0
  1014. package/dist/services/router.d.ts +384 -0
  1015. package/dist/services/router.d.ts.map +1 -0
  1016. package/dist/services/router.js +1986 -0
  1017. package/dist/services/router.js.map +1 -0
  1018. package/dist/services/scoring.d.ts +156 -0
  1019. package/dist/services/scoring.d.ts.map +1 -0
  1020. package/dist/services/scoring.js +435 -0
  1021. package/dist/services/scoring.js.map +1 -0
  1022. package/dist/services/url-tokens.d.ts +15 -0
  1023. package/dist/services/url-tokens.d.ts.map +1 -0
  1024. package/dist/services/url-tokens.js +55 -0
  1025. package/dist/services/url-tokens.js.map +1 -0
  1026. package/package.json +77 -4
  1027. package/README.md +0 -3
@@ -0,0 +1,954 @@
1
+ // Generative-media routing (image, video, and audio/TTS).
2
+ //
3
+ // Self-contained, exactly like embeddings: media models live in their OWN
4
+ // `media_models` table so they can NEVER enter the chat router's candidate pool
5
+ // (a chat request can't misroute to an image model) and never pollute the chat
6
+ // token budget. Each platform has a small adapter here; routing fails over
7
+ // across the providers serving the same modality. The rows are maintained in the
8
+ // published catalog and arrive via catalog-sync (monthly snapshot refresh
9
+ // every 12h) — never seeded by migrations.
10
+ import { getDb } from '../db/index.js';
11
+ import { parseRetryAfterMs } from '../providers/base.js';
12
+ import { RetryHintTracker } from '../lib/retry-hint.js';
13
+ import { secondsUntilNextMonth } from './key-budget.js';
14
+ import { getClientContext } from '../lib/client-context.js';
15
+ import { reserveProviderCredential } from './provider-credential.js';
16
+ import { proxyFetch } from '../lib/proxy.js';
17
+ import { bearerAuthHeader } from '../lib/credential.js';
18
+ import { assessProviderUrl } from '../lib/url-guard.js';
19
+ import { isOnCooldown, setCooldown } from './ratelimit.js';
20
+ import { SPEECHIFY_BASE_URL, SPEECHIFY_VERSION } from '../providers/speechify.js';
21
+ /** Platforms with a media adapter below. catalog-sync gates media rows on this
22
+ * (decoupled from the chat provider registry — e.g. SiliconFlow is media-only). */
23
+ export const MEDIA_PLATFORMS = new Set(['nvidia', 'pollinations', 'cloudflare', 'siliconflow', 'google', 'speechify']);
24
+ /** Video uses a dedicated optional catalog registry so binaries that predate
25
+ * this modality ignore the rows instead of accidentally ingesting them as
26
+ * chat models. Keep its adapter allowlist separate for the same reason. */
27
+ export const VIDEO_PLATFORMS = new Set(['pollinations', 'huggingface']);
28
+ /** Platforms whose free media path needs no API key (anonymous). */
29
+ const KEYLESS_CAPABLE = new Set(['pollinations']);
30
+ /** Platforms with a speech-to-text adapter below. catalog-sync gates the
31
+ * catalog's `transcriptionModels` entries on this, the way MEDIA_PLATFORMS
32
+ * gates the generative-media rows. */
33
+ export const TRANSCRIPTION_PLATFORMS = new Set(['groq', 'cloudflare']);
34
+ export class MediaError extends Error {
35
+ status;
36
+ /** Optional machine-readable error code surfaced in the OpenAI-shaped body. */
37
+ code;
38
+ /** Back-off the upstream provider stated (`Retry-After` header), in
39
+ * milliseconds — relayed to the client so an SDK sleeps the stated amount
40
+ * instead of hammering the chain, mirroring the embeddings path. */
41
+ retryAfterMs;
42
+ constructor(message, status, code, retryAfterMs) {
43
+ super(message);
44
+ this.status = status;
45
+ this.code = code;
46
+ this.retryAfterMs = retryAfterMs;
47
+ }
48
+ }
49
+ // OpenAI clients commonly send one of these voice names even when the user did
50
+ // not choose a voice explicitly. Native TTS providers use unrelated voice
51
+ // vocabularies, so forwarding the value verbatim makes an otherwise ordinary
52
+ // OpenAI request fail. Keep the translation here, at the provider boundary;
53
+ // custom OpenAI-compatible endpoints still receive the caller's value as-is.
54
+ // Pollinations' openai-audio route exposes the original OpenAI TTS vocabulary.
55
+ const POLLINATIONS_VOICES = new Set([
56
+ 'alloy', 'echo', 'fable', 'nova', 'onyx', 'shimmer',
57
+ ]);
58
+ const SILICONFLOW_NATIVE_VOICES = new Set([
59
+ 'alex', 'anna', 'bella', 'benjamin', 'charles', 'claire', 'david', 'diana',
60
+ ]);
61
+ const SILICONFLOW_OPENAI_VOICE_MAP = {
62
+ alloy: 'alex',
63
+ ash: 'claire',
64
+ ballad: 'diana',
65
+ coral: 'bella',
66
+ echo: 'benjamin',
67
+ fable: 'charles',
68
+ nova: 'anna',
69
+ onyx: 'david',
70
+ sage: 'benjamin',
71
+ shimmer: 'bella',
72
+ verse: 'charles',
73
+ marin: 'anna',
74
+ cedar: 'david',
75
+ };
76
+ const GEMINI_NATIVE_VOICES = [
77
+ 'Zephyr', 'Puck', 'Charon', 'Kore', 'Fenrir', 'Leda', 'Orus', 'Aoede',
78
+ 'Callirrhoe', 'Autonoe', 'Enceladus', 'Iapetus', 'Umbriel', 'Algieba',
79
+ 'Despina', 'Erinome', 'Algenib', 'Rasalgethi', 'Laomedeia', 'Achernar',
80
+ 'Alnilam', 'Schedar', 'Gacrux', 'Pulcherrima', 'Achird', 'Zubenelgenubi',
81
+ 'Vindemiatrix', 'Sadachbia', 'Sadaltager', 'Sulafat',
82
+ ];
83
+ const GEMINI_NATIVE_VOICE_LOOKUP = new Map(GEMINI_NATIVE_VOICES.map(voice => [voice.toLowerCase(), voice]));
84
+ const GEMINI_OPENAI_VOICE_MAP = {
85
+ alloy: 'Schedar', // even
86
+ ash: 'Charon', // informative
87
+ ballad: 'Enceladus', // breathy
88
+ coral: 'Sulafat', // warm
89
+ echo: 'Iapetus', // clear
90
+ fable: 'Puck', // upbeat
91
+ nova: 'Zephyr', // bright
92
+ onyx: 'Gacrux', // mature
93
+ sage: 'Sadaltager', // knowledgeable
94
+ shimmer: 'Achernar', // soft
95
+ verse: 'Laomedeia', // upbeat
96
+ marin: 'Aoede', // breezy
97
+ cedar: 'Kore', // firm
98
+ };
99
+ function normalizedVoice(voice) {
100
+ const value = voice?.trim().toLowerCase();
101
+ return value || undefined;
102
+ }
103
+ function siliconFlowVoice(modelId, requested) {
104
+ const raw = requested?.trim();
105
+ const prefix = `${modelId}:`;
106
+ const qualifiedNative = raw?.startsWith(prefix)
107
+ ? normalizedVoice(raw.slice(prefix.length))
108
+ : undefined;
109
+ const name = qualifiedNative && SILICONFLOW_NATIVE_VOICES.has(qualifiedNative)
110
+ ? qualifiedNative
111
+ : normalizedVoice(raw);
112
+ const native = name && SILICONFLOW_NATIVE_VOICES.has(name) ? name : undefined;
113
+ const mapped = name ? SILICONFLOW_OPENAI_VOICE_MAP[name] : undefined;
114
+ return `${modelId}:${native ?? mapped ?? 'alex'}`;
115
+ }
116
+ function pollinationsVoice(requested) {
117
+ const voice = normalizedVoice(requested);
118
+ return voice && POLLINATIONS_VOICES.has(voice) ? voice : 'alloy';
119
+ }
120
+ function geminiVoice(requested) {
121
+ const voice = normalizedVoice(requested);
122
+ if (!voice)
123
+ return 'Kore';
124
+ return GEMINI_NATIVE_VOICE_LOOKUP.get(voice)
125
+ ?? GEMINI_OPENAI_VOICE_MAP[voice]
126
+ ?? 'Kore';
127
+ }
128
+ // Media generations are slower than chat — a cold FLUX/SDXL run can take 30-60s.
129
+ const FETCH_TIMEOUT_MS = 60_000;
130
+ // Video providers submit long-running jobs and can legitimately need several
131
+ // minutes before the MP4 is ready. This is still bounded end-to-end.
132
+ const VIDEO_FETCH_TIMEOUT_MS = 5 * 60_000;
133
+ /** Bail out of a long-running media job whose API caller has hung up. Video is
134
+ * the only surface here that can poll for minutes, so without this a client
135
+ * that disconnects after two seconds still costs a full generation — and a
136
+ * second one, when the chain fails over to the next provider. */
137
+ function throwIfClientGone(signal) {
138
+ if (signal?.aborted)
139
+ throw new MediaError('client closed the request', 499);
140
+ }
141
+ export function listMediaModels(modality) {
142
+ return getDb()
143
+ .prepare('SELECT * FROM media_models WHERE modality = ? AND enabled = 1 ORDER BY priority, id')
144
+ .all(modality);
145
+ }
146
+ /** All media models (both modalities, including disabled) for the dashboard. */
147
+ export function listAllMediaModels() {
148
+ return getDb()
149
+ .prepare('SELECT * FROM media_models ORDER BY modality, priority, id')
150
+ .all();
151
+ }
152
+ async function mediaFetch(url, platform, modality, init, timeoutMs = FETCH_TIMEOUT_MS,
153
+ /** The caller's own cancellation (a disconnected API client), folded in on
154
+ * top of the per-request timeout so a departed client drops the upstream
155
+ * request instead of leaving it to run out the clock. */
156
+ clientSignal) {
157
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
158
+ const signal = clientSignal ? AbortSignal.any([timeoutSignal, clientSignal]) : timeoutSignal;
159
+ const r = await proxyFetch(url, { ...init, signal }, platform, modality, timeoutMs);
160
+ if (!r.ok) {
161
+ const body = await r.text().catch(() => '');
162
+ throw new MediaError(`${platform} ${r.status}: ${body.slice(0, 200)}`, r.status, undefined, parseRetryAfterMs(r.headers?.get('retry-after') ?? null));
163
+ }
164
+ return r;
165
+ }
166
+ function parseSize(size) {
167
+ if (size && /^\d+x\d+$/.test(size)) {
168
+ const [w, h] = size.split('x').map(Number);
169
+ return [w, h];
170
+ }
171
+ return [1024, 1024];
172
+ }
173
+ function parseCfKey(key) {
174
+ if (!key)
175
+ throw new MediaError('cloudflare key required (account_id:token)', 401);
176
+ const sep = key.indexOf(':');
177
+ if (sep === -1)
178
+ throw new MediaError('cloudflare key is not in account_id:token form', 500);
179
+ return { accountId: key.slice(0, sep), token: key.slice(sep + 1) };
180
+ }
181
+ /** Adapter request flavor for a generative media row, read from meta_json.
182
+ * One platform can host several deployment styles: Cloudflare takes a JSON
183
+ * body for most image models but multipart/form-data for the FLUX.2 family.
184
+ * Absent meta = the platform's default style, so old rows are untouched. */
185
+ function mediaRequestStyle(row) {
186
+ if (!row.meta_json)
187
+ return null;
188
+ try {
189
+ const parsed = JSON.parse(row.meta_json);
190
+ return typeof parsed?.requestStyle === 'string' ? parsed.requestStyle : null;
191
+ }
192
+ catch {
193
+ return null;
194
+ }
195
+ }
196
+ function videoMeta(row) {
197
+ if (!row.meta_json)
198
+ return {};
199
+ try {
200
+ const parsed = JSON.parse(row.meta_json);
201
+ return parsed && typeof parsed === 'object' ? parsed : {};
202
+ }
203
+ catch {
204
+ return {};
205
+ }
206
+ }
207
+ function contentTypeFor(fmt) {
208
+ switch (fmt) {
209
+ case 'wav': return 'audio/wav';
210
+ case 'opus': return 'audio/ogg';
211
+ case 'aac': return 'audio/aac';
212
+ case 'flac': return 'audio/flac';
213
+ case 'pcm': return 'audio/L16';
214
+ case 'mp3':
215
+ default: return 'audio/mpeg';
216
+ }
217
+ }
218
+ function parseRate(mime) {
219
+ const m = mime?.match(/rate=(\d+)/);
220
+ return m ? Number(m[1]) : undefined;
221
+ }
222
+ /** Wrap raw 16-bit mono PCM (what Gemini TTS returns) in a WAV header so any
223
+ * client can play it without knowing the sample rate out of band. */
224
+ function wrapPcmAsWav(pcm, sampleRate) {
225
+ const numChannels = 1;
226
+ const bitsPerSample = 16;
227
+ const byteRate = (sampleRate * numChannels * bitsPerSample) / 8;
228
+ const blockAlign = (numChannels * bitsPerSample) / 8;
229
+ const header = Buffer.alloc(44);
230
+ header.write('RIFF', 0);
231
+ header.writeUInt32LE(36 + pcm.length, 4);
232
+ header.write('WAVE', 8);
233
+ header.write('fmt ', 12);
234
+ header.writeUInt32LE(16, 16);
235
+ header.writeUInt16LE(1, 20); // PCM
236
+ header.writeUInt16LE(numChannels, 22);
237
+ header.writeUInt32LE(sampleRate, 24);
238
+ header.writeUInt32LE(byteRate, 28);
239
+ header.writeUInt16LE(blockAlign, 32);
240
+ header.writeUInt16LE(bitsPerSample, 34);
241
+ header.write('data', 36);
242
+ header.writeUInt32LE(pcm.length, 40);
243
+ return Buffer.concat([header, pcm]);
244
+ }
245
+ async function callImageProvider(row, credential, p) {
246
+ const key = credential.key;
247
+ const [w, h] = parseSize(p.size);
248
+ switch (row.platform) {
249
+ case 'custom': {
250
+ if (!credential.baseUrl)
251
+ throw new MediaError('custom image provider is missing base_url', 500);
252
+ const body = { model: row.model_id, prompt: p.prompt };
253
+ if (p.n !== undefined)
254
+ body.n = p.n;
255
+ if (p.size)
256
+ body.size = p.size;
257
+ const r = await mediaFetch(`${credential.baseUrl}/images/generations`, 'custom', 'image', {
258
+ method: 'POST',
259
+ headers: { 'Content-Type': 'application/json', ...bearerAuthHeader(key) },
260
+ body: JSON.stringify(body),
261
+ });
262
+ const j = (await r.json());
263
+ return (j.data ?? []).map(i => ({ b64_json: i.b64_json, url: i.url }));
264
+ }
265
+ case 'nvidia': {
266
+ // NVIDIA NIM image models live at ai.api.nvidia.com/v1/genai/{model};
267
+ // response is { artifacts: [{ base64 }] }. The hosted FLUX deployments
268
+ // do not share one request schema: FLUX.1 Dev rejects the four-step
269
+ // defaults used by Schnell, while FLUX.2 Klein rejects `mode` and
270
+ // guidance fields entirely.
271
+ let body;
272
+ switch (row.model_id) {
273
+ case 'black-forest-labs/flux.1-dev':
274
+ body = {
275
+ prompt: p.prompt,
276
+ mode: 'base',
277
+ steps: 28,
278
+ width: 1024,
279
+ height: 1024,
280
+ cfg_scale: 3.5,
281
+ };
282
+ break;
283
+ case 'black-forest-labs/flux.2-klein-4b':
284
+ body = { prompt: p.prompt, steps: 4, width: 1024, height: 1024 };
285
+ break;
286
+ default:
287
+ body = { prompt: p.prompt, mode: 'base', steps: 4, width: w, height: h };
288
+ }
289
+ const r = await mediaFetch(`https://ai.api.nvidia.com/v1/genai/${row.model_id}`, 'nvidia', 'image', {
290
+ method: 'POST',
291
+ headers: { 'Content-Type': 'application/json', Accept: 'application/json', Authorization: `Bearer ${key}` },
292
+ body: JSON.stringify(body),
293
+ });
294
+ const j = (await r.json());
295
+ return (j.artifacts ?? []).map(a => ({ b64_json: a.base64 }));
296
+ }
297
+ case 'pollinations': {
298
+ // Keyless GET image endpoint returns raw image bytes.
299
+ const url = `https://image.pollinations.ai/prompt/${encodeURIComponent(p.prompt)}?width=${w}&height=${h}&nologo=true&model=${encodeURIComponent(row.model_id)}`;
300
+ const r = await mediaFetch(url, 'pollinations', 'image', { method: 'GET' });
301
+ const buf = Buffer.from(await r.arrayBuffer());
302
+ return [{ b64_json: buf.toString('base64') }];
303
+ }
304
+ case 'cloudflare': {
305
+ const { accountId, token } = parseCfKey(key);
306
+ const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${row.model_id}`;
307
+ // Most CF image models take a JSON body. The FLUX.2 family declares a
308
+ // multipart input schema and rejects JSON outright ("required properties
309
+ // at '/' are 'multipart'"), so those rows opt in through catalog meta.
310
+ let init;
311
+ if (mediaRequestStyle(row) === 'multipart') {
312
+ // Never set Content-Type by hand — FormData supplies the boundary.
313
+ const form = new FormData();
314
+ form.append('prompt', p.prompt);
315
+ form.append('width', String(w));
316
+ form.append('height', String(h));
317
+ init = { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: form };
318
+ }
319
+ else {
320
+ // Cloudflare's FLUX.1 Schnell schema is prompt-only; dimensions that
321
+ // the other JSON image models accept are rejected as extra fields.
322
+ const body = row.model_id === '@cf/black-forest-labs/flux-1-schnell'
323
+ ? { prompt: p.prompt }
324
+ : { prompt: p.prompt, width: w, height: h };
325
+ init = {
326
+ method: 'POST',
327
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
328
+ body: JSON.stringify(body),
329
+ };
330
+ }
331
+ const r = await mediaFetch(url, 'cloudflare', 'image', init);
332
+ // FLUX returns JSON { result: { image: <b64> } }; SDXL returns raw PNG bytes.
333
+ const ct = r.headers.get('content-type') ?? '';
334
+ if (ct.includes('application/json')) {
335
+ const j = (await r.json());
336
+ const b64 = j.result?.image;
337
+ if (!b64)
338
+ throw new MediaError('cloudflare returned no image', 502);
339
+ return [{ b64_json: b64 }];
340
+ }
341
+ const buf = Buffer.from(await r.arrayBuffer());
342
+ return [{ b64_json: buf.toString('base64') }];
343
+ }
344
+ case 'siliconflow': {
345
+ const r = await mediaFetch('https://api.siliconflow.com/v1/images/generations', 'siliconflow', 'image', {
346
+ method: 'POST',
347
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
348
+ body: JSON.stringify({ model: row.model_id, prompt: p.prompt, image_size: `${w}x${h}` }),
349
+ });
350
+ const j = (await r.json());
351
+ return (j.images ?? j.data ?? []).map(i => ({ url: i.url }));
352
+ }
353
+ default:
354
+ throw new MediaError(`no image adapter for platform '${row.platform}'`, 500);
355
+ }
356
+ }
357
+ async function callHuggingFaceVideo(row, key, p, clientSignal) {
358
+ if (!key)
359
+ throw new MediaError('huggingface key required', 401);
360
+ const providerModelId = videoMeta(row).providerModelId;
361
+ if (!providerModelId) {
362
+ throw new MediaError(`huggingface video model '${row.model_id}' is missing providerModelId metadata`, 500);
363
+ }
364
+ const deadline = Date.now() + VIDEO_FETCH_TIMEOUT_MS;
365
+ const remaining = () => Math.max(1, deadline - Date.now());
366
+ const query = '?_subdomain=queue';
367
+ const queueUrl = `https://router.huggingface.co/fal-ai/${providerModelId}${query}`;
368
+ const submitted = await mediaFetch(queueUrl, 'huggingface', 'video', {
369
+ method: 'POST',
370
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
371
+ body: JSON.stringify({
372
+ prompt: p.prompt,
373
+ ...(p.seed !== undefined ? { seed: p.seed } : {}),
374
+ }),
375
+ }, remaining(), clientSignal);
376
+ const queue = (await submitted.json());
377
+ if (!queue.request_id || !queue.response_url) {
378
+ throw new MediaError('huggingface returned no video job id', 502);
379
+ }
380
+ // Hugging Face's fal router returns a fal queue response URL. Only reuse its
381
+ // path; all polling stays on router.huggingface.co so the HF token is never
382
+ // sent to an arbitrary host supplied in an upstream JSON body.
383
+ let responsePath;
384
+ try {
385
+ responsePath = new URL(queue.response_url, 'https://queue.fal.run').pathname;
386
+ }
387
+ catch {
388
+ throw new MediaError('huggingface returned an invalid video job URL', 502);
389
+ }
390
+ const routedJobUrl = `https://router.huggingface.co/fal-ai${responsePath}`;
391
+ let status = queue.status ?? 'IN_QUEUE';
392
+ while (status !== 'COMPLETED') {
393
+ if (status === 'FAILED' || status === 'CANCELLED') {
394
+ throw new MediaError(`huggingface video job ${status.toLowerCase()}`, 502);
395
+ }
396
+ if (remaining() <= 1)
397
+ throw new MediaError('huggingface video generation timed out', 504);
398
+ throwIfClientGone(clientSignal);
399
+ await new Promise(resolve => setTimeout(resolve, 500));
400
+ const polled = await mediaFetch(`${routedJobUrl}/status${query}`, 'huggingface', 'video', {
401
+ method: 'GET',
402
+ headers: { Authorization: `Bearer ${key}` },
403
+ }, remaining(), clientSignal);
404
+ const state = (await polled.json());
405
+ if (!state.status)
406
+ throw new MediaError('huggingface returned an invalid video job status', 502);
407
+ status = state.status;
408
+ }
409
+ const completed = await mediaFetch(`${routedJobUrl}${query}`, 'huggingface', 'video', {
410
+ method: 'GET',
411
+ headers: { Authorization: `Bearer ${key}` },
412
+ }, remaining(), clientSignal);
413
+ const result = (await completed.json());
414
+ const videoUrl = result.video?.url;
415
+ if (!videoUrl)
416
+ throw new MediaError('huggingface returned no video URL', 502);
417
+ // Every other URL this adapter contacts is built from constants; this one
418
+ // comes out of an upstream JSON body, which is exactly the shape #440's
419
+ // guard exists for. Refuse anything that is not plain http(s) on a routable
420
+ // address before spending a request on it — a result URL pointing at cloud
421
+ // metadata or a link-local address would otherwise be fetched and streamed
422
+ // straight back to the caller.
423
+ const verdict = await assessProviderUrl(videoUrl);
424
+ if (!verdict.allowed) {
425
+ throw new MediaError(`huggingface returned an unusable video URL: ${verdict.reason}`, 502);
426
+ }
427
+ const downloaded = await mediaFetch(videoUrl, 'huggingface', 'video', { method: 'GET' }, remaining(), clientSignal);
428
+ const video = Buffer.from(await downloaded.arrayBuffer());
429
+ return {
430
+ video,
431
+ contentType: downloaded.headers.get('content-type') ?? result.video?.content_type ?? 'video/mp4',
432
+ };
433
+ }
434
+ async function callVideoProvider(row, credential, p, clientSignal) {
435
+ const key = credential.key;
436
+ switch (row.platform) {
437
+ case 'pollinations': {
438
+ if (!key)
439
+ throw new MediaError('pollinations key required', 401);
440
+ // Every Pollinations video model advertises its own allowed durations
441
+ // (nova-reel 6-120 in steps of 6, veo 4/6/8, minimax-h3 exactly 5,
442
+ // wan 5/10/15, seedance-2.5 exactly 4...). There is no length that is
443
+ // valid everywhere, so an omitted `duration` is left off the query and
444
+ // the provider applies the model's own default instead of a guess that
445
+ // would 400 on most of the roster.
446
+ const duration = p.duration;
447
+ if (duration !== undefined && row.model_id === 'nova-reel'
448
+ && (duration < 6 || duration > 120 || duration % 6 !== 0)) {
449
+ throw new MediaError('nova-reel duration must be a multiple of 6 between 6 and 120 seconds', 400);
450
+ }
451
+ const params = new URLSearchParams({ model: row.model_id });
452
+ if (duration !== undefined)
453
+ params.set('duration', String(duration));
454
+ if (p.aspectRatio)
455
+ params.set('aspectRatio', p.aspectRatio);
456
+ if (p.image)
457
+ params.set('image', p.image);
458
+ if (p.seed !== undefined)
459
+ params.set('seed', String(p.seed));
460
+ if (p.audio !== undefined)
461
+ params.set('audio', String(p.audio));
462
+ const r = await mediaFetch(`https://gen.pollinations.ai/video/${encodeURIComponent(p.prompt)}?${params}`, 'pollinations', 'video', { method: 'GET', headers: { Authorization: `Bearer ${key}` } }, VIDEO_FETCH_TIMEOUT_MS, clientSignal);
463
+ return {
464
+ video: Buffer.from(await r.arrayBuffer()),
465
+ contentType: r.headers.get('content-type') ?? 'video/mp4',
466
+ };
467
+ }
468
+ case 'huggingface':
469
+ return callHuggingFaceVideo(row, key, p, clientSignal);
470
+ default:
471
+ throw new MediaError(`no video adapter for platform '${row.platform}'`, 500);
472
+ }
473
+ }
474
+ async function callSpeechProvider(row, credential, p) {
475
+ const key = credential.key;
476
+ switch (row.platform) {
477
+ case 'speechify': {
478
+ const fmt = p.format ?? 'mp3';
479
+ // Speechify exposes ogg, not OpenAI's opus name. Do not silently return
480
+ // a different codec for unsupported formats such as pcm or flac.
481
+ if (!['mp3', 'wav', 'ogg', 'aac'].includes(fmt)) {
482
+ throw new MediaError('Speechify supports mp3, wav, ogg and aac audio formats', 400);
483
+ }
484
+ const standardVoices = new Set(['alloy', 'ash', 'ballad', 'coral', 'echo', 'fable', 'nova', 'onyx', 'sage', 'shimmer', 'verse', 'marin', 'cedar']);
485
+ const voice = !p.voice || standardVoices.has(p.voice.toLowerCase()) ? 'alec' : p.voice;
486
+ const r = await mediaFetch(`${SPEECHIFY_BASE_URL}/audio/speech`, 'speechify', 'audio', {
487
+ method: 'POST',
488
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}`, 'Speechify-Version': SPEECHIFY_VERSION },
489
+ body: JSON.stringify({ model: row.model_id, input: p.input, voice_id: voice, audio_format: fmt }),
490
+ });
491
+ const j = await r.json();
492
+ const b64 = j.audio_data;
493
+ if (typeof b64 !== 'string' || !b64 || b64.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(b64)) {
494
+ throw new MediaError('Speechify returned missing or invalid base64 audio', 502);
495
+ }
496
+ if (j.audio_format !== fmt)
497
+ throw new MediaError('Speechify returned a different audio format', 502);
498
+ return { audio: Buffer.from(b64, 'base64'), contentType: fmt === 'ogg' ? 'audio/ogg' : contentTypeFor(fmt) };
499
+ }
500
+ case 'custom': {
501
+ if (!credential.baseUrl)
502
+ throw new MediaError('custom audio provider is missing base_url', 500);
503
+ const fmt = p.format ?? 'mp3';
504
+ const body = { model: row.model_id, input: p.input };
505
+ if (p.voice)
506
+ body.voice = p.voice;
507
+ if (p.format)
508
+ body.response_format = p.format;
509
+ const r = await mediaFetch(`${credential.baseUrl}/audio/speech`, 'custom', 'audio', {
510
+ method: 'POST',
511
+ headers: { 'Content-Type': 'application/json', ...bearerAuthHeader(key) },
512
+ body: JSON.stringify(body),
513
+ });
514
+ return {
515
+ audio: Buffer.from(await r.arrayBuffer()),
516
+ contentType: r.headers.get('content-type') ?? contentTypeFor(fmt),
517
+ };
518
+ }
519
+ case 'cloudflare': {
520
+ const { accountId, token } = parseCfKey(key);
521
+ const r = await mediaFetch(`https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${row.model_id}`, 'cloudflare', 'audio', {
522
+ method: 'POST',
523
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
524
+ // MeloTTS has a language selector, not a voice selector. In particular,
525
+ // OpenAI's default `alloy` must never be sent as `lang`.
526
+ body: JSON.stringify({ prompt: p.input, lang: 'en' }),
527
+ });
528
+ const j = (await r.json());
529
+ const b64 = j.result?.audio;
530
+ if (!b64)
531
+ throw new MediaError('cloudflare returned no audio', 502);
532
+ return { audio: Buffer.from(b64, 'base64'), contentType: 'audio/mpeg' };
533
+ }
534
+ case 'siliconflow': {
535
+ const fmt = p.format ?? 'mp3';
536
+ const r = await mediaFetch('https://api.siliconflow.com/v1/audio/speech', 'siliconflow', 'audio', {
537
+ method: 'POST',
538
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
539
+ body: JSON.stringify({
540
+ model: row.model_id,
541
+ input: p.input,
542
+ voice: siliconFlowVoice(row.model_id, p.voice),
543
+ response_format: fmt,
544
+ }),
545
+ });
546
+ return { audio: Buffer.from(await r.arrayBuffer()), contentType: contentTypeFor(fmt) };
547
+ }
548
+ case 'pollinations': {
549
+ // OpenAI-shaped chat-completions with the audio modality returns b64 audio.
550
+ // The anonymous tier needs no key; only send one when it's a real sk_ token.
551
+ const realKey = key && key.startsWith('sk_') ? key : null;
552
+ const r = await mediaFetch('https://gen.pollinations.ai/v1/chat/completions', 'pollinations', 'audio', {
553
+ method: 'POST',
554
+ headers: { 'Content-Type': 'application/json', ...(realKey ? { Authorization: `Bearer ${realKey}` } : {}) },
555
+ body: JSON.stringify({
556
+ model: row.model_id,
557
+ modalities: ['text', 'audio'],
558
+ audio: { voice: pollinationsVoice(p.voice), format: p.format ?? 'mp3' },
559
+ messages: [{ role: 'user', content: p.input }],
560
+ }),
561
+ });
562
+ const j = (await r.json());
563
+ const b64 = j.choices?.[0]?.message?.audio?.data;
564
+ if (!b64)
565
+ throw new MediaError('pollinations returned no audio', 502);
566
+ return { audio: Buffer.from(b64, 'base64'), contentType: contentTypeFor(p.format ?? 'mp3') };
567
+ }
568
+ case 'google': {
569
+ // Gemini TTS via generateContent (AUDIO modality) returns base64 PCM
570
+ // (L16, mono, ~24kHz); wrap it in a WAV header so clients can play it.
571
+ const r = await mediaFetch(`https://generativelanguage.googleapis.com/v1beta/models/${row.model_id}:generateContent`, 'google', 'audio', {
572
+ method: 'POST',
573
+ headers: { 'Content-Type': 'application/json', 'x-goog-api-key': key ?? '' },
574
+ body: JSON.stringify({
575
+ contents: [{ parts: [{ text: p.input }] }],
576
+ generationConfig: {
577
+ responseModalities: ['AUDIO'],
578
+ speechConfig: { voiceConfig: { prebuiltVoiceConfig: { voiceName: geminiVoice(p.voice) } } },
579
+ },
580
+ }),
581
+ });
582
+ const j = (await r.json());
583
+ const part = j.candidates?.[0]?.content?.parts?.find(pt => pt.inlineData?.data);
584
+ const b64 = part?.inlineData?.data;
585
+ if (!b64)
586
+ throw new MediaError('gemini returned no audio', 502);
587
+ const rate = parseRate(part?.inlineData?.mimeType) ?? 24000;
588
+ return { audio: wrapPcmAsWav(Buffer.from(b64, 'base64'), rate), contentType: 'audio/wav' };
589
+ }
590
+ default:
591
+ throw new MediaError(`no speech adapter for platform '${row.platform}'`, 500);
592
+ }
593
+ }
594
+ /** Map the request's `model` to a candidate chain within one modality:
595
+ * 'auto'/empty → every enabled provider for the modality (failover order),
596
+ * a provider model id → just that row. */
597
+ function resolveMediaChain(model, modality) {
598
+ const rows = listMediaModels(modality);
599
+ if (rows.length === 0) {
600
+ throw new MediaError(`No enabled ${modality} providers configured.`, 503);
601
+ }
602
+ if (!model || model === 'auto')
603
+ return rows;
604
+ const matches = rows.filter(r => r.model_id === model);
605
+ if (matches.length === 0) {
606
+ throw new MediaError(`Unknown ${modality} model '${model}'. Use 'auto' or a provider model id.`, 400);
607
+ }
608
+ return matches;
609
+ }
610
+ function logMedia(row, keyId, status, latencyMs, error) {
611
+ try {
612
+ const client = getClientContext();
613
+ getDb()
614
+ .prepare(`INSERT INTO requests (platform, model_id, key_id, status, input_tokens, output_tokens, latency_ms, error, request_type, client_ip, client_user_agent, client_agent)
615
+ VALUES (?, ?, ?, ?, 0, 0, ?, ?, ?, ?, ?, ?)`)
616
+ .run(row.platform, row.model_id, keyId, status, latencyMs, error, row.modality, client.ip, client.userAgent, client.agent);
617
+ }
618
+ catch (e) {
619
+ console.error('Failed to log media request:', e);
620
+ }
621
+ }
622
+ function chainError(modality, lastError, hints) {
623
+ // Only statuses the CALLER can act on are passed through. 400 (bad prompt,
624
+ // duration the model does not accept) and 413 (upload too large) describe
625
+ // the caller's own request, and 429 is the long-standing rate-limit signal.
626
+ // An upstream 401 is deliberately NOT among them: it means the operator's
627
+ // provider key was rejected, and forwarding it would tell an OpenAI client
628
+ // its own gateway key is bad — which sends SDKs into a credential error
629
+ // instead of the retryable upstream failure this actually is.
630
+ const status = lastError && [400, 413, 429].includes(lastError.status)
631
+ ? lastError.status
632
+ : 502;
633
+ return new MediaError(`All ${modality} providers failed${lastError ? ` (last: ${lastError.message.slice(0, 160)})` : ' (no usable keys)'}.`, status, lastError?.code,
634
+ // A 429 from one provider is failed over, so its Retry-After is only the
635
+ // client's business when EVERY provider was rate limited with a stated
636
+ // delay — then the soonest one (same rule as the chat exhaustion path).
637
+ status === 429 ? hints.retryAfterMs() : undefined);
638
+ }
639
+ /** Generate image(s), failing over across providers serving the modality. */
640
+ export async function runImageGeneration(model, params) {
641
+ const chain = resolveMediaChain(model, 'image');
642
+ let lastError = null;
643
+ // Only a chain rate limited end to end earns a Retry-After (the soonest).
644
+ const hints = new RetryHintTracker();
645
+ for (const row of chain) {
646
+ const { credential, budgetBlocked, budgetRetryAfterMs } = KEYLESS_CAPABLE.has(row.platform)
647
+ ? { credential: { id: null, key: null, baseUrl: null, release: () => { } }, budgetBlocked: false, budgetRetryAfterMs: undefined }
648
+ : reserveProviderCredential(row, 0);
649
+ if (!credential) {
650
+ if (budgetBlocked) {
651
+ lastError = new MediaError('Key budget exhausted', 429, 'quota_exceeded');
652
+ hints.record(429, budgetRetryAfterMs ?? secondsUntilNextMonth() * 1000);
653
+ }
654
+ continue;
655
+ }
656
+ const started = Date.now();
657
+ try {
658
+ const images = await callImageProvider(row, credential, params);
659
+ if (!images.length || images.every(i => !i.b64_json && !i.url)) {
660
+ throw new MediaError('upstream returned no image', 502);
661
+ }
662
+ logMedia(row, credential.id, 'success', Date.now() - started, null);
663
+ return { platform: row.platform, modelId: row.model_id, images };
664
+ }
665
+ catch (err) {
666
+ const e = err instanceof MediaError ? err : new MediaError(String(err?.message ?? err), 502);
667
+ logMedia(row, credential.id, 'error', Date.now() - started, e.message.slice(0, 300));
668
+ lastError = e;
669
+ hints.record(e.status, e.retryAfterMs);
670
+ }
671
+ finally {
672
+ credential.release();
673
+ }
674
+ }
675
+ throw chainError('image', lastError, hints);
676
+ }
677
+ /** Generate a video, failing over across catalogued text-to-video providers.
678
+ * `clientSignal` is the API caller's connection: when it aborts, the in-flight
679
+ * upstream request is dropped and no further provider is tried. */
680
+ export async function runVideoGeneration(model, params, clientSignal) {
681
+ const chain = resolveMediaChain(model, 'video');
682
+ let lastError = null;
683
+ // Only a chain rate limited end to end earns a Retry-After (the soonest).
684
+ const hints = new RetryHintTracker();
685
+ for (const row of chain) {
686
+ throwIfClientGone(clientSignal);
687
+ const { credential, budgetBlocked, budgetRetryAfterMs } = reserveProviderCredential(row, 0, keyId => isOnCooldown(row.platform, row.model_id, keyId));
688
+ if (!credential) {
689
+ if (budgetBlocked) {
690
+ lastError = new MediaError('Key budget exhausted', 429, 'quota_exceeded');
691
+ hints.record(429, budgetRetryAfterMs ?? secondsUntilNextMonth() * 1000);
692
+ }
693
+ continue;
694
+ }
695
+ const started = Date.now();
696
+ try {
697
+ const out = await callVideoProvider(row, credential, params, clientSignal);
698
+ if (!out.video.length)
699
+ throw new MediaError('upstream returned no video', 502);
700
+ logMedia(row, credential.id, 'success', Date.now() - started, null);
701
+ return { platform: row.platform, modelId: row.model_id, ...out };
702
+ }
703
+ catch (err) {
704
+ const e = err instanceof MediaError ? err : new MediaError(String(err?.message ?? err), 502);
705
+ logMedia(row, credential.id, 'error', Date.now() - started, e.message.slice(0, 300));
706
+ if (e.status === 429 && credential.id != null)
707
+ setCooldown(row.platform, row.model_id, credential.id);
708
+ lastError = e;
709
+ hints.record(e.status, e.retryAfterMs);
710
+ // A caller that hung up gets no second generation charged to its account.
711
+ throwIfClientGone(clientSignal);
712
+ }
713
+ finally {
714
+ credential.release();
715
+ }
716
+ }
717
+ throw chainError('video', lastError, hints);
718
+ }
719
+ /** Global upload ceiling enforced by the route (multer) so it can reject
720
+ * early with a clean OpenAI-shaped 413 instead of buffering an upload no
721
+ * provider can accept. Per-model catalog `maxBytes` may only lower this. */
722
+ export const MAX_TRANSCRIPTION_BYTES = 25 * 1024 * 1024;
723
+ function parseTranscriptionMeta(raw) {
724
+ if (!raw)
725
+ return {};
726
+ try {
727
+ const parsed = JSON.parse(raw);
728
+ return parsed && typeof parsed === 'object' ? parsed : {};
729
+ }
730
+ catch {
731
+ return {};
732
+ }
733
+ }
734
+ function toSttCandidate(row) {
735
+ const meta = parseTranscriptionMeta(row.meta_json);
736
+ const maxBytes = typeof meta.maxBytes === 'number' && meta.maxBytes > 0
737
+ ? Math.min(meta.maxBytes, MAX_TRANSCRIPTION_BYTES)
738
+ : MAX_TRANSCRIPTION_BYTES;
739
+ return {
740
+ platform: row.platform,
741
+ modelId: row.model_id,
742
+ keyId: row.key_id,
743
+ nativeSubtitles: new Set(Array.isArray(meta.subtitleFormats) ? meta.subtitleFormats : []),
744
+ maxBytes,
745
+ requestStyle: typeof meta.requestStyle === 'string' ? meta.requestStyle : null,
746
+ };
747
+ }
748
+ /** Model ids that mean "let the router decide". `whisper-1` is what stock
749
+ * OpenAI clients send by default, so it must route rather than 400. */
750
+ const TRANSCRIPTION_AUTO_IDS = new Set(['auto', 'whisper-1']);
751
+ function resolveTranscriptionChain(model, responseFormat) {
752
+ let chain = listMediaModels('transcription').map(toSttCandidate);
753
+ if (chain.length === 0) {
754
+ // Never-synced install (or the catalog retired every STT model): the
755
+ // registry only ever arrives via catalog-sync, so tell the caller to wait
756
+ // for / trigger a sync rather than pretending the model id is wrong.
757
+ throw new MediaError('No transcription models are configured yet. The registry arrives via catalog sync; ' +
758
+ 'wait for the next sync (or trigger one from the dashboard) and retry, ' +
759
+ 'or register your own OpenAI-compatible endpoint from the Keys page.', 503, 'no_transcription_models');
760
+ }
761
+ if (model && !TRANSCRIPTION_AUTO_IDS.has(model.toLowerCase())) {
762
+ chain = chain.filter(m => m.modelId === model);
763
+ if (chain.length === 0) {
764
+ throw new MediaError(`Unknown transcription model '${model}'. Use 'auto', 'whisper-1', or a provider model id.`, 400);
765
+ }
766
+ }
767
+ if (responseFormat === 'vtt') {
768
+ chain = chain.filter(m => m.nativeSubtitles.has('vtt'));
769
+ if (chain.length === 0) {
770
+ throw new MediaError("response_format 'vtt' is only served by providers that produce it natively; " +
771
+ 'the requested model does not.', 400);
772
+ }
773
+ }
774
+ return chain;
775
+ }
776
+ async function callTranscriptionProvider(m, credential, p) {
777
+ const key = credential.key;
778
+ switch (m.platform) {
779
+ case 'custom': {
780
+ // Any OpenAI-compatible STT server (faster-whisper-server, LocalAI,
781
+ // whisper.cpp's server, vLLM…). Same multipart shape as groq below;
782
+ // only the base URL and the optional key differ.
783
+ if (!credential.baseUrl)
784
+ throw new MediaError('custom transcription provider is missing base_url', 500);
785
+ const form = new FormData();
786
+ form.append('file', new Blob([p.file], { type: p.mimeType || 'application/octet-stream' }), p.filename);
787
+ form.append('model', m.modelId);
788
+ if (p.language)
789
+ form.append('language', p.language);
790
+ if (p.prompt)
791
+ form.append('prompt', p.prompt);
792
+ if (p.temperature !== undefined)
793
+ form.append('temperature', String(p.temperature));
794
+ // 'text' is derived locally from the json shape so failover output stays
795
+ // uniform; 'vtt' never reaches here (no native subtitle support is
796
+ // declared for custom rows, so resolveTranscriptionChain filters them).
797
+ form.append('response_format', p.responseFormat === 'verbose_json' ? 'verbose_json' : 'json');
798
+ // Never set Content-Type by hand — FormData supplies the boundary.
799
+ const r = await mediaFetch(`${credential.baseUrl}/audio/transcriptions`, 'custom', 'transcription', {
800
+ method: 'POST',
801
+ headers: { ...bearerAuthHeader(key) },
802
+ body: form,
803
+ });
804
+ const j = (await r.json());
805
+ if (typeof j.text !== 'string')
806
+ throw new MediaError('custom endpoint returned no transcription text', 502);
807
+ return { text: j.text, language: j.language, duration: j.duration, segments: j.segments };
808
+ }
809
+ case 'groq': {
810
+ // Groq's OpenAI-compatible audio endpoint takes multipart form data.
811
+ // Never set Content-Type by hand — FormData supplies the boundary.
812
+ const form = new FormData();
813
+ form.append('file', new Blob([p.file], { type: p.mimeType || 'application/octet-stream' }), p.filename);
814
+ form.append('model', m.modelId);
815
+ if (p.language)
816
+ form.append('language', p.language);
817
+ if (p.prompt)
818
+ form.append('prompt', p.prompt);
819
+ if (p.temperature !== undefined)
820
+ form.append('temperature', String(p.temperature));
821
+ // Groq supports json/verbose_json/text natively; text is derived locally
822
+ // from the json shape so failover output stays uniform.
823
+ form.append('response_format', p.responseFormat === 'verbose_json' ? 'verbose_json' : 'json');
824
+ const r = await mediaFetch('https://api.groq.com/openai/v1/audio/transcriptions', 'groq', 'transcription', {
825
+ method: 'POST',
826
+ headers: { Authorization: `Bearer ${key}` },
827
+ body: form,
828
+ });
829
+ const j = (await r.json());
830
+ if (typeof j.text !== 'string')
831
+ throw new MediaError('groq returned no transcription text', 502);
832
+ return { text: j.text, language: j.language, duration: j.duration, segments: j.segments };
833
+ }
834
+ case 'cloudflare': {
835
+ const { accountId, token } = parseCfKey(key);
836
+ const url = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${m.modelId}`;
837
+ const init = m.requestStyle === 'json'
838
+ ? {
839
+ method: 'POST',
840
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
841
+ body: JSON.stringify({
842
+ audio: p.file.toString('base64'),
843
+ ...(p.language ? { language: p.language } : {}),
844
+ ...(p.prompt ? { initial_prompt: p.prompt } : {}),
845
+ }),
846
+ }
847
+ : {
848
+ method: 'POST',
849
+ headers: { 'Content-Type': 'application/octet-stream', Authorization: `Bearer ${token}` },
850
+ body: p.file,
851
+ };
852
+ const r = await mediaFetch(url, 'cloudflare', 'transcription', init);
853
+ const j = (await r.json());
854
+ const result = j.result;
855
+ if (!result || typeof result.text !== 'string') {
856
+ throw new MediaError('cloudflare returned no transcription text', 502);
857
+ }
858
+ return {
859
+ text: result.text,
860
+ language: result.transcription_info?.language,
861
+ duration: result.transcription_info?.duration,
862
+ segments: result.segments,
863
+ vtt: typeof result.vtt === 'string' ? result.vtt : undefined,
864
+ };
865
+ }
866
+ default:
867
+ throw new MediaError(`no transcription adapter for platform '${m.platform}'`, 500);
868
+ }
869
+ }
870
+ /** Transcribe audio, failing over across STT providers. 429s bench the
871
+ * (platform, model, key) triple through the standard cooldown machinery so a
872
+ * rate-limited key is skipped on subsequent requests, exactly like chat. */
873
+ export async function runTranscription(model, p) {
874
+ const chain = resolveTranscriptionChain(model, p.responseFormat);
875
+ const usable = chain.filter(m => p.file.length <= m.maxBytes);
876
+ if (usable.length === 0) {
877
+ const maxMb = Math.max(...chain.map(m => m.maxBytes)) / (1024 * 1024);
878
+ throw new MediaError(`Audio file exceeds the ${maxMb} MB provider upload limit.`, 413);
879
+ }
880
+ let lastError = null;
881
+ // Only a chain rate limited end to end earns a Retry-After (the soonest).
882
+ const hints = new RetryHintTracker();
883
+ for (const m of usable) {
884
+ const { credential, budgetBlocked, budgetRetryAfterMs } = reserveProviderCredential({ platform: m.platform, key_id: m.keyId }, 0, keyId => isOnCooldown(m.platform, m.modelId, keyId));
885
+ if (!credential) {
886
+ if (budgetBlocked) {
887
+ lastError = new MediaError('Key budget exhausted', 429, 'quota_exceeded');
888
+ hints.record(429, budgetRetryAfterMs ?? secondsUntilNextMonth() * 1000);
889
+ }
890
+ continue;
891
+ }
892
+ const logRow = { platform: m.platform, model_id: m.modelId, modality: 'transcription' };
893
+ const started = Date.now();
894
+ try {
895
+ const out = await callTranscriptionProvider(m, credential, p);
896
+ if (p.responseFormat === 'vtt' && !out.vtt) {
897
+ throw new MediaError('upstream returned no vtt subtitles', 502);
898
+ }
899
+ logMedia(logRow, credential.id, 'success', Date.now() - started, null);
900
+ return { platform: m.platform, modelId: m.modelId, ...out };
901
+ }
902
+ catch (err) {
903
+ const e = err instanceof MediaError ? err : new MediaError(String(err?.message ?? err), 502);
904
+ logMedia(logRow, credential.id, 'error', Date.now() - started, e.message.slice(0, 300));
905
+ if (e.status === 429 && credential.id != null) {
906
+ setCooldown(m.platform, m.modelId, credential.id);
907
+ }
908
+ lastError = e;
909
+ hints.record(e.status, e.retryAfterMs);
910
+ }
911
+ finally {
912
+ credential.release();
913
+ }
914
+ }
915
+ throw chainError('transcription', lastError, hints);
916
+ }
917
+ /** Synthesize speech, failing over across providers serving the modality. */
918
+ export async function runSpeech(model, params) {
919
+ const chain = resolveMediaChain(model, 'audio');
920
+ let lastError = null;
921
+ // Only a chain rate limited end to end earns a Retry-After (the soonest).
922
+ const hints = new RetryHintTracker();
923
+ for (const row of chain) {
924
+ const { credential, budgetBlocked, budgetRetryAfterMs } = KEYLESS_CAPABLE.has(row.platform)
925
+ ? { credential: { id: null, key: null, baseUrl: null, release: () => { } }, budgetBlocked: false, budgetRetryAfterMs: undefined }
926
+ : reserveProviderCredential(row, 0);
927
+ if (!credential) {
928
+ if (budgetBlocked) {
929
+ lastError = new MediaError('Key budget exhausted', 429, 'quota_exceeded');
930
+ hints.record(429, budgetRetryAfterMs ?? secondsUntilNextMonth() * 1000);
931
+ }
932
+ continue;
933
+ }
934
+ const started = Date.now();
935
+ try {
936
+ const out = await callSpeechProvider(row, credential, params);
937
+ if (!out.audio.length)
938
+ throw new MediaError('upstream returned no audio', 502);
939
+ logMedia(row, credential.id, 'success', Date.now() - started, null);
940
+ return { platform: row.platform, modelId: row.model_id, audio: out.audio, contentType: out.contentType };
941
+ }
942
+ catch (err) {
943
+ const e = err instanceof MediaError ? err : new MediaError(String(err?.message ?? err), 502);
944
+ logMedia(row, credential.id, 'error', Date.now() - started, e.message.slice(0, 300));
945
+ lastError = e;
946
+ hints.record(e.status, e.retryAfterMs);
947
+ }
948
+ finally {
949
+ credential.release();
950
+ }
951
+ }
952
+ throw chainError('audio', lastError, hints);
953
+ }
954
+ //# sourceMappingURL=media.js.map