maxpool 1.8.9 → 1.10.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 +48 -8
- package/src/capacity-ledger.js +86 -0
- package/src/tui.js +20 -1
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { refreshAccessToken, isTokenExpiringSoon, modelFamily, tokenFingerprint } from './oauth.js';
|
|
2
2
|
import { CapacityLedger } from './capacity-ledger.js';
|
|
3
3
|
|
|
4
|
+
// Nominal window length per kind — mirrors capacity-ledger's WINDOW_MS (kept here as a
|
|
5
|
+
// local table so account-manager does not import a private constant).
|
|
6
|
+
const WINDOW_MS_BY_KIND = { ses: 5 * 3600_000, wk: 7 * 86400_000 };
|
|
7
|
+
|
|
4
8
|
// A capacity window boundary must move by at least this much to count as a real
|
|
5
9
|
// window ADVANCE rather than reset-stamp jitter (see noteCapacityWindowAdvance).
|
|
6
10
|
const WINDOW_ADVANCE_EPSILON_MS = 60_000;
|
|
@@ -2699,12 +2703,14 @@ export class AccountManager {
|
|
|
2699
2703
|
if (usage.sevenDay.utilization != null) q.unified7d = clamp01(usage.sevenDay.utilization);
|
|
2700
2704
|
if (usage.sevenDay.resetAt != null) q.unified7dReset = usage.sevenDay.resetAt;
|
|
2701
2705
|
}
|
|
2702
|
-
// Stamp-advance close (same guard as the provider path): a FRESHER stamp means
|
|
2703
|
-
// the old window rolled over — close its cycle at the old boundary.
|
|
2704
|
-
// noteCapacityWindowAdvance already no-ops on a missing/unchanged/older stamp, so
|
|
2705
|
-
// there is nothing to gate here — pass the raw (possibly undefined) value through.
|
|
2706
2706
|
this.noteCapacityWindowAdvance(account.name, 'ses', prevSesReset, usage.fiveHour?.resetAt);
|
|
2707
2707
|
this.noteCapacityWindowAdvance(account.name, 'wk', prevWkReset, usage.sevenDay?.resetAt);
|
|
2708
|
+
// Utilization readings feed the capacity ESTIMATE. The probe path passes per-window
|
|
2709
|
+
// marks so the DELTA method can difference consecutive readings.
|
|
2710
|
+
this.capacity.noteUtilizationObserved(Date.now(), [
|
|
2711
|
+
{ name: account.name, window: 'ses', utilization: usage.fiveHour?.utilization },
|
|
2712
|
+
{ name: account.name, window: 'wk', utilization: usage.sevenDay?.utilization },
|
|
2713
|
+
]);
|
|
2708
2714
|
// Only a SUCCESSFUL probe carrying the flag speaks to this. A header-driven update
|
|
2709
2715
|
// can't see the limits[] array, and a FAILED read knows nothing about the account's
|
|
2710
2716
|
// caps — either one claiming "uncapped" would mislabel a capped account as having no
|
|
@@ -2847,12 +2853,12 @@ export class AccountManager {
|
|
|
2847
2853
|
q.weeklyAbsent = true;
|
|
2848
2854
|
}
|
|
2849
2855
|
q.lastProbeOkAt = Date.now();
|
|
2850
|
-
// Stamp-advance close: a FRESHER reset stamp means the old window rolled over —
|
|
2851
|
-
// the tokens accrued since the last close belong to the cycle that just ended.
|
|
2852
|
-
// noteCapacityWindowAdvance already no-ops on a missing/unchanged/older stamp
|
|
2853
|
-
// (same-window re-report, clock-skew backward re-report) — pass values through.
|
|
2854
2856
|
this.noteCapacityWindowAdvance(account.name, 'ses', prevSesReset, usage.ses?.resetAt);
|
|
2855
2857
|
this.noteCapacityWindowAdvance(account.name, 'wk', prevWkReset, usage.wk?.resetAt);
|
|
2858
|
+
this.capacity.noteUtilizationObserved(Date.now(), [
|
|
2859
|
+
{ name: account.name, window: 'ses', utilization: usage.ses?.utilization },
|
|
2860
|
+
{ name: account.name, window: 'wk', utilization: usage.wk?.utilization },
|
|
2861
|
+
]);
|
|
2856
2862
|
}
|
|
2857
2863
|
|
|
2858
2864
|
/**
|
|
@@ -2879,6 +2885,17 @@ export class AccountManager {
|
|
|
2879
2885
|
const account = this.accounts[accountIndex];
|
|
2880
2886
|
if (!account) return;
|
|
2881
2887
|
|
|
2888
|
+
// Utilization from RESPONSE HEADERS (every request) feeds the capacity estimate's
|
|
2889
|
+
// delta marks too — header-driven moves arrive far more often than probe cycles.
|
|
2890
|
+
const hdrU5 = parseFloat(headers['anthropic-ratelimit-unified-5h-utilization']);
|
|
2891
|
+
const hdrU7 = parseFloat(headers['anthropic-ratelimit-unified-7d-utilization']);
|
|
2892
|
+
if (!isNaN(hdrU5) || !isNaN(hdrU7)) {
|
|
2893
|
+
this.capacity.noteUtilizationObserved(Date.now(), [
|
|
2894
|
+
{ name: account.name, window: 'ses', utilization: isNaN(hdrU5) ? undefined : clamp01(hdrU5) },
|
|
2895
|
+
{ name: account.name, window: 'wk', utilization: isNaN(hdrU7) ? undefined : clamp01(hdrU7) },
|
|
2896
|
+
]);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2882
2899
|
// Unified rate limits (Claude Max)
|
|
2883
2900
|
const u5h = parseFloat(headers['anthropic-ratelimit-unified-5h-utilization']);
|
|
2884
2901
|
const u7d = parseFloat(headers['anthropic-ratelimit-unified-7d-utilization']);
|
|
@@ -3018,6 +3035,19 @@ export class AccountManager {
|
|
|
3018
3035
|
|
|
3019
3036
|
/** Accrue ONE request's tokens into the capacity ledger (per-request values; the
|
|
3020
3037
|
* server seam has already applied max-semantics for streamed output). */
|
|
3038
|
+
/** The capacity ESTIMATE for an account+window, from live utilization. Falls back
|
|
3039
|
+
* to null when the vendor reports no utilization for that window (the no-weekly GLM
|
|
3040
|
+
* plans), the account has not accrued, or it is already throttled. */
|
|
3041
|
+
capacityEstimate(accountIndex, window) {
|
|
3042
|
+
const a = this.accounts[accountIndex];
|
|
3043
|
+
if (!a) return null;
|
|
3044
|
+
const q = a.quota || {};
|
|
3045
|
+
const util = window === 'wk'
|
|
3046
|
+
? (a.type === 'provider' ? q.providerWk : q.unified7d)
|
|
3047
|
+
: (a.type === 'provider' ? q.providerSes : q.unified5h);
|
|
3048
|
+
return this.capacity.estimateFromUtilization(a.name, window, util) || null;
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3021
3051
|
accrueCapacity(accountIndex, { input = 0, output = 0 } = {}) {
|
|
3022
3052
|
const account = this.accounts[accountIndex];
|
|
3023
3053
|
if (!account) return;
|
|
@@ -3034,6 +3064,16 @@ export class AccountManager {
|
|
|
3034
3064
|
const pairs = a.type === 'provider'
|
|
3035
3065
|
? [['ses', 'providerSesReset'], ['wk', 'providerWkReset']]
|
|
3036
3066
|
: [['ses', 'unified5hReset'], ['wk', 'unified7dReset']];
|
|
3067
|
+
// Keep the open cycle's windowStartedAt fresh: a NEW reset stamp whose window
|
|
3068
|
+
// start precedes the cycle's open means we joined mid-window (the absolute
|
|
3069
|
+
// estimate is then only a lower bound — see the delta method).
|
|
3070
|
+
for (const [win, stampKey] of pairs) {
|
|
3071
|
+
const resetAt = q[stampKey];
|
|
3072
|
+
const open = this.capacity.openCycle(a.name, win);
|
|
3073
|
+
if (resetAt && open && open.startedAt > resetAt - WINDOW_MS_BY_KIND[win]) {
|
|
3074
|
+
open.windowStartedAt = resetAt - WINDOW_MS_BY_KIND[win];
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3037
3077
|
for (const [win, stampKey] of pairs) {
|
|
3038
3078
|
const resetAt = q[stampKey];
|
|
3039
3079
|
// Close ONLY. This path deliberately does NOT null the stamp: the rollover
|
package/src/capacity-ledger.js
CHANGED
|
@@ -53,6 +53,7 @@ export class CapacityLedger {
|
|
|
53
53
|
// True only for a ledger restored with no usable history — see fromSerialized.
|
|
54
54
|
// Cleared per account+window once that window's first boundary is behind us.
|
|
55
55
|
this._joinedMidWindow = false;
|
|
56
|
+
this._utilObservedAt = 0; // last utilization-reading arrival (see estimateFromUtilization)
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
/** Restore from a serialized payload (state.json). Tolerant: unknown schemaVersion →
|
|
@@ -231,6 +232,91 @@ export class CapacityLedger {
|
|
|
231
232
|
return rec[window].closed[rec[window].closed.length - 1];
|
|
232
233
|
}
|
|
233
234
|
|
|
235
|
+
/**
|
|
236
|
+
* ESTIMATED window capacity from live utilization: tokens observed in the OPEN
|
|
237
|
+
* cycle ÷ the vendor's own fullness fraction (0..1). A window at 96% holding 812k
|
|
238
|
+
* tokens implies a ~846k tank — no completed cycle needed. This is the same math the
|
|
239
|
+
* user does in their head ("if 10% took A tokens, 100% is A×10") and it makes the
|
|
240
|
+
* page useful from minute one, while completed cycles remain the precise column.
|
|
241
|
+
*
|
|
242
|
+
* Returns { tokens, utilization, fresh } or null when no estimate exists.
|
|
243
|
+
* null cases — utilization 0/unknown (0÷0), no accrual yet, or util ≥ 1 (the
|
|
244
|
+
* account is throttled; the fraction says nothing about the tank size).
|
|
245
|
+
* `fresh` = the utilization reading and the accrual are from the same window
|
|
246
|
+
* (utilization refreshes on probe/header; the open cycle closes at the boundary —
|
|
247
|
+
* a stale util from the PREVIOUS window silently understates the estimate).
|
|
248
|
+
*/
|
|
249
|
+
estimateFromUtilization(name, window, utilization) {
|
|
250
|
+
if (!(utilization > 0) || !(utilization < 1)) return null;
|
|
251
|
+
const open = this.openCycle(name, window);
|
|
252
|
+
if (!open || !(open.tokensSoFar > 0)) return null;
|
|
253
|
+
|
|
254
|
+
// DELTA METHOD (preferred). The absolute form (tokens ÷ utilization) silently
|
|
255
|
+
// assumes we watched the WHOLE window — false whenever the ledger joined late (a
|
|
256
|
+
// restart, a migration, a new account). Measured 2026-08-24: all four weekly
|
|
257
|
+
// estimates joined 8.9-95h into their window, so every one understated the tank,
|
|
258
|
+
// max@dubner.io by ~2x.
|
|
259
|
+
//
|
|
260
|
+
// Between two readings the tank is invariant, so pre-join usage cancels:
|
|
261
|
+
// tank = (tokens observed between them) / (u2 - u1)
|
|
262
|
+
// No assumption about what happened before we started counting. Requires a rising
|
|
263
|
+
// utilization AND tokens accrued across the same span; falls back to absolute when
|
|
264
|
+
// we genuinely did watch from the start.
|
|
265
|
+
const mark = this._utilMarks?.get(`${name}:${window}`);
|
|
266
|
+
if (mark && utilization > mark.utilization) {
|
|
267
|
+
const deltaTokens = open.tokensSoFar - mark.tokensSoFar;
|
|
268
|
+
const deltaUtil = utilization - mark.utilization;
|
|
269
|
+
// A meaningful denominator only: a 0.5pp move on a coarse-rounded percentage
|
|
270
|
+
// (vendors report whole percents) turns rounding noise into a 200x multiplier.
|
|
271
|
+
if (deltaTokens > 0 && deltaUtil >= 0.02) {
|
|
272
|
+
return {
|
|
273
|
+
tokens: Math.round(deltaTokens / deltaUtil),
|
|
274
|
+
utilization, fresh: true, method: 'delta',
|
|
275
|
+
basis: { deltaTokens, deltaUtil },
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
// Fresh = we can prove the reading and the accrual describe the SAME window: the
|
|
280
|
+
// reading arrived after the open cycle began. A reading that predates the cycle (or
|
|
281
|
+
// was never noted at all) describes the previous window — mark it and let the UI
|
|
282
|
+
// caveat it, never silently trust it.
|
|
283
|
+
const fresh = this._utilObservedAt > 0 && open.startedAt != null && this._utilObservedAt >= open.startedAt;
|
|
284
|
+
// ABSOLUTE fallback. Only a LOWER BOUND unless we observed the window from its very
|
|
285
|
+
// start — flagged so the UI can say "≥" rather than present a floor as the answer.
|
|
286
|
+
const wholeWindow = open.startedAt != null && open.windowStartedAt != null
|
|
287
|
+
&& open.startedAt <= open.windowStartedAt + 60_000;
|
|
288
|
+
return {
|
|
289
|
+
tokens: Math.round(open.tokensSoFar / utilization),
|
|
290
|
+
utilization, fresh, method: 'absolute', lowerBound: !wholeWindow,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/** Record when a utilization reading arrived, so estimateFromUtilization can tell
|
|
295
|
+
* same-window freshness from a stale previous-window reading. */
|
|
296
|
+
noteUtilizationObserved(at = this._now(), marks = null) {
|
|
297
|
+
this._utilObservedAt = at;
|
|
298
|
+
// Snapshot (utilization, tokensSoFar) per account+window so the NEXT reading can be
|
|
299
|
+
// differenced against it. `marks` is [{name, window, utilization}] from the caller,
|
|
300
|
+
// which owns the per-account-type quota fields.
|
|
301
|
+
if (!marks) return;
|
|
302
|
+
this._utilMarks = this._utilMarks || new Map();
|
|
303
|
+
for (const m of marks) {
|
|
304
|
+
if (!(m.utilization >= 0) || !(m.utilization < 1)) continue;
|
|
305
|
+
const open = this.openCycle(m.name, m.window);
|
|
306
|
+
if (!open) continue;
|
|
307
|
+
const key = `${m.name}:${m.window}`;
|
|
308
|
+
const prev = this._utilMarks.get(key);
|
|
309
|
+
// Keep the OLDEST usable mark within this cycle: a wider span means a larger
|
|
310
|
+
// denominator and less rounding sensitivity. Reset when the cycle rolls.
|
|
311
|
+
if (!prev || prev.cycleStartedAt !== open.startedAt || m.utilization < prev.utilization) {
|
|
312
|
+
this._utilMarks.set(key, {
|
|
313
|
+
utilization: m.utilization, tokensSoFar: open.tokensSoFar,
|
|
314
|
+
cycleStartedAt: open.startedAt, at,
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
234
320
|
// ── Queries ─────────────────────────────────────────────────────────────────
|
|
235
321
|
|
|
236
322
|
/** The columns the TUI renders: last, prev, prev1, avg3, avg10, allTime — over
|
package/src/tui.js
CHANGED
|
@@ -2056,7 +2056,25 @@ export class TUI {
|
|
|
2056
2056
|
}
|
|
2057
2057
|
const st = ledger.windowStats(a.name, win);
|
|
2058
2058
|
if (!st) {
|
|
2059
|
-
|
|
2059
|
+
// No completed cycle yet — but the vendor's own fullness reading still yields
|
|
2060
|
+
// an ESTIMATE (tokens seen ÷ utilization): useful from minute one, honest about
|
|
2061
|
+
// being an estimate. `~` marks it; a measured column replaces it after the first
|
|
2062
|
+
// full window. A stale-util caveat only when we cannot prove same-window.
|
|
2063
|
+
const est = this.am.capacityEstimate?.(i, win);
|
|
2064
|
+
if (est) {
|
|
2065
|
+
anyData = true;
|
|
2066
|
+
const caveat = est.fresh ? '' : ' (utilization reading may be from the previous window)';
|
|
2067
|
+
// ≥ = absolute method on a window we joined late: the true tank is at least
|
|
2068
|
+
// this. No ≥ when the delta method fired — it is join-independent, or the
|
|
2069
|
+
// window was observed from its start.
|
|
2070
|
+
const op = est.lowerBound ? '≥' : '~';
|
|
2071
|
+
const via = est.method === 'delta'
|
|
2072
|
+
? `Δ ${(est.utilization * 100).toFixed(0)}% full` : `${(est.utilization * 100).toFixed(0)}% full`;
|
|
2073
|
+
out.push(' ' + name + ' ' + prov + ' ' + cyan(op + formatTokens(est.tokens).padStart(CW - 1))
|
|
2074
|
+
+ dim(` est from ${via}${caveat} — measured after this window completes`));
|
|
2075
|
+
} else {
|
|
2076
|
+
out.push(' ' + name + ' ' + prov + ' ' + dim('no completed cycle yet'));
|
|
2077
|
+
}
|
|
2060
2078
|
continue;
|
|
2061
2079
|
}
|
|
2062
2080
|
anyData = true;
|
|
@@ -2075,6 +2093,7 @@ export class TUI {
|
|
|
2075
2093
|
: ' A session figure appears after an account\'s 5h window resets once.'));
|
|
2076
2094
|
}
|
|
2077
2095
|
out.push(' ' + dim('A cycle counts only if maxpool ran for all of it and the account stayed enabled.'));
|
|
2096
|
+
out.push(' ' + dim('~ = estimated from utilization; ≥ = at least this (window joined late); Δ = exact-by-difference; measured replaces both.'));
|
|
2078
2097
|
return out;
|
|
2079
2098
|
}
|
|
2080
2099
|
|