@lastbrain/module-ai 2.0.26 → 2.0.30

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 (365) hide show
  1. package/README.md +52 -1
  2. package/dist/ai.build.config.d.ts.map +1 -1
  3. package/dist/ai.build.config.js +508 -9
  4. package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
  5. package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
  6. package/dist/api/admin/ai-provider-models/[id].js +58 -0
  7. package/dist/api/admin/ai-provider-models.d.ts +20 -0
  8. package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
  9. package/dist/api/admin/ai-provider-models.js +26 -0
  10. package/dist/api/admin/ai-providers/[key].d.ts +18 -0
  11. package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
  12. package/dist/api/admin/ai-providers/[key].js +55 -0
  13. package/dist/api/admin/ai-providers.d.ts +20 -0
  14. package/dist/api/admin/ai-providers.d.ts.map +1 -0
  15. package/dist/api/admin/ai-providers.js +26 -0
  16. package/dist/api/admin/billing-analytics.d.ts +43 -0
  17. package/dist/api/admin/billing-analytics.d.ts.map +1 -0
  18. package/dist/api/admin/billing-analytics.js +144 -0
  19. package/dist/api/admin/global-ai-settings.d.ts +14 -0
  20. package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
  21. package/dist/api/admin/global-ai-settings.js +63 -0
  22. package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
  23. package/dist/api/admin/token-packs/[id].js +3 -2
  24. package/dist/api/admin/token-packs.d.ts.map +1 -1
  25. package/dist/api/admin/token-packs.js +3 -2
  26. package/dist/api/admin/user-monthly-details.d.ts +49 -0
  27. package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
  28. package/dist/api/admin/user-monthly-details.js +140 -0
  29. package/dist/api/admin/user-quota.d.ts +21 -0
  30. package/dist/api/admin/user-quota.d.ts.map +1 -0
  31. package/dist/api/admin/user-quota.js +59 -0
  32. package/dist/api/admin/user-token/[id].d.ts.map +1 -1
  33. package/dist/api/admin/user-token/[id].js +2 -1
  34. package/dist/api/admin/user-token.d.ts +5 -2
  35. package/dist/api/admin/user-token.d.ts.map +1 -1
  36. package/dist/api/admin/user-token.js +91 -17
  37. package/dist/api/admin/user-usage-by-model.d.ts +22 -0
  38. package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
  39. package/dist/api/admin/user-usage-by-model.js +78 -0
  40. package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
  41. package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
  42. package/dist/api/admin/user-wallet-analytics.js +67 -0
  43. package/dist/api/admin/wallet-repair/route.d.ts +30 -0
  44. package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
  45. package/dist/api/admin/wallet-repair/route.js +63 -0
  46. package/dist/api/auth/ai-model-settings.d.ts +21 -0
  47. package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
  48. package/dist/api/auth/ai-model-settings.js +86 -0
  49. package/dist/api/auth/ai-settings.d.ts +17 -0
  50. package/dist/api/auth/ai-settings.d.ts.map +1 -0
  51. package/dist/api/auth/ai-settings.js +87 -0
  52. package/dist/api/auth/api-keys/[id].d.ts +17 -0
  53. package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
  54. package/dist/api/auth/api-keys/[id].js +66 -0
  55. package/dist/api/auth/api-keys.d.ts +19 -0
  56. package/dist/api/auth/api-keys.d.ts.map +1 -0
  57. package/dist/api/auth/api-keys.js +94 -0
  58. package/dist/api/auth/create-checkout.d.ts +1 -1
  59. package/dist/api/auth/create-checkout.d.ts.map +1 -1
  60. package/dist/api/auth/create-checkout.js +8 -6
  61. package/dist/api/auth/generate-image.d.ts +2 -2
  62. package/dist/api/auth/generate-image.d.ts.map +1 -1
  63. package/dist/api/auth/generate-image.js +404 -104
  64. package/dist/api/auth/generate-text.d.ts +3 -2
  65. package/dist/api/auth/generate-text.d.ts.map +1 -1
  66. package/dist/api/auth/generate-text.js +130 -58
  67. package/dist/api/auth/process-ocr.d.ts +9 -0
  68. package/dist/api/auth/process-ocr.d.ts.map +1 -0
  69. package/dist/api/auth/process-ocr.js +43 -0
  70. package/dist/api/auth/prompts/stats.d.ts +14 -0
  71. package/dist/api/auth/prompts/stats.d.ts.map +1 -0
  72. package/dist/api/auth/prompts/stats.js +46 -0
  73. package/dist/api/auth/prompts.d.ts +26 -0
  74. package/dist/api/auth/prompts.d.ts.map +1 -0
  75. package/dist/api/auth/prompts.js +175 -0
  76. package/dist/api/auth/token-balance.d.ts +26 -0
  77. package/dist/api/auth/token-balance.d.ts.map +1 -0
  78. package/dist/api/auth/token-balance.js +47 -0
  79. package/dist/api/auth/token-checkout.d.ts.map +1 -1
  80. package/dist/api/auth/token-checkout.js +22 -4
  81. package/dist/api/auth/token-packs.d.ts.map +1 -1
  82. package/dist/api/auth/token-packs.js +2 -1
  83. package/dist/api/auth/usage-by-model.d.ts +25 -0
  84. package/dist/api/auth/usage-by-model.d.ts.map +1 -0
  85. package/dist/api/auth/usage-by-model.js +95 -0
  86. package/dist/api/auth/usage.d.ts +26 -0
  87. package/dist/api/auth/usage.d.ts.map +1 -0
  88. package/dist/api/auth/usage.js +127 -0
  89. package/dist/api/auth/user-tokens.d.ts.map +1 -1
  90. package/dist/api/auth/user-tokens.js +36 -2
  91. package/dist/api/auth/wallet/route.d.ts +17 -0
  92. package/dist/api/auth/wallet/route.d.ts.map +1 -0
  93. package/dist/api/auth/wallet/route.js +68 -0
  94. package/dist/api/auth/wallet.d.ts +16 -0
  95. package/dist/api/auth/wallet.d.ts.map +1 -0
  96. package/dist/api/auth/wallet.js +71 -0
  97. package/dist/api/public/gateway-models.d.ts +25 -0
  98. package/dist/api/public/gateway-models.d.ts.map +1 -0
  99. package/dist/api/public/gateway-models.js +49 -0
  100. package/dist/api/public/pricing-summary.d.ts +46 -0
  101. package/dist/api/public/pricing-summary.d.ts.map +1 -0
  102. package/dist/api/public/pricing-summary.js +70 -0
  103. package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
  104. package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
  105. package/dist/api/public/prompts/[id]/stats.js +37 -0
  106. package/dist/api/public/prompts/[id]/view.d.ts +15 -0
  107. package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
  108. package/dist/api/public/prompts/[id]/view.js +41 -0
  109. package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
  110. package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
  111. package/dist/api/public/prompts/slug/[slug].js +40 -0
  112. package/dist/api/public/prompts/user/[username].d.ts +17 -0
  113. package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
  114. package/dist/api/public/prompts/user/[username].js +51 -0
  115. package/dist/api/public/prompts.d.ts +15 -0
  116. package/dist/api/public/prompts.d.ts.map +1 -0
  117. package/dist/api/public/prompts.js +45 -0
  118. package/dist/api/public/token-packs.d.ts +11 -0
  119. package/dist/api/public/token-packs.d.ts.map +1 -0
  120. package/dist/api/public/token-packs.js +25 -0
  121. package/dist/api/public/token-pricing.d.ts +44 -0
  122. package/dist/api/public/token-pricing.d.ts.map +1 -0
  123. package/dist/api/public/token-pricing.js +168 -0
  124. package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
  125. package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
  126. package/dist/api/public/v1/_lib/artifacts.js +217 -0
  127. package/dist/api/public/v1/_lib/auth.d.ts +43 -0
  128. package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
  129. package/dist/api/public/v1/_lib/auth.js +108 -0
  130. package/dist/api/public/v1/_lib/errors.d.ts +17 -0
  131. package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
  132. package/dist/api/public/v1/_lib/errors.js +16 -0
  133. package/dist/api/public/v1/_lib/log.d.ts +29 -0
  134. package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
  135. package/dist/api/public/v1/_lib/log.js +68 -0
  136. package/dist/api/public/v1/_lib/quota.d.ts +24 -0
  137. package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
  138. package/dist/api/public/v1/_lib/quota.js +118 -0
  139. package/dist/api/public/v1/_lib/router.d.ts +54 -0
  140. package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
  141. package/dist/api/public/v1/_lib/router.js +119 -0
  142. package/dist/api/public/v1/connect.d.ts +20 -0
  143. package/dist/api/public/v1/connect.d.ts.map +1 -0
  144. package/dist/api/public/v1/connect.js +119 -0
  145. package/dist/api/public/v1/doc.d.ts +239 -0
  146. package/dist/api/public/v1/doc.d.ts.map +1 -0
  147. package/dist/api/public/v1/doc.js +253 -0
  148. package/dist/api/public/v1/history/[id].d.ts +92 -0
  149. package/dist/api/public/v1/history/[id].d.ts.map +1 -0
  150. package/dist/api/public/v1/history/[id].js +176 -0
  151. package/dist/api/public/v1/history.d.ts +30 -0
  152. package/dist/api/public/v1/history.d.ts.map +1 -0
  153. package/dist/api/public/v1/history.js +142 -0
  154. package/dist/api/public/v1/image-ai.d.ts +24 -0
  155. package/dist/api/public/v1/image-ai.d.ts.map +1 -0
  156. package/dist/api/public/v1/image-ai.js +233 -0
  157. package/dist/api/public/v1/prompts.d.ts +19 -0
  158. package/dist/api/public/v1/prompts.d.ts.map +1 -0
  159. package/dist/api/public/v1/prompts.js +107 -0
  160. package/dist/api/public/v1/provider.d.ts +16 -0
  161. package/dist/api/public/v1/provider.d.ts.map +1 -0
  162. package/dist/api/public/v1/provider.js +130 -0
  163. package/dist/api/public/v1/purchase.d.ts +11 -0
  164. package/dist/api/public/v1/purchase.d.ts.map +1 -0
  165. package/dist/api/public/v1/purchase.js +18 -0
  166. package/dist/api/public/v1/status.d.ts +35 -0
  167. package/dist/api/public/v1/status.d.ts.map +1 -0
  168. package/dist/api/public/v1/status.js +163 -0
  169. package/dist/api/public/v1/text-ai.d.ts +26 -0
  170. package/dist/api/public/v1/text-ai.d.ts.map +1 -0
  171. package/dist/api/public/v1/text-ai.js +239 -0
  172. package/dist/api/public/webhook.d.ts.map +1 -1
  173. package/dist/api/public/webhook.js +50 -39
  174. package/dist/api/track-usage.d.ts +12 -0
  175. package/dist/api/track-usage.d.ts.map +1 -0
  176. package/dist/api/track-usage.js +37 -0
  177. package/dist/components/Doc.d.ts.map +1 -1
  178. package/dist/components/Doc.js +1 -1
  179. package/dist/components/DocUsageCustom.js +6 -6
  180. package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
  181. package/dist/components/admin/UserTokenTab.js +170 -23
  182. package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
  183. package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
  184. package/dist/components/auth/AuthDashboardAi.js +53 -0
  185. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
  186. package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
  187. package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
  188. package/dist/index.d.ts +15 -1
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +18 -1
  191. package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
  192. package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
  193. package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
  194. package/dist/server/__tests__/billing.test.d.ts +5 -0
  195. package/dist/server/__tests__/billing.test.d.ts.map +1 -0
  196. package/dist/server/__tests__/billing.test.js +223 -0
  197. package/dist/server/ai-client.d.ts +59 -0
  198. package/dist/server/ai-client.d.ts.map +1 -0
  199. package/dist/server/ai-client.js +111 -0
  200. package/dist/server/ai-generation-service.d.ts +66 -0
  201. package/dist/server/ai-generation-service.d.ts.map +1 -0
  202. package/dist/server/ai-generation-service.js +274 -0
  203. package/dist/server/billing.d.ts +200 -0
  204. package/dist/server/billing.d.ts.map +1 -0
  205. package/dist/server/billing.js +488 -0
  206. package/dist/server/gateway-service.d.ts +13 -0
  207. package/dist/server/gateway-service.d.ts.map +1 -0
  208. package/dist/server/gateway-service.js +161 -0
  209. package/dist/server/global-settings.d.ts +16 -0
  210. package/dist/server/global-settings.d.ts.map +1 -0
  211. package/dist/server/global-settings.js +42 -0
  212. package/dist/server/model-filter.d.ts +25 -0
  213. package/dist/server/model-filter.d.ts.map +1 -0
  214. package/dist/server/model-filter.js +240 -0
  215. package/dist/server/ocr.d.ts +39 -0
  216. package/dist/server/ocr.d.ts.map +1 -0
  217. package/dist/server/ocr.js +280 -0
  218. package/dist/server/openai-client.d.ts +19 -0
  219. package/dist/server/openai-client.d.ts.map +1 -0
  220. package/dist/server/openai-client.js +26 -0
  221. package/dist/server/pricing-config.d.ts +18 -0
  222. package/dist/server/pricing-config.d.ts.map +1 -0
  223. package/dist/server/pricing-config.js +94 -0
  224. package/dist/server/pricing-validator.d.ts +41 -0
  225. package/dist/server/pricing-validator.d.ts.map +1 -0
  226. package/dist/server/pricing-validator.js +113 -0
  227. package/dist/server/pricing.d.ts +121 -0
  228. package/dist/server/pricing.d.ts.map +1 -0
  229. package/dist/server/pricing.js +225 -0
  230. package/dist/server/quota.d.ts +66 -0
  231. package/dist/server/quota.d.ts.map +1 -0
  232. package/dist/server/quota.js +538 -0
  233. package/dist/server/wallet-repair.d.ts +32 -0
  234. package/dist/server/wallet-repair.d.ts.map +1 -0
  235. package/dist/server/wallet-repair.js +189 -0
  236. package/dist/server.d.ts +13 -1
  237. package/dist/server.d.ts.map +1 -1
  238. package/dist/server.js +87 -16
  239. package/dist/sitemap/handlers/prompts.d.ts +6 -0
  240. package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
  241. package/dist/sitemap/handlers/prompts.js +72 -0
  242. package/dist/sitemap/handlers/users.d.ts +6 -0
  243. package/dist/sitemap/handlers/users.d.ts.map +1 -0
  244. package/dist/sitemap/handlers/users.js +80 -0
  245. package/dist/sitemap/manifest.d.ts +8 -0
  246. package/dist/sitemap/manifest.d.ts.map +1 -0
  247. package/dist/sitemap/manifest.js +27 -0
  248. package/dist/types/gateway.d.ts +40 -0
  249. package/dist/types/gateway.d.ts.map +1 -0
  250. package/dist/types/gateway.js +4 -0
  251. package/dist/types/quota.d.ts +74 -0
  252. package/dist/types/quota.d.ts.map +1 -0
  253. package/dist/types/quota.js +11 -0
  254. package/dist/utils/date.d.ts +7 -0
  255. package/dist/utils/date.d.ts.map +1 -0
  256. package/dist/utils/date.js +17 -0
  257. package/dist/web/admin/AdminTokenPacksPage.js +1 -1
  258. package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
  259. package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
  260. package/dist/web/admin/BillingAnalyticsPage.js +141 -0
  261. package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
  262. package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
  263. package/dist/web/admin/GlobalAISettingsPage.js +93 -0
  264. package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
  265. package/dist/web/admin/UserTokenPage.js +20 -7
  266. package/dist/web/auth/AISettingsPage.d.ts +2 -0
  267. package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
  268. package/dist/web/auth/AISettingsPage.js +258 -0
  269. package/dist/web/auth/APIKeysPage.d.ts +2 -0
  270. package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
  271. package/dist/web/auth/APIKeysPage.js +154 -0
  272. package/dist/web/auth/HistoryPage.d.ts +2 -0
  273. package/dist/web/auth/HistoryPage.d.ts.map +1 -0
  274. package/dist/web/auth/HistoryPage.js +279 -0
  275. package/dist/web/auth/PromptsPage.d.ts +5 -0
  276. package/dist/web/auth/PromptsPage.d.ts.map +1 -0
  277. package/dist/web/auth/PromptsPage.js +137 -0
  278. package/dist/web/auth/TokenPage.d.ts.map +1 -1
  279. package/dist/web/auth/TokenPage.js +88 -31
  280. package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
  281. package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
  282. package/dist/web/auth/UsageAndTokensPage.js +157 -0
  283. package/dist/web/auth/UsagePage.d.ts +2 -0
  284. package/dist/web/auth/UsagePage.d.ts.map +1 -0
  285. package/dist/web/auth/UsagePage.js +62 -0
  286. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
  287. package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
  288. package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
  289. package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
  290. package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
  291. package/dist/web/auth/components/ModelUsageTable.js +37 -0
  292. package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
  293. package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
  294. package/dist/web/auth/components/PurchaseButton.js +13 -0
  295. package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
  296. package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
  297. package/dist/web/auth/components/TokenHistoryCard.js +76 -0
  298. package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
  299. package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
  300. package/dist/web/auth/components/TokenKpiGrid.js +38 -0
  301. package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
  302. package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
  303. package/dist/web/auth/components/UsageByDayChart.js +32 -0
  304. package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
  305. package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
  306. package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
  307. package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
  308. package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
  309. package/dist/web/auth/components/WalletStatusCard.js +50 -0
  310. package/dist/web/components/ImageGenerative.d.ts +3 -1
  311. package/dist/web/components/ImageGenerative.d.ts.map +1 -1
  312. package/dist/web/components/ImageGenerative.js +139 -52
  313. package/dist/web/components/TextareaGenerative.d.ts +3 -1
  314. package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
  315. package/dist/web/components/TextareaGenerative.js +10 -5
  316. package/dist/web/public/PromptDetailPage.d.ts +25 -0
  317. package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
  318. package/dist/web/public/PromptDetailPage.js +71 -0
  319. package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
  320. package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
  321. package/dist/web/public/PromptDetailPageServer.js +68 -0
  322. package/dist/web/public/PublicPromptsPage.d.ts +5 -0
  323. package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
  324. package/dist/web/public/PublicPromptsPage.js +110 -0
  325. package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
  326. package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
  327. package/dist/web/public/PurchaseTokensPage.js +98 -0
  328. package/dist/web/public/UserAvatar.d.ts +13 -0
  329. package/dist/web/public/UserAvatar.d.ts.map +1 -0
  330. package/dist/web/public/UserAvatar.js +13 -0
  331. package/dist/web/public/UserDetailPageServer.d.ts +15 -0
  332. package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
  333. package/dist/web/public/UserDetailPageServer.js +31 -0
  334. package/dist/web/public/UserPromptsPage.d.ts +9 -0
  335. package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
  336. package/dist/web/public/UserPromptsPage.js +112 -0
  337. package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
  338. package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
  339. package/dist/web/public/UserPromptsPageServer.js +31 -0
  340. package/package.json +18 -9
  341. package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
  342. package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
  343. package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
  344. package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
  345. package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
  346. package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
  347. package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
  348. package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
  349. package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
  350. package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
  351. package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
  352. package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
  353. package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
  354. package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
  355. package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
  356. package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
  357. package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
  358. package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
  359. package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
  360. package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
  361. package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
  362. package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
  363. package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
  364. package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
  365. package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
