| const { logger } = require('@librechat/data-schemas'); |
| const { ViolationTypes } = require('librechat-data-provider'); |
| const { createAutoRefillTransaction } = require('./Transaction'); |
| const { logViolation } = require('~/cache'); |
| const { getMultiplier } = require('./tx'); |
| const { Balance } = require('~/db/models'); |
|
|
| function isInvalidDate(date) { |
| return isNaN(date); |
| } |
|
|
| |
| |
| |
| |
| const checkBalanceRecord = async function ({ |
| user, |
| model, |
| endpoint, |
| valueKey, |
| tokenType, |
| amount, |
| endpointTokenConfig, |
| }) { |
| const multiplier = getMultiplier({ valueKey, tokenType, model, endpoint, endpointTokenConfig }); |
| const tokenCost = amount * multiplier; |
|
|
| |
| let record = await Balance.findOne({ user }).lean(); |
| if (!record) { |
| logger.debug('[Balance.check] No balance record found for user', { user }); |
| return { |
| canSpend: false, |
| balance: 0, |
| tokenCost, |
| }; |
| } |
| let balance = record.tokenCredits; |
|
|
| logger.debug('[Balance.check] Initial state', { |
| user, |
| model, |
| endpoint, |
| valueKey, |
| tokenType, |
| amount, |
| balance, |
| multiplier, |
| endpointTokenConfig: !!endpointTokenConfig, |
| }); |
|
|
| |
| if (balance - tokenCost <= 0 && record.autoRefillEnabled && record.refillAmount > 0) { |
| const lastRefillDate = new Date(record.lastRefill); |
| const now = new Date(); |
| if ( |
| isInvalidDate(lastRefillDate) || |
| now >= |
| addIntervalToDate(lastRefillDate, record.refillIntervalValue, record.refillIntervalUnit) |
| ) { |
| try { |
| |
| const result = await createAutoRefillTransaction({ |
| user: user, |
| tokenType: 'credits', |
| context: 'autoRefill', |
| rawAmount: record.refillAmount, |
| }); |
| balance = result.balance; |
| } catch (error) { |
| logger.error('[Balance.check] Failed to record transaction for auto-refill', error); |
| } |
| } |
| } |
|
|
| logger.debug('[Balance.check] Token cost', { tokenCost }); |
| return { canSpend: balance >= tokenCost, balance, tokenCost }; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| const addIntervalToDate = (date, value, unit) => { |
| const result = new Date(date); |
| switch (unit) { |
| case 'seconds': |
| result.setSeconds(result.getSeconds() + value); |
| break; |
| case 'minutes': |
| result.setMinutes(result.getMinutes() + value); |
| break; |
| case 'hours': |
| result.setHours(result.getHours() + value); |
| break; |
| case 'days': |
| result.setDate(result.getDate() + value); |
| break; |
| case 'weeks': |
| result.setDate(result.getDate() + value * 7); |
| break; |
| case 'months': |
| result.setMonth(result.getMonth() + value); |
| break; |
| default: |
| break; |
| } |
| return result; |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| const checkBalance = async ({ req, res, txData }) => { |
| const { canSpend, balance, tokenCost } = await checkBalanceRecord(txData); |
| if (canSpend) { |
| return true; |
| } |
|
|
| const type = ViolationTypes.TOKEN_BALANCE; |
| const errorMessage = { |
| type, |
| balance, |
| tokenCost, |
| promptTokens: txData.amount, |
| }; |
|
|
| if (txData.generations && txData.generations.length > 0) { |
| errorMessage.generations = txData.generations; |
| } |
|
|
| await logViolation(req, res, type, errorMessage, 0); |
| throw new Error(JSON.stringify(errorMessage)); |
| }; |
|
|
| module.exports = { |
| checkBalance, |
| }; |
|
|