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