@@ -0,0 +1,488 @@
1
+ /**
2
+ * Core billing functions for wallet-based AI token system
3
+ *
4
+ * RÈGLE ÉCONOMIQUE FONDAMENTALE :
5
+ * ================================
6
+ * Un pack vendu X$ donne un budget fournisseur limité (ex: 5$ vendu => 2$ budget provider)
7
+ * Le reste (3$) = marge + infrastructure.
8
+ *
9
+ * L'utilisateur NE PEUT PAS générer des milliers de recettes pour 5$.
10
+ * Le système ne doit JAMAIS permettre d'être à perte.
11
+ *
12
+ * Pour garantir cela, CHAQUE action a un coût minimum de vente (minSellUsd):
13
+ * - generate-recipe-text: min 0.20$ (action à forte valeur)
14
+ * - generate-image: min calculé selon le modèle
15
+ * - autocomplete UI (input/textarea): min 0$ (micro-calls)
16
+ *
17
+ * Le calcul de marge cible est 60% sur le prix de vente:
18
+ * sellCostUsd = providerCostUsd / (1 - 0.60) = providerCostUsd / 0.40
19
+ *
20
+ * MAIS on respecte TOUJOURS la contrainte pack:
21
+ * - on ne peut pas dépasser le budget provider restant
22
+ * - on applique le minimum par action si configuré
23
+ */
24
+ // ============================================================================
25
+ // CORE FUNCTIONS
26
+ // ============================================================================
27
+ /**
28
+ * Compute provider cost in USD from usage and pricing
29
+ * @param usage Actual tokens/images used
30
+ * @param pricing Provider pricing ($/token, $/image)
31
+ * @returns Total provider cost in USD
32
+ */
33
+ export function computeProviderCostUsd(usage, pricing) {
34
+ let cost = 0;
35
+ console.log("[BILLING] computeProviderCostUsd START", {
36
+ usage,
37
+ pricing,
38
+ });
39
+ // Input tokens
40
+ if (usage.inputTokens && pricing.inputUsdPerToken) {
41
+ const inputCost = usage.inputTokens * pricing.inputUsdPerToken;
42
+ console.log("[BILLING] Input tokens cost:", {
43
+ inputTokens: usage.inputTokens,
44
+ inputUsdPerToken: pricing.inputUsdPerToken,
45
+ inputCost,
46
+ });
47
+ cost += inputCost;
48
+ }
49
+ // Output tokens
50
+ if (usage.outputTokens && pricing.outputUsdPerToken) {
51
+ const outputCost = usage.outputTokens * pricing.outputUsdPerToken;
52
+ console.log("[BILLING] Output tokens cost:", {
53
+ outputTokens: usage.outputTokens,
54
+ outputUsdPerToken: pricing.outputUsdPerToken,
55
+ outputCost,
56
+ });
57
+ cost += outputCost;
58
+ }
59
+ // Cache read tokens
60
+ if (usage.cacheReadTokens && pricing.cacheReadUsdPerToken) {
61
+ const cacheCost = usage.cacheReadTokens * pricing.cacheReadUsdPerToken;
62
+ console.log("[BILLING] Cache read tokens cost:", {
63
+ cacheReadTokens: usage.cacheReadTokens,
64
+ cacheReadUsdPerToken: pricing.cacheReadUsdPerToken,
65
+ cacheCost,
66
+ });
67
+ cost += cacheCost;
68
+ }
69
+ // Web search
70
+ if (usage.webSearchUnits && pricing.webSearchUsdPerUnit) {
71
+ const searchCost = usage.webSearchUnits * pricing.webSearchUsdPerUnit;
72
+ console.log("[BILLING] Web search cost:", {
73
+ webSearchUnits: usage.webSearchUnits,
74
+ webSearchUsdPerUnit: pricing.webSearchUsdPerUnit,
75
+ searchCost,
76
+ });
77
+ cost += searchCost;
78
+ }
79
+ // Images
80
+ if (usage.images && pricing.imageUsdPerImage) {
81
+ const imageCost = usage.images * pricing.imageUsdPerImage;
82
+ console.log("[BILLING] Images cost:", {
83
+ images: usage.images,
84
+ imageUsdPerImage: pricing.imageUsdPerImage,
85
+ imageCost,
86
+ });
87
+ cost += imageCost;
88
+ }
89
+ const roundedCost = roundUsd(cost);
90
+ console.log("[BILLING] computeProviderCostUsd RESULT:", {
91
+ rawCost: cost,
92
+ roundedCost,
93
+ });
94
+ return roundedCost;
95
+ }
96
+ /**
97
+ * Compute token debit from wallet (with automatic margin smoothing)
98
+ * @param wallet Current wallet state
99
+ * @param providerCostUsd Provider cost for this transaction
100
+ * @returns Debit details (tokens, sell value, margin)
101
+ */
102
+ export function computeDebitFromWallet(wallet, providerCostUsd) {
103
+ console.log("[BILLING] computeDebitFromWallet START", {
104
+ wallet,
105
+ providerCostUsd,
106
+ });
107
+ // Validate wallet state
108
+ if (wallet.tokenBalance <= 0) {
109
+ throw new Error("Insufficient token balance");
110
+ }
111
+ // FALLBACK: If wallet is at 0 USD but tokens exist (legacy credits, manual edits, etc.)
112
+ // Use default pack_coef = 1.35 (35% margin) to compute rates
113
+ const DEFAULT_PACK_COEF = 1.35;
114
+ const hasWalletBudget = wallet.walletProviderBudgetUsd > 0 && wallet.walletSellValueUsd > 0;
115
+ let usdProviderPerToken;
116
+ let usdSellPerToken;
117
+ console.log("[BILLING] Wallet budget status:", {
118
+ hasWalletBudget,
119
+ walletProviderBudgetUsd: wallet.walletProviderBudgetUsd,
120
+ walletSellValueUsd: wallet.walletSellValueUsd,
121
+ tokenBalance: wallet.tokenBalance,
122
+ });
123
+ if (hasWalletBudget) {
124
+ // Normal case: use smoothed rates from wallet
125
+ usdProviderPerToken = wallet.walletProviderBudgetUsd / wallet.tokenBalance;
126
+ usdSellPerToken = wallet.walletSellValueUsd / wallet.tokenBalance;
127
+ console.log("[BILLING] Using wallet smoothed rates:", {
128
+ usdProviderPerToken,
129
+ usdSellPerToken,
130
+ margin: ((usdSellPerToken - usdProviderPerToken) / usdSellPerToken) * 100 + "%",
131
+ });
132
+ }
133
+ else {
134
+ // Fallback case: reconstruct rates using default coef
135
+ // Assume token price = provider cost × coef
136
+ // We estimate: 1 token ≈ $0.00005 sell (typical for 100K @ $5)
137
+ const FALLBACK_USD_SELL_PER_TOKEN = 0.00005;
138
+ usdSellPerToken = FALLBACK_USD_SELL_PER_TOKEN;
139
+ usdProviderPerToken = usdSellPerToken / DEFAULT_PACK_COEF;
140
+ console.log("[BILLING] Using fallback rates:", {
141
+ DEFAULT_PACK_COEF,
142
+ FALLBACK_USD_SELL_PER_TOKEN,
143
+ usdProviderPerToken,
144
+ usdSellPerToken,
145
+ margin: ((usdSellPerToken - usdProviderPerToken) / usdSellPerToken) * 100 + "%",
146
+ });
147
+ }
148
+ // Compute tokens to debit (round up)
149
+ const debitTokens = Math.ceil(providerCostUsd / usdProviderPerToken);
150
+ // Compute sell value and margin
151
+ const sellUsd = roundUsd(debitTokens * usdSellPerToken);
152
+ const marginUsd = roundUsd(sellUsd - providerCostUsd);
153
+ console.log("[BILLING] Debit calculation:", {
154
+ providerCostUsd,
155
+ usdProviderPerToken,
156
+ debitTokens,
157
+ usdSellPerToken,
158
+ sellUsd,
159
+ marginUsd,
160
+ marginPercent: ((marginUsd / sellUsd) * 100).toFixed(2) + "%",
161
+ });
162
+ // Safety check
163
+ if (debitTokens > wallet.tokenBalance) {
164
+ throw new Error(`Insufficient balance: need ${debitTokens}, have ${wallet.tokenBalance}`);
165
+ }
166
+ console.log("[BILLING] computeDebitFromWallet RESULT:", {
167
+ debitTokens,
168
+ sellUsd,
169
+ marginUsd,
170
+ usdProviderPerToken: roundUsd(usdProviderPerToken),
171
+ usdSellPerToken: roundUsd(usdSellPerToken),
172
+ });
173
+ return {
174
+ debitTokens,
175
+ sellUsd,
176
+ marginUsd,
177
+ usdProviderPerToken: roundUsd(usdProviderPerToken),
178
+ usdSellPerToken: roundUsd(usdSellPerToken),
179
+ };
180
+ }
181
+ /**
182
+ * Compute wallet update for pack purchase
183
+ * @param pack Pack details (tokens, price, coef)
184
+ * @returns Wallet deltas to add
185
+ */
186
+ export function computeWalletUpdateForPurchase(pack) {
187
+ // Provider budget = what we can spend on provider APIs
188
+ const providerBudgetAddedUsd = roundUsd(pack.packPriceUsd / pack.packCoef);
189
+ // Sell value = what user paid (our revenue)
190
+ const sellValueAddedUsd = roundUsd(pack.packPriceUsd);
191
+ return {
192
+ providerBudgetAddedUsd,
193
+ sellValueAddedUsd,
194
+ };
195
+ }
196
+ /**
197
+ * Estimate debit tokens for display (using pack coef)
198
+ * Useful for public pricing page before user has wallet
199
+ * @param providerCostUsd Provider cost
200
+ * @param packCoef Pack margin coefficient
201
+ * @param packTokens Pack size
202
+ * @param packPriceUsd Pack price
203
+ * @returns Estimated tokens
204
+ */
205
+ export function estimateDebitTokens(providerCostUsd, packCoef, packTokens, packPriceUsd) {
206
+ // Simulate wallet rates for this pack
207
+ const providerBudgetUsd = packPriceUsd / packCoef;
208
+ const usdProviderPerToken = providerBudgetUsd / packTokens;
209
+ return Math.ceil(providerCostUsd / usdProviderPerToken);
210
+ }
211
+ // ============================================================================
212
+ // UTILITIES
213
+ // ============================================================================
214
+ /**
215
+ * Round USD to 10 decimals (handles nano-dollar precision)
216
+ */
217
+ function roundUsd(value) {
218
+ return Math.round(value * 10_000_000_000) / 10_000_000_000;
219
+ }
220
+ /**
221
+ * Validate that pricing has required fields for usage type
222
+ */
223
+ export function validatePricing(pricing, usageType) {
224
+ if (usageType === "image") {
225
+ return pricing.imageUsdPerImage !== undefined;
226
+ }
227
+ return (pricing.inputUsdPerToken !== undefined ||
228
+ pricing.outputUsdPerToken !== undefined);
229
+ }
230
+ /**
231
+ * Minimum sell cost per action type (USD)
232
+ * Ces minimums garantissent qu'on ne sera jamais à perte
233
+ * et que les actions à forte valeur ajoutée sont facturées correctement
234
+ */
235
+ const ACTION_MIN_SELL_USD = {
236
+ "generate-recipe-text": 0.1, // Génération complète de recette : min 0.10$
237
+ "generate-image": 0.1, // Génération image : min 0.10$
238
+ "generate-text": 0.001, // Génération texte générique : min 0.001$ (évite coûts quasi-nuls)
239
+ autocomplete: 0.0, // Autocomplétion UI : micro-calls, pas de minimum
240
+ "suggestion-input": 0.0, // Suggestions : micro-calls
241
+ "suggestion-textarea": 0.0, // Suggestions : micro-calls
242
+ };
243
+ /**
244
+ * Get minimum sell cost for action type
245
+ */
246
+ function getActionMinSellUsd(actionType) {
247
+ return ACTION_MIN_SELL_USD[actionType] || 0;
248
+ }
249
+ /**
250
+ * Get current wallet state for a user
251
+ * CENTRALIZED - This is the single source of truth for wallet state
252
+ */
253
+ export async function getWalletState(userId) {
254
+ const { getSupabaseServiceClient } = await import("@lastbrain/core/server");
255
+ const { logger } = await import("@lastbrain/core");
256
+ const supabase = await getSupabaseServiceClient();
257
+ logger.info(`[getWalletState] Fetching wallet for user: ${userId}`);
258
+ // Get token balance (ledger uses owner_id not user_id)
259
+ const { data: ledgerData, error: ledgerError } = await supabase
260
+ .from("user_token_ledger")
261
+ .select("amount")
262
+ .eq("owner_id", userId);
263
+ if (ledgerError) {
264
+ logger.error("[getWalletState] Error fetching ledger:", ledgerError);
265
+ throw new Error("Failed to fetch token balance");
266
+ }
267
+ const tokenBalance = (ledgerData || []).reduce((sum, row) => sum + (row.amount || 0), 0);
268
+ logger.info(`[getWalletState] Token balance for user ${userId}: ${tokenBalance}`);
269
+ // Get wallet USD values (wallet uses user_id)
270
+ const { data: walletData, error: walletError } = await supabase
271
+ .from("user_token_wallet")
272
+ .select("wallet_provider_budget_usd, wallet_sell_value_usd")
273
+ .eq("user_id", userId)
274
+ .single();
275
+ logger.info(`[getWalletState] Wallet query result - data: ${JSON.stringify(walletData)}, error: ${JSON.stringify(walletError)}`);
276
+ if (walletError || !walletData) {
277
+ logger.warn(`[getWalletState] No wallet found for user ${userId}, creating new wallet`);
278
+ // Initialize wallet
279
+ const { data: insertData, error: insertError } = await supabase
280
+ .from("user_token_wallet")
281
+ .insert({
282
+ user_id: userId,
283
+ wallet_provider_budget_usd: 0,
284
+ wallet_sell_value_usd: 0,
285
+ })
286
+ .select()
287
+ .single();
288
+ logger.info(`[getWalletState] Insert result - data: ${JSON.stringify(insertData)}, error: ${JSON.stringify(insertError)}`);
289
+ return {
290
+ tokenBalance,
291
+ walletProviderBudgetUsd: 0,
292
+ walletSellValueUsd: 0,
293
+ };
294
+ }
295
+ const result = {
296
+ tokenBalance,
297
+ walletProviderBudgetUsd: parseFloat(walletData.wallet_provider_budget_usd.toString()),
298
+ walletSellValueUsd: parseFloat(walletData.wallet_sell_value_usd.toString()),
299
+ };
300
+ logger.info(`[getWalletState] Returning wallet state for user ${userId}: ${JSON.stringify(result)}`);
301
+ return result;
302
+ }
303
+ /**
304
+ * FONCTION CENTRALE DE BILLING
305
+ * ============================
306
+ * Calcule et débite l'utilisation IA selon les règles économiques.
307
+ *
308
+ * Cette fonction est la SEULE source de vérité pour tous les calculs de billing.
309
+ * Elle est appelée par TOUS les endpoints (generate-text, generate-image, recipes, etc.)
310
+ *
311
+ * RÈGLES DE CALCUL :
312
+ * 1. Marge cible = 60% du prix de vente
313
+ * => sellCostUsd = providerCostUsd / (1 - 0.60) = providerCostUsd / 0.40 = providerCostUsd * 2.5
314
+ *
315
+ * 2. Minimum par action (si configuré)
316
+ * => sellCostUsd = max(sellCostUsd calculé, minSellUsd)
317
+ *
318
+ * 3. Contrainte budget provider
319
+ * => on ne peut jamais débiter plus que le budget provider restant
320
+ *
321
+ * 4. Arrondi safe (ceil) pour éviter sous-facturation
322
+ *
323
+ * LOGGING : log ANONYME de chaque débit pour contrôle
324
+ */
325
+ export function computeAndDebitAIUsage(params) {
326
+ const { userId, actionType, providerCostUsd, wallet, metadata, minSellUsdOverride, } = params;
327
+ // Hash user ID for anonymous logging (first 8 chars)
328
+ const userHash = userId.substring(0, 8);
329
+ // 1. Calculate initial costs
330
+ // Formula: sellCost = providerCost / (1 - margin%) = providerCost / 0.40
331
+ const MARGIN_TARGET = 0.6; // 60% margin on sell price
332
+ let sellCostUsd = providerCostUsd / (1 - MARGIN_TARGET);
333
+ let adjustedProviderCostUsd = providerCostUsd;
334
+ // 2. Apply minimum sell cost for action type
335
+ const minSellUsd = minSellUsdOverride !== undefined
336
+ ? minSellUsdOverride
337
+ : getActionMinSellUsd(actionType);
338
+ if (sellCostUsd < minSellUsd) {
339
+ sellCostUsd = minSellUsd;
340
+ // IMPORTANT: When minimum is applied, recalculate provider cost proportionally
341
+ // to maintain the 60% margin: providerCost = sellCost * (1 - 0.60) = sellCost * 0.40
342
+ adjustedProviderCostUsd = sellCostUsd * (1 - MARGIN_TARGET);
343
+ }
344
+ // 3. Round up (safe rounding to avoid under-charging)
345
+ sellCostUsd = Math.ceil(sellCostUsd * 1_000_000) / 1_000_000;
346
+ adjustedProviderCostUsd =
347
+ Math.ceil(adjustedProviderCostUsd * 1_000_000) / 1_000_000;
348
+ // 4. Check provider budget sufficiency (use adjusted cost)
349
+ if (wallet.walletProviderBudgetUsd < adjustedProviderCostUsd) {
350
+ const errorMsg = `Insufficient provider budget: have $${wallet.walletProviderBudgetUsd.toFixed(6)}, need $${adjustedProviderCostUsd.toFixed(6)}`;
351
+ console.error(`[BILLING] user=${userHash} action=${actionType} ERROR: ${errorMsg}`);
352
+ return {
353
+ success: false,
354
+ error: "insufficient_credits",
355
+ };
356
+ }
357
+ // 5. Check sell budget sufficiency
358
+ if (wallet.walletSellValueUsd < sellCostUsd) {
359
+ const errorMsg = `Insufficient sell value: have $${wallet.walletSellValueUsd.toFixed(6)}, need $${sellCostUsd.toFixed(6)}`;
360
+ console.error(`[BILLING] user=${userHash} action=${actionType} ERROR: ${errorMsg}`);
361
+ return {
362
+ success: false,
363
+ error: "insufficient_credits",
364
+ };
365
+ }
366
+ // 6. Calculate margin (use adjusted provider cost)
367
+ const marginUsd = roundUsd(sellCostUsd - adjustedProviderCostUsd);
368
+ const marginPercent = sellCostUsd > 0 ? (marginUsd / sellCostUsd) * 100 : 0;
369
+ // 7. Calculate new wallet state (debit adjusted provider cost)
370
+ const newProviderBudget = wallet.walletProviderBudgetUsd - adjustedProviderCostUsd;
371
+ const newSellValue = wallet.walletSellValueUsd - sellCostUsd;
372
+ const percentRemaining = wallet.walletSellValueUsd > 0
373
+ ? (newSellValue / wallet.walletSellValueUsd) * 100
374
+ : 0;
375
+ // 8. Calculate tokens debited (for backward compatibility with ledger)
376
+ // Use current wallet rates
377
+ const tokensDebited = wallet.tokenBalance > 0 && wallet.walletSellValueUsd > 0
378
+ ? Math.ceil((sellCostUsd / wallet.walletSellValueUsd) * wallet.tokenBalance)
379
+ : 0;
380
+ // 9. ANONYMOUS LOGGING (no prompt, no content)
381
+ console.log(`[BILLING] user=${userHash} action=${actionType} ` +
382
+ `model=${metadata.model || "unknown"} ` +
383
+ `provider=${metadata.provider || "unknown"} ` +
384
+ `adjustedCost=$${adjustedProviderCostUsd.toFixed(6)} ` +
385
+ `sell=$${sellCostUsd.toFixed(6)} ` +
386
+ `margin=$${marginUsd.toFixed(6)} (${marginPercent.toFixed(2)}%) ` +
387
+ `providerLeft=$${newProviderBudget.toFixed(6)} ` +
388
+ `sellLeft=$${newSellValue.toFixed(6)} ` +
389
+ `remaining=${percentRemaining.toFixed(1)}%`);
390
+ return {
391
+ success: true,
392
+ sellCostUsd,
393
+ providerCostUsd: adjustedProviderCostUsd, // Return adjusted cost for ledger
394
+ marginUsd,
395
+ marginPercent,
396
+ tokensDebited,
397
+ newWallet: {
398
+ walletProviderBudgetUsd: newProviderBudget,
399
+ walletSellValueUsd: newSellValue,
400
+ percentRemaining,
401
+ },
402
+ };
403
+ }
404
+ // ============================================================================
405
+ // ADMIN WALLET CREDITS MANAGEMENT
406
+ // ============================================================================
407
+ /**
408
+ * Add wallet credits to a user (admin function)
409
+ * Adds both provider budget and sell value proportionally
410
+ * @param userId User ID
411
+ * @param sellValueUsd Amount in USD to add to sell value
412
+ * @param reason Admin reason for adding credits
413
+ * @returns Success status and new wallet state
414
+ */
415
+ export async function addWalletCredits(userId, sellValueUsd, reason) {
416
+ const { getSupabaseServiceClient } = await import("@lastbrain/core/server");
417
+ const { logger } = await import("@lastbrain/core");
418
+ const supabase = await getSupabaseServiceClient();
419
+ try {
420
+ // Calculate provider budget proportionally (40% of sell value for 60% margin)
421
+ const MARGIN_TARGET = 0.6;
422
+ const providerBudgetUsd = sellValueUsd * (1 - MARGIN_TARGET); // 40% goes to provider
423
+ // Insert into ledger
424
+ const { error: ledgerError } = await supabase
425
+ .from("user_token_ledger")
426
+ .insert({
427
+ owner_id: userId,
428
+ type: "gift", // Manual credit addition by admin (gift from admin)
429
+ amount: 0, // Tokens not used anymore, but keep for backward compat
430
+ sell_value_added_usd: sellValueUsd,
431
+ provider_budget_added_usd: providerBudgetUsd,
432
+ meta: {
433
+ reason,
434
+ admin_action: true,
435
+ },
436
+ });
437
+ if (ledgerError) {
438
+ logger.error("[addWalletCredits] Ledger insert error:", ledgerError);
439
+ return { success: false, error: ledgerError.message };
440
+ }
441
+ // Update wallet table - First, get current wallet values
442
+ const { data: currentWallet } = await supabase
443
+ .from("user_token_wallet")
444
+ .select("wallet_provider_budget_usd, wallet_sell_value_usd")
445
+ .eq("user_id", userId)
446
+ .single();
447
+ // Calculate new values
448
+ const newProviderBudget = (currentWallet?.wallet_provider_budget_usd || 0) + providerBudgetUsd;
449
+ const newSellValue = (currentWallet?.wallet_sell_value_usd || 0) + sellValueUsd;
450
+ // Upsert wallet
451
+ const { error: upsertError } = await supabase
452
+ .from("user_token_wallet")
453
+ .upsert({
454
+ user_id: userId,
455
+ wallet_provider_budget_usd: newProviderBudget,
456
+ wallet_sell_value_usd: newSellValue,
457
+ updated_at: new Date().toISOString(),
458
+ }, {
459
+ onConflict: "user_id",
460
+ });
461
+ if (upsertError) {
462
+ logger.error("[addWalletCredits] Wallet update error:", upsertError);
463
+ return { success: false, error: upsertError.message };
464
+ }
465
+ // Get new balance from wallet view
466
+ const { data: walletData, error: walletError } = await supabase
467
+ .from("user_token_wallet")
468
+ .select("wallet_sell_value_usd")
469
+ .eq("user_id", userId)
470
+ .single();
471
+ if (walletError || !walletData) {
472
+ logger.error("[addWalletCredits] Wallet query error:", walletError);
473
+ return {
474
+ success: false,
475
+ error: walletError?.message || "Wallet not found",
476
+ };
477
+ }
478
+ logger.info(`[addWalletCredits] Added $${sellValueUsd} to user ${userId}, new balance: $${walletData.wallet_sell_value_usd}`);
479
+ return {
480
+ success: true,
481
+ newBalance: walletData.wallet_sell_value_usd,
482
+ };
483
+ }
484
+ catch (error) {
485
+ logger.error("[addWalletCredits] Error:", error);
486
+ return { success: false, error: error.message };
487
+ }
488
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Service for fetching and caching AI models from Vercel AI Gateway
3
+ */
4
+ import type { GatewayModelsResponse } from "../types/gateway";
5
+ /**
6
+ * Fetch models from Vercel AI Gateway with fallback cache
7
+ */
8
+ export declare function fetchGatewayModels(): Promise<GatewayModelsResponse>;
9
+ /**
10
+ * Clear cache (useful for testing or manual refresh)
11
+ */
12
+ export declare function clearGatewayCache(): void;
13
+ //# sourceMappingURL=gateway-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gateway-service.d.ts","sourceRoot":"","sources":["../../src/server/gateway-service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EACV,qBAAqB,EAGtB,MAAM,kBAAkB,CAAC;AAU1B;;GAEG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,qBAAqB,CAAC,CA0DzE;AAiHD;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Service for fetching and caching AI models from Vercel AI Gateway
3
+ */
4
+ import { logger } from "@lastbrain/core";
5
+ const GATEWAY_BASE_URL = process.env.AI_GATEWAY_BASE_URL || "https://ai-gateway.vercel.sh";
6
+ const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
7
+ // In-memory cache fallback
8
+ let cachedModels = null;
9
+ let cacheTimestamp = null;
10
+ /**
11
+ * Fetch models from Vercel AI Gateway with fallback cache
12
+ */
13
+ export async function fetchGatewayModels() {
14
+ const now = Date.now();
15
+ // Check if cache is still valid
16
+ if (cachedModels && cacheTimestamp && now - cacheTimestamp < CACHE_TTL_MS) {
17
+ logger.debug("[Gateway] Returning cached models (still valid)");
18
+ return cachedModels;
19
+ }
20
+ try {
21
+ const response = await fetch(`${GATEWAY_BASE_URL}/v1/models`, {
22
+ method: "GET",
23
+ headers: {
24
+ "Content-Type": "application/json",
25
+ },
26
+ // Timeout after 10 seconds
27
+ signal: AbortSignal.timeout(10000),
28
+ });
29
+ if (!response.ok) {
30
+ throw new Error(`Gateway returned ${response.status}: ${response.statusText}`);
31
+ }
32
+ const data = await response.json();
33
+ // Transform gateway response to our format
34
+ const transformed = transformGatewayResponse(data);
35
+ // Update cache
36
+ cachedModels = transformed;
37
+ cacheTimestamp = now;
38
+ return transformed;
39
+ }
40
+ catch (error) {
41
+ logger.error("[Gateway] Failed to fetch models:", error);
42
+ // Return stale cache if available
43
+ if (cachedModels) {
44
+ return {
45
+ ...cachedModels,
46
+ stale: true,
47
+ cached_at: cacheTimestamp
48
+ ? new Date(cacheTimestamp).toISOString()
49
+ : undefined,
50
+ };
51
+ }
52
+ // No cache available, return empty response
53
+ logger.error("[Gateway] No cache available, returning empty providers list");
54
+ return {
55
+ providers: [],
56
+ stale: true,
57
+ };
58
+ }
59
+ }
60
+ /**
61
+ * Transform gateway API response to our internal format
62
+ */
63
+ function transformGatewayResponse(data) {
64
+ // Gateway response format: { object: "list", data: [...models] }
65
+ const providers = [];
66
+ // Try data.data first (Vercel Gateway format), fallback to data.models
67
+ const modelsArray = data.data || data.models || [];
68
+ if (Array.isArray(modelsArray) && modelsArray.length > 0) {
69
+ // Group models by provider
70
+ const providerMap = new Map();
71
+ for (const model of modelsArray) {
72
+ const modelData = {
73
+ id: model.id || model.model_id,
74
+ name: model.name || model.display_name || model.id,
75
+ description: model.description,
76
+ type: model.type || inferModelType(model.id),
77
+ provider: extractProvider(model.id),
78
+ context_window: model.context_window || model.context_length,
79
+ max_tokens: model.max_output_tokens || model.max_tokens,
80
+ pricing: model.pricing
81
+ ? {
82
+ input: model.pricing.input || model.pricing.prompt || 0,
83
+ output: model.pricing.output || model.pricing.completion || 0,
84
+ image: model.pricing.image,
85
+ request: model.pricing.request,
86
+ web_search: model.pricing.web_search,
87
+ }
88
+ : undefined,
89
+ tags: model.tags || [],
90
+ supports_tools: model.supports_tools || model.supports_function_calling,
91
+ supports_vision: model.supports_vision || model.modalities?.includes("vision"),
92
+ };
93
+ const providerName = modelData.provider;
94
+ if (!providerMap.has(providerName)) {
95
+ providerMap.set(providerName, []);
96
+ }
97
+ providerMap.get(providerName).push(modelData);
98
+ }
99
+ // Convert map to providers array
100
+ for (const [providerName, models] of providerMap) {
101
+ providers.push({
102
+ name: providerName,
103
+ display_name: formatProviderName(providerName),
104
+ models,
105
+ });
106
+ }
107
+ }
108
+ return { providers };
109
+ }
110
+ /**
111
+ * Extract provider name from model ID (e.g., "openai/gpt-4" -> "openai")
112
+ */
113
+ function extractProvider(modelId) {
114
+ if (modelId.includes("/")) {
115
+ return modelId.split("/")[0] || "unknown";
116
+ }
117
+ return "unknown";
118
+ }
119
+ /**
120
+ * Infer model type from ID
121
+ */
122
+ function inferModelType(modelId) {
123
+ const id = modelId.toLowerCase();
124
+ if (id.includes("dall-e") || id.includes("image") || id.includes("imagen")) {
125
+ return "image";
126
+ }
127
+ if (id.includes("embedding") || id.includes("ada")) {
128
+ return "embedding";
129
+ }
130
+ if (id.includes("whisper") || id.includes("audio")) {
131
+ return "audio";
132
+ }
133
+ return "text";
134
+ }
135
+ /**
136
+ * Format provider name for display
137
+ */
138
+ function formatProviderName(provider) {
139
+ const names = {
140
+ openai: "OpenAI",
141
+ anthropic: "Anthropic",
142
+ google: "Google",
143
+ mistral: "Mistral AI",
144
+ meta: "Meta",
145
+ cohere: "Cohere",
146
+ };
147
+ return (names[provider] || provider.charAt(0).toUpperCase() + provider.slice(1));
148
+ }
149
+ /**
150
+ * Count total models across all providers
151
+ */
152
+ function countModels(response) {
153
+ return response.providers.reduce((sum, p) => sum + p.models.length, 0);
154
+ }
155
+ /**
156
+ * Clear cache (useful for testing or manual refresh)
157
+ */
158
+ export function clearGatewayCache() {
159
+ cachedModels = null;
160
+ cacheTimestamp = null;
161
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Get global AI configuration (default models for text and image)
3
+ * This is the single source of truth for all AI generation calls
4
+ */
5
+ export declare function getGlobalAISettings(): Promise<any>;
6
+ /**
7
+ * Get default text model from global settings
8
+ * Returns "provider/model" format
9
+ */
10
+ export declare function getDefaultTextModel(): Promise<string>;
11
+ /**
12
+ * Get default image model from global settings
13
+ * Returns "provider/model" format
14
+ */
15
+ export declare function getDefaultImageModel(): Promise<string>;
16
+ //# sourceMappingURL=global-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global-settings.d.ts","sourceRoot":"","sources":["../../src/server/global-settings.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,mBAAmB,iBAyBxC;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG3D;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG5D"}