maxpool 1.21.3 → 1.22.0
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/package.json +1 -1
- package/src/account-manager.js +247 -22
- package/src/index.js +8 -2
- package/src/server.js +6 -1
- package/src/tui.js +59 -11
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -68,6 +68,16 @@ function emptyQuota() {
|
|
|
68
68
|
// Provider (z.ai / Kimi) quota — kept SEPARATE from unified* so a provider
|
|
69
69
|
// reading never leaks into the OAuth quota gates (_isAvailable / _weeklyRawState
|
|
70
70
|
// / _accountScarcity read unified* only). z.ai is pollable; Kimi is not.
|
|
71
|
+
// Largest "time until reset" ever OBSERVED for each window, i.e. a lower bound on
|
|
72
|
+
// the window's true length. The dynamic cap needs a duration to know how far into a
|
|
73
|
+
// window it is, and the nominal 5h/7d table is an assumption about the VENDOR: a
|
|
74
|
+
// provider whose "weekly" is really 3 days would sit part-way up the ramp from the
|
|
75
|
+
// moment the window is born. Learning the span from observation fixes that, and the
|
|
76
|
+
// error direction of an UNDER-estimate (seen only late in a window) is conservative —
|
|
77
|
+
// it reports less elapsed, so the cap stays nearer its floor. Grows monotonically and
|
|
78
|
+
// self-corrects the first time a fresh window is seen.
|
|
79
|
+
capSpanSes: null,
|
|
80
|
+
capSpanWk: null,
|
|
71
81
|
providerSes: null, // utilization 0-1 (z.ai 5h token window)
|
|
72
82
|
providerSesReset: null, // ms
|
|
73
83
|
providerWk: null, // utilization 0-1 (z.ai weekly), null if plan has none
|
|
@@ -141,6 +151,11 @@ const DEFAULT_SCHEDULER = {
|
|
|
141
151
|
// ACTIVE (_peakTier ≥ 1). Default OFF — mechanism shipped, immediate use declined.
|
|
142
152
|
criticalPeakUnlock: false,
|
|
143
153
|
weeklyBurnDebtWeight: 0.6,
|
|
154
|
+
// DYNAMIC CAP: the fraction of a quota window that elapses before a dynamic cap
|
|
155
|
+
// starts lifting off its floor. 0.5 holds the owner's full reserve through the first
|
|
156
|
+
// half of every window, then opens it up over the second half — so the reserve is
|
|
157
|
+
// protected while there is still time to use it, and spent when there is not.
|
|
158
|
+
capRampStart: 0.5,
|
|
144
159
|
// Routing-cost tuning (lower cost = preferred). The goal is to AVOID
|
|
145
160
|
// short-term (rate/concurrency) throttling by spreading load across healthy
|
|
146
161
|
// accounts. So in-flight concurrency is the DOMINANT term, with a steep
|
|
@@ -289,7 +304,7 @@ const PERSISTED_QUOTA_FIELDS = [
|
|
|
289
304
|
// flag and the fast-refill discount (plus the TUI "Wk none" rendering) silently
|
|
290
305
|
// drops until the next probe sweep re-learns it. The probe still rewrites it on
|
|
291
306
|
// every successful sweep, so a plan change heals on the same cadence.
|
|
292
|
-
'weeklyAbsent', 'providerSes', 'providerSesReset', 'providerWk', 'providerWkReset',
|
|
307
|
+
'weeklyAbsent', 'capSpanSes', 'capSpanWk', 'providerSes', 'providerSesReset', 'providerWk', 'providerWkReset',
|
|
293
308
|
'providerQuotaSource', 'lastProbeOkAt',
|
|
294
309
|
];
|
|
295
310
|
|
|
@@ -344,14 +359,70 @@ function parseResetHeader(value) {
|
|
|
344
359
|
* also logs once so a hand-edited config doesn't silently fail open (a NaN cap makes
|
|
345
360
|
* every `util >= cap` comparison false = uncapped, with no error anywhere).
|
|
346
361
|
*/
|
|
347
|
-
function _sanitizeCap(value, name) {
|
|
362
|
+
function _sanitizeCap(value, name, quiet = false) {
|
|
348
363
|
if (value == null) return null;
|
|
349
364
|
const n = Number(value);
|
|
350
365
|
if (Number.isFinite(n) && n > 0 && n < 1) return n;
|
|
351
|
-
|
|
366
|
+
// `quiet` is for the second call that only resolves the MODE from the same value —
|
|
367
|
+
// it would otherwise print the identical complaint twice per account per load.
|
|
368
|
+
if (!quiet) console.log(`[Maxpool] Ignoring invalid capUtilization ${JSON.stringify(value)} for "${name}" — expected 0-1`);
|
|
352
369
|
return null;
|
|
353
370
|
}
|
|
354
371
|
|
|
372
|
+
/**
|
|
373
|
+
* The cap MODE for an account. Only meaningful when a cap is set. 'fixed' must be
|
|
374
|
+
* asked for EXPLICITLY; everything else (absent, unknown string) resolves to
|
|
375
|
+
* 'dynamic' — the owner's directive that the dynamic cap "become the new default for
|
|
376
|
+
* the accounts that currently have the fixed cap", which is exactly the config shape
|
|
377
|
+
* with a `capUtilization` and no `capMode`.
|
|
378
|
+
*/
|
|
379
|
+
/** A cap ramp start is a fraction of the window in [0,1); anything else means "use the
|
|
380
|
+
* default". 1 or more would mean "never ramp", which is what `capMode:'fixed'` says
|
|
381
|
+
* properly, so it is rejected here rather than silently creating a second way to say it. */
|
|
382
|
+
function _sanitizeRampStart(value, name) {
|
|
383
|
+
if (value == null) return null;
|
|
384
|
+
const n = Number(value);
|
|
385
|
+
if (Number.isFinite(n) && n >= 0 && n < 1) return n;
|
|
386
|
+
console.log(`[Maxpool] Ignoring invalid capRampStart ${JSON.stringify(value)} for "${name}" — expected 0 to <1 (use capMode:"fixed" for no ramp)`);
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function _capMode(value, sanitizedCap, name) {
|
|
391
|
+
if (sanitizedCap == null) return null;
|
|
392
|
+
if (value === 'fixed' || value === 'dynamic') return value;
|
|
393
|
+
// Absent is the migration case and is silent BY DESIGN — every pre-2026-09-24 config
|
|
394
|
+
// has a cap and no mode, and the owner asked for those to become dynamic. A value that
|
|
395
|
+
// is present but unrecognised is a typo, and swallowing it silently would be the same
|
|
396
|
+
// fail-open-without-a-word shape _sanitizeCap exists to prevent for the number.
|
|
397
|
+
if (value != null) {
|
|
398
|
+
console.log(`[Maxpool] Ignoring unknown capMode ${JSON.stringify(value)} for "${name}" — expected "fixed" or "dynamic"; using dynamic`);
|
|
399
|
+
}
|
|
400
|
+
return 'dynamic';
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* How far into a quota window we are, 0-1, from its reset stamp. Returns null when the
|
|
405
|
+
* stamp is unusable (absent / non-finite) so every caller can fail CLOSED rather than
|
|
406
|
+
* inventing a position in a window it cannot see. A stamp in the past clamps to 1
|
|
407
|
+
* (window over) and one further out than a nominal window clamps to 0 — both are real
|
|
408
|
+
* states around a rollover, and neither may produce a NaN.
|
|
409
|
+
*/
|
|
410
|
+
function _windowElapsedRatio(resetAt, durationMs, now) {
|
|
411
|
+
if (!Number.isFinite(resetAt) || !Number.isFinite(durationMs) || durationMs <= 0) return null;
|
|
412
|
+
// A stamp in the PAST means the window already rolled and we have not yet learned the
|
|
413
|
+
// new one — so our POSITION in the live window is unknown, and the utilization we hold
|
|
414
|
+
// belongs to the window that closed. Treating that as "fully elapsed" would ramp the cap
|
|
415
|
+
// to its ceiling on stale data: reproduced on a provider account (providerWk 0.55, stamp
|
|
416
|
+
// 3 days past) reading `normal` where the fixed cap says `capped`, and provider stamps
|
|
417
|
+
// are never cleared by _clearExpiredQuotas, so a probe outage holds that state open
|
|
418
|
+
// indefinitely. Unknown position must fail CLOSED to the floor, like a missing stamp.
|
|
419
|
+
if (resetAt <= now) return null;
|
|
420
|
+
// `resetAt > now` is guaranteed above and `durationMs > 0`, so the ratio cannot exceed
|
|
421
|
+
// 1; only the LOW side needs bounding, for a stamp further out than one nominal window
|
|
422
|
+
// (a vendor window longer than we assume) which must read as "window just started".
|
|
423
|
+
return Math.max(0, (durationMs - (resetAt - now)) / durationMs);
|
|
424
|
+
}
|
|
425
|
+
|
|
355
426
|
export class AccountManager {
|
|
356
427
|
constructor(accounts, switchThreshold = 0.90, schedulerOptions = {}, dependencies = {}) {
|
|
357
428
|
this.scheduler = { ...DEFAULT_SCHEDULER, ...schedulerOptions };
|
|
@@ -388,6 +459,18 @@ export class AccountManager {
|
|
|
388
459
|
// to null HERE, visibly (below), so a hand-edited "50" or "abc" in the config
|
|
389
460
|
// can never fail the >= comparisons open as NaN.
|
|
390
461
|
capUtilization: _sanitizeCap(acct.capUtilization, acct.name),
|
|
462
|
+
// CAP MODE (owner-directed 2026-09-24). 'dynamic' is the DEFAULT for any account
|
|
463
|
+
// that carries a cap: the value above becomes a FLOOR, and the effective cap ramps
|
|
464
|
+
// toward switchThreshold as the window nears its reset, so reserved-but-unused
|
|
465
|
+
// capacity is spent instead of dying at reset. `capMode:'fixed'` opts back in to the
|
|
466
|
+
// constant cap. Uncapped accounts carry no mode (nothing to modulate).
|
|
467
|
+
capMode: _capMode(acct.capMode, _sanitizeCap(acct.capUtilization, acct.name, true), acct.name),
|
|
468
|
+
// Per-account overrides for the two numbers that decide how much reserve is kept
|
|
469
|
+
// and for how long. Defaults live in DEFAULT_SCHEDULER; these exist because the
|
|
470
|
+
// right answer is the OWNER's (how much of their own account to hold back, and
|
|
471
|
+
// from when), not a constant buried in the scheduler.
|
|
472
|
+
capCeiling: _sanitizeCap(acct.capCeiling, acct.name, true),
|
|
473
|
+
capRampStart: _sanitizeRampStart(acct.capRampStart, acct.name),
|
|
391
474
|
model: acct.model || null,
|
|
392
475
|
modelMap: acct.modelMap || null,
|
|
393
476
|
stripBetaHeaders: Boolean(acct.stripBetaHeaders),
|
|
@@ -869,11 +952,15 @@ export class AccountManager {
|
|
|
869
952
|
if (account.inFlight >= this.scheduler.safetyMaxActivePerAccount) return false;
|
|
870
953
|
if (this.getGlobalInFlight() >= this.scheduler.safetyMaxGlobalActive) return false;
|
|
871
954
|
if (account.status === 'exhausted' || account.status === 'error') return false;
|
|
872
|
-
|
|
955
|
+
// `now` is threaded deliberately: under a DYNAMIC cap the bench threshold moves with
|
|
956
|
+
// the clock, so an availability decision and the retry oracle that explains it must
|
|
957
|
+
// read the same instant. The drift is milliseconds today, but it is always in the
|
|
958
|
+
// direction that manufactures a spurious 'capped' verdict, and nothing else enforces it.
|
|
959
|
+
if (this._isSessionQuotaUnavailable(account, now)) return false;
|
|
873
960
|
// Gate on RAW weekly usage, not pace-adjusted: an account with real
|
|
874
961
|
// headroom (e.g. 69% used, resets in days) must stay in the healthy-spread
|
|
875
962
|
// pool even if it's burning fast. Pace is a soft SCORE cost, never a bench.
|
|
876
|
-
const weeklyState = this._weeklyRawState(account);
|
|
963
|
+
const weeklyState = this._weeklyRawState(account, now);
|
|
877
964
|
if (weeklyState === 'exhausted' || weeklyState === 'capped') return false;
|
|
878
965
|
if (weeklyState === 'critical' && !options.allowWeeklyCritical) return false;
|
|
879
966
|
if (weeklyState === 'reserve' && !options.allowWeeklyReserve) return false;
|
|
@@ -1310,21 +1397,125 @@ export class AccountManager {
|
|
|
1310
1397
|
* oracle can never desync from the bench (a capped-benched account MUST report a
|
|
1311
1398
|
* finite retry time or a live session holding on it gets error-fasted).
|
|
1312
1399
|
*/
|
|
1313
|
-
_sessionBenchThreshold(account) {
|
|
1314
|
-
const cap = account
|
|
1400
|
+
_sessionBenchThreshold(account, now = Date.now()) {
|
|
1401
|
+
const cap = this._effectiveCap(account, 'ses', now);
|
|
1315
1402
|
return (cap != null && cap < this.switchThreshold) ? cap : this.switchThreshold;
|
|
1316
1403
|
}
|
|
1317
1404
|
|
|
1405
|
+
/**
|
|
1406
|
+
* The reset stamp + nominal duration of one quota window on this account. OAuth
|
|
1407
|
+
* accounts carry `unified*`; providers carry `provider*` (the two never mix — see
|
|
1408
|
+
* the note on emptyQuota). An account type with no reset stamp for the window
|
|
1409
|
+
* (API-key) yields a null stamp, which the caller treats as "cannot ramp".
|
|
1410
|
+
*/
|
|
1411
|
+
/** Record the largest remaining-time seen for a window — see capSpanSes/capSpanWk. */
|
|
1412
|
+
_noteCapWindowSpan(account, window, resetAt, now = Date.now()) {
|
|
1413
|
+
if (!Number.isFinite(resetAt)) return;
|
|
1414
|
+
const remaining = resetAt - now;
|
|
1415
|
+
if (!(remaining > 0)) return;
|
|
1416
|
+
const key = window === 'ses' ? 'capSpanSes' : 'capSpanWk';
|
|
1417
|
+
const q = account.quota;
|
|
1418
|
+
const nominal = WINDOW_MS_BY_KIND[window];
|
|
1419
|
+
// Never learn a span LONGER than nominal: that direction would push the cap up the
|
|
1420
|
+
// ramp on an assumption, which is the fail-open side.
|
|
1421
|
+
const bounded = Math.min(remaining, nominal);
|
|
1422
|
+
if (q[key] == null || bounded > q[key]) q[key] = bounded;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
_capWindowFields(account, window) {
|
|
1426
|
+
const q = account?.quota || {};
|
|
1427
|
+
const learned = window === 'ses' ? q.capSpanSes : q.capSpanWk;
|
|
1428
|
+
// Prefer the observed span over the nominal assumption; both are bounded above by
|
|
1429
|
+
// nominal, so this can only ever move the cap DOWN toward its floor.
|
|
1430
|
+
const durationMs = Number.isFinite(learned) && learned > 0
|
|
1431
|
+
? Math.min(learned, WINDOW_MS_BY_KIND[window])
|
|
1432
|
+
: WINDOW_MS_BY_KIND[window];
|
|
1433
|
+
if (account?.type === 'provider') {
|
|
1434
|
+
return { resetAt: window === 'ses' ? q.providerSesReset : q.providerWkReset, durationMs };
|
|
1435
|
+
}
|
|
1436
|
+
return { resetAt: window === 'ses' ? q.unified5hReset : q.unified7dReset, durationMs };
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* The cap THIS account is actually held to on THIS window RIGHT NOW.
|
|
1441
|
+
*
|
|
1442
|
+
* fixed mode / no reset stamp -> the configured value, unchanged
|
|
1443
|
+
* dynamic mode -> floor early in the window, ramping to the
|
|
1444
|
+
* ceiling (switchThreshold) as the reset nears
|
|
1445
|
+
*
|
|
1446
|
+
* The ceiling is switchThreshold and never 1.0, deliberately: the owner asked for a
|
|
1447
|
+
* cap that "always preserves some meaningful room for usage of those accounts outside
|
|
1448
|
+
* of MaxPool". A fully-ramped dynamic account therefore behaves exactly like an
|
|
1449
|
+
* UNCAPPED one — never more aggressively — so this mechanism can only ever make an
|
|
1450
|
+
* account MORE available than the fixed cap it replaces (pinned by T3c). That
|
|
1451
|
+
* one-directionality is why it is not a live-session control surface.
|
|
1452
|
+
*
|
|
1453
|
+
* Fails CLOSED in every uncertain case (no stamp, unusable duration): an unknown
|
|
1454
|
+
* window position returns the floor, never an opened-up cap.
|
|
1455
|
+
*/
|
|
1456
|
+
_effectiveCap(account, window = 'wk', now = Date.now()) {
|
|
1457
|
+
const floor = account?.capUtilization;
|
|
1458
|
+
if (floor == null) return null; // uncapped — unchanged
|
|
1459
|
+
if (account.capMode !== 'dynamic') return floor; // fixed — byte-for-byte as before
|
|
1460
|
+
const ceiling = account.capCeiling ?? this.switchThreshold;
|
|
1461
|
+
if (floor >= ceiling) return floor; // never pull a high floor DOWN
|
|
1462
|
+
const { resetAt, durationMs } = this._capWindowFields(account, window);
|
|
1463
|
+
const elapsed = _windowElapsedRatio(resetAt, durationMs, now);
|
|
1464
|
+
if (elapsed == null) return floor; // fail closed
|
|
1465
|
+
const rampStart = account.capRampStart ?? this.scheduler.capRampStart;
|
|
1466
|
+
const ramp = rampStart >= 1 ? 0 : clamp01((elapsed - rampStart) / (1 - rampStart));
|
|
1467
|
+
return floor + (ceiling - floor) * ramp;
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* When a DYNAMIC cap will have risen far enough to stop benching `utilization` on
|
|
1472
|
+
* this window — i.e. the instant the ramp crosses it. Null when that never happens
|
|
1473
|
+
* before the reset (utilization at/above the ceiling), when the cap is fixed, or
|
|
1474
|
+
* when the window position is unknown; the caller then falls back to the reset.
|
|
1475
|
+
*
|
|
1476
|
+
* Without this the cap's hold is keyed to the window RESET, which is correct for a
|
|
1477
|
+
* fixed cap (only a reset unbenches it) and systematically too long for a rising one:
|
|
1478
|
+
* util 0.85 on a 0.50-floor weekly with 20h left is routable in 15h, but the oracle
|
|
1479
|
+
* would have told the client to wait the full 20h — an over-hold that lands exactly
|
|
1480
|
+
* in this feature's own target regime (near reset). Linear in elapsed, so invert it.
|
|
1481
|
+
*/
|
|
1482
|
+
_capUnbenchAt(account, utilization, window = 'wk', now = Date.now()) {
|
|
1483
|
+
if (account?.capMode !== 'dynamic' || utilization == null) return null;
|
|
1484
|
+
const floor = account.capUtilization;
|
|
1485
|
+
const ceiling = account.capCeiling ?? this.switchThreshold;
|
|
1486
|
+
if (floor == null || floor >= ceiling) return null;
|
|
1487
|
+
if (utilization >= ceiling) return null; // the ramp never reaches it
|
|
1488
|
+
if (utilization < floor) return null; // not cap-benched at all
|
|
1489
|
+
const { resetAt, durationMs } = this._capWindowFields(account, window);
|
|
1490
|
+
if (_windowElapsedRatio(resetAt, durationMs, now) == null) return null;
|
|
1491
|
+
const rampStart = account.capRampStart ?? this.scheduler.capRampStart;
|
|
1492
|
+
if (rampStart >= 1) return null;
|
|
1493
|
+
// effective(t) = floor + (ceiling-floor) * (elapsed(t) - rampStart)/(1 - rampStart)
|
|
1494
|
+
// solve effective(t) = utilization for elapsed, then convert back to wall clock.
|
|
1495
|
+
const neededRamp = (utilization - floor) / (ceiling - floor);
|
|
1496
|
+
const neededElapsed = rampStart + neededRamp * (1 - rampStart);
|
|
1497
|
+
const at = resetAt - durationMs * (1 - neededElapsed);
|
|
1498
|
+
if (!Number.isFinite(at)) return null;
|
|
1499
|
+
// Never report a time outside (now, reset]: a crossing already behind us means the
|
|
1500
|
+
// account is not actually benched, and one past the reset is the reset's own case.
|
|
1501
|
+
if (at <= now) return null;
|
|
1502
|
+
// No upper bound needed: at = reset - duration*(1 - neededElapsed), and neededElapsed
|
|
1503
|
+
// exceeds 1 only when utilization exceeds the ceiling, which returned null above. A
|
|
1504
|
+
// `Math.min(at, resetAt)` here was dead defensive code — a brute-force sweep over
|
|
1505
|
+
// span x remaining x utilization found the crossing never once landed past the reset.
|
|
1506
|
+
return at;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1318
1509
|
/** True when the account's usage cap has it benched on the given window reading. */
|
|
1319
|
-
_capped(account, utilization) {
|
|
1320
|
-
const cap = account
|
|
1510
|
+
_capped(account, utilization, window = 'wk', now = Date.now()) {
|
|
1511
|
+
const cap = this._effectiveCap(account, window, now);
|
|
1321
1512
|
return cap != null && utilization != null && utilization >= cap;
|
|
1322
1513
|
}
|
|
1323
1514
|
|
|
1324
|
-
_isSessionQuotaUnavailable(account) {
|
|
1515
|
+
_isSessionQuotaUnavailable(account, now = Date.now()) {
|
|
1325
1516
|
const q = account.quota;
|
|
1326
1517
|
this._clearExpiredQuotas(account);
|
|
1327
|
-
const bench = this._sessionBenchThreshold(account);
|
|
1518
|
+
const bench = this._sessionBenchThreshold(account, now);
|
|
1328
1519
|
|
|
1329
1520
|
// Unified 5h quota is immediate availability. Weekly quota is handled
|
|
1330
1521
|
// separately as long-horizon admission control.
|
|
@@ -1646,9 +1837,17 @@ export class AccountManager {
|
|
|
1646
1837
|
// nextRetryForRequest → retryAfterMs: Infinity → server.js error-fasts → the
|
|
1647
1838
|
// live session is KILLED, despite the real reset time being known all along.
|
|
1648
1839
|
// Reproduced 2026-08-18 with providerWk=0.9995 + a known providerWkReset.
|
|
1840
|
+
// A DYNAMIC cap unbenches on its own ramp, BEFORE the reset — so hold until the
|
|
1841
|
+
// crossing, not the window end. `weeklyState === 'capped'` is the only case where
|
|
1842
|
+
// that applies; a genuinely exhausted account still waits for the reset.
|
|
1843
|
+
const wkReset = q.unified7dReset || q.providerWkReset || null;
|
|
1844
|
+
const wkUtil = account.type === 'provider' ? q.providerWk : q.unified7d;
|
|
1845
|
+
const capCrossing = weeklyState === 'capped'
|
|
1846
|
+
? this._capUnbenchAt(account, wkUtil, 'wk', now)
|
|
1847
|
+
: null;
|
|
1649
1848
|
return {
|
|
1650
1849
|
cause: 'weekly_exhausted',
|
|
1651
|
-
retryAt:
|
|
1850
|
+
retryAt: capCrossing ?? wkReset,
|
|
1652
1851
|
queueable: false,
|
|
1653
1852
|
};
|
|
1654
1853
|
}
|
|
@@ -1733,7 +1932,7 @@ export class AccountManager {
|
|
|
1733
1932
|
// _isSessionQuotaUnavailable (_sessionBenchThreshold) — a capped account benched
|
|
1734
1933
|
// at 50% MUST report a finite retryAt here or a live session holding on it gets
|
|
1735
1934
|
// error-fasted instead of waiting out the window (red-team blocker 2).
|
|
1736
|
-
const bench = this._sessionBenchThreshold(account);
|
|
1935
|
+
const bench = this._sessionBenchThreshold(account, now);
|
|
1737
1936
|
if (account.status === 'throttled' && account.rateLimitedUntil && now < account.rateLimitedUntil) {
|
|
1738
1937
|
return { cause: 'rate_limited', retryAt: account.rateLimitedUntil, queueable: true };
|
|
1739
1938
|
}
|
|
@@ -2951,7 +3150,7 @@ export class AccountManager {
|
|
|
2951
3150
|
|| (Number.isFinite(q.unified7d) && q.unified7d >= floor);
|
|
2952
3151
|
}
|
|
2953
3152
|
|
|
2954
|
-
_weeklyRawState(account) {
|
|
3153
|
+
_weeklyRawState(account, now = Date.now()) {
|
|
2955
3154
|
const q = account.quota;
|
|
2956
3155
|
this._clearExpiredQuotas(account);
|
|
2957
3156
|
if (this._isAccountWideRejected(account)) return 'exhausted';
|
|
@@ -2968,7 +3167,7 @@ export class AccountManager {
|
|
|
2968
3167
|
// and outranks both the tier ladder and the upstream verdict. There is no
|
|
2969
3168
|
// upstreamAllows carve-out for providers anyway, but the ordering documents
|
|
2970
3169
|
// that a cap can never be talked out of by the vendor's "allowed".
|
|
2971
|
-
if (this._capped(account, used)) return 'capped';
|
|
3170
|
+
if (this._capped(account, used, 'wk', now)) return 'capped';
|
|
2972
3171
|
if (used >= this.scheduler.weeklyExhaustedThreshold) return 'exhausted';
|
|
2973
3172
|
if (used >= this.scheduler.weeklyCriticalThreshold) return 'critical';
|
|
2974
3173
|
if (used >= this.scheduler.weeklyReserveThreshold) return 'reserve';
|
|
@@ -2994,7 +3193,7 @@ export class AccountManager {
|
|
|
2994
3193
|
// right through the cap — the override exists for genuine over-limit-but-allowed
|
|
2995
3194
|
// states and would otherwise make the cap a no-op on exactly the account it is
|
|
2996
3195
|
// for (measured: this exact shape sat at unified7d=1.00 'allowed_warning').
|
|
2997
|
-
if (this._capped(account, used)) return 'capped';
|
|
3196
|
+
if (this._capped(account, used, 'wk', now)) return 'capped';
|
|
2998
3197
|
const upstreamAllows = typeof q.unifiedStatus === 'string' && q.unifiedStatus.startsWith('allowed');
|
|
2999
3198
|
if (used >= this.scheduler.weeklyExhaustedThreshold && !upstreamAllows) return 'exhausted';
|
|
3000
3199
|
if (used >= this.scheduler.weeklyCriticalThreshold) return 'critical';
|
|
@@ -3003,9 +3202,9 @@ export class AccountManager {
|
|
|
3003
3202
|
return 'normal';
|
|
3004
3203
|
}
|
|
3005
3204
|
|
|
3006
|
-
_weeklyPaceState(account) {
|
|
3205
|
+
_weeklyPaceState(account, now = Date.now()) {
|
|
3007
3206
|
// Provider quota lives in separate fields — see _weeklyRawState.
|
|
3008
|
-
if (account.type === 'provider') return this._weeklyRawState(account);
|
|
3207
|
+
if (account.type === 'provider') return this._weeklyRawState(account, now);
|
|
3009
3208
|
if (account.quota.unified7d == null) return 'unknown';
|
|
3010
3209
|
const effective = this._effectiveWeeklyUsage(account);
|
|
3011
3210
|
if (effective >= this.scheduler.weeklyExhaustedThreshold) return 'exhausted';
|
|
@@ -3047,11 +3246,17 @@ export class AccountManager {
|
|
|
3047
3246
|
|
|
3048
3247
|
if (usage.fiveHour) {
|
|
3049
3248
|
if (usage.fiveHour.utilization != null) q.unified5h = clamp01(usage.fiveHour.utilization);
|
|
3050
|
-
if (usage.fiveHour.resetAt != null)
|
|
3249
|
+
if (usage.fiveHour.resetAt != null) {
|
|
3250
|
+
q.unified5hReset = usage.fiveHour.resetAt;
|
|
3251
|
+
this._noteCapWindowSpan(account, 'ses', usage.fiveHour.resetAt);
|
|
3252
|
+
}
|
|
3051
3253
|
}
|
|
3052
3254
|
if (usage.sevenDay) {
|
|
3053
3255
|
if (usage.sevenDay.utilization != null) q.unified7d = clamp01(usage.sevenDay.utilization);
|
|
3054
|
-
if (usage.sevenDay.resetAt != null)
|
|
3256
|
+
if (usage.sevenDay.resetAt != null) {
|
|
3257
|
+
q.unified7dReset = usage.sevenDay.resetAt;
|
|
3258
|
+
this._noteCapWindowSpan(account, 'wk', usage.sevenDay.resetAt);
|
|
3259
|
+
}
|
|
3055
3260
|
}
|
|
3056
3261
|
this.noteCapacityWindowAdvance(account.name, 'ses', prevSesReset, usage.fiveHour?.resetAt, prevSesUtil);
|
|
3057
3262
|
this.noteCapacityWindowAdvance(account.name, 'wk', prevWkReset, usage.sevenDay?.resetAt, prevWkUtil);
|
|
@@ -3202,11 +3407,17 @@ export class AccountManager {
|
|
|
3202
3407
|
q.providerQuotaSource = usage.source || 'zai';
|
|
3203
3408
|
if (usage.ses) {
|
|
3204
3409
|
if (usage.ses.utilization != null) q.providerSes = clamp01(usage.ses.utilization);
|
|
3205
|
-
if (usage.ses.resetAt != null)
|
|
3410
|
+
if (usage.ses.resetAt != null) {
|
|
3411
|
+
q.providerSesReset = usage.ses.resetAt;
|
|
3412
|
+
this._noteCapWindowSpan(account, 'ses', usage.ses.resetAt);
|
|
3413
|
+
}
|
|
3206
3414
|
}
|
|
3207
3415
|
if (usage.wk) {
|
|
3208
3416
|
if (usage.wk.utilization != null) q.providerWk = clamp01(usage.wk.utilization);
|
|
3209
|
-
if (usage.wk.resetAt != null)
|
|
3417
|
+
if (usage.wk.resetAt != null) {
|
|
3418
|
+
q.providerWkReset = usage.wk.resetAt;
|
|
3419
|
+
this._noteCapWindowSpan(account, 'wk', usage.wk.resetAt);
|
|
3420
|
+
}
|
|
3210
3421
|
q.weeklyAbsent = false;
|
|
3211
3422
|
} else {
|
|
3212
3423
|
// Weekly window absent from this plan/response — clear so a stale weekly
|
|
@@ -3880,6 +4091,9 @@ export class AccountManager {
|
|
|
3880
4091
|
configSourced: Boolean(acctData.configSourced),
|
|
3881
4092
|
secretName: acctData.secretName || null,
|
|
3882
4093
|
capUtilization: _sanitizeCap(acctData.capUtilization, acctData.name),
|
|
4094
|
+
capMode: _capMode(acctData.capMode, _sanitizeCap(acctData.capUtilization, acctData.name, true), acctData.name),
|
|
4095
|
+
capCeiling: _sanitizeCap(acctData.capCeiling, acctData.name, true),
|
|
4096
|
+
capRampStart: _sanitizeRampStart(acctData.capRampStart, acctData.name),
|
|
3883
4097
|
enabled: acctData.enabled !== false,
|
|
3884
4098
|
refreshToken: acctData.refreshToken || null,
|
|
3885
4099
|
expiresAt: acctData.expiresAt || null,
|
|
@@ -3945,6 +4159,7 @@ export class AccountManager {
|
|
|
3945
4159
|
// cap the user set in the TUI.
|
|
3946
4160
|
if (acctData.capUtilization !== undefined) {
|
|
3947
4161
|
account.capUtilization = _sanitizeCap(acctData.capUtilization, account.name);
|
|
4162
|
+
account.capMode = _capMode(acctData.capMode ?? account.capMode, account.capUtilization, account.name);
|
|
3948
4163
|
}
|
|
3949
4164
|
if (account.status === 'error' && changed) {
|
|
3950
4165
|
account.status = 'active';
|
|
@@ -3986,6 +4201,11 @@ export class AccountManager {
|
|
|
3986
4201
|
// And the usage cap, same reasoning: a TUI-set reservation must survive both
|
|
3987
4202
|
// the restart AND the next `cc all` header re-send (the upsert guard).
|
|
3988
4203
|
capUtilization: a.capUtilization ?? null,
|
|
4204
|
+
capMode: a.capMode ?? null,
|
|
4205
|
+
capEffective: a.capUtilization == null ? null : {
|
|
4206
|
+
ses: this._effectiveCap(a, 'ses'),
|
|
4207
|
+
wk: this._effectiveCap(a, 'wk'),
|
|
4208
|
+
},
|
|
3989
4209
|
}));
|
|
3990
4210
|
}
|
|
3991
4211
|
|
|
@@ -4199,6 +4419,11 @@ export class AccountManager {
|
|
|
4199
4419
|
profiles: a.profiles,
|
|
4200
4420
|
priority: a.priority,
|
|
4201
4421
|
capUtilization: a.capUtilization ?? null,
|
|
4422
|
+
capMode: a.capMode ?? null,
|
|
4423
|
+
capEffective: a.capUtilization == null ? null : {
|
|
4424
|
+
ses: this._effectiveCap(a, 'ses'),
|
|
4425
|
+
wk: this._effectiveCap(a, 'wk'),
|
|
4426
|
+
},
|
|
4202
4427
|
runtime: a.runtime,
|
|
4203
4428
|
status: a.status,
|
|
4204
4429
|
refreshDead: Boolean(a.refreshDead),
|
package/src/index.js
CHANGED
|
@@ -2113,9 +2113,15 @@ async function syncAccountsFromDisk(diskConfig, memConfig, accountManager) {
|
|
|
2113
2113
|
// this a config-edit cap is stale until the next full reload).
|
|
2114
2114
|
const diskCap = Number.isFinite(diskAcct.capUtilization) && diskAcct.capUtilization > 0 && diskAcct.capUtilization < 1
|
|
2115
2115
|
? diskAcct.capUtilization : null;
|
|
2116
|
-
|
|
2116
|
+
// The MODE rides along with the cap: a hand-edited config that adds/changes a cap
|
|
2117
|
+
// without naming a mode gets the dynamic default, exactly as a fresh load would —
|
|
2118
|
+
// otherwise a hot edit would silently produce a capped account with no mode, whose
|
|
2119
|
+
// effective cap is the floor forever (a fixed cap wearing the new feature's name).
|
|
2120
|
+
const diskMode = diskCap == null ? null : (diskAcct.capMode === 'fixed' ? 'fixed' : 'dynamic');
|
|
2121
|
+
if (mgr.capUtilization !== diskCap || mgr.capMode !== diskMode) {
|
|
2117
2122
|
mgr.capUtilization = diskCap;
|
|
2118
|
-
|
|
2123
|
+
mgr.capMode = diskMode;
|
|
2124
|
+
console.log(`[Maxpool] Usage cap for "${mgr.name}" ${diskCap ? `set to ${Math.round(diskCap * 100)}% (${diskMode})` : 'removed'} from config`);
|
|
2119
2125
|
}
|
|
2120
2126
|
memConfig.accounts[memIdx] = { ...memConfig.accounts[memIdx], ...diskAcct };
|
|
2121
2127
|
|
package/src/server.js
CHANGED
|
@@ -3337,8 +3337,13 @@ async function streamResponse(webStream, res, status, responseHeaders, accountIn
|
|
|
3337
3337
|
modelEchoBuffer = modelEchoBuffer ? [...modelEchoBuffer, value] : [value];
|
|
3338
3338
|
const s = decoder.decode(concatUint8(modelEchoBuffer));
|
|
3339
3339
|
if (s.includes('"model"') && s.includes('\n\n')) {
|
|
3340
|
+
// \s* — providers serialize SSE JSON with spaces ("model": "glm-5.3"),
|
|
3341
|
+
// Anthropic compact ("model":"…"). The tight form shipped 2026-08-31 and never
|
|
3342
|
+
// matched a single real z.ai byte (all 1,546 glm rows in this very session
|
|
3343
|
+
// leaked through it; fixture JSON was hand-written compact, so tests stayed
|
|
3344
|
+
// green while production leaked. 2026-09-23.
|
|
3340
3345
|
const normalized = s.replace(
|
|
3341
|
-
/("model"
|
|
3346
|
+
/("model"\s*:\s*")[^"]+(")/,
|
|
3342
3347
|
`$1${requestInfo.model.replace(/["\\]/g, '\\$&')}$2`,
|
|
3343
3348
|
);
|
|
3344
3349
|
out = Buffer.from(normalized, 'utf8');
|
package/src/tui.js
CHANGED
|
@@ -202,12 +202,34 @@ function loadText(load) {
|
|
|
202
202
|
* built for (max@gomokka.com) showed no cap anywhere: reported 2026-08-27, "I need
|
|
203
203
|
* to be able to see whether an account has a cap or not." Yellow while the cap is
|
|
204
204
|
* actively holding traffic back, dim otherwise. */
|
|
205
|
-
function capText(a, benched) {
|
|
205
|
+
function capText(a, benched, am) {
|
|
206
206
|
if (a?.capUtilization == null) return '';
|
|
207
|
-
const
|
|
207
|
+
const floorPct = Math.round(a.capUtilization * 100);
|
|
208
|
+
// DYNAMIC CAP (2026-09-24): the configured value is only a FLOOR — what routing
|
|
209
|
+
// actually holds the account to right now is the ramped effective cap, which climbs
|
|
210
|
+
// as the window nears its reset. Showing the floor alone would state a number the
|
|
211
|
+
// scheduler is not using. `cap 50%>67%` reads as "reserved 50%, currently allowing
|
|
212
|
+
// 67%"; the two collapse to one number while the cap sits at its floor, so an
|
|
213
|
+
// early-window dynamic account looks exactly like the fixed one it replaced.
|
|
214
|
+
const eff = capEffectivePct(am, a);
|
|
215
|
+
const t = (eff != null && eff !== floorPct)
|
|
216
|
+
? `cap ${floorPct}%>${eff}%`
|
|
217
|
+
: `cap ${floorPct}%`;
|
|
208
218
|
return benched ? yellow(t) : dim(t);
|
|
209
219
|
}
|
|
210
220
|
|
|
221
|
+
/** The percentage routing is ACTUALLY enforcing on this account right now: the worse
|
|
222
|
+
* (lower) of its two windows' effective caps, which is the one that benches first.
|
|
223
|
+
* Null for a fixed cap or when the manager cannot compute one. */
|
|
224
|
+
function capEffectivePct(am, a) {
|
|
225
|
+
if (!am?._effectiveCap || a?.capMode !== 'dynamic') return null;
|
|
226
|
+
const vals = ['ses', 'wk']
|
|
227
|
+
.map(w => am._effectiveCap(a, w))
|
|
228
|
+
.filter(v => typeof v === 'number' && Number.isFinite(v));
|
|
229
|
+
if (!vals.length) return null;
|
|
230
|
+
return Math.round(Math.min(...vals) * 100);
|
|
231
|
+
}
|
|
232
|
+
|
|
211
233
|
/** PER-ACCOUNT SETTINGS the user set by hand — the last column's whole job
|
|
212
234
|
* (owner, 2026-08-27: "the last column should contain any and all settings that
|
|
213
235
|
* are custom per account"). Fleet-wide settings (routing mode, peak policy) stay
|
|
@@ -223,7 +245,7 @@ function settingsTags(am, a) {
|
|
|
223
245
|
if (am?.routingMode === 'preferred' && a?.name === am.preferredAccountName) {
|
|
224
246
|
tags.push(cyan('preferred'));
|
|
225
247
|
}
|
|
226
|
-
const capTag = capText(a, capBenched(am, a));
|
|
248
|
+
const capTag = capText(a, capBenched(am, a), am);
|
|
227
249
|
if (capTag) tags.push(capTag);
|
|
228
250
|
return tags;
|
|
229
251
|
}
|
|
@@ -236,7 +258,10 @@ function capBenched(am, a) {
|
|
|
236
258
|
const q = a.quota || {};
|
|
237
259
|
const ses = a.type === 'provider' ? q.providerSes : q.unified5h;
|
|
238
260
|
const wk = a.type === 'provider' ? q.providerWk : q.unified7d;
|
|
239
|
-
|
|
261
|
+
// Each reading is judged against ITS OWN window's cap — under the dynamic cap the
|
|
262
|
+
// two differ (a 5h window nearly over is lifted while the weekly is still at its
|
|
263
|
+
// floor), so passing the default window for both would mislabel one of them.
|
|
264
|
+
return !!(am?._capped?.(a, ses, 'ses') || am?._capped?.(a, wk, 'wk'));
|
|
240
265
|
}
|
|
241
266
|
|
|
242
267
|
export function weeklyPolicyText(am, account) {
|
|
@@ -246,7 +271,10 @@ export function weeklyPolicyText(am, account) {
|
|
|
246
271
|
// exhausted, it's deliberately held, and labelling it "Wk exhausted 50%" (red team)
|
|
247
272
|
// while the bar shows half-full misstates the owner's own setting.
|
|
248
273
|
if (state === 'capped') {
|
|
249
|
-
|
|
274
|
+
// The WEEKLY window's own effective cap — the number that actually benched it.
|
|
275
|
+
const eff = am._effectiveCap?.(account, 'wk');
|
|
276
|
+
const pct = Math.round((Number.isFinite(eff) ? eff : account.capUtilization || 0) * 100);
|
|
277
|
+
return yellow(`Cap ${pct}%`);
|
|
250
278
|
}
|
|
251
279
|
if (!state || state === 'unknown' || state === 'normal') return '';
|
|
252
280
|
// SAY IT ONCE (2026-08-27). An account Anthropic is rejecting outright already
|
|
@@ -353,7 +381,7 @@ export function applyProviderEnabledToConfig(config, name, enabled) {
|
|
|
353
381
|
return { changed: true, previous };
|
|
354
382
|
}
|
|
355
383
|
|
|
356
|
-
export const __tuiTest = { applyProviderEnabledToConfig, formatReset, quotaLabel, bar, emptyBar, strip, loadText, countdown, acctHeader, fitLine, providerLabel };
|
|
384
|
+
export const __tuiTest = { applyProviderEnabledToConfig, formatReset, quotaLabel, bar, emptyBar, strip, loadText, countdown, acctHeader, fitLine, providerLabel, weeklyPolicyText, capText, capEffectivePct };
|
|
357
385
|
|
|
358
386
|
function timestamp() {
|
|
359
387
|
return new Date().toLocaleTimeString('en-US', { hour12: false });
|
|
@@ -1166,9 +1194,14 @@ export class TUI {
|
|
|
1166
1194
|
} else if (this.selAction === 'cap') {
|
|
1167
1195
|
const targetIdx = this.selIdx;
|
|
1168
1196
|
const current = account.name;
|
|
1169
|
-
const
|
|
1197
|
+
const existingAcct = this.am.accounts[targetIdx];
|
|
1198
|
+
const existing = existingAcct?.capUtilization;
|
|
1199
|
+
const existingMode = existingAcct?.capMode;
|
|
1200
|
+
const nowLabel = existing
|
|
1201
|
+
? `${Math.round(existing * 100)}%${existingMode === 'dynamic' ? ' dynamic' : ' fixed'}`
|
|
1202
|
+
: 'off';
|
|
1170
1203
|
this.mode = 'input';
|
|
1171
|
-
this.inputPrompt = `Usage cap
|
|
1204
|
+
this.inputPrompt = `Usage cap for "${current}" — NN = dynamic floor (rises near reset), fNN = fixed, 0 = off, now ${nowLabel}`;
|
|
1172
1205
|
this.inputBuf = '';
|
|
1173
1206
|
this.inputSensitive = false;
|
|
1174
1207
|
this.inputCb = value => this._doSetCap(targetIdx, String(value || '').trim());
|
|
@@ -1359,35 +1392,50 @@ export class TUI {
|
|
|
1359
1392
|
if (!account) { this._addLog('Account no longer exists'); return; }
|
|
1360
1393
|
const v = String(raw || '').trim().toLowerCase();
|
|
1361
1394
|
const off = v === '' || v === '0' || v === 'off' || v === '100';
|
|
1395
|
+
// MODE PREFIX/SUFFIX: a bare number means the DYNAMIC cap (the default since
|
|
1396
|
+
// 2026-09-24 — the floor that lifts as the window nears reset); `f` marks the
|
|
1397
|
+
// fixed cap explicitly. `d` is accepted as the explicit dynamic spelling so the
|
|
1398
|
+
// two modes are symmetric to type.
|
|
1399
|
+
const explicitFixed = /^f/.test(v) || /f$/.test(v);
|
|
1362
1400
|
let pct = null;
|
|
1363
1401
|
if (!off) {
|
|
1364
|
-
|
|
1402
|
+
const digits = v.replace(/[fd]/g, '');
|
|
1403
|
+
pct = parseInt(digits, 10);
|
|
1365
1404
|
if (!Number.isInteger(pct) || pct < 1 || pct > 99) {
|
|
1366
|
-
this._addLog(`Usage cap must be 1-99 (
|
|
1405
|
+
this._addLog(`Usage cap must be 1-99, optionally f-prefixed for fixed (0 to remove) — got "${raw}"`);
|
|
1367
1406
|
return;
|
|
1368
1407
|
}
|
|
1369
1408
|
}
|
|
1370
1409
|
const cap = off ? null : pct / 100;
|
|
1410
|
+
const mode = cap == null ? null : (explicitFixed ? 'fixed' : 'dynamic');
|
|
1371
1411
|
|
|
1372
1412
|
const loc = this._configLocation(account);
|
|
1373
1413
|
if (loc) {
|
|
1374
1414
|
const prev = this.config[loc.array][loc.index].capUtilization ?? null;
|
|
1415
|
+
const prevMode = this.config[loc.array][loc.index].capMode ?? null;
|
|
1375
1416
|
if (cap == null) delete this.config[loc.array][loc.index].capUtilization;
|
|
1376
1417
|
else this.config[loc.array][loc.index].capUtilization = cap;
|
|
1418
|
+
if (mode == null) delete this.config[loc.array][loc.index].capMode;
|
|
1419
|
+
else this.config[loc.array][loc.index].capMode = mode;
|
|
1377
1420
|
try {
|
|
1378
1421
|
await this.saveConfig(this.config);
|
|
1379
1422
|
} catch (error) {
|
|
1380
1423
|
// rollback both config and (below) skip the live apply
|
|
1381
1424
|
if (prev == null) delete this.config[loc.array][loc.index].capUtilization;
|
|
1382
1425
|
else this.config[loc.array][loc.index].capUtilization = prev;
|
|
1426
|
+
if (prevMode == null) delete this.config[loc.array][loc.index].capMode;
|
|
1427
|
+
else this.config[loc.array][loc.index].capMode = prevMode;
|
|
1383
1428
|
throw error;
|
|
1384
1429
|
}
|
|
1385
1430
|
}
|
|
1386
1431
|
// No loc: a runtime provider — in-memory + state.json persistence (same as enabled).
|
|
1387
1432
|
account.capUtilization = cap;
|
|
1433
|
+
account.capMode = mode;
|
|
1388
1434
|
this._addLog(cap == null
|
|
1389
1435
|
? `Usage cap removed for "${account.name}" — fully utilized`
|
|
1390
|
-
:
|
|
1436
|
+
: mode === 'dynamic'
|
|
1437
|
+
? `Dynamic cap ${pct}% set for "${account.name}" — it keeps ${100 - pct}% free early in each window, then opens up as the window nears its reset so nothing is stranded`
|
|
1438
|
+
: `Fixed cap ${pct}% set for "${account.name}" — the proxy stops routing to it at ${pct}% of the 5h and weekly windows`);
|
|
1391
1439
|
}
|
|
1392
1440
|
|
|
1393
1441
|
async _doRename(idx, newName) {
|