@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,26 @@
1
+ import OpenAI from "openai";
2
+ let openaiInstance = null;
3
+ /**
4
+ * Obtenir le client OpenAI (singleton)
5
+ */
6
+ export function getOpenAIClient() {
7
+ if (!openaiInstance) {
8
+ openaiInstance = new OpenAI({
9
+ apiKey: process.env.OPENAI_API_KEY,
10
+ });
11
+ }
12
+ return openaiInstance;
13
+ }
14
+ /**
15
+ * Appeler OpenAI Chat Completion avec les paramètres fournis
16
+ */
17
+ export async function createChatCompletion(params) {
18
+ const client = getOpenAIClient();
19
+ return await client.chat.completions.create({
20
+ model: params.model || "gpt-4o",
21
+ messages: params.messages,
22
+ temperature: params.temperature ?? 0.7,
23
+ max_tokens: params.max_tokens,
24
+ response_format: params.response_format,
25
+ });
26
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Database access layer for pricing configuration
3
+ */
4
+ import type { PricingConfig } from "./pricing";
5
+ /**
6
+ * Get current pricing configuration from database
7
+ * Uses best pack price (lowest $/token) as reference
8
+ */
9
+ export declare function getPricingConfig(): Promise<PricingConfig>;
10
+ /**
11
+ * Get pricing config for a specific pack
12
+ */
13
+ export declare function getPricingConfigForPack(packId: string): Promise<PricingConfig>;
14
+ /**
15
+ * Get pricing config with custom markup
16
+ */
17
+ export declare function getPricingConfigWithMarkup(markupCoef: number): Promise<PricingConfig>;
18
+ //# sourceMappingURL=pricing-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing-config.d.ts","sourceRoot":"","sources":["../../src/server/pricing-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAI/C;;;GAGG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,aAAa,CAAC,CA4C/D;AAaD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,CAAC,CA8BxB;AAED;;GAEG;AACH,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,CAAC,CAMxB"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Database access layer for pricing configuration
3
+ */
4
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
5
+ import { logger } from "@lastbrain/core";
6
+ const DEFAULT_MARKUP_COEF = 1.35; // 35% margin on cost
7
+ /**
8
+ * Get current pricing configuration from database
9
+ * Uses best pack price (lowest $/token) as reference
10
+ */
11
+ export async function getPricingConfig() {
12
+ try {
13
+ const supabase = await getSupabaseServiceClient();
14
+ const { data: packs, error } = await supabase
15
+ .from("token_packs")
16
+ .select("tokens, price_cents, currency")
17
+ .eq("is_active", true);
18
+ if (error || !packs || packs.length === 0) {
19
+ logger.warn("[PricingConfig] No active packs found, using fallback");
20
+ return getFallbackConfig();
21
+ }
22
+ // Find best pack (lowest price per token)
23
+ let bestPack = null;
24
+ for (const pack of packs) {
25
+ const priceUsd = pack.price_cents / 100;
26
+ if (!bestPack ||
27
+ priceUsd / pack.tokens < bestPack.priceUsd / bestPack.tokens) {
28
+ bestPack = { tokens: pack.tokens, priceUsd };
29
+ }
30
+ }
31
+ if (!bestPack) {
32
+ return getFallbackConfig();
33
+ }
34
+ logger.debug(`[PricingConfig] Using pack: ${bestPack.tokens} tokens = $${bestPack.priceUsd.toFixed(2)} (${((bestPack.priceUsd / bestPack.tokens) * 1_000_000).toFixed(4)} $/M tokens)`);
35
+ return {
36
+ packTokens: bestPack.tokens,
37
+ packPriceUsd: bestPack.priceUsd,
38
+ markupCoef: DEFAULT_MARKUP_COEF,
39
+ };
40
+ }
41
+ catch (error) {
42
+ logger.error("[PricingConfig] Error fetching config:", error);
43
+ return getFallbackConfig();
44
+ }
45
+ }
46
+ /**
47
+ * Fallback configuration (Creator pack equivalent)
48
+ */
49
+ function getFallbackConfig() {
50
+ return {
51
+ packTokens: 2_000_000, // 2M tokens
52
+ packPriceUsd: 5.0, // $5 USD
53
+ markupCoef: DEFAULT_MARKUP_COEF,
54
+ };
55
+ }
56
+ /**
57
+ * Get pricing config for a specific pack
58
+ */
59
+ export async function getPricingConfigForPack(packId) {
60
+ try {
61
+ const supabase = await getSupabaseServiceClient();
62
+ const { data: pack, error } = await supabase
63
+ .from("token_packs")
64
+ .select("tokens, price_cents, currency")
65
+ .eq("id", packId)
66
+ .eq("is_active", true)
67
+ .single();
68
+ if (error || !pack) {
69
+ logger.warn(`[PricingConfig] Pack ${packId} not found, using default`);
70
+ return getPricingConfig();
71
+ }
72
+ const priceUsd = pack.price_cents / 100;
73
+ logger.debug(`[PricingConfig] Using specific pack: ${pack.tokens} tokens = $${priceUsd.toFixed(2)} (${((priceUsd / pack.tokens) * 1_000_000).toFixed(4)} $/M tokens)`);
74
+ return {
75
+ packTokens: pack.tokens,
76
+ packPriceUsd: priceUsd,
77
+ markupCoef: DEFAULT_MARKUP_COEF,
78
+ };
79
+ }
80
+ catch (error) {
81
+ logger.error("[PricingConfig] Error fetching pack config:", error);
82
+ return getPricingConfig();
83
+ }
84
+ }
85
+ /**
86
+ * Get pricing config with custom markup
87
+ */
88
+ export async function getPricingConfigWithMarkup(markupCoef) {
89
+ const config = await getPricingConfig();
90
+ return {
91
+ ...config,
92
+ markupCoef,
93
+ };
94
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Centralized pricing validation to prevent free API calls
3
+ *
4
+ * CRITICAL SECURITY:
5
+ * - ALL endpoints MUST validate pricing before calling expensive APIs
6
+ * - Pricing must be > 0 to prevent free consumption
7
+ * - Gateway pricing is ALREADY in $/token (not $/1M tokens)
8
+ */
9
+ import type { ProviderPricing } from "./billing";
10
+ import type { GatewayPricing } from "../types/gateway";
11
+ export interface PricingValidationResult {
12
+ valid: boolean;
13
+ pricing?: ProviderPricing;
14
+ error?: string;
15
+ errorCode?: "NO_PRICING" | "INVALID_PRICING";
16
+ }
17
+ /**
18
+ * Validate and convert Gateway pricing to ProviderPricing for text models
19
+ *
20
+ * @param gatewayPricing Pricing from Gateway API ($/1M tokens)
21
+ * @param modelId Model identifier for logging
22
+ * @returns Validation result with converted pricing
23
+ */
24
+ export declare function validateTextPricing(gatewayPricing: GatewayPricing | undefined, modelId: string): PricingValidationResult;
25
+ /**
26
+ * Validate and convert Gateway pricing to ProviderPricing for image models
27
+ *
28
+ * @param gatewayPricing Pricing from Gateway API
29
+ * @param modelId Model identifier for logging
30
+ * @returns Validation result with converted pricing
31
+ */
32
+ export declare function validateImagePricing(gatewayPricing: GatewayPricing | undefined, modelId: string): PricingValidationResult;
33
+ /**
34
+ * Verify estimated cost is valid (> 0)
35
+ *
36
+ * @param estimatedCost Estimated cost in USD
37
+ * @param modelId Model identifier for logging
38
+ * @returns True if valid, false otherwise
39
+ */
40
+ export declare function validateEstimatedCost(estimatedCost: number, modelId: string): boolean;
41
+ //# sourceMappingURL=pricing-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing-validator.d.ts","sourceRoot":"","sources":["../../src/server/pricing-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,YAAY,GAAG,iBAAiB,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,GACd,uBAAuB,CAuDzB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,GACd,uBAAuB,CAwCzB;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAYT"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Centralized pricing validation to prevent free API calls
3
+ *
4
+ * CRITICAL SECURITY:
5
+ * - ALL endpoints MUST validate pricing before calling expensive APIs
6
+ * - Pricing must be > 0 to prevent free consumption
7
+ * - Gateway pricing is ALREADY in $/token (not $/1M tokens)
8
+ */
9
+ import { logger } from "@lastbrain/core";
10
+ /**
11
+ * Validate and convert Gateway pricing to ProviderPricing for text models
12
+ *
13
+ * @param gatewayPricing Pricing from Gateway API ($/1M tokens)
14
+ * @param modelId Model identifier for logging
15
+ * @returns Validation result with converted pricing
16
+ */
17
+ export function validateTextPricing(gatewayPricing, modelId) {
18
+ // Check pricing exists
19
+ if (!gatewayPricing) {
20
+ logger.error(`[PricingValidator] No pricing available for model ${modelId}`);
21
+ return {
22
+ valid: false,
23
+ error: "Pricing non disponible pour ce modèle. Réessayez plus tard.",
24
+ errorCode: "NO_PRICING",
25
+ };
26
+ }
27
+ // Check input pricing
28
+ if (!gatewayPricing.input || gatewayPricing.input <= 0) {
29
+ logger.error(`[PricingValidator] Missing or invalid input pricing for model ${modelId}`, { gatewayPricing });
30
+ return {
31
+ valid: false,
32
+ error: "Pricing incomplet pour ce modèle. Contactez le support.",
33
+ errorCode: "INVALID_PRICING",
34
+ };
35
+ }
36
+ // Check output pricing
37
+ if (!gatewayPricing.output || gatewayPricing.output <= 0) {
38
+ logger.error(`[PricingValidator] Missing or invalid output pricing for model ${modelId}`, { gatewayPricing });
39
+ return {
40
+ valid: false,
41
+ error: "Pricing incomplet pour ce modèle. Contactez le support.",
42
+ errorCode: "INVALID_PRICING",
43
+ };
44
+ }
45
+ // Gateway pricing is ALREADY in $/token (e.g., "0.00000015" = $0.15/1M tokens)
46
+ // No conversion needed - use as-is
47
+ const pricing = {
48
+ inputUsdPerToken: parseFloat(gatewayPricing.input.toString()),
49
+ outputUsdPerToken: parseFloat(gatewayPricing.output.toString()),
50
+ };
51
+ logger.debug(`[PricingValidator] Validated pricing for ${modelId}:`, {
52
+ gateway: gatewayPricing,
53
+ converted: pricing,
54
+ });
55
+ return {
56
+ valid: true,
57
+ pricing,
58
+ };
59
+ }
60
+ /**
61
+ * Validate and convert Gateway pricing to ProviderPricing for image models
62
+ *
63
+ * @param gatewayPricing Pricing from Gateway API
64
+ * @param modelId Model identifier for logging
65
+ * @returns Validation result with converted pricing
66
+ */
67
+ export function validateImagePricing(gatewayPricing, modelId) {
68
+ // Check pricing exists
69
+ if (!gatewayPricing) {
70
+ logger.error(`[PricingValidator] No pricing available for model ${modelId}`);
71
+ return {
72
+ valid: false,
73
+ error: "Pricing non disponible pour ce modèle. Réessayez plus tard.",
74
+ errorCode: "NO_PRICING",
75
+ };
76
+ }
77
+ // Check image pricing
78
+ if (!gatewayPricing.image || gatewayPricing.image <= 0) {
79
+ logger.error(`[PricingValidator] Missing or invalid image pricing for model ${modelId}`, { gatewayPricing });
80
+ return {
81
+ valid: false,
82
+ error: "Pricing incomplet pour ce modèle. Contactez le support.",
83
+ errorCode: "INVALID_PRICING",
84
+ };
85
+ }
86
+ // Image pricing is already in $/image (no conversion needed)
87
+ const pricing = {
88
+ imageUsdPerImage: parseFloat(gatewayPricing.image.toString()),
89
+ };
90
+ logger.debug(`[PricingValidator] Validated pricing for ${modelId}:`, {
91
+ gateway: gatewayPricing,
92
+ converted: pricing,
93
+ });
94
+ return {
95
+ valid: true,
96
+ pricing,
97
+ };
98
+ }
99
+ /**
100
+ * Verify estimated cost is valid (> 0)
101
+ *
102
+ * @param estimatedCost Estimated cost in USD
103
+ * @param modelId Model identifier for logging
104
+ * @returns True if valid, false otherwise
105
+ */
106
+ export function validateEstimatedCost(estimatedCost, modelId) {
107
+ if (estimatedCost <= 0) {
108
+ logger.error(`[PricingValidator] Invalid cost estimation for model ${modelId}: $${estimatedCost}`);
109
+ return false;
110
+ }
111
+ logger.debug(`[PricingValidator] Estimated cost for ${modelId}: $${estimatedCost.toFixed(6)}`);
112
+ return true;
113
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * CENTRALIZED PRICING ENGINE - Server Only
3
+ * Single source of truth for all cost ↔ tokens ↔ margin calculations
4
+ *
5
+ * Business logic:
6
+ * - We sell token packs (e.g., 100K tokens = $5 USD)
7
+ * - Internal price: sale_usd_per_token = pack_price_usd / pack_tokens
8
+ * - We apply markup to provider costs: sell_usd = provider_cost * markup_coef
9
+ * - We debit in tokens: debit_tokens = ceil(sell_usd / sale_usd_per_token)
10
+ */
11
+ export interface PricingConfig {
12
+ /** Tokens in the reference pack (e.g., 100000) */
13
+ packTokens: number;
14
+ /** Pack price in USD (e.g., 5.00) */
15
+ packPriceUsd: number;
16
+ /** Markup coefficient applied to provider cost (e.g., 1.35 = 35% margin on cost) */
17
+ markupCoef: number;
18
+ }
19
+ export interface ProviderPricing {
20
+ /** Input tokens cost ($/1M tokens) */
21
+ inputUsdPerMToken?: number;
22
+ /** Output tokens cost ($/1M tokens) */
23
+ outputUsdPerMToken?: number;
24
+ /** Cache read cost ($/1M tokens) */
25
+ cacheReadUsdPerMToken?: number;
26
+ /** Web search cost ($/unit) */
27
+ webSearchUsdPerUnit?: number;
28
+ /** Image generation cost ($/image) */
29
+ imageUsdPerImage?: number;
30
+ /** Tiered pricing for input (advanced) */
31
+ inputTiers?: Array<{
32
+ upTo: number;
33
+ pricePerMToken: number;
34
+ }>;
35
+ /** Tiered pricing for output (advanced) */
36
+ outputTiers?: Array<{
37
+ upTo: number;
38
+ pricePerMToken: number;
39
+ }>;
40
+ }
41
+ export interface Usage {
42
+ /** Input tokens consumed */
43
+ inputTokens?: number;
44
+ /** Output tokens generated */
45
+ outputTokens?: number;
46
+ /** Cache read tokens */
47
+ cacheReadTokens?: number;
48
+ /** Web search units used */
49
+ webSearchUnits?: number;
50
+ /** Images generated */
51
+ images?: number;
52
+ }
53
+ export interface BillingResult {
54
+ /** Provider cost in USD */
55
+ providerCostUsd: number;
56
+ /** Selling price in USD (with markup) */
57
+ sellUsd: number;
58
+ /** Tokens to debit from user account */
59
+ debitTokens: number;
60
+ /** Internal token price ($/token) */
61
+ saleUsdPerToken: number;
62
+ /** Markup coefficient used */
63
+ markupCoef: number;
64
+ /** Margin in USD (sellUsd - providerCostUsd) */
65
+ marginUsd: number;
66
+ /** Margin as % of sell price */
67
+ marginPctVsSell: number;
68
+ /** Margin as % of provider cost */
69
+ marginPctVsCost: number;
70
+ }
71
+ export interface ModelDisplayPricing {
72
+ /** Model ID */
73
+ modelId: string;
74
+ /** Estimated tokens for display */
75
+ estimatedTokens: number | null;
76
+ /** Display string (e.g., "8,500 tokens/img" or "N/A") */
77
+ displayPrice: string;
78
+ /** True if pricing is available */
79
+ available: boolean;
80
+ }
81
+ /**
82
+ * Compute billing for AI usage
83
+ * @param config Pricing configuration (pack size, markup)
84
+ * @param pricing Provider pricing data
85
+ * @param usage Actual usage (tokens, images, etc.)
86
+ * @returns Complete billing breakdown
87
+ */
88
+ export declare function computeBilling(config: PricingConfig, pricing: ProviderPricing, usage: Usage): BillingResult;
89
+ /**
90
+ * Convert tokens to sell price in USD
91
+ */
92
+ export declare function tokensToSellUsd(tokens: number, config: PricingConfig): number;
93
+ /**
94
+ * Convert USD to tokens (rounded up)
95
+ */
96
+ export declare function sellUsdToTokens(usd: number, config: PricingConfig): number;
97
+ /**
98
+ * Convert provider cost to debit tokens (with markup)
99
+ */
100
+ export declare function providerUsdToDebitTokens(providerCostUsd: number, config: PricingConfig): number;
101
+ /**
102
+ * Convert image price to debit tokens
103
+ */
104
+ export declare function imagePriceUsdToDebitTokens(pricePerImageUsd: number, images: number, config: PricingConfig): number;
105
+ /**
106
+ * Calculate display pricing for a model (for UI)
107
+ */
108
+ export declare function calculateDisplayPricing(modelId: string, modelType: "text" | "image", pricing: ProviderPricing | null | undefined, config: PricingConfig): ModelDisplayPricing;
109
+ /**
110
+ * Calculate markup coefficient from desired margin percentage
111
+ * @param marginPct Desired margin as % of cost (e.g., 35 for 35%)
112
+ * @returns Markup coefficient (e.g., 1.35)
113
+ */
114
+ export declare function marginPctToMarkupCoef(marginPct: number): number;
115
+ /**
116
+ * Calculate margin percentage from markup coefficient
117
+ * @param markupCoef Markup coefficient (e.g., 1.35)
118
+ * @returns Margin as % of cost (e.g., 35)
119
+ */
120
+ export declare function markupCoefToMarginPct(markupCoef: number): number;
121
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/server/pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * CENTRALIZED PRICING ENGINE - Server Only
3
+ * Single source of truth for all cost ↔ tokens ↔ margin calculations
4
+ *
5
+ * Business logic:
6
+ * - We sell token packs (e.g., 100K tokens = $5 USD)
7
+ * - Internal price: sale_usd_per_token = pack_price_usd / pack_tokens
8
+ * - We apply markup to provider costs: sell_usd = provider_cost * markup_coef
9
+ * - We debit in tokens: debit_tokens = ceil(sell_usd / sale_usd_per_token)
10
+ */
11
+ // ============================================================================
12
+ // CORE CALCULATION FUNCTION
13
+ // ============================================================================
14
+ /**
15
+ * Compute billing for AI usage
16
+ * @param config Pricing configuration (pack size, markup)
17
+ * @param pricing Provider pricing data
18
+ * @param usage Actual usage (tokens, images, etc.)
19
+ * @returns Complete billing breakdown
20
+ */
21
+ export function computeBilling(config, pricing, usage) {
22
+ // 1. Calculate internal token sale price
23
+ const saleUsdPerToken = config.packPriceUsd / config.packTokens;
24
+ // 2. Calculate provider cost in USD
25
+ let providerCostUsd = 0;
26
+ // Input tokens cost
27
+ if (usage.inputTokens && pricing.inputUsdPerMToken) {
28
+ if (pricing.inputTiers && pricing.inputTiers.length > 0) {
29
+ providerCostUsd += calculateTieredCost(usage.inputTokens, pricing.inputTiers);
30
+ }
31
+ else {
32
+ providerCostUsd +=
33
+ (usage.inputTokens / 1_000_000) * pricing.inputUsdPerMToken;
34
+ }
35
+ }
36
+ // Output tokens cost
37
+ if (usage.outputTokens && pricing.outputUsdPerMToken) {
38
+ if (pricing.outputTiers && pricing.outputTiers.length > 0) {
39
+ providerCostUsd += calculateTieredCost(usage.outputTokens, pricing.outputTiers);
40
+ }
41
+ else {
42
+ providerCostUsd +=
43
+ (usage.outputTokens / 1_000_000) * pricing.outputUsdPerMToken;
44
+ }
45
+ }
46
+ // Cache read tokens cost
47
+ if (usage.cacheReadTokens && pricing.cacheReadUsdPerMToken) {
48
+ providerCostUsd +=
49
+ (usage.cacheReadTokens / 1_000_000) * pricing.cacheReadUsdPerMToken;
50
+ }
51
+ // Web search cost
52
+ if (usage.webSearchUnits && pricing.webSearchUsdPerUnit) {
53
+ providerCostUsd += usage.webSearchUnits * pricing.webSearchUsdPerUnit;
54
+ }
55
+ // Image generation cost
56
+ if (usage.images && pricing.imageUsdPerImage) {
57
+ providerCostUsd += usage.images * pricing.imageUsdPerImage;
58
+ }
59
+ // 3. Apply markup to get sell price
60
+ const sellUsd = providerCostUsd * config.markupCoef;
61
+ // 4. Convert to tokens (always round up to protect margin)
62
+ const debitTokens = Math.ceil(sellUsd / saleUsdPerToken);
63
+ // 5. Calculate margins
64
+ const marginUsd = sellUsd - providerCostUsd;
65
+ const marginPctVsSell = sellUsd > 0 ? (marginUsd / sellUsd) * 100 : 0;
66
+ const marginPctVsCost = providerCostUsd > 0 ? (marginUsd / providerCostUsd) * 100 : 0;
67
+ return {
68
+ providerCostUsd: roundUsd(providerCostUsd),
69
+ sellUsd: roundUsd(sellUsd),
70
+ debitTokens,
71
+ saleUsdPerToken: roundUsd(saleUsdPerToken),
72
+ markupCoef: config.markupCoef,
73
+ marginUsd: roundUsd(marginUsd),
74
+ marginPctVsSell: roundPct(marginPctVsSell),
75
+ marginPctVsCost: roundPct(marginPctVsCost),
76
+ };
77
+ }
78
+ /**
79
+ * Calculate cost with tiered pricing
80
+ */
81
+ function calculateTieredCost(tokens, tiers) {
82
+ let remainingTokens = tokens;
83
+ let cost = 0;
84
+ let previousLimit = 0;
85
+ for (const tier of tiers) {
86
+ const tierLimit = tier.upTo;
87
+ const tierTokens = Math.min(remainingTokens, tierLimit - previousLimit);
88
+ if (tierTokens <= 0)
89
+ break;
90
+ cost += (tierTokens / 1_000_000) * tier.pricePerMToken;
91
+ remainingTokens -= tierTokens;
92
+ previousLimit = tierLimit;
93
+ if (remainingTokens <= 0)
94
+ break;
95
+ }
96
+ // Remaining tokens use last tier price
97
+ if (remainingTokens > 0 && tiers.length > 0) {
98
+ const lastTier = tiers[tiers.length - 1];
99
+ cost += (remainingTokens / 1_000_000) * lastTier.pricePerMToken;
100
+ }
101
+ return cost;
102
+ }
103
+ // ============================================================================
104
+ // HELPER FUNCTIONS
105
+ // ============================================================================
106
+ /**
107
+ * Convert tokens to sell price in USD
108
+ */
109
+ export function tokensToSellUsd(tokens, config) {
110
+ const saleUsdPerToken = config.packPriceUsd / config.packTokens;
111
+ return roundUsd(tokens * saleUsdPerToken);
112
+ }
113
+ /**
114
+ * Convert USD to tokens (rounded up)
115
+ */
116
+ export function sellUsdToTokens(usd, config) {
117
+ const saleUsdPerToken = config.packPriceUsd / config.packTokens;
118
+ return Math.ceil(usd / saleUsdPerToken);
119
+ }
120
+ /**
121
+ * Convert provider cost to debit tokens (with markup)
122
+ */
123
+ export function providerUsdToDebitTokens(providerCostUsd, config) {
124
+ const saleUsdPerToken = config.packPriceUsd / config.packTokens;
125
+ const sellUsd = providerCostUsd * config.markupCoef;
126
+ return Math.ceil(sellUsd / saleUsdPerToken);
127
+ }
128
+ /**
129
+ * Convert image price to debit tokens
130
+ */
131
+ export function imagePriceUsdToDebitTokens(pricePerImageUsd, images, config) {
132
+ const saleUsdPerToken = config.packPriceUsd / config.packTokens;
133
+ const providerCost = pricePerImageUsd * images;
134
+ const sellUsd = providerCost * config.markupCoef;
135
+ return Math.ceil(sellUsd / saleUsdPerToken);
136
+ }
137
+ /**
138
+ * Calculate display pricing for a model (for UI)
139
+ */
140
+ export function calculateDisplayPricing(modelId, modelType, pricing, config) {
141
+ if (!pricing) {
142
+ return {
143
+ modelId,
144
+ estimatedTokens: null,
145
+ displayPrice: "N/A",
146
+ available: false,
147
+ };
148
+ }
149
+ if (modelType === "image") {
150
+ if (!pricing.imageUsdPerImage) {
151
+ return {
152
+ modelId,
153
+ estimatedTokens: null,
154
+ displayPrice: "N/A",
155
+ available: false,
156
+ };
157
+ }
158
+ const tokens = imagePriceUsdToDebitTokens(pricing.imageUsdPerImage, 1, config);
159
+ return {
160
+ modelId,
161
+ estimatedTokens: tokens,
162
+ displayPrice: `${tokens.toLocaleString()} tokens/img`,
163
+ available: true,
164
+ };
165
+ }
166
+ if (modelType === "text") {
167
+ // Display cost per 1M tokens (500K input + 500K output)
168
+ if (!pricing.inputUsdPerMToken && !pricing.outputUsdPerMToken) {
169
+ return {
170
+ modelId,
171
+ estimatedTokens: null,
172
+ displayPrice: "Variable",
173
+ available: true, // Text models can still work without exact pricing
174
+ };
175
+ }
176
+ const result = computeBilling(config, pricing, {
177
+ inputTokens: 500_000,
178
+ outputTokens: 500_000,
179
+ });
180
+ const tokensPerM = Math.round(result.debitTokens);
181
+ return {
182
+ modelId,
183
+ estimatedTokens: tokensPerM,
184
+ displayPrice: `~${tokensPerM.toLocaleString()} tokens/1M`,
185
+ available: true,
186
+ };
187
+ }
188
+ return {
189
+ modelId,
190
+ estimatedTokens: null,
191
+ displayPrice: "N/A",
192
+ available: false,
193
+ };
194
+ }
195
+ // ============================================================================
196
+ // UTILITIES
197
+ // ============================================================================
198
+ /**
199
+ * Round USD to 8 decimals
200
+ */
201
+ function roundUsd(value) {
202
+ return Math.round(value * 100_000_000) / 100_000_000;
203
+ }
204
+ /**
205
+ * Round percentage to 2 decimals
206
+ */
207
+ function roundPct(value) {
208
+ return Math.round(value * 100) / 100;
209
+ }
210
+ /**
211
+ * Calculate markup coefficient from desired margin percentage
212
+ * @param marginPct Desired margin as % of cost (e.g., 35 for 35%)
213
+ * @returns Markup coefficient (e.g., 1.35)
214
+ */
215
+ export function marginPctToMarkupCoef(marginPct) {
216
+ return 1 + marginPct / 100;
217
+ }
218
+ /**
219
+ * Calculate margin percentage from markup coefficient
220
+ * @param markupCoef Markup coefficient (e.g., 1.35)
221
+ * @returns Margin as % of cost (e.g., 35)
222
+ */
223
+ export function markupCoefToMarginPct(markupCoef) {
224
+ return (markupCoef - 1) * 100;
225
+ }