@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,42 @@
1
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
2
+ import { logger } from "@lastbrain/core";
3
+ /**
4
+ * Get global AI configuration (default models for text and image)
5
+ * This is the single source of truth for all AI generation calls
6
+ */
7
+ export async function getGlobalAISettings() {
8
+ const supabase = getSupabaseServiceClient();
9
+ const { data, error } = await supabase
10
+ .from("ai_global_settings")
11
+ .select("*")
12
+ .eq("id", 1)
13
+ .single();
14
+ if (error) {
15
+ logger.error("[getGlobalAISettings] Error fetching global settings:", error);
16
+ // Fallback to defaults if DB query fails
17
+ return {
18
+ default_text_provider: "openai",
19
+ default_text_model: "gpt-4.1-mini",
20
+ default_image_provider: "google",
21
+ default_image_model: "gemini-2.5-flash-image",
22
+ max_tokens_per_call: 16384,
23
+ };
24
+ }
25
+ return data;
26
+ }
27
+ /**
28
+ * Get default text model from global settings
29
+ * Returns "provider/model" format
30
+ */
31
+ export async function getDefaultTextModel() {
32
+ const settings = await getGlobalAISettings();
33
+ return `${settings.default_text_provider}/${settings.default_text_model}`;
34
+ }
35
+ /**
36
+ * Get default image model from global settings
37
+ * Returns "provider/model" format
38
+ */
39
+ export async function getDefaultImageModel() {
40
+ const settings = await getGlobalAISettings();
41
+ return `${settings.default_image_provider}/${settings.default_image_model}`;
42
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Helper functions for model filtering and validation
3
+ */
4
+ import type { GatewayModel } from "../types/gateway";
5
+ export interface ModelFilterResult {
6
+ allowed: boolean;
7
+ model?: GatewayModel;
8
+ error?: string;
9
+ }
10
+ /**
11
+ * Check if a model is enabled for the given user
12
+ */
13
+ export declare function isModelEnabled(ownerId: string, modelId: string): Promise<ModelFilterResult>;
14
+ /**
15
+ * Get list of enabled models for a user
16
+ */
17
+ export declare function getEnabledModels(ownerId: string): Promise<GatewayModel[]>;
18
+ /**
19
+ * Validate model and return error response if not allowed
20
+ */
21
+ export declare function createModelNotAllowedError(modelId: string, error: string): {
22
+ error: any;
23
+ status: number;
24
+ };
25
+ //# sourceMappingURL=model-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-filter.d.ts","sourceRoot":"","sources":["../../src/server/model-filter.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA2BrD,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAmL5B;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,YAAY,EAAE,CAAC,CA2CzB;AAyBD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ;IAAE,KAAK,EAAE,GAAG,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAShC"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Helper functions for model filtering and validation
3
+ */
4
+ import { logger } from "@lastbrain/core";
5
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
6
+ import { fetchGatewayModels } from "./gateway-service";
7
+ import { getDefaultTextModel, getDefaultImageModel } from "./global-settings";
8
+ // Default models auto-enabled on first use
9
+ const DEFAULT_MODELS = [
10
+ "openai/gpt-4o-mini",
11
+ "google/imagen-4.0-fast-generate-001",
12
+ "google/gemini-2.5-flash-image",
13
+ ];
14
+ /**
15
+ * Get default models from ai_global_settings
16
+ * These models should always be allowed for all users
17
+ */
18
+ async function getGlobalDefaultModels() {
19
+ try {
20
+ const [textModel, imageModel] = await Promise.all([
21
+ getDefaultTextModel(),
22
+ getDefaultImageModel(),
23
+ ]);
24
+ return [textModel, imageModel];
25
+ }
26
+ catch (err) {
27
+ logger.error("[ModelFilter] Failed to fetch global default models", err);
28
+ return [];
29
+ }
30
+ }
31
+ /**
32
+ * Check if a model is enabled for the given user
33
+ */
34
+ export async function isModelEnabled(ownerId, modelId) {
35
+ try {
36
+ // 0. Get global default models - these are ALWAYS allowed
37
+ const globalDefaultModels = await getGlobalDefaultModels();
38
+ const isGlobalDefault = globalDefaultModels.includes(modelId);
39
+ if (isGlobalDefault) {
40
+ logger.debug(`[ModelFilter] Model ${modelId} is a global default, auto-allowing`);
41
+ }
42
+ // 1. Get user settings from user_ai_settings
43
+ const supabase = await getSupabaseServiceClient();
44
+ const { data: settings, error } = await supabase
45
+ .from("user_ai_settings")
46
+ .select("allowed_models")
47
+ .eq("owner_id", ownerId)
48
+ .single();
49
+ if (error && error.code !== "PGRST116") {
50
+ // PGRST116 = no rows returned (user has no settings yet)
51
+ logger.error("[ModelFilter] Error fetching settings:", error);
52
+ throw error;
53
+ }
54
+ // 2. If no settings exist, create default settings with basic models + global defaults
55
+ if (!settings) {
56
+ logger.info(`[ModelFilter] No settings for user ${ownerId}, creating default with gpt-4o-mini and imagen-4.0-fast`);
57
+ // Merge static defaults with global defaults
58
+ const allDefaultModels = Array.from(new Set([...DEFAULT_MODELS, ...globalDefaultModels]));
59
+ const { error: insertError } = await supabase
60
+ .from("user_ai_settings")
61
+ .insert({
62
+ owner_id: ownerId,
63
+ allowed_models: allDefaultModels,
64
+ });
65
+ if (insertError) {
66
+ logger.error("[ModelFilter] Failed to create default settings:", insertError);
67
+ // Continue anyway, model might still be in default list
68
+ }
69
+ // After creating settings, check if requested model is in default list OR global defaults
70
+ if (!DEFAULT_MODELS.includes(modelId) && !isGlobalDefault) {
71
+ return {
72
+ allowed: false,
73
+ error: `Model "${modelId}" is not enabled. Please enable it in your settings at /auth/ai/settings`,
74
+ };
75
+ }
76
+ // Model is in default list, fetch metadata
77
+ const gatewayModels = await fetchGatewayModels();
78
+ const model = findModelById(gatewayModels.providers, modelId);
79
+ if (!model) {
80
+ logger.warn(`[ModelFilter] Model ${modelId} not found in gateway`);
81
+ return {
82
+ allowed: false,
83
+ error: `Model "${modelId}" not found or unavailable`,
84
+ };
85
+ }
86
+ return {
87
+ allowed: true,
88
+ model,
89
+ };
90
+ }
91
+ // Parse allowed_models - can be array of strings or JSONB
92
+ let enabledModels = [];
93
+ if (settings?.allowed_models) {
94
+ if (Array.isArray(settings.allowed_models)) {
95
+ enabledModels = settings.allowed_models;
96
+ }
97
+ else if (typeof settings.allowed_models === "object") {
98
+ // Handle JSONB format: [{"provider":"openai","models":["gpt-4o-mini"]}]
99
+ enabledModels = settings.allowed_models.flatMap((item) => {
100
+ if (item.models && Array.isArray(item.models)) {
101
+ const provider = item.provider;
102
+ return item.models.map((model) => `${provider}/${model}`);
103
+ }
104
+ return [];
105
+ });
106
+ }
107
+ }
108
+ // 3. If settings exist but empty, add default models + global defaults
109
+ if (enabledModels.length === 0) {
110
+ logger.info(`[ModelFilter] Empty allowed_models for user ${ownerId}, adding defaults`);
111
+ const allDefaultModels = Array.from(new Set([...DEFAULT_MODELS, ...globalDefaultModels]));
112
+ const { error: updateError } = await supabase
113
+ .from("user_ai_settings")
114
+ .update({
115
+ allowed_models: allDefaultModels,
116
+ })
117
+ .eq("owner_id", ownerId);
118
+ if (updateError) {
119
+ logger.error("[ModelFilter] Failed to update settings with defaults:", updateError);
120
+ }
121
+ enabledModels = allDefaultModels;
122
+ }
123
+ // 4. Check specific model filter - allow if in user settings OR global defaults
124
+ if (!enabledModels.includes(modelId) && !isGlobalDefault) {
125
+ logger.warn(`[ModelFilter] Model ${modelId} not enabled for user ${ownerId}`);
126
+ return {
127
+ allowed: false,
128
+ error: `Model "${modelId}" is not enabled for your account. Please enable it in your settings.`,
129
+ };
130
+ }
131
+ // If model is global default but not in user settings, add it
132
+ if (isGlobalDefault && !enabledModels.includes(modelId)) {
133
+ logger.info(`[ModelFilter] Adding global default model ${modelId} to user ${ownerId} settings`);
134
+ const updatedModels = Array.from(new Set([...enabledModels, modelId]));
135
+ const { error: updateError } = await supabase
136
+ .from("user_ai_settings")
137
+ .update({ allowed_models: updatedModels })
138
+ .eq("owner_id", ownerId);
139
+ if (updateError) {
140
+ logger.error("[ModelFilter] Failed to add global default to user settings:", updateError);
141
+ }
142
+ }
143
+ // 5. Model is allowed, fetch metadata
144
+ const gatewayModels = await fetchGatewayModels();
145
+ const model = findModelById(gatewayModels.providers, modelId);
146
+ if (!model) {
147
+ logger.warn(`[ModelFilter] Model ${modelId} not found in gateway`);
148
+ return {
149
+ allowed: false,
150
+ error: `Model "${modelId}" not found or unavailable`,
151
+ };
152
+ }
153
+ logger.debug(`[ModelFilter] Model ${modelId} allowed for user ${ownerId}`);
154
+ return {
155
+ allowed: true,
156
+ model,
157
+ };
158
+ }
159
+ catch (error) {
160
+ logger.error("[ModelFilter] Error checking model:", error);
161
+ return {
162
+ allowed: false,
163
+ error: `Failed to validate model: ${error.message}`,
164
+ };
165
+ }
166
+ }
167
+ /**
168
+ * Get list of enabled models for a user
169
+ */
170
+ export async function getEnabledModels(ownerId) {
171
+ try {
172
+ const supabase = await getSupabaseServiceClient();
173
+ const { data: settings } = await supabase
174
+ .from("user_ai_settings")
175
+ .select("allowed_models")
176
+ .eq("owner_id", ownerId)
177
+ .single();
178
+ // Parse allowed_models
179
+ let enabledModelIds = [];
180
+ if (settings?.allowed_models) {
181
+ if (Array.isArray(settings.allowed_models)) {
182
+ enabledModelIds = settings.allowed_models;
183
+ }
184
+ else if (typeof settings.allowed_models === "object") {
185
+ // Handle JSONB format
186
+ enabledModelIds = settings.allowed_models.flatMap((item) => {
187
+ if (item.models && Array.isArray(item.models)) {
188
+ const provider = item.provider;
189
+ return item.models.map((model) => `${provider}/${model}`);
190
+ }
191
+ return [];
192
+ });
193
+ }
194
+ }
195
+ // Fetch all models from gateway
196
+ const gatewayModels = await fetchGatewayModels();
197
+ // If no enabled models, return empty array (explicit enable required)
198
+ if (enabledModelIds.length === 0) {
199
+ return [];
200
+ }
201
+ // Filter by enabled models
202
+ const allModels = getAllModels(gatewayModels.providers);
203
+ return allModels.filter((model) => enabledModelIds.includes(model.id));
204
+ }
205
+ catch (error) {
206
+ logger.error("[ModelFilter] Error getting enabled models:", error);
207
+ return [];
208
+ }
209
+ }
210
+ /**
211
+ * Find a model by ID across all providers
212
+ */
213
+ function findModelById(providers, modelId) {
214
+ for (const provider of providers) {
215
+ const model = provider.models.find((m) => m.id === modelId);
216
+ if (model) {
217
+ return model;
218
+ }
219
+ }
220
+ return undefined;
221
+ }
222
+ /**
223
+ * Get all models from all providers as flat array
224
+ */
225
+ function getAllModels(providers) {
226
+ return providers.flatMap((p) => p.models);
227
+ }
228
+ /**
229
+ * Validate model and return error response if not allowed
230
+ */
231
+ export function createModelNotAllowedError(modelId, error) {
232
+ return {
233
+ error: {
234
+ code: "MODEL_NOT_ALLOWED",
235
+ message: error,
236
+ model_id: modelId,
237
+ },
238
+ status: 403,
239
+ };
240
+ }
@@ -0,0 +1,39 @@
1
+ export interface ProcessOCRParams {
2
+ userId: string;
3
+ files: Array<{
4
+ name: string;
5
+ path: string;
6
+ extension: string;
7
+ bucket?: string;
8
+ }>;
9
+ mode: "verify" | "extract" | "verify_and_extract";
10
+ prompt: string;
11
+ extractionSchema?: Record<string, unknown>;
12
+ allowOverdraft?: boolean;
13
+ }
14
+ export interface OCRResult {
15
+ file_name: string;
16
+ file_path: string;
17
+ verified: boolean;
18
+ verification_message?: string;
19
+ extracted_data?: Record<string, unknown>;
20
+ confidence_score?: number;
21
+ processing_error?: string;
22
+ }
23
+ export interface ProcessOCRResponse {
24
+ success: boolean;
25
+ results: OCRResult[];
26
+ tokensUsed: number;
27
+ tokensRemaining?: number;
28
+ debitedFromQuota?: number;
29
+ debitedFromPurchased?: number;
30
+ remainingQuota?: number;
31
+ remainingPurchased?: number;
32
+ error?: string;
33
+ }
34
+ /**
35
+ * Process OCR using OpenAI Vision with proper token debit
36
+ * This function can be called directly by other modules
37
+ */
38
+ export declare function processOCRWithTokens(params: ProcessOCRParams): Promise<ProcessOCRResponse>;
39
+ //# sourceMappingURL=ocr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../src/server/ocr.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,IAAI,EAAE,QAAQ,GAAG,SAAS,GAAG,oBAAoB,CAAC;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAiU7B"}
@@ -0,0 +1,280 @@
1
+ import { getSupabaseServiceClient } from "@lastbrain/core/server";
2
+ import { debitTokensWithPriority } from "./quota";
3
+ import { generateText } from "ai";
4
+ import { logger } from "@lastbrain/core";
5
+ /**
6
+ * Process OCR using OpenAI Vision with proper token debit
7
+ * This function can be called directly by other modules
8
+ */
9
+ export async function processOCRWithTokens(params) {
10
+ const { userId, files, mode, prompt, extractionSchema, allowOverdraft = false, } = params;
11
+ try {
12
+ const supabase = getSupabaseServiceClient();
13
+ if (!files || files.length === 0) {
14
+ return {
15
+ success: false,
16
+ results: [],
17
+ tokensUsed: 0,
18
+ error: "Aucun fichier à traiter",
19
+ };
20
+ }
21
+ if (!prompt) {
22
+ return {
23
+ success: false,
24
+ results: [],
25
+ tokensUsed: 0,
26
+ error: "Le prompt est requis",
27
+ };
28
+ }
29
+ // Estimate tokens needed (conservative: ~500 tokens per image)
30
+ const estimatedTokensPerFile = 500;
31
+ const estimatedTokensNeeded = files.length * estimatedTokensPerFile;
32
+ // Pre-check balance/quota
33
+ const { data: balanceData } = await supabase
34
+ .from("user_token_balance_v")
35
+ .select("balance")
36
+ .eq("owner_id", userId)
37
+ .single();
38
+ const purchasedBalance = balanceData?.balance || 0;
39
+ const { data: quotaData } = await supabase
40
+ .from("user_token_quota_monthly")
41
+ .select("effective_included_tokens, used_included_tokens, period_start, period_end")
42
+ .eq("owner_id", userId)
43
+ .single();
44
+ let remainingQuota = 0;
45
+ if (quotaData) {
46
+ const now = new Date();
47
+ const periodStart = quotaData.period_start
48
+ ? new Date(quotaData.period_start)
49
+ : null;
50
+ const periodEnd = quotaData.period_end
51
+ ? new Date(quotaData.period_end)
52
+ : null;
53
+ const isActive = periodStart && periodEnd && now >= periodStart && now <= periodEnd;
54
+ if (isActive) {
55
+ remainingQuota = Math.max(0, quotaData.effective_included_tokens -
56
+ (quotaData.used_included_tokens || 0));
57
+ }
58
+ }
59
+ if (purchasedBalance < 0 && !allowOverdraft) {
60
+ return {
61
+ success: false,
62
+ results: [],
63
+ tokensUsed: 0,
64
+ error: "Solde insuffisant (compte en négatif). Veuillez recharger.",
65
+ };
66
+ }
67
+ if (purchasedBalance + remainingQuota < estimatedTokensNeeded &&
68
+ !allowOverdraft) {
69
+ return {
70
+ success: false,
71
+ results: [],
72
+ tokensUsed: 0,
73
+ error: "Solde insuffisant pour traiter les fichiers OCR.",
74
+ };
75
+ }
76
+ // Process each file
77
+ const results = [];
78
+ let totalTokensUsed = 0;
79
+ const model = "openai/gpt-4o"; // Use string model ID for Vercel AI Gateway
80
+ for (const file of files) {
81
+ try {
82
+ // Skip non-image files
83
+ const imageExtensions = ["jpg", "jpeg", "png", "webp", "gif"];
84
+ if (!imageExtensions.includes(file.extension.toLowerCase())) {
85
+ results.push({
86
+ file_name: file.name,
87
+ file_path: file.path,
88
+ verified: false,
89
+ processing_error: `Type de fichier non supporté: ${file.extension}. Seules les images sont supportées pour l'OCR.`,
90
+ });
91
+ continue;
92
+ }
93
+ // Download file from Supabase Storage
94
+ const bucket = file.bucket || "app";
95
+ const { data: fileData, error: downloadError } = await supabase.storage
96
+ .from(bucket)
97
+ .download(file.path);
98
+ if (downloadError || !fileData) {
99
+ logger.error(`Error downloading file ${file.path}:`, downloadError);
100
+ results.push({
101
+ file_name: file.name,
102
+ file_path: file.path,
103
+ verified: false,
104
+ processing_error: "Impossible de télécharger le fichier",
105
+ });
106
+ continue;
107
+ }
108
+ // Convert file to base64
109
+ const buffer = await fileData.arrayBuffer();
110
+ const base64 = Buffer.from(buffer).toString("base64");
111
+ const imageUrl = `data:image/${file.extension};base64,${base64}`;
112
+ // Build prompt based on mode
113
+ let systemPrompt = "";
114
+ let userPrompt = prompt;
115
+ if (mode === "verify") {
116
+ systemPrompt =
117
+ "Tu es un expert en vérification de documents. Analyse l'image et vérifie si elle correspond aux critères demandés.";
118
+ userPrompt +=
119
+ "\n\nRéponds au format JSON avec les champs: verified (boolean), message (string), confidence (0-1)";
120
+ }
121
+ else if (mode === "extract") {
122
+ systemPrompt =
123
+ "Tu es un expert en extraction de données depuis des documents. Analyse l'image et extrait les informations demandées.";
124
+ userPrompt += `\n\nRéponds au format JSON avec les champs extraits selon ce schéma: ${JSON.stringify(extractionSchema)}`;
125
+ }
126
+ else {
127
+ // verify_and_extract
128
+ systemPrompt =
129
+ "Tu es un expert en vérification et extraction de données depuis des documents.";
130
+ userPrompt += `\n\nRéponds au format JSON avec:
131
+ - verified (boolean): si le document correspond aux critères
132
+ - message (string): message de vérification
133
+ - confidence (0-1): niveau de confiance
134
+ - data (object): données extraites selon ce schéma: ${JSON.stringify(extractionSchema)}`;
135
+ }
136
+ // Call Vercel AI SDK Vision API with correct message format
137
+ const completion = await generateText({
138
+ model: model,
139
+ messages: [
140
+ {
141
+ role: "system",
142
+ content: systemPrompt,
143
+ },
144
+ {
145
+ role: "user",
146
+ content: [
147
+ { type: "text", text: userPrompt },
148
+ {
149
+ type: "image",
150
+ image: imageUrl,
151
+ },
152
+ ],
153
+ },
154
+ ],
155
+ temperature: 0.1,
156
+ });
157
+ const content = completion.text;
158
+ const tokensUsed = completion.usage?.totalTokens || 0;
159
+ totalTokensUsed += tokensUsed;
160
+ if (!content) {
161
+ results.push({
162
+ file_name: file.name,
163
+ file_path: file.path,
164
+ verified: false,
165
+ processing_error: "Aucune réponse de l'API",
166
+ });
167
+ continue;
168
+ }
169
+ // Parse JSON response
170
+ try {
171
+ const parsed = JSON.parse(content);
172
+ if (mode === "verify") {
173
+ results.push({
174
+ file_name: file.name,
175
+ file_path: file.path,
176
+ verified: parsed.verified || false,
177
+ verification_message: parsed.message,
178
+ confidence_score: parsed.confidence,
179
+ });
180
+ }
181
+ else if (mode === "extract") {
182
+ results.push({
183
+ file_name: file.name,
184
+ file_path: file.path,
185
+ verified: true,
186
+ extracted_data: parsed,
187
+ });
188
+ }
189
+ else {
190
+ // verify_and_extract
191
+ results.push({
192
+ file_name: file.name,
193
+ file_path: file.path,
194
+ verified: parsed.verified || false,
195
+ verification_message: parsed.message,
196
+ confidence_score: parsed.confidence,
197
+ extracted_data: parsed.data || {},
198
+ });
199
+ }
200
+ }
201
+ catch (parseError) {
202
+ logger.error(`Error parsing OCR response for ${file.name}:`, parseError);
203
+ results.push({
204
+ file_name: file.name,
205
+ file_path: file.path,
206
+ verified: false,
207
+ processing_error: "Erreur lors du parsing de la réponse",
208
+ });
209
+ }
210
+ }
211
+ catch (error) {
212
+ logger.error(`Error processing OCR for ${file.name}:`, error);
213
+ results.push({
214
+ file_name: file.name,
215
+ file_path: file.path,
216
+ verified: false,
217
+ processing_error: error instanceof Error ? error.message : "Erreur inconnue",
218
+ });
219
+ }
220
+ }
221
+ // Debit tokens using the same system as generate-text
222
+ if (totalTokensUsed > 0) {
223
+ // Calculate cost for gpt-4o vision
224
+ // gpt-4o: $2.50/1M input, $10/1M output
225
+ const inputTokens = Math.floor(totalTokensUsed * 0.4); // Approximate (images are input)
226
+ const outputTokens = Math.floor(totalTokensUsed * 0.6);
227
+ const cost = (inputTokens / 1_000_000) * 2.5 + (outputTokens / 1_000_000) * 10;
228
+ const tokenResult = await debitTokensWithPriority(userId, totalTokensUsed, "gpt-4o", `OCR processing: ${files.length} files`, {
229
+ inputTokens,
230
+ outputTokens,
231
+ cost,
232
+ generatedText: `OCR results for ${files.length} files`,
233
+ allowOverdraft,
234
+ });
235
+ if (!tokenResult.success) {
236
+ return {
237
+ success: false,
238
+ results,
239
+ tokensUsed: totalTokensUsed,
240
+ error: tokenResult.error ||
241
+ "Erreur lors de la déduction des tokens (quota/achat)",
242
+ };
243
+ }
244
+ const tokensRemainingTotal = tokenResult.remainingQuota + tokenResult.remainingPurchased;
245
+ return {
246
+ success: true,
247
+ results,
248
+ tokensUsed: totalTokensUsed,
249
+ tokensRemaining: tokensRemainingTotal,
250
+ debitedFromQuota: tokenResult.debitedFromQuota,
251
+ debitedFromPurchased: tokenResult.debitedFromPurchased,
252
+ remainingQuota: tokenResult.remainingQuota,
253
+ remainingPurchased: tokenResult.remainingPurchased,
254
+ };
255
+ }
256
+ return {
257
+ success: true,
258
+ results,
259
+ tokensUsed: 0,
260
+ };
261
+ }
262
+ catch (error) {
263
+ logger.error("Erreur de traitement OCR:", error);
264
+ const err = error;
265
+ if (err.code === "insufficient_quota") {
266
+ return {
267
+ success: false,
268
+ results: [],
269
+ tokensUsed: 0,
270
+ error: "Quota IA dépassé. Contactez l'administrateur.",
271
+ };
272
+ }
273
+ return {
274
+ success: false,
275
+ results: [],
276
+ tokensUsed: 0,
277
+ error: err.message || "Erreur lors du traitement OCR",
278
+ };
279
+ }
280
+ }
@@ -0,0 +1,19 @@
1
+ import OpenAI from "openai";
2
+ /**
3
+ * Obtenir le client OpenAI (singleton)
4
+ */
5
+ export declare function getOpenAIClient(): OpenAI;
6
+ export interface ChatCompletionParams {
7
+ model?: string;
8
+ messages: OpenAI.Chat.ChatCompletionMessageParam[];
9
+ temperature?: number;
10
+ max_tokens?: number;
11
+ response_format?: {
12
+ type: "json_object" | "text";
13
+ };
14
+ }
15
+ /**
16
+ * Appeler OpenAI Chat Completion avec les paramètres fournis
17
+ */
18
+ export declare function createChatCompletion(params: ChatCompletionParams): Promise<OpenAI.Chat.ChatCompletion>;
19
+ //# sourceMappingURL=openai-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-client.d.ts","sourceRoot":"","sources":["../../src/server/openai-client.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAI5B;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE,CAAC;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE;QAAE,IAAI,EAAE,aAAa,GAAG,MAAM,CAAA;KAAE,CAAC;CACpD;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,oBAAoB,GAC3B,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAUrC"}