Ctrl K
ComunidadGuías…

src/rate-limit-burst.js

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
/**
 * Process-scoped "IP-level" rate-limit burst detector.
 *
 * WHY this is not per-request state: Windsurf applies some cooldowns to the
 * CLIENT IP, not to a single account. When several pooled accounts hit a 429 on
 * the same model inside a few seconds, burning through the rest of the pool just
 * marks every account dead for the cooldown window and tells the caller nothing.
 * The signal is only visible ACROSS requests, so the event window must live for
 * the process lifetime (like the metrics counters), never on a request context.
 *
 * Scope: events are grouped by `model` (the routing model key). Accounts are
 * recorded for diagnostics only; a repeated 429 from the SAME account still
 * counts, matching the original per-request implementation.
 *
 * This module is deliberately dependency-free: both the non-stream and the
 * stream Cascade paths import it, and tests reset it through the seam below.
 */

export const RATE_LIMIT_BURST_WINDOW_MS = 8_000;
export const RATE_LIMIT_BURST_THRESHOLD = 3;

// A burst window is 8s; a handful of events per model is all that can survive
// pruning. The cap is a belt-and-braces bound for pathological callers.
const MAX_EVENTS = 256;

/** @type {{time:number, model:string|null, account:string|null, cooldownMs:number}[]} */
const _events = [];

/**
 * Record one upstream rate-limit observation and report whether the window for
 * this model has now crossed the burst threshold.
 *
 * @param {object} [event]
 * @param {string|null} [event.model] routing model key the 429 applied to
 * @param {string|null} [event.account] account id (diagnostics only)
 * @param {number} [event.cooldownMs] cooldown the caller intends to apply
 * @param {number} [event.now] injectable clock for tests
 * @returns {{tripped:boolean, count:number, maxCooldownMs:number}}
 */
export function recordRateLimitBurstEvent({ model = null, account = null, cooldownMs = 0, now = Date.now() } = {}) {
  const cooldown = Number.isFinite(cooldownMs) && cooldownMs > 0 ? cooldownMs : 0;
  _events.push({ time: now, model, account, cooldownMs: cooldown });

  const cutoff = now - RATE_LIMIT_BURST_WINDOW_MS;
  while (_events.length && _events[0].time < cutoff) _events.shift();
  if (_events.length > MAX_EVENTS) _events.splice(0, _events.length - MAX_EVENTS);

  const sameModel = _events.filter((e) => e.model === model);
  const maxCooldownMs = sameModel.reduce((max, e) => Math.max(max, e.cooldownMs), 0);
  return {
    tripped: sameModel.length >= RATE_LIMIT_BURST_THRESHOLD,
    count: sameModel.length,
    maxCooldownMs,
  };
}

/** Test seam: forget every recorded event. */
export function __resetRateLimitBurstForTest() {
  _events.length = 0;
}

/** Test seam: snapshot of the live window (oldest first). */
export function __rateLimitBurstStateForTest() {
  return _events.map((e) => ({ ...e }));
}