maxpool 1.5.42 → 1.5.44
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/README.md +1 -1
- package/package.json +1 -1
- package/src/account-manager.js +6 -1
- package/src/config.js +3 -1
- package/src/index.js +22 -4
- package/src/server.js +9 -3
- package/src/tui.js +1 -1
- package/src/updater.js +8 -0
package/README.md
CHANGED
|
@@ -270,7 +270,7 @@ TEAMCLAUDE_CONFIG=./my-config.json maxpool server
|
|
|
270
270
|
"weeklySoftThreshold": 0.65,
|
|
271
271
|
"weeklyReserveThreshold": 0.85,
|
|
272
272
|
"weeklyCriticalThreshold": 0.95,
|
|
273
|
-
"weeklyExhaustedThreshold": 0.
|
|
273
|
+
"weeklyExhaustedThreshold": 0.999,
|
|
274
274
|
"weeklyBurnDebtWeight": 0.6
|
|
275
275
|
},
|
|
276
276
|
"retry": {
|
package/package.json
CHANGED
package/src/account-manager.js
CHANGED
|
@@ -91,7 +91,12 @@ const DEFAULT_SCHEDULER = {
|
|
|
91
91
|
weeklySoftThreshold: 0.65,
|
|
92
92
|
weeklyReserveThreshold: 0.85,
|
|
93
93
|
weeklyCriticalThreshold: 0.95,
|
|
94
|
-
|
|
94
|
+
// Use-it-or-lose-it (2026-07-24): bench only at 99.9%, not 98.5% — the weekly quota
|
|
95
|
+
// resets, so reserving the top ~1.5% just wastes it. The thin 0.1% floor is the ONE
|
|
96
|
+
// remaining guard: don't fire a request at an account whose own header already says
|
|
97
|
+
// it's essentially full (a near-guaranteed-waste hard-429). A real 429 sets util≈1.0
|
|
98
|
+
// and still benches here. Critical (0.95-0.999) stays last-resort-only (pass 2).
|
|
99
|
+
weeklyExhaustedThreshold: 0.999,
|
|
95
100
|
weeklyBurnDebtWeight: 0.6,
|
|
96
101
|
// Routing-cost tuning (lower cost = preferred). The goal is to AVOID
|
|
97
102
|
// short-term (rate/concurrency) throttling by spreading load across healthy
|
package/src/config.js
CHANGED
|
@@ -90,7 +90,9 @@ export function createDefaultConfig() {
|
|
|
90
90
|
weeklySoftThreshold: 0.65,
|
|
91
91
|
weeklyReserveThreshold: 0.85,
|
|
92
92
|
weeklyCriticalThreshold: 0.95,
|
|
93
|
-
|
|
93
|
+
// Use-it-or-lose-it: bench only at 99.9% (the weekly quota resets, so reserving
|
|
94
|
+
// the top ~1.5% wastes it). A real 429 sets util≈1.0 and still benches.
|
|
95
|
+
weeklyExhaustedThreshold: 0.999,
|
|
94
96
|
// Cross-PROVIDER fallback policy for 'cc all' (profile=all): whether a session
|
|
95
97
|
// may be served by a provider family other than its home (Claude → GLM/Kimi).
|
|
96
98
|
// 'never' — (DEFAULT) a Claude Code session stays on Anthropic; no
|
package/src/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import { loginOAuth, fetchProfile, refreshAccessToken, isTokenExpiringSoon, toke
|
|
|
12
12
|
import { TUI } from './tui.js';
|
|
13
13
|
import { RestartController } from './restart-controller.js';
|
|
14
14
|
import { resolveAccounts } from './account-config.js';
|
|
15
|
-
import { maybeCheckForUpdate, getCurrentVersion, markApplied } from './updater.js';
|
|
15
|
+
import { maybeCheckForUpdate, getCurrentVersion, markApplied, clearQuarantine } from './updater.js';
|
|
16
16
|
import {
|
|
17
17
|
runReloadBaton,
|
|
18
18
|
RELOAD_SWAPPED, RELOAD_ROLLED_BACK,
|
|
@@ -23,10 +23,20 @@ import {
|
|
|
23
23
|
const args = process.argv.slice(2);
|
|
24
24
|
const command = args[0];
|
|
25
25
|
const SERVER_RESTART_EXIT_CODE = 75;
|
|
26
|
+
// Reload readiness handshake budget. On a loaded machine (Bitdefender, many node procs,
|
|
27
|
+
// a local LLM) a fresh worker can need well over 10s to boot + signal ready; too tight →
|
|
28
|
+
// the seamless reload rolls back and the update/restart silently never lands. Generous +
|
|
29
|
+
// env-tunable; covers BOTH the ready and the takeover waits in runReloadBaton.
|
|
30
|
+
const RELOAD_READY_MS = Math.max(10_000, Number(process.env.MAXPOOL_RELOAD_READY_MS) || 30_000);
|
|
26
31
|
// If a seamless reload doesn't release us (→ take over) within this window, the new
|
|
27
|
-
// worker rolled back — self-heal admission so we don't 503 forever.
|
|
28
|
-
// readiness
|
|
29
|
-
|
|
32
|
+
// worker rolled back — self-heal admission so we don't 503 forever. Must sit ABOVE the
|
|
33
|
+
// baton readiness budget + margin so it never fires WHILE the baton is still handshaking.
|
|
34
|
+
// Default (RELOAD_READY_MS + 20s = 50s) sits comfortably above the readiness budget, so
|
|
35
|
+
// the self-heal never fires mid-handshake. An explicit MAXPOOL_RELOAD_SELFHEAL_MS override
|
|
36
|
+
// is honored down to a 15s floor (used by the reload integration test) — a sub-ready
|
|
37
|
+
// override only risks a spurious "rolled back" log + brief admission flap (no corruption:
|
|
38
|
+
// the new worker holds no lease until takeover), never a false double-writer.
|
|
39
|
+
const RELOAD_ROLLBACK_SELFHEAL_MS = Math.max(15_000, Number(process.env.MAXPOOL_RELOAD_SELFHEAL_MS) || RELOAD_READY_MS + 20_000);
|
|
30
40
|
// Seamless-reload drain cap. On a seamless reload the NEW worker already serves
|
|
31
41
|
// ALL new traffic while the OLD worker only finishes its own in-flight requests,
|
|
32
42
|
// so a long old-worker drain has zero request-facing cost — let a long streaming
|
|
@@ -326,6 +336,10 @@ async function supervisorCommand() {
|
|
|
326
336
|
// listener to cover the cutover gap, then hand THAT live handle to the
|
|
327
337
|
// new worker. The new worker's MSG_PRIMARY then closes it again.
|
|
328
338
|
prepareHandle: async () => { await relistenMaster(); return masterServer; },
|
|
339
|
+
// Generous, env-tunable readiness/takeover budgets — the 10s default rolled back
|
|
340
|
+
// on this user's loaded Mac (the update never landed). See RELOAD_READY_MS.
|
|
341
|
+
readyTimeoutMs: RELOAD_READY_MS,
|
|
342
|
+
takeoverTimeoutMs: RELOAD_READY_MS,
|
|
329
343
|
log: msg => console.log(`[Maxpool] ${msg}`),
|
|
330
344
|
});
|
|
331
345
|
|
|
@@ -1083,6 +1097,10 @@ async function serverWorkerCommand() {
|
|
|
1083
1097
|
if (updateInFlight) { notifyUpdate('Update check already running'); return; }
|
|
1084
1098
|
if (!hasLease) { notifyUpdate('Updates run on the primary worker only'); return; }
|
|
1085
1099
|
if (config?.updateCheck === false) { notifyUpdate('Update checks are disabled in config'); return; }
|
|
1100
|
+
// An EXPLICIT manual apply always re-attempts — clear any quarantine a prior auto-reload
|
|
1101
|
+
// left (e.g. a rollback from a too-tight readiness timeout), so 'u'→'c' can't dead-end on
|
|
1102
|
+
// "already attempted — will retry only a newer release".
|
|
1103
|
+
clearQuarantine();
|
|
1086
1104
|
notifyUpdate('Checking for updates…');
|
|
1087
1105
|
const r = await runUpdateCheck({ announce: true, apply: applyNow, forceInstall: true });
|
|
1088
1106
|
if (r && !r.hasUpdate) notifyUpdate('Already on the latest version');
|
package/src/server.js
CHANGED
|
@@ -1348,16 +1348,22 @@ function classifyRateLimit(account, headers, body, opts = {}) {
|
|
|
1348
1348
|
// genuine account cap with stripped headers must bench the whole account, not one
|
|
1349
1349
|
// model). Neither bucket may be at/above the exhaustion floor.
|
|
1350
1350
|
const haveUnifiedEvidence = Number.isFinite(weekly) || Number.isFinite(fiveHour);
|
|
1351
|
+
// Exhaustion floor for 429-SCOPE classification — kept in sync with the scheduler's
|
|
1352
|
+
// weeklyExhaustedThreshold (0.999, use-it-or-lose-it). A 429 whose unified buckets are
|
|
1353
|
+
// still below the floor isn't weekly-exhaustion, so a model-family + long-retry-after
|
|
1354
|
+
// 429 benches only that model, not the whole (still-usable) account — matching routing,
|
|
1355
|
+
// which now treats 0.95-0.999 as usable (critical), not benched.
|
|
1356
|
+
const EXHAUSTION_FLOOR = 0.999;
|
|
1351
1357
|
const unifiedNotExhausted =
|
|
1352
|
-
(!Number.isFinite(weekly) || weekly <
|
|
1358
|
+
(!Number.isFinite(weekly) || weekly < EXHAUSTION_FLOOR) && (!Number.isFinite(fiveHour) || fiveHour < EXHAUSTION_FLOOR);
|
|
1353
1359
|
const modelScope =
|
|
1354
1360
|
(fam && haveUnifiedEvidence && unifiedNotExhausted && Number.isFinite(retryAfter) && retryAfter >= 30 * 60)
|
|
1355
1361
|
? fam : null;
|
|
1356
1362
|
|
|
1357
1363
|
const quotaHeaderExhaustion =
|
|
1358
1364
|
unifiedStatus === 'rejected'
|
|
1359
|
-
|| (Number.isFinite(fiveHour) && fiveHour >=
|
|
1360
|
-
|| (Number.isFinite(weekly) && weekly >=
|
|
1365
|
+
|| (Number.isFinite(fiveHour) && fiveHour >= EXHAUSTION_FLOOR)
|
|
1366
|
+
|| (Number.isFinite(weekly) && weekly >= EXHAUSTION_FLOOR)
|
|
1361
1367
|
|| (headers['anthropic-ratelimit-tokens-remaining'] != null && tokensRemaining <= 0)
|
|
1362
1368
|
|| (headers['anthropic-ratelimit-requests-remaining'] != null && requestsRemaining <= 0);
|
|
1363
1369
|
if (quotaHeaderExhaustion) return { scope: 'account', fingerprint: null, modelScope };
|
package/src/tui.js
CHANGED
|
@@ -1294,7 +1294,7 @@ export class TUI {
|
|
|
1294
1294
|
// still usable, "Fable maxed" (red) ONLY at genuine exhaustion. This is the
|
|
1295
1295
|
// SAME predicate the router benches on (_scopedExhausted), so "maxed" renders
|
|
1296
1296
|
// iff the model is actually benched — 90%/critical is no longer mislabelled.
|
|
1297
|
-
const exhaustedFloor = this.am.scheduler?.weeklyExhaustedThreshold ?? 0.
|
|
1297
|
+
const exhaustedFloor = this.am.scheduler?.weeklyExhaustedThreshold ?? 0.999;
|
|
1298
1298
|
const reserveFloor = this.am.scheduler?.weeklyReserveThreshold ?? 0.85;
|
|
1299
1299
|
const scopedTags = [];
|
|
1300
1300
|
for (const [fam, e] of Object.entries(q.scopedWeekly || {})) {
|
package/src/updater.js
CHANGED
|
@@ -95,6 +95,14 @@ export function markApplied(version) {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
/** Clear the applied-version quarantine floor. The AUTO path quarantines a version it
|
|
99
|
+
* ATTEMPTED (markApplied, before the reload) so a genuinely boot-broken release can't
|
|
100
|
+
* reload-loop — but a reload that rolled back for a NON-version reason (e.g. a slow
|
|
101
|
+
* readiness handshake on a loaded machine) then wrongly strands a perfectly-good version
|
|
102
|
+
* ("already attempted — will retry only a newer release"). An EXPLICIT manual apply calls
|
|
103
|
+
* this first so the user can always re-attempt the current latest. */
|
|
104
|
+
export function clearQuarantine() { _lastAttemptedTarget = null; }
|
|
105
|
+
|
|
98
106
|
/**
|
|
99
107
|
* Check for an update; with `config.autoUpdate` also self-install; with
|
|
100
108
|
* `config.autoApply` SIGNAL that the caller should seamlessly reload to APPLY it
|