@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.
- package/README.md +52 -1
- package/dist/ai.build.config.d.ts.map +1 -1
- package/dist/ai.build.config.js +508 -9
- package/dist/api/admin/ai-provider-models/[id].d.ts +18 -0
- package/dist/api/admin/ai-provider-models/[id].d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models/[id].js +58 -0
- package/dist/api/admin/ai-provider-models.d.ts +20 -0
- package/dist/api/admin/ai-provider-models.d.ts.map +1 -0
- package/dist/api/admin/ai-provider-models.js +26 -0
- package/dist/api/admin/ai-providers/[key].d.ts +18 -0
- package/dist/api/admin/ai-providers/[key].d.ts.map +1 -0
- package/dist/api/admin/ai-providers/[key].js +55 -0
- package/dist/api/admin/ai-providers.d.ts +20 -0
- package/dist/api/admin/ai-providers.d.ts.map +1 -0
- package/dist/api/admin/ai-providers.js +26 -0
- package/dist/api/admin/billing-analytics.d.ts +43 -0
- package/dist/api/admin/billing-analytics.d.ts.map +1 -0
- package/dist/api/admin/billing-analytics.js +144 -0
- package/dist/api/admin/global-ai-settings.d.ts +14 -0
- package/dist/api/admin/global-ai-settings.d.ts.map +1 -0
- package/dist/api/admin/global-ai-settings.js +63 -0
- package/dist/api/admin/token-packs/[id].d.ts.map +1 -1
- package/dist/api/admin/token-packs/[id].js +3 -2
- package/dist/api/admin/token-packs.d.ts.map +1 -1
- package/dist/api/admin/token-packs.js +3 -2
- package/dist/api/admin/user-monthly-details.d.ts +49 -0
- package/dist/api/admin/user-monthly-details.d.ts.map +1 -0
- package/dist/api/admin/user-monthly-details.js +140 -0
- package/dist/api/admin/user-quota.d.ts +21 -0
- package/dist/api/admin/user-quota.d.ts.map +1 -0
- package/dist/api/admin/user-quota.js +59 -0
- package/dist/api/admin/user-token/[id].d.ts.map +1 -1
- package/dist/api/admin/user-token/[id].js +2 -1
- package/dist/api/admin/user-token.d.ts +5 -2
- package/dist/api/admin/user-token.d.ts.map +1 -1
- package/dist/api/admin/user-token.js +91 -17
- package/dist/api/admin/user-usage-by-model.d.ts +22 -0
- package/dist/api/admin/user-usage-by-model.d.ts.map +1 -0
- package/dist/api/admin/user-usage-by-model.js +78 -0
- package/dist/api/admin/user-wallet-analytics.d.ts +15 -0
- package/dist/api/admin/user-wallet-analytics.d.ts.map +1 -0
- package/dist/api/admin/user-wallet-analytics.js +67 -0
- package/dist/api/admin/wallet-repair/route.d.ts +30 -0
- package/dist/api/admin/wallet-repair/route.d.ts.map +1 -0
- package/dist/api/admin/wallet-repair/route.js +63 -0
- package/dist/api/auth/ai-model-settings.d.ts +21 -0
- package/dist/api/auth/ai-model-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-model-settings.js +86 -0
- package/dist/api/auth/ai-settings.d.ts +17 -0
- package/dist/api/auth/ai-settings.d.ts.map +1 -0
- package/dist/api/auth/ai-settings.js +87 -0
- package/dist/api/auth/api-keys/[id].d.ts +17 -0
- package/dist/api/auth/api-keys/[id].d.ts.map +1 -0
- package/dist/api/auth/api-keys/[id].js +66 -0
- package/dist/api/auth/api-keys.d.ts +19 -0
- package/dist/api/auth/api-keys.d.ts.map +1 -0
- package/dist/api/auth/api-keys.js +94 -0
- package/dist/api/auth/create-checkout.d.ts +1 -1
- package/dist/api/auth/create-checkout.d.ts.map +1 -1
- package/dist/api/auth/create-checkout.js +8 -6
- package/dist/api/auth/generate-image.d.ts +2 -2
- package/dist/api/auth/generate-image.d.ts.map +1 -1
- package/dist/api/auth/generate-image.js +404 -104
- package/dist/api/auth/generate-text.d.ts +3 -2
- package/dist/api/auth/generate-text.d.ts.map +1 -1
- package/dist/api/auth/generate-text.js +130 -58
- package/dist/api/auth/process-ocr.d.ts +9 -0
- package/dist/api/auth/process-ocr.d.ts.map +1 -0
- package/dist/api/auth/process-ocr.js +43 -0
- package/dist/api/auth/prompts/stats.d.ts +14 -0
- package/dist/api/auth/prompts/stats.d.ts.map +1 -0
- package/dist/api/auth/prompts/stats.js +46 -0
- package/dist/api/auth/prompts.d.ts +26 -0
- package/dist/api/auth/prompts.d.ts.map +1 -0
- package/dist/api/auth/prompts.js +175 -0
- package/dist/api/auth/token-balance.d.ts +26 -0
- package/dist/api/auth/token-balance.d.ts.map +1 -0
- package/dist/api/auth/token-balance.js +47 -0
- package/dist/api/auth/token-checkout.d.ts.map +1 -1
- package/dist/api/auth/token-checkout.js +22 -4
- package/dist/api/auth/token-packs.d.ts.map +1 -1
- package/dist/api/auth/token-packs.js +2 -1
- package/dist/api/auth/usage-by-model.d.ts +25 -0
- package/dist/api/auth/usage-by-model.d.ts.map +1 -0
- package/dist/api/auth/usage-by-model.js +95 -0
- package/dist/api/auth/usage.d.ts +26 -0
- package/dist/api/auth/usage.d.ts.map +1 -0
- package/dist/api/auth/usage.js +127 -0
- package/dist/api/auth/user-tokens.d.ts.map +1 -1
- package/dist/api/auth/user-tokens.js +36 -2
- package/dist/api/auth/wallet/route.d.ts +17 -0
- package/dist/api/auth/wallet/route.d.ts.map +1 -0
- package/dist/api/auth/wallet/route.js +68 -0
- package/dist/api/auth/wallet.d.ts +16 -0
- package/dist/api/auth/wallet.d.ts.map +1 -0
- package/dist/api/auth/wallet.js +71 -0
- package/dist/api/public/gateway-models.d.ts +25 -0
- package/dist/api/public/gateway-models.d.ts.map +1 -0
- package/dist/api/public/gateway-models.js +49 -0
- package/dist/api/public/pricing-summary.d.ts +46 -0
- package/dist/api/public/pricing-summary.d.ts.map +1 -0
- package/dist/api/public/pricing-summary.js +70 -0
- package/dist/api/public/prompts/[id]/stats.d.ts +15 -0
- package/dist/api/public/prompts/[id]/stats.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/stats.js +37 -0
- package/dist/api/public/prompts/[id]/view.d.ts +15 -0
- package/dist/api/public/prompts/[id]/view.d.ts.map +1 -0
- package/dist/api/public/prompts/[id]/view.js +41 -0
- package/dist/api/public/prompts/slug/[slug].d.ts +15 -0
- package/dist/api/public/prompts/slug/[slug].d.ts.map +1 -0
- package/dist/api/public/prompts/slug/[slug].js +40 -0
- package/dist/api/public/prompts/user/[username].d.ts +17 -0
- package/dist/api/public/prompts/user/[username].d.ts.map +1 -0
- package/dist/api/public/prompts/user/[username].js +51 -0
- package/dist/api/public/prompts.d.ts +15 -0
- package/dist/api/public/prompts.d.ts.map +1 -0
- package/dist/api/public/prompts.js +45 -0
- package/dist/api/public/token-packs.d.ts +11 -0
- package/dist/api/public/token-packs.d.ts.map +1 -0
- package/dist/api/public/token-packs.js +25 -0
- package/dist/api/public/token-pricing.d.ts +44 -0
- package/dist/api/public/token-pricing.d.ts.map +1 -0
- package/dist/api/public/token-pricing.js +168 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts +52 -0
- package/dist/api/public/v1/_lib/artifacts.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/artifacts.js +217 -0
- package/dist/api/public/v1/_lib/auth.d.ts +43 -0
- package/dist/api/public/v1/_lib/auth.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/auth.js +108 -0
- package/dist/api/public/v1/_lib/errors.d.ts +17 -0
- package/dist/api/public/v1/_lib/errors.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/errors.js +16 -0
- package/dist/api/public/v1/_lib/log.d.ts +29 -0
- package/dist/api/public/v1/_lib/log.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/log.js +68 -0
- package/dist/api/public/v1/_lib/quota.d.ts +24 -0
- package/dist/api/public/v1/_lib/quota.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/quota.js +118 -0
- package/dist/api/public/v1/_lib/router.d.ts +54 -0
- package/dist/api/public/v1/_lib/router.d.ts.map +1 -0
- package/dist/api/public/v1/_lib/router.js +119 -0
- package/dist/api/public/v1/connect.d.ts +20 -0
- package/dist/api/public/v1/connect.d.ts.map +1 -0
- package/dist/api/public/v1/connect.js +119 -0
- package/dist/api/public/v1/doc.d.ts +239 -0
- package/dist/api/public/v1/doc.d.ts.map +1 -0
- package/dist/api/public/v1/doc.js +253 -0
- package/dist/api/public/v1/history/[id].d.ts +92 -0
- package/dist/api/public/v1/history/[id].d.ts.map +1 -0
- package/dist/api/public/v1/history/[id].js +176 -0
- package/dist/api/public/v1/history.d.ts +30 -0
- package/dist/api/public/v1/history.d.ts.map +1 -0
- package/dist/api/public/v1/history.js +142 -0
- package/dist/api/public/v1/image-ai.d.ts +24 -0
- package/dist/api/public/v1/image-ai.d.ts.map +1 -0
- package/dist/api/public/v1/image-ai.js +233 -0
- package/dist/api/public/v1/prompts.d.ts +19 -0
- package/dist/api/public/v1/prompts.d.ts.map +1 -0
- package/dist/api/public/v1/prompts.js +107 -0
- package/dist/api/public/v1/provider.d.ts +16 -0
- package/dist/api/public/v1/provider.d.ts.map +1 -0
- package/dist/api/public/v1/provider.js +130 -0
- package/dist/api/public/v1/purchase.d.ts +11 -0
- package/dist/api/public/v1/purchase.d.ts.map +1 -0
- package/dist/api/public/v1/purchase.js +18 -0
- package/dist/api/public/v1/status.d.ts +35 -0
- package/dist/api/public/v1/status.d.ts.map +1 -0
- package/dist/api/public/v1/status.js +163 -0
- package/dist/api/public/v1/text-ai.d.ts +26 -0
- package/dist/api/public/v1/text-ai.d.ts.map +1 -0
- package/dist/api/public/v1/text-ai.js +239 -0
- package/dist/api/public/webhook.d.ts.map +1 -1
- package/dist/api/public/webhook.js +50 -39
- package/dist/api/track-usage.d.ts +12 -0
- package/dist/api/track-usage.d.ts.map +1 -0
- package/dist/api/track-usage.js +37 -0
- package/dist/components/Doc.d.ts.map +1 -1
- package/dist/components/Doc.js +1 -1
- package/dist/components/DocUsageCustom.js +6 -6
- package/dist/components/admin/UserTokenTab.d.ts.map +1 -1
- package/dist/components/admin/UserTokenTab.js +170 -23
- package/dist/components/auth/AuthDashboardAi.d.ts +2 -0
- package/dist/components/auth/AuthDashboardAi.d.ts.map +1 -0
- package/dist/components/auth/AuthDashboardAi.js +53 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts +277 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.d.ts.map +1 -0
- package/dist/docs/REFACTORING_BILLING_GUIDE.js +276 -0
- package/dist/index.d.ts +15 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/scripts/migrate-tokens-to-wallet.d.ts +13 -0
- package/dist/scripts/migrate-tokens-to-wallet.d.ts.map +1 -0
- package/dist/scripts/migrate-tokens-to-wallet.js +165 -0
- package/dist/server/__tests__/billing.test.d.ts +5 -0
- package/dist/server/__tests__/billing.test.d.ts.map +1 -0
- package/dist/server/__tests__/billing.test.js +223 -0
- package/dist/server/ai-client.d.ts +59 -0
- package/dist/server/ai-client.d.ts.map +1 -0
- package/dist/server/ai-client.js +111 -0
- package/dist/server/ai-generation-service.d.ts +66 -0
- package/dist/server/ai-generation-service.d.ts.map +1 -0
- package/dist/server/ai-generation-service.js +274 -0
- package/dist/server/billing.d.ts +200 -0
- package/dist/server/billing.d.ts.map +1 -0
- package/dist/server/billing.js +488 -0
- package/dist/server/gateway-service.d.ts +13 -0
- package/dist/server/gateway-service.d.ts.map +1 -0
- package/dist/server/gateway-service.js +161 -0
- package/dist/server/global-settings.d.ts +16 -0
- package/dist/server/global-settings.d.ts.map +1 -0
- package/dist/server/global-settings.js +42 -0
- package/dist/server/model-filter.d.ts +25 -0
- package/dist/server/model-filter.d.ts.map +1 -0
- package/dist/server/model-filter.js +240 -0
- package/dist/server/ocr.d.ts +39 -0
- package/dist/server/ocr.d.ts.map +1 -0
- package/dist/server/ocr.js +280 -0
- package/dist/server/openai-client.d.ts +19 -0
- package/dist/server/openai-client.d.ts.map +1 -0
- package/dist/server/openai-client.js +26 -0
- package/dist/server/pricing-config.d.ts +18 -0
- package/dist/server/pricing-config.d.ts.map +1 -0
- package/dist/server/pricing-config.js +94 -0
- package/dist/server/pricing-validator.d.ts +41 -0
- package/dist/server/pricing-validator.d.ts.map +1 -0
- package/dist/server/pricing-validator.js +113 -0
- package/dist/server/pricing.d.ts +121 -0
- package/dist/server/pricing.d.ts.map +1 -0
- package/dist/server/pricing.js +225 -0
- package/dist/server/quota.d.ts +66 -0
- package/dist/server/quota.d.ts.map +1 -0
- package/dist/server/quota.js +538 -0
- package/dist/server/wallet-repair.d.ts +32 -0
- package/dist/server/wallet-repair.d.ts.map +1 -0
- package/dist/server/wallet-repair.js +189 -0
- package/dist/server.d.ts +13 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +87 -16
- package/dist/sitemap/handlers/prompts.d.ts +6 -0
- package/dist/sitemap/handlers/prompts.d.ts.map +1 -0
- package/dist/sitemap/handlers/prompts.js +72 -0
- package/dist/sitemap/handlers/users.d.ts +6 -0
- package/dist/sitemap/handlers/users.d.ts.map +1 -0
- package/dist/sitemap/handlers/users.js +80 -0
- package/dist/sitemap/manifest.d.ts +8 -0
- package/dist/sitemap/manifest.d.ts.map +1 -0
- package/dist/sitemap/manifest.js +27 -0
- package/dist/types/gateway.d.ts +40 -0
- package/dist/types/gateway.d.ts.map +1 -0
- package/dist/types/gateway.js +4 -0
- package/dist/types/quota.d.ts +74 -0
- package/dist/types/quota.d.ts.map +1 -0
- package/dist/types/quota.js +11 -0
- package/dist/utils/date.d.ts +7 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +17 -0
- package/dist/web/admin/AdminTokenPacksPage.js +1 -1
- package/dist/web/admin/BillingAnalyticsPage.d.ts +2 -0
- package/dist/web/admin/BillingAnalyticsPage.d.ts.map +1 -0
- package/dist/web/admin/BillingAnalyticsPage.js +141 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts +2 -0
- package/dist/web/admin/GlobalAISettingsPage.d.ts.map +1 -0
- package/dist/web/admin/GlobalAISettingsPage.js +93 -0
- package/dist/web/admin/UserTokenPage.d.ts.map +1 -1
- package/dist/web/admin/UserTokenPage.js +20 -7
- package/dist/web/auth/AISettingsPage.d.ts +2 -0
- package/dist/web/auth/AISettingsPage.d.ts.map +1 -0
- package/dist/web/auth/AISettingsPage.js +258 -0
- package/dist/web/auth/APIKeysPage.d.ts +2 -0
- package/dist/web/auth/APIKeysPage.d.ts.map +1 -0
- package/dist/web/auth/APIKeysPage.js +154 -0
- package/dist/web/auth/HistoryPage.d.ts +2 -0
- package/dist/web/auth/HistoryPage.d.ts.map +1 -0
- package/dist/web/auth/HistoryPage.js +279 -0
- package/dist/web/auth/PromptsPage.d.ts +5 -0
- package/dist/web/auth/PromptsPage.d.ts.map +1 -0
- package/dist/web/auth/PromptsPage.js +137 -0
- package/dist/web/auth/TokenPage.d.ts.map +1 -1
- package/dist/web/auth/TokenPage.js +88 -31
- package/dist/web/auth/UsageAndTokensPage.d.ts +2 -0
- package/dist/web/auth/UsageAndTokensPage.d.ts.map +1 -0
- package/dist/web/auth/UsageAndTokensPage.js +157 -0
- package/dist/web/auth/UsagePage.d.ts +2 -0
- package/dist/web/auth/UsagePage.d.ts.map +1 -0
- package/dist/web/auth/UsagePage.js +62 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts +13 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.d.ts.map +1 -0
- package/dist/web/auth/components/ApiKeyFilterSelect.js +16 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts +19 -0
- package/dist/web/auth/components/ModelUsageTable.d.ts.map +1 -0
- package/dist/web/auth/components/ModelUsageTable.js +37 -0
- package/dist/web/auth/components/PurchaseButton.d.ts +7 -0
- package/dist/web/auth/components/PurchaseButton.d.ts.map +1 -0
- package/dist/web/auth/components/PurchaseButton.js +13 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts +20 -0
- package/dist/web/auth/components/TokenHistoryCard.d.ts.map +1 -0
- package/dist/web/auth/components/TokenHistoryCard.js +76 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts +24 -0
- package/dist/web/auth/components/TokenKpiGrid.d.ts.map +1 -0
- package/dist/web/auth/components/TokenKpiGrid.js +38 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts +11 -0
- package/dist/web/auth/components/UsageByDayChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByDayChart.js +32 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts +12 -0
- package/dist/web/auth/components/UsageByModelBarChart.d.ts.map +1 -0
- package/dist/web/auth/components/UsageByModelBarChart.js +32 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts +9 -0
- package/dist/web/auth/components/WalletStatusCard.d.ts.map +1 -0
- package/dist/web/auth/components/WalletStatusCard.js +50 -0
- package/dist/web/components/ImageGenerative.d.ts +3 -1
- package/dist/web/components/ImageGenerative.d.ts.map +1 -1
- package/dist/web/components/ImageGenerative.js +139 -52
- package/dist/web/components/TextareaGenerative.d.ts +3 -1
- package/dist/web/components/TextareaGenerative.d.ts.map +1 -1
- package/dist/web/components/TextareaGenerative.js +10 -5
- package/dist/web/public/PromptDetailPage.d.ts +25 -0
- package/dist/web/public/PromptDetailPage.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPage.js +71 -0
- package/dist/web/public/PromptDetailPageServer.d.ts +15 -0
- package/dist/web/public/PromptDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/PromptDetailPageServer.js +68 -0
- package/dist/web/public/PublicPromptsPage.d.ts +5 -0
- package/dist/web/public/PublicPromptsPage.d.ts.map +1 -0
- package/dist/web/public/PublicPromptsPage.js +110 -0
- package/dist/web/public/PurchaseTokensPage.d.ts +2 -0
- package/dist/web/public/PurchaseTokensPage.d.ts.map +1 -0
- package/dist/web/public/PurchaseTokensPage.js +98 -0
- package/dist/web/public/UserAvatar.d.ts +13 -0
- package/dist/web/public/UserAvatar.d.ts.map +1 -0
- package/dist/web/public/UserAvatar.js +13 -0
- package/dist/web/public/UserDetailPageServer.d.ts +15 -0
- package/dist/web/public/UserDetailPageServer.d.ts.map +1 -0
- package/dist/web/public/UserDetailPageServer.js +31 -0
- package/dist/web/public/UserPromptsPage.d.ts +9 -0
- package/dist/web/public/UserPromptsPage.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPage.js +112 -0
- package/dist/web/public/UserPromptsPageServer.d.ts +15 -0
- package/dist/web/public/UserPromptsPageServer.d.ts.map +1 -0
- package/dist/web/public/UserPromptsPageServer.js +31 -0
- package/package.json +18 -9
- package/supabase/migrations/20251125000000_ai_tokens.sql +7 -0
- package/supabase/migrations/20260123100002_user_token_quota_monthly copy.sql +173 -0
- package/supabase/migrations/20260128100003_update_and_add_table.sql +368 -0
- package/supabase/migrations/20260128120000_seed_providers_models.sql +78 -0
- package/supabase/migrations/20260128131405_add_api_key_id_to_ledgers.sql +41 -0
- package/supabase/migrations/20260128140000_ai_artifacts_storage.sql +99 -0
- package/supabase/migrations/20260128140002_ai_user_settings.sql +57 -0
- package/supabase/migrations/20260128150000_drop_ai_user_settings.sql +21 -0
- package/supabase/migrations/20260128160000_wallet_billing_system.sql +192 -0
- package/supabase/migrations/20260128160001_wallet_rpc_functions.sql +165 -0
- package/supabase/migrations/20260128170000_add_pack_coef_to_token_packs.sql +30 -0
- package/supabase/migrations/20260129120000_wallet_view_rpc.sql +41 -0
- package/supabase/migrations/20260129220003_update_pack_margins.sql +31 -0
- package/supabase/migrations/20260129330004_ai_user_prompts.sql +151 -0
- package/supabase/migrations/20260129330005_ai_prompts_ip_tracking.sql +92 -0
- package/supabase/migrations/20260129330006_ai_prompts_slug.sql +64 -0
- package/supabase/migrations/20260129330007_ai_prompts_view_slug.sql +26 -0
- package/supabase/migrations/20260129440000_ai_prompts_view_username.sql +33 -0
- package/supabase/migrations/20260129450000_ai_prompts_add_lang.sql +40 -0
- package/supabase/migrations/20260131000000_extract_model_prompt_in_ledger.sql +92 -0
- package/supabase/migrations/20260131140000_fix_duplicate_purchases.sql +64 -0
- package/supabase/migrations/20260201120000_module-ai_default_models.sql +63 -0
- package/supabase/migrations/20260201130000_module-ai_remove_provider_tables.sql +17 -0
- package/supabase/migrations-down/20251217120000_user_token_quota_monthly.sql +34 -0
- package/supabase/migrations-down/20260128131405_add_api_key_id_to_ledgers.sql +25 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
-
import { Card, CardBody, CardHeader, Button, Input, Spinner, addToast,
|
|
5
|
-
import { Plus,
|
|
6
|
-
import { useModuleTranslation } from "@lastbrain/core";
|
|
4
|
+
import { Card, CardBody, CardHeader, Button, Input, Spinner, addToast, Select, SelectItem, } from "@lastbrain/ui";
|
|
5
|
+
import { Plus, Coins, DollarSign, TrendingDown, TrendingUp, Wallet, TrendingUp as UsageIcon, Calendar, } from "lucide-react";
|
|
6
|
+
import { logger, useModuleTranslation } from "@lastbrain/core";
|
|
7
|
+
import { formatDateFr } from "../../utils/date";
|
|
7
8
|
export function UserTokenTab({ userId }) {
|
|
8
9
|
const t = useModuleTranslation("ai");
|
|
9
10
|
const [loading, setLoading] = useState(true);
|
|
@@ -12,15 +13,65 @@ export function UserTokenTab({ userId }) {
|
|
|
12
13
|
const [adjustmentAmount, setAdjustmentAmount] = useState("");
|
|
13
14
|
const [adjustmentDescription, setAdjustmentDescription] = useState("");
|
|
14
15
|
const [processing, setProcessing] = useState(false);
|
|
15
|
-
|
|
16
|
+
const [walletAnalytics, setWalletAnalytics] = useState(null);
|
|
17
|
+
const [modelUsage, setModelUsage] = useState([]);
|
|
18
|
+
const [loadingUsage, setLoadingUsage] = useState(false);
|
|
19
|
+
const [selectedMonth, setSelectedMonth] = useState("all");
|
|
20
|
+
// Quota state
|
|
21
|
+
const [quotaStatus, setQuotaStatus] = useState(null);
|
|
22
|
+
const [customQuotaAmount, setCustomQuotaAmount] = useState("");
|
|
23
|
+
const [editingQuota, setEditingQuota] = useState(false);
|
|
24
|
+
// Charger analytics wallet
|
|
25
|
+
const fetchWalletAnalytics = async () => {
|
|
26
|
+
try {
|
|
27
|
+
const response = await fetch(`/api/ai/admin/user-wallet-analytics?userId=${userId}`);
|
|
28
|
+
if (response.ok) {
|
|
29
|
+
const data = await response.json();
|
|
30
|
+
setWalletAnalytics(data);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
logger.error("Error loading wallet analytics:", error);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
// Charger usage par modèle
|
|
38
|
+
const fetchModelUsage = async () => {
|
|
39
|
+
try {
|
|
40
|
+
setLoadingUsage(true);
|
|
41
|
+
const params = new URLSearchParams({ userId });
|
|
42
|
+
if (selectedMonth !== "all") {
|
|
43
|
+
params.append("month", selectedMonth);
|
|
44
|
+
}
|
|
45
|
+
const response = await fetch(`/api/ai/admin/user-usage-by-model?${params.toString()}`);
|
|
46
|
+
if (response.ok) {
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
setModelUsage(data.data || []);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger.error("Error loading model usage:", error);
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
setLoadingUsage(false);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
// Charger le solde, l'historique et le quota
|
|
16
59
|
const fetchTokenData = async () => {
|
|
17
60
|
try {
|
|
18
61
|
setLoading(true);
|
|
19
|
-
|
|
20
|
-
|
|
62
|
+
// Fetch tokens, quota, and analytics in parallel
|
|
63
|
+
const [tokenRes, quotaRes] = await Promise.all([
|
|
64
|
+
// Request a large limit so the UI can display the full history (credit & debit)
|
|
65
|
+
fetch(`/api/ai/admin/user-token/${userId}?limit=1000&offset=0`),
|
|
66
|
+
fetch(`/api/ai/admin/user-quota?userId=${userId}`),
|
|
67
|
+
]);
|
|
68
|
+
// Fetch analytics separately (non-blocking)
|
|
69
|
+
fetchWalletAnalytics();
|
|
70
|
+
if (!tokenRes.ok) {
|
|
21
71
|
throw new Error(t("admin.user_token.error.loading") ||
|
|
22
72
|
"Échec chargement détails tokens");
|
|
23
|
-
|
|
73
|
+
}
|
|
74
|
+
const data = await tokenRes.json();
|
|
24
75
|
const currentBalance = data.balance || 0;
|
|
25
76
|
setBalance(currentBalance);
|
|
26
77
|
const history = data.history || data.data || [];
|
|
@@ -29,10 +80,13 @@ export function UserTokenTab({ userId }) {
|
|
|
29
80
|
const computed = history.map((entry) => {
|
|
30
81
|
const balanceAfter = running;
|
|
31
82
|
running -= entry.amount; // préparer balance pour l'entrée suivante
|
|
83
|
+
// Utiliser les montants USD si disponibles, sinon tokens (pour backward compat)
|
|
84
|
+
const displayAmount = entry.meta?.sell_usd || entry.meta?.sellUsd || entry.amount || 0;
|
|
85
|
+
const displayBalance = walletAnalytics?.walletSellValueUsd || balanceAfter;
|
|
32
86
|
return {
|
|
33
87
|
...entry,
|
|
34
|
-
balance_after:
|
|
35
|
-
display_amount:
|
|
88
|
+
balance_after: displayBalance,
|
|
89
|
+
display_amount: displayAmount,
|
|
36
90
|
display_description: entry.meta?.reason ||
|
|
37
91
|
entry.description ||
|
|
38
92
|
entry.type ||
|
|
@@ -41,9 +95,14 @@ export function UserTokenTab({ userId }) {
|
|
|
41
95
|
};
|
|
42
96
|
});
|
|
43
97
|
setLedger(computed);
|
|
98
|
+
// Load quota
|
|
99
|
+
if (quotaRes.ok) {
|
|
100
|
+
const quotaData = await quotaRes.json();
|
|
101
|
+
setQuotaStatus(quotaData.quota);
|
|
102
|
+
}
|
|
44
103
|
}
|
|
45
104
|
catch (error) {
|
|
46
|
-
|
|
105
|
+
logger.error(t("admin.user_token.error.loading_tokens") ||
|
|
47
106
|
"Erreur lors du chargement des tokens:", error);
|
|
48
107
|
addToast({
|
|
49
108
|
color: "danger",
|
|
@@ -58,9 +117,13 @@ export function UserTokenTab({ userId }) {
|
|
|
58
117
|
useEffect(() => {
|
|
59
118
|
fetchTokenData();
|
|
60
119
|
}, [userId]);
|
|
61
|
-
//
|
|
120
|
+
// Charger model usage quand le mois change
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
fetchModelUsage();
|
|
123
|
+
}, [userId, selectedMonth]);
|
|
124
|
+
// Ajuster le solde (crédit seulement pour l'instant)
|
|
62
125
|
const handleAdjustment = async (type) => {
|
|
63
|
-
const amount =
|
|
126
|
+
const amount = parseFloat(adjustmentAmount);
|
|
64
127
|
if (isNaN(amount) || amount <= 0) {
|
|
65
128
|
addToast({
|
|
66
129
|
color: "danger",
|
|
@@ -76,6 +139,13 @@ export function UserTokenTab({ userId }) {
|
|
|
76
139
|
});
|
|
77
140
|
return;
|
|
78
141
|
}
|
|
142
|
+
if (type === "debit") {
|
|
143
|
+
addToast({
|
|
144
|
+
color: "danger",
|
|
145
|
+
title: "Le débit de crédits n'est pas encore supporté",
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
79
149
|
try {
|
|
80
150
|
setProcessing(true);
|
|
81
151
|
const response = await fetch(`/api/ai/admin/user-token`, {
|
|
@@ -83,8 +153,7 @@ export function UserTokenTab({ userId }) {
|
|
|
83
153
|
headers: { "Content-Type": "application/json" },
|
|
84
154
|
body: JSON.stringify({
|
|
85
155
|
userId,
|
|
86
|
-
amount:
|
|
87
|
-
type: "adjust",
|
|
156
|
+
amount: amount, // USD amount
|
|
88
157
|
reason: adjustmentDescription,
|
|
89
158
|
}),
|
|
90
159
|
});
|
|
@@ -94,18 +163,18 @@ export function UserTokenTab({ userId }) {
|
|
|
94
163
|
}
|
|
95
164
|
addToast({
|
|
96
165
|
color: "success",
|
|
97
|
-
title:
|
|
98
|
-
? t("admin.user_token.success.credited") || "Tokens crédités"
|
|
99
|
-
: t("admin.user_token.success.debited") || "Tokens débités",
|
|
166
|
+
title: `$${amount.toFixed(2)} de crédits IA ajoutés`,
|
|
100
167
|
});
|
|
101
168
|
// Reset
|
|
102
169
|
setAdjustmentAmount("");
|
|
103
170
|
setAdjustmentDescription("");
|
|
104
171
|
// Recharger les données
|
|
105
172
|
await fetchTokenData();
|
|
173
|
+
await fetchWalletAnalytics();
|
|
174
|
+
await fetchModelUsage();
|
|
106
175
|
}
|
|
107
176
|
catch (error) {
|
|
108
|
-
|
|
177
|
+
logger.error(t("admin.user_token.error.adjustment") || "Erreur:", error);
|
|
109
178
|
addToast({
|
|
110
179
|
color: "danger",
|
|
111
180
|
title: t("admin.user_token.error.adjustment_error") ||
|
|
@@ -116,12 +185,90 @@ export function UserTokenTab({ userId }) {
|
|
|
116
185
|
setProcessing(false);
|
|
117
186
|
}
|
|
118
187
|
};
|
|
188
|
+
// Update custom quota
|
|
189
|
+
const handleUpdateCustomQuota = async () => {
|
|
190
|
+
const amount = customQuotaAmount.trim() === "" ? null : parseInt(customQuotaAmount);
|
|
191
|
+
if (amount !== null && (isNaN(amount) || amount < 0)) {
|
|
192
|
+
addToast({
|
|
193
|
+
color: "danger",
|
|
194
|
+
title: "Montant invalide",
|
|
195
|
+
});
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
try {
|
|
199
|
+
setProcessing(true);
|
|
200
|
+
const response = await fetch(`/api/ai/admin/user-quota`, {
|
|
201
|
+
method: "POST",
|
|
202
|
+
headers: { "Content-Type": "application/json" },
|
|
203
|
+
body: JSON.stringify({
|
|
204
|
+
userId,
|
|
205
|
+
customTokens: amount,
|
|
206
|
+
}),
|
|
207
|
+
});
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
throw new Error("Échec de la mise à jour du quota");
|
|
210
|
+
}
|
|
211
|
+
const data = await response.json();
|
|
212
|
+
setQuotaStatus(data.quota);
|
|
213
|
+
setEditingQuota(false);
|
|
214
|
+
setCustomQuotaAmount("");
|
|
215
|
+
addToast({
|
|
216
|
+
color: "success",
|
|
217
|
+
title: amount === null
|
|
218
|
+
? "Quota réinitialisé"
|
|
219
|
+
: "Quota personnalisé mis à jour",
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
logger.error("Erreur lors de la mise à jour du quota:", error);
|
|
224
|
+
addToast({
|
|
225
|
+
color: "danger",
|
|
226
|
+
title: "Erreur lors de la mise à jour du quota",
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
finally {
|
|
230
|
+
setProcessing(false);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
119
233
|
if (loading) {
|
|
120
234
|
return (_jsx("div", { className: "flex justify-center items-center min-h-64", children: _jsx(Spinner, { size: "lg" }) }));
|
|
121
235
|
}
|
|
122
|
-
return (_jsxs("div", { className: "w-full space-y-6 mt-4", children: [_jsxs("div", { className: "
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
236
|
+
return (_jsxs("div", { className: "w-full space-y-6 mt-4", children: [walletAnalytics && (_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-4 gap-4 mb-6", children: [_jsx(Card, { className: "border-2 border-success", children: _jsx(CardBody, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(DollarSign, { size: 42, className: "text-success" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-default-500", children: "CA Total (USD)" }), _jsxs("p", { className: "text-2xl font-bold text-success", children: ["$", walletAnalytics.totalSellUsd.toFixed(2)] })] })] }) }) }), _jsx(Card, { className: "border-2 border-warning", children: _jsx(CardBody, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(TrendingDown, { size: 42, className: "text-warning" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-default-500", children: "Co\u00FBt Fournisseur (USD)" }), _jsxs("p", { className: "text-2xl font-bold text-warning", children: ["$", walletAnalytics.totalProviderCostUsd.toFixed(2)] })] })] }) }) }), _jsx(Card, { className: "border-2 border-secondary", children: _jsx(CardBody, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(TrendingUp, { size: 42, className: "text-secondary" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-default-500", children: "Marge Totale (USD)" }), _jsxs("p", { className: "text-2xl font-bold text-secondary", children: ["$", walletAnalytics.totalMarginUsd.toFixed(2)] }), _jsxs("p", { className: "text-xs text-default-400", children: ["\u2248 ", walletAnalytics.marginPercent.toFixed(1), "% moyen"] })] })] }) }) }), _jsx(Card, { className: "border-2 border-primary", children: _jsx(CardBody, { children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Wallet, { size: 42, className: "text-primary" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-default-500", children: "Wallet Budget" }), _jsxs("p", { className: "text-2xl font-bold text-primary", children: ["$", walletAnalytics.walletProviderBudgetUsd.toFixed(4)] }), _jsxs("p", { className: "text-xs text-default-400", children: ["Tokens:", " ", walletAnalytics.tokensAdded - walletAnalytics.tokensUsed] })] })] }) }) })] })), quotaStatus && quotaStatus.hasQuota && (_jsxs(Card, { className: "border-2 border-primary", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between w-full", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Coins, { size: 20, className: "text-primary" }), _jsxs("h3", { className: "text-lg font-semibold", children: ["Quota mensuel inclus (", quotaStatus.plan.toUpperCase(), ")"] })] }), !editingQuota && (_jsx(Button, { size: "sm", color: "primary", variant: "flat", onPress: () => setEditingQuota(true), children: "Personnaliser" }))] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: [_jsxs("div", { className: "text-center p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Quota effectif" }), _jsx("p", { className: "text-3xl font-bold text-primary", children: quotaStatus.effectiveQuota.toLocaleString() })] }), _jsxs("div", { className: "text-center p-4 bg-warning-50 dark:bg-warning-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Utilis\u00E9s" }), _jsx("p", { className: "text-3xl font-bold text-warning", children: quotaStatus.usedQuota.toLocaleString() })] }), _jsxs("div", { className: "text-center p-4 bg-success-50 dark:bg-success-900/20 rounded-lg", children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400 mb-1", children: "Restants" }), _jsx("p", { className: "text-3xl font-bold text-success", children: quotaStatus.remainingQuota.toLocaleString() })] })] }), quotaStatus.periodStart && quotaStatus.periodEnd && (_jsxs("div", { className: "text-center text-sm text-gray-500", children: [_jsxs("p", { children: ["P\u00E9riode: ", formatDateFr(quotaStatus.periodStart), " - ", formatDateFr(quotaStatus.periodEnd)] }), _jsx("p", { className: `mt-1 ${quotaStatus.isActive ? "text-success" : "text-danger"}`, children: quotaStatus.isActive ? "✓ Période active" : "✗ Hors période" })] })), editingQuota && (_jsxs("div", { className: "space-y-4 pt-4 border-t", children: [_jsx("h4", { className: "font-semibold", children: "Personnaliser le quota mensuel" }), _jsx(Input, { type: "number", label: "Quota personnalis\u00E9", placeholder: "Laisser vide pour r\u00E9initialiser au d\u00E9faut du plan", value: customQuotaAmount, onChange: (e) => setCustomQuotaAmount(e.target.value), min: "0", endContent: _jsx("span", { className: "text-sm text-gray-500", children: "tokens/mois" }) }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { color: "primary", onPress: handleUpdateCustomQuota, isLoading: processing, children: "Appliquer" }), _jsx(Button, { variant: "flat", onPress: () => {
|
|
237
|
+
setEditingQuota(false);
|
|
238
|
+
setCustomQuotaAmount("");
|
|
239
|
+
}, children: "Annuler" }), _jsx(Button, { color: "warning", variant: "flat", onPress: () => {
|
|
240
|
+
setCustomQuotaAmount("");
|
|
241
|
+
handleUpdateCustomQuota();
|
|
242
|
+
}, isLoading: processing, children: "R\u00E9initialiser au d\u00E9faut" })] }), _jsx("p", { className: "text-xs text-gray-500", children: "Note: La modification s'appliquera imm\u00E9diatement. Le quota sera r\u00E9initialis\u00E9 au prochain renouvellement de p\u00E9riode." })] }))] })] })), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(UsageIcon, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: "Utilisation par mod\u00E8le" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Calendar, { size: 16, className: "text-gray-500" }), _jsx(Select, { size: "sm", label: "", placeholder: "P\u00E9riode", selectedKeys: [selectedMonth], onSelectionChange: (keys) => {
|
|
243
|
+
const key = Array.from(keys)[0];
|
|
244
|
+
setSelectedMonth(key);
|
|
245
|
+
}, className: "min-w-[180px]", children: [
|
|
246
|
+
{ value: "all", label: "Tous les mois" },
|
|
247
|
+
...(() => {
|
|
248
|
+
const months = [];
|
|
249
|
+
const now = new Date();
|
|
250
|
+
for (let i = 0; i < 12; i++) {
|
|
251
|
+
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
|
252
|
+
const value = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}`;
|
|
253
|
+
const label = date.toLocaleDateString("fr-FR", {
|
|
254
|
+
year: "numeric",
|
|
255
|
+
month: "long",
|
|
256
|
+
});
|
|
257
|
+
months.push({ value, label });
|
|
258
|
+
}
|
|
259
|
+
return months;
|
|
260
|
+
})(),
|
|
261
|
+
].map((month) => (_jsx(SelectItem, { children: month.label }, month.value))) })] })] }), _jsx(CardBody, { children: loadingUsage ? (_jsx("div", { className: "flex justify-center items-center py-12", children: _jsx(Spinner, { size: "lg" }) })) : modelUsage.length === 0 ? (_jsx("div", { className: "text-center py-12 text-gray-500", children: "Aucune utilisation enregistr\u00E9e" })) : (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "bg-gradient-to-r from-blue-50 to-indigo-50 dark:from-blue-950/20 dark:to-indigo-950/20 p-4 rounded-lg border border-blue-200 dark:border-blue-800", children: _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Total d\u00E9pens\u00E9 (p\u00E9riode)" }), _jsxs("p", { className: "text-2xl font-bold text-blue-600 dark:text-blue-400", children: ["$", modelUsage
|
|
262
|
+
.reduce((sum, item) => sum + item.total_sell_usd, 0)
|
|
263
|
+
.toFixed(2)] })] }), _jsxs("div", { children: [_jsx("p", { className: "text-sm text-gray-600 dark:text-gray-400", children: "Cr\u00E9dit restant" }), _jsxs("p", { className: "text-2xl font-bold text-green-600 dark:text-green-400", children: ["$", (walletAnalytics?.walletSellValueUsd || 0).toFixed(2)] })] })] }) }), _jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-gray-200 dark:border-gray-700", children: [_jsx("th", { className: "text-left py-3 px-2 text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Mod\u00E8le" }), _jsx("th", { className: "text-left py-3 px-2 text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Provider" }), _jsx("th", { className: "text-right py-3 px-2 text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Co\u00FBt total" }), _jsx("th", { className: "text-right py-3 px-2 text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Appels" }), _jsx("th", { className: "text-right py-3 px-2 text-sm font-semibold text-gray-700 dark:text-gray-300", children: "Co\u00FBt/appel" })] }) }), _jsx("tbody", { children: modelUsage.map((item, index) => {
|
|
264
|
+
const totalSpent = modelUsage.reduce((sum, i) => sum + i.total_sell_usd, 0);
|
|
265
|
+
const percentOfTotal = totalSpent > 0
|
|
266
|
+
? (item.total_sell_usd / totalSpent) * 100
|
|
267
|
+
: 0;
|
|
268
|
+
return (_jsxs("tr", { className: "border-b border-gray-100 dark:border-gray-800 hover:bg-gray-50 dark:hover:bg-gray-900/50 transition-colors", children: [_jsx("td", { className: "py-3 px-2", children: _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "font-medium text-sm", children: item.model }), item.endpoint && (_jsx("span", { className: "text-xs text-gray-500", children: item.endpoint }))] }) }), _jsx("td", { className: "py-3 px-2", children: _jsx("span", { className: "text-sm text-gray-600 dark:text-gray-400 capitalize", children: item.provider || "—" }) }), _jsx("td", { className: "py-3 px-2 text-right", children: _jsxs("div", { className: "flex flex-col items-end", children: [_jsxs("span", { className: "font-semibold text-sm", children: ["$", item.total_sell_usd.toFixed(2)] }), _jsxs("span", { className: "text-xs text-gray-500", children: [percentOfTotal.toFixed(1), "%"] })] }) }), _jsx("td", { className: "py-3 px-2 text-right", children: _jsx("span", { className: "text-sm font-medium", children: item.call_count.toLocaleString() }) }), _jsx("td", { className: "py-3 px-2 text-right", children: _jsxs("span", { className: "text-sm text-gray-600 dark:text-gray-400", children: ["$", item.avg_cost_per_call.toFixed(4)] }) })] }, `${item.model}-${index}`));
|
|
269
|
+
}) })] }) }), _jsx("div", { className: "text-xs text-gray-500 pt-2", children: _jsx("p", { children: "\uD83D\uDCA1 Les co\u00FBts affich\u00E9s sont les montants d\u00E9bit\u00E9s du cr\u00E9dit IA (incluant marge et infrastructure)." }) })] })) })] }), _jsxs("div", { className: "w-full flex flex-col md:flex-row gap-6", children: [_jsxs(Card, { className: "min-w-72", children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(DollarSign, { size: 20 }), _jsx("h3", { className: "text-lg font-semibold", children: t("admin.user_token.balance.title") || "Crédits IA" })] }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-xs text-gray-500 uppercase tracking-wide mb-1", children: "Valeur offerte (100%)" }), _jsxs("p", { className: "text-3xl font-bold text-primary", children: ["$", walletAnalytics?.walletSellValueUsd?.toFixed(2) ||
|
|
270
|
+
balance.toFixed(2)] })] }), _jsx("div", { className: "border-t pt-4", children: _jsxs("div", { className: "text-center", children: [_jsx("p", { className: "text-xs text-gray-500 uppercase tracking-wide mb-1", children: "Budget r\u00E9el de consommation (40%)" }), _jsxs("p", { className: "text-3xl font-bold text-success", children: ["$", walletAnalytics?.walletProviderBudgetUsd?.toFixed(2) ||
|
|
271
|
+
"0.00"] }), _jsx("p", { className: "text-xs text-gray-500 mt-1", children: "\uD83D\uDCA1 C'est ce montant qui sera d\u00E9bit\u00E9 des providers" })] }) })] })] }), _jsxs(Card, { className: "flex-1 ", children: [_jsx(CardHeader, { children: _jsx("h3", { className: "text-lg font-semibold", children: t("admin.user_token.adjustment.title") || "Ajuster le solde" }) }), _jsxs(CardBody, { className: "space-y-4", children: [_jsxs("div", { className: "bg-blue-50 dark:bg-blue-950/20 p-3 rounded-lg border border-blue-200 dark:border-blue-800", children: [_jsxs("p", { className: "text-xs text-gray-700 dark:text-gray-300", children: ["\uD83D\uDCA1 ", _jsx("strong", { children: "Important :" }), " Quand vous offrez des cr\u00E9dits, l'utilisateur re\u00E7oit :"] }), _jsxs("ul", { className: "text-xs text-gray-600 dark:text-gray-400 mt-2 space-y-1 pl-4", children: [_jsxs("li", { children: ["\u2022 ", _jsx("strong", { children: "100%" }), " affich\u00E9 comme \"valeur offerte\""] }), _jsxs("li", { children: ["\u2022 ", _jsx("strong", { children: "40%" }), " comme budget r\u00E9el de consommation"] }), _jsxs("li", { children: ["\u2022 ", _jsx("strong", { children: "60%" }), " sera la marge lors de l'utilisation"] })] }), _jsx("p", { className: "text-xs text-gray-600 dark:text-gray-400 mt-2", children: "Exemple : Offrir $5 = $5 affich\u00E9s, mais $2 de budget r\u00E9el" })] }), _jsx(Input, { type: "number", label: t("admin.user_token.adjustment.amount") || "Montant (USD)", placeholder: t("admin.user_token.adjustment.amount_placeholder") || "5.00", value: adjustmentAmount, onChange: (e) => setAdjustmentAmount(e.target.value), min: "0.01", max: "1000", step: "0.01", endContent: _jsx("span", { className: "text-sm text-gray-500", children: t("chip.usd") || "$" }) }), _jsx(Input, { label: t("admin.user_token.adjustment.description") || "Description", placeholder: t("admin.user_token.adjustment.description_placeholder") ||
|
|
272
|
+
"Raison de l'ajustement...", value: adjustmentDescription, onChange: (e) => setAdjustmentDescription(e.target.value), maxLength: 200 }), _jsx("div", { className: "flex justify-end gap-2", children: _jsx(Button, { color: "success", startContent: _jsx(Plus, { size: 16 }), onPress: () => handleAdjustment("credit"), isLoading: processing, isDisabled: !adjustmentAmount || !adjustmentDescription.trim(), children: t("admin.user_token.adjustment.credit_button") ||
|
|
273
|
+
"Offrir des crédits" }) })] })] })] })] }));
|
|
127
274
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthDashboardAi.d.ts","sourceRoot":"","sources":["../../../src/components/auth/AuthDashboardAi.tsx"],"names":[],"mappings":"AAgBA,wBAAgB,eAAe,mDAkI9B"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
import { Card, CardBody, Progress, Spinner } from "@lastbrain/ui";
|
|
5
|
+
import { TrendingDown, Wallet, Gauge } from "lucide-react";
|
|
6
|
+
import { useAuth, useModuleTranslation, logger } from "@lastbrain/core";
|
|
7
|
+
export function AuthDashboardAi() {
|
|
8
|
+
const t = useModuleTranslation("ai");
|
|
9
|
+
const { user } = useAuth();
|
|
10
|
+
const [balance, setBalance] = useState(null);
|
|
11
|
+
const [loading, setLoading] = useState(true);
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
if (user) {
|
|
14
|
+
fetchBalance();
|
|
15
|
+
}
|
|
16
|
+
}, [user]);
|
|
17
|
+
const fetchBalance = async () => {
|
|
18
|
+
try {
|
|
19
|
+
setLoading(true);
|
|
20
|
+
const response = await fetch("/api/ai/auth/wallet");
|
|
21
|
+
if (response.ok) {
|
|
22
|
+
const data = await response.json();
|
|
23
|
+
setBalance(data);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
logger.error("Failed to fetch wallet balance", error);
|
|
28
|
+
}
|
|
29
|
+
finally {
|
|
30
|
+
setLoading(false);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const formatUsd = (amount) => {
|
|
34
|
+
if (amount === undefined || amount === null)
|
|
35
|
+
return "$0.00";
|
|
36
|
+
return `$${amount.toFixed(2)}`;
|
|
37
|
+
};
|
|
38
|
+
if (loading) {
|
|
39
|
+
return (_jsx("div", { className: "flex justify-center py-8", children: _jsx(Spinner, {}) }));
|
|
40
|
+
}
|
|
41
|
+
if (!balance)
|
|
42
|
+
return null;
|
|
43
|
+
const usagePercentage = balance.percentage || 0;
|
|
44
|
+
const getUsageColor = (percentage) => {
|
|
45
|
+
if (percentage > 90)
|
|
46
|
+
return "danger";
|
|
47
|
+
if (percentage >= 80)
|
|
48
|
+
return "warning";
|
|
49
|
+
return "success";
|
|
50
|
+
};
|
|
51
|
+
const usageColor = getUsageColor(usagePercentage);
|
|
52
|
+
return (_jsxs("div", { className: "max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4", children: [_jsx(Card, { className: "border-2 border-primary", children: _jsxs(CardBody, { className: "p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsx("div", { className: "p-2 rounded-lg bg-primary/10", children: _jsx(Wallet, { size: 24, className: "text-primary" }) }), _jsx("div", { className: "flex-1", children: _jsx("p", { className: "text-sm text-default-800", children: t("wallet.balance.current") || "Crédits disponibles" }) })] }), _jsx("p", { className: "text-3xl font-bold text-primary", children: formatUsd(balance.walletSellValueUsd) }), _jsx("p", { className: "text-xs text-default-700 mt-1", children: t("wallet.balance.available") || "Solde disponible" })] }) }), _jsx(Card, { className: "border-2 border-danger", children: _jsxs(CardBody, { className: "p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsx("div", { className: "p-2 rounded-lg bg-danger/10", children: _jsx(TrendingDown, { size: 24, className: "text-danger" }) }), _jsx("div", { className: "flex-1", children: _jsx("p", { className: "text-sm text-default-800", children: t("wallet.balance.total_used") || "Total dépensé" }) })] }), _jsx("p", { className: "text-3xl font-bold text-danger", children: formatUsd(balance.totalUsed) }), _jsx("p", { className: "text-xs text-default-700 mt-1", children: t("wallet.balance.consumed") || "Consommé" })] }) }), _jsx(Card, { className: `border-2 border-${usageColor}`, children: _jsxs(CardBody, { className: "p-6", children: [_jsxs("div", { className: "flex items-center gap-3 mb-4", children: [_jsx("div", { className: `p-2 rounded-lg bg-${usageColor}/10`, children: _jsx(Gauge, { size: 24, className: `text-${usageColor}` }) }), _jsx("div", { className: "flex-1", children: _jsx("p", { className: "text-sm text-default-800", children: t("wallet.usage.percentage") || "Utilisation" }) })] }), _jsxs("p", { className: `text-3xl font-bold text-${usageColor}`, children: [usagePercentage.toFixed(1), "%"] }), _jsx("div", { className: "mt-3", children: _jsx(Progress, { value: usagePercentage, color: usageColor, className: "h-2" }) }), _jsxs("p", { className: "text-xs text-default-700 mt-2", children: [formatUsd(balance.totalUsed), " / ", formatUsd(balance.totalAdded)] })] }) })] }));
|
|
53
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* REFACTORING BILLING IA - GUIDE D'IMPLÉMENTATION
|
|
3
|
+
* =================================================
|
|
4
|
+
*
|
|
5
|
+
* Ce fichier documente les changements nécessaires pour centraliser
|
|
6
|
+
* la logique de billing IA et passer à un modèle "Crédits IA" (USD).
|
|
7
|
+
*
|
|
8
|
+
* ✅ DÉJÀ FAIT :
|
|
9
|
+
* ==============
|
|
10
|
+
* 1. `packages/module-ai/src/server/billing.ts`
|
|
11
|
+
* - Ajout documentation règles économiques
|
|
12
|
+
* - Création fonction `computeAndDebitAIUsage()` centralisée
|
|
13
|
+
* - Configuration minimums par action (ACTION_MIN_SELL_USD)
|
|
14
|
+
* - Logging anonyme de chaque débit
|
|
15
|
+
*
|
|
16
|
+
* 2. `packages/module-ai/src/api/public/pricing-summary.ts`
|
|
17
|
+
* - Endpoint GET pour afficher estimations selon nouveau modèle
|
|
18
|
+
*
|
|
19
|
+
* ⚠️ À FAIRE :
|
|
20
|
+
* ============
|
|
21
|
+
*
|
|
22
|
+
* A) Modifier tous les endpoints pour utiliser `computeAndDebitAIUsage()`
|
|
23
|
+
* -------------------------------------------------------------------
|
|
24
|
+
*
|
|
25
|
+
* Fichiers à modifier :
|
|
26
|
+
* - `packages/module-ai/src/api/auth/generate-text.ts`
|
|
27
|
+
* - `packages/module-ai/src/api/auth/generate-image.ts`
|
|
28
|
+
* - `packages/module-recipes-pro/src/api/auth/recipes-bilingual.ts`
|
|
29
|
+
* - Tous les endpoints de suggestions UI (autocomplete, etc.)
|
|
30
|
+
*
|
|
31
|
+
* Pattern de remplacement :
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // AVANT (ancien système avec recordConsumption)
|
|
35
|
+
* const consumeResult = await recordConsumption(user.id, usage, pricing, metadata);
|
|
36
|
+
*
|
|
37
|
+
* // APRÈS (nouveau système centralisé)
|
|
38
|
+
* import { computeAndDebitAIUsage, computeProviderCostUsd } from "@lastbrain/module-ai/server/billing";
|
|
39
|
+
* import { getWalletState } from "@lastbrain/module-ai/server/billing";
|
|
40
|
+
*
|
|
41
|
+
* // 1. Calculer le coût provider
|
|
42
|
+
* const providerCostUsd = computeProviderCostUsd(usage, pricing);
|
|
43
|
+
*
|
|
44
|
+
* // 2. Récupérer wallet
|
|
45
|
+
* const wallet = await getWalletState(user.id);
|
|
46
|
+
*
|
|
47
|
+
* // 3. Compute & debit avec actionType approprié
|
|
48
|
+
* const billingResult = computeAndDebitAIUsage({
|
|
49
|
+
* userId: user.id,
|
|
50
|
+
* actionType: "generate-recipe-text", // ou "generate-image", "generate-text", etc.
|
|
51
|
+
* providerCostUsd,
|
|
52
|
+
* wallet,
|
|
53
|
+
* metadata: {
|
|
54
|
+
* model: modelName,
|
|
55
|
+
* provider,
|
|
56
|
+
* endpoint: "generate-text",
|
|
57
|
+
* prompt: prompt.substring(0, 32),
|
|
58
|
+
* generatedText: generatedText.substring(0, 500),
|
|
59
|
+
* },
|
|
60
|
+
* });
|
|
61
|
+
*
|
|
62
|
+
* if (!billingResult.success) {
|
|
63
|
+
* return NextResponse.json(
|
|
64
|
+
* { error: "Crédits insuffisants" },
|
|
65
|
+
* { status: 402 } // Payment Required
|
|
66
|
+
* );
|
|
67
|
+
* }
|
|
68
|
+
*
|
|
69
|
+
* // 4. Enregistrer dans le ledger via RPC
|
|
70
|
+
* const { error: ledgerError } = await supabase.rpc("record_token_consumption", {
|
|
71
|
+
* p_user_id: user.id,
|
|
72
|
+
* p_debit_tokens: billingResult.tokensDebited || 0,
|
|
73
|
+
* p_provider_cost_usd: providerCostUsd,
|
|
74
|
+
* p_sell_usd: billingResult.sellCostUsd,
|
|
75
|
+
* p_margin_usd: billingResult.marginUsd,
|
|
76
|
+
* p_meta: metadata,
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
*
|
|
81
|
+
* B) Refactorer PurchaseTokensPage.tsx
|
|
82
|
+
* ------------------------------------
|
|
83
|
+
*
|
|
84
|
+
* Modifications UI :
|
|
85
|
+
*
|
|
86
|
+
* 1. Remplacer tous "Tokens" par "Crédits IA"
|
|
87
|
+
*
|
|
88
|
+
* 2. Afficher pour chaque pack :
|
|
89
|
+
* ```tsx
|
|
90
|
+
* <div className="mb-6">
|
|
91
|
+
* <p className="text-4xl font-bold text-primary mb-1">
|
|
92
|
+
* {formatPrice(pack.price_cents, pack.currency)}
|
|
93
|
+
* </p>
|
|
94
|
+
* <p className="text-sm text-default-700">Crédit IA</p>
|
|
95
|
+
* </div>
|
|
96
|
+
*
|
|
97
|
+
* <div className="mb-4 p-3 bg-blue-50 rounded-lg">
|
|
98
|
+
* <p className="text-xs text-gray-600 mb-1">
|
|
99
|
+
* Budget fournisseur inclus
|
|
100
|
+
* </p>
|
|
101
|
+
* <p className="text-sm font-semibold text-blue-600">
|
|
102
|
+
* ${((pack.price_cents / 100) / 2.5).toFixed(2)}
|
|
103
|
+
* </p>
|
|
104
|
+
* <p className="text-xs text-gray-500 mt-1">
|
|
105
|
+
* (marge et infrastructure incluses)
|
|
106
|
+
* </p>
|
|
107
|
+
* </div>
|
|
108
|
+
*
|
|
109
|
+
* <div className="mb-6 text-xs text-gray-600">
|
|
110
|
+
* <p>· Recette complète : à partir de 0.20$</p>
|
|
111
|
+
* <p>· Image : à partir de 0.05$</p>
|
|
112
|
+
* <p className="mt-2 font-semibold text-green-600">
|
|
113
|
+
* ≈ {Math.floor((pack.price_cents / 100) / 0.20)} recettes max
|
|
114
|
+
* </p>
|
|
115
|
+
* </div>
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* 3. Supprimer/simplifier la section "Models Pricing"
|
|
119
|
+
* - Ne plus afficher prix token/million tokens
|
|
120
|
+
* - Afficher coûts estimés par action en USD
|
|
121
|
+
* - Utiliser endpoint `/api/ai/public/pricing-summary`
|
|
122
|
+
*
|
|
123
|
+
*
|
|
124
|
+
* C) Refactorer UsageAndTokensPage.tsx
|
|
125
|
+
* ------------------------------------
|
|
126
|
+
*
|
|
127
|
+
* Modifications UI :
|
|
128
|
+
*
|
|
129
|
+
* 1. Remplacer TokenKpiGrid par une barre de progression simple :
|
|
130
|
+
* ```tsx
|
|
131
|
+
* <Card>
|
|
132
|
+
* <CardHeader>
|
|
133
|
+
* <h3>Crédits IA restants</h3>
|
|
134
|
+
* </CardHeader>
|
|
135
|
+
* <CardBody>
|
|
136
|
+
* <div className="mb-2">
|
|
137
|
+
* <div className="flex justify-between mb-1">
|
|
138
|
+
* <span className="text-2xl font-bold">
|
|
139
|
+
* {wallet ? `$${wallet.walletSellValueUsd.toFixed(2)}` : "—"}
|
|
140
|
+
* </span>
|
|
141
|
+
* <span className={`text-sm ${getColorClass(percentRemaining)}`}>
|
|
142
|
+
* {percentRemaining.toFixed(1)}%
|
|
143
|
+
* </span>
|
|
144
|
+
* </div>
|
|
145
|
+
* <div className="w-full bg-gray-200 rounded-full h-3">
|
|
146
|
+
* <div
|
|
147
|
+
* className={`h-3 rounded-full ${getProgressBarColor(percentRemaining)}`}
|
|
148
|
+
* style={{ width: `${percentRemaining}%` }}
|
|
149
|
+
* />
|
|
150
|
+
* </div>
|
|
151
|
+
* </div>
|
|
152
|
+
*
|
|
153
|
+
* {percentRemaining < 25 && (
|
|
154
|
+
* <Alert color="warning" className="mt-4">
|
|
155
|
+
* <AlertCircle size={20} />
|
|
156
|
+
* <p>Vos crédits sont faibles. Rechargez maintenant.</p>
|
|
157
|
+
* <PurchaseButton />
|
|
158
|
+
* </Alert>
|
|
159
|
+
* )}
|
|
160
|
+
* </CardBody>
|
|
161
|
+
* </Card>
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* 2. Modifier UsageByModelBarChart pour afficher :
|
|
165
|
+
* - Modèle
|
|
166
|
+
* - Nombre d'appels
|
|
167
|
+
* - Total facturé (sellUsd)
|
|
168
|
+
* - Ne PAS afficher tokens
|
|
169
|
+
*
|
|
170
|
+
* 3. Seuils couleurs :
|
|
171
|
+
* ```typescript
|
|
172
|
+
* function getColorClass(percent: number): string {
|
|
173
|
+
* if (percent > 50) return "text-green-600";
|
|
174
|
+
* if (percent > 25) return "text-orange-500";
|
|
175
|
+
* return "text-red-600";
|
|
176
|
+
* }
|
|
177
|
+
*
|
|
178
|
+
* function getProgressBarColor(percent: number): string {
|
|
179
|
+
* if (percent > 50) return "bg-green-500";
|
|
180
|
+
* if (percent > 25) return "bg-orange-500";
|
|
181
|
+
* return "bg-red-500";
|
|
182
|
+
* }
|
|
183
|
+
* ```
|
|
184
|
+
*
|
|
185
|
+
*
|
|
186
|
+
* D) Endpoint wallet
|
|
187
|
+
* ------------------
|
|
188
|
+
*
|
|
189
|
+
* Créer/modifier `/api/ai/auth/wallet` pour retourner :
|
|
190
|
+
* ```typescript
|
|
191
|
+
* {
|
|
192
|
+
* walletSellValueUsd: number, // Crédit restant
|
|
193
|
+
* walletProviderBudgetUsd: number, // Budget provider restant
|
|
194
|
+
* percentRemaining: number, // % restant
|
|
195
|
+
* tokenBalance: number, // Pour compatibilité (ne pas afficher en UI)
|
|
196
|
+
* }
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
*
|
|
200
|
+
* E) Tests à ajouter
|
|
201
|
+
* ------------------
|
|
202
|
+
*
|
|
203
|
+
* Créer `packages/module-ai/src/server/__tests__/billing.test.ts` :
|
|
204
|
+
*
|
|
205
|
+
* ```typescript
|
|
206
|
+
* import { computeAndDebitAIUsage } from "../billing";
|
|
207
|
+
*
|
|
208
|
+
* describe("Billing centralisé", () => {
|
|
209
|
+
* test("Pack 5$ : recette doit débiter min 0.20$", () => {
|
|
210
|
+
* const result = computeAndDebitAIUsage({
|
|
211
|
+
* userId: "test-user",
|
|
212
|
+
* actionType: "generate-recipe-text",
|
|
213
|
+
* providerCostUsd: 0.001, // Très bas
|
|
214
|
+
* wallet: {
|
|
215
|
+
* tokenBalance: 100000,
|
|
216
|
+
* walletProviderBudgetUsd: 2.0,
|
|
217
|
+
* walletSellValueUsd: 5.0,
|
|
218
|
+
* },
|
|
219
|
+
* metadata: { model: "gpt-4o-mini" },
|
|
220
|
+
* });
|
|
221
|
+
*
|
|
222
|
+
* expect(result.success).toBe(true);
|
|
223
|
+
* expect(result.sellCostUsd).toBeGreaterThanOrEqual(0.20);
|
|
224
|
+
* });
|
|
225
|
+
*
|
|
226
|
+
* test("Budget provider insuffisant => refus", () => {
|
|
227
|
+
* const result = computeAndDebitAIUsage({
|
|
228
|
+
* userId: "test-user",
|
|
229
|
+
* actionType: "generate-recipe-text",
|
|
230
|
+
* providerCostUsd: 0.15,
|
|
231
|
+
* wallet: {
|
|
232
|
+
* tokenBalance: 100000,
|
|
233
|
+
* walletProviderBudgetUsd: 0.10, // Insuffisant
|
|
234
|
+
* walletSellValueUsd: 5.0,
|
|
235
|
+
* },
|
|
236
|
+
* metadata: { model: "gpt-4o-mini" },
|
|
237
|
+
* });
|
|
238
|
+
*
|
|
239
|
+
* expect(result.success).toBe(false);
|
|
240
|
+
* expect(result.error).toBe("insufficient_credits");
|
|
241
|
+
* });
|
|
242
|
+
* });
|
|
243
|
+
* ```
|
|
244
|
+
*
|
|
245
|
+
*
|
|
246
|
+
* F) Migration base de données
|
|
247
|
+
* ----------------------------
|
|
248
|
+
*
|
|
249
|
+
* Aucune migration nécessaire - le système est rétrocompatible.
|
|
250
|
+
* Les anciennes données continuent de fonctionner avec les nouveaux calculs.
|
|
251
|
+
*
|
|
252
|
+
*
|
|
253
|
+
* G) Documentation utilisateur
|
|
254
|
+
* ----------------------------
|
|
255
|
+
*
|
|
256
|
+
* Mettre à jour la doc pour expliquer :
|
|
257
|
+
* - Le système de Crédits IA (USD) remplace les tokens
|
|
258
|
+
* - Coûts minimum par action garantissent qualité service
|
|
259
|
+
* - Budget provider séparé évite les pertes
|
|
260
|
+
* - Marge de 60% permet maintenance infrastructure
|
|
261
|
+
*
|
|
262
|
+
*
|
|
263
|
+
* VALIDATION FINALE :
|
|
264
|
+
* ==================
|
|
265
|
+
*
|
|
266
|
+
* Avant mise en prod, vérifier :
|
|
267
|
+
*
|
|
268
|
+
* ✅ Tous les endpoints appellent computeAndDebitAIUsage()
|
|
269
|
+
* ✅ UI affiche "Crédits IA" au lieu de "tokens"
|
|
270
|
+
* ✅ Minimums appliqués : recette ≥ 0.20$, image ≥ 0.05$
|
|
271
|
+
* ✅ walletProviderBudgetUsd ne passe jamais sous 0
|
|
272
|
+
* ✅ Logs anonymes apparaissent à chaque débit
|
|
273
|
+
* ✅ Tests passent
|
|
274
|
+
* ✅ Pas de régression pour utilisateurs existants
|
|
275
|
+
*/
|
|
276
|
+
export {};
|
|
277
|
+
//# sourceMappingURL=REFACTORING_BILLING_GUIDE.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"REFACTORING_BILLING_GUIDE.d.ts","sourceRoot":"","sources":["../../src/docs/REFACTORING_BILLING_GUIDE.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkRG;AAGH,OAAO,EAAE,CAAC"}
|