Spaces:
Runtime error
Runtime error
| /** | |
| * burnRate.ts β Burn-rate EMA estimator for quota consumption. | |
| * | |
| * Computes an exponential moving average (alpha=0.3) over a series of | |
| * (timestamp, consumed) samples and projects time to exhaustion. | |
| * | |
| * Part of: Group B β Quota Sharing Engine (plan 22, frente F6). | |
| */ | |
| const EMA_ALPHA = 0.3; | |
| export interface BurnRateSample { | |
| ts: number; // epoch ms | |
| consumed: number; // cumulative consumed value at this ts | |
| } | |
| export interface BurnRateResult { | |
| /** Estimated tokens (or units) consumed per second. */ | |
| tokensPerSecond: number; | |
| /** | |
| * Estimated milliseconds until the remaining quota is exhausted. | |
| * null if rate is 0 or the caller did not provide a remaining value. | |
| */ | |
| timeToExhaustionMs: number | null; | |
| } | |
| /** | |
| * Compute burn rate from a single snapshot using the sliding window context. | |
| * | |
| * When only one sample is available (the common case for on-demand pool usage | |
| * queries), we derive the rate from the consumption within the current window: | |
| * rate = consumedTotal / elapsedInWindow | |
| * | |
| * This assumes consumption is roughly uniform within the window β a reasonable | |
| * approximation for token/request budgets over hourly/daily/weekly periods. | |
| * | |
| * @param consumedTotal Cumulative consumption in the current sliding window. | |
| * @param windowMs The window duration in milliseconds (e.g. 5h = 18_000_000). | |
| * @param remaining Optional remaining quota (same unit as consumedTotal). | |
| */ | |
| export function computeBurnRateFromWindow( | |
| consumedTotal: number, | |
| windowMs: number, | |
| remaining?: number | |
| ): BurnRateResult { | |
| if (consumedTotal <= 0 || windowMs <= 0) { | |
| return { tokensPerSecond: 0, timeToExhaustionMs: null }; | |
| } | |
| const nowMs = Date.now(); | |
| const currentBucketIndex = Math.floor(nowMs / windowMs); | |
| const windowStartMs = currentBucketIndex * windowMs; | |
| const elapsedMs = Math.max(1, nowMs - windowStartMs); // avoid division by zero | |
| const safeRate = consumedTotal / (elapsedMs / 1000); // per second | |
| const timeToExhaustionMs = | |
| safeRate > 0 && remaining !== undefined && remaining >= 0 | |
| ? (remaining / safeRate) * 1000 | |
| : null; | |
| return { tokensPerSecond: safeRate, timeToExhaustionMs }; | |
| } | |
| /** | |
| * Compute the current burn rate from a series of samples. | |
| * | |
| * @param history Array of { ts, consumed } ordered oldest β newest. | |
| * Needs at least 2 entries; fewer returns zeros. | |
| * @param remaining Optional remaining quota (same unit as consumed). | |
| * When provided, `timeToExhaustionMs` is calculated. | |
| */ | |
| export function computeBurnRate( | |
| history: BurnRateSample[], | |
| remaining?: number | |
| ): BurnRateResult { | |
| if (history.length < 2) { | |
| return { tokensPerSecond: 0, timeToExhaustionMs: null }; | |
| } | |
| // Build EMA over consecutive deltas. | |
| let emaRate = 0; | |
| let initialized = false; | |
| for (let i = 1; i < history.length; i++) { | |
| const deltaConsumed = history[i].consumed - history[i - 1].consumed; | |
| const deltaTs = history[i].ts - history[i - 1].ts; // ms | |
| if (deltaTs <= 0) continue; // skip duplicate or out-of-order timestamps | |
| const instantRate = deltaConsumed / (deltaTs / 1000); // per second | |
| if (!initialized) { | |
| emaRate = instantRate; | |
| initialized = true; | |
| } else { | |
| emaRate = EMA_ALPHA * instantRate + (1 - EMA_ALPHA) * emaRate; | |
| } | |
| } | |
| if (!initialized) { | |
| return { tokensPerSecond: 0, timeToExhaustionMs: null }; | |
| } | |
| const safeRate = Math.max(0, emaRate); | |
| const timeToExhaustionMs = | |
| safeRate > 0 && remaining !== undefined && remaining >= 0 | |
| ? (remaining / safeRate) * 1000 | |
| : null; | |
| return { tokensPerSecond: safeRate, timeToExhaustionMs }; | |
| } | |