export function loadBillingConfig() { const useBackendCost = process.env.H5_USE_BACKEND_COST !== '0'; return { useBackendCost, usdCnyRate: Number(process.env.H5_USD_CNY_RATE ?? 7.2), inputCentsPer1k: Number(process.env.H5_BILL_INPUT_CENTS_PER_1K ?? 2), outputCentsPer1k: Number(process.env.H5_BILL_OUTPUT_CENTS_PER_1K ?? 6), minBillCents: Number(process.env.H5_MIN_BILL_CENTS ?? 1), }; } export function normalizeTokenState(raw) { if (!raw || typeof raw !== 'object') { return { inputTokens: 0, outputTokens: 0, totalTokens: 0, accumulatedInputTokens: 0, accumulatedOutputTokens: 0, accumulatedTotalTokens: 0, accumulatedCost: null, }; } return { inputTokens: Number(raw.inputTokens ?? raw.input_tokens ?? 0), outputTokens: Number(raw.outputTokens ?? raw.output_tokens ?? 0), totalTokens: Number(raw.totalTokens ?? raw.total_tokens ?? 0), accumulatedInputTokens: Number( raw.accumulatedInputTokens ?? raw.accumulated_input_tokens ?? raw.inputTokens ?? 0, ), accumulatedOutputTokens: Number( raw.accumulatedOutputTokens ?? raw.accumulated_output_tokens ?? raw.outputTokens ?? 0, ), accumulatedTotalTokens: Number( raw.accumulatedTotalTokens ?? raw.accumulated_total_tokens ?? raw.totalTokens ?? 0, ), accumulatedCost: raw.accumulatedCost ?? raw.accumulated_cost ?? null, }; } export function computeDeltaCostCents(previous, current, config = loadBillingConfig()) { const prevCost = previous?.lastAccumulatedCost == null ? null : Number(previous.lastAccumulatedCost); const currCost = current.accumulatedCost == null ? null : Number(current.accumulatedCost); if (config.useBackendCost && prevCost != null && currCost != null && currCost >= prevCost) { const deltaUsd = currCost - prevCost; return Math.max(config.minBillCents, Math.ceil(deltaUsd * config.usdCnyRate * 100)); } if (config.useBackendCost && prevCost == null && currCost != null && currCost > 0) { return Math.max(config.minBillCents, Math.ceil(currCost * config.usdCnyRate * 100)); } const prevIn = Number(previous?.lastInputTokens ?? 0); const prevOut = Number(previous?.lastOutputTokens ?? 0); const deltaIn = Math.max(0, current.accumulatedInputTokens - prevIn); const deltaOut = Math.max(0, current.accumulatedOutputTokens - prevOut); if (deltaIn === 0 && deltaOut === 0) return 0; const raw = (deltaIn / 1000) * config.inputCentsPer1k + (deltaOut / 1000) * config.outputCentsPer1k; if (raw <= 0) return 0; return Math.max(config.minBillCents, Math.ceil(raw)); }