@goodandready/dsh-key-rotation 0.7.23 → 0.7.25
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/lib/index.js +45 -5
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -477,6 +477,7 @@ export function apply(ctx, config = {}) {
|
|
|
477
477
|
const backupKeep = cfg.backupKeep ?? 7;
|
|
478
478
|
const rotationScheduleDays = cfg.rotationScheduleDays ?? 0;
|
|
479
479
|
const rateLimitThreshold = cfg.rateLimitThreshold ?? 0.1;
|
|
480
|
+
const incidentThreshold = cfg.incidentThreshold ?? 5;
|
|
480
481
|
|
|
481
482
|
// ref -> pool (every key env of every configured provider)
|
|
482
483
|
const poolByRef = new Map();
|
|
@@ -491,9 +492,24 @@ export function apply(ctx, config = {}) {
|
|
|
491
492
|
let st = poolState.get(base);
|
|
492
493
|
if (!st) {
|
|
493
494
|
st = {
|
|
494
|
-
failedUntil: new Map(),
|
|
495
|
-
|
|
496
|
-
|
|
495
|
+
failedUntil: new Map(),
|
|
496
|
+
failCounts: new Map(),
|
|
497
|
+
authFailCounts: new Map(),
|
|
498
|
+
brokenUntil: new Map(),
|
|
499
|
+
costPerKey: new Map(),
|
|
500
|
+
lastUsedAt: new Map(),
|
|
501
|
+
usageCounts: new Map(),
|
|
502
|
+
byModel: new Map(),
|
|
503
|
+
usageDays: new Map(),
|
|
504
|
+
quotaWindows: new Map(),
|
|
505
|
+
pointer: 0,
|
|
506
|
+
lastUsed: undefined,
|
|
507
|
+
switches: 0,
|
|
508
|
+
lastReason: undefined,
|
|
509
|
+
lastSwitchAt: undefined,
|
|
510
|
+
lastExhaustionAt: undefined,
|
|
511
|
+
exhaustionCount: 0,
|
|
512
|
+
events: [],
|
|
497
513
|
};
|
|
498
514
|
poolState.set(base, st);
|
|
499
515
|
}
|
|
@@ -704,7 +720,28 @@ export function apply(ctx, config = {}) {
|
|
|
704
720
|
const switchable = !yielded && kind === 'error' &&
|
|
705
721
|
(effectiveSwitchCodes.has(code) || SWITCHABLE_MESSAGE_PATTERN.test(message));
|
|
706
722
|
if (switchable) {
|
|
707
|
-
if (pool.state.lastUsed) {
|
|
723
|
+
if (pool.state.lastUsed) {
|
|
724
|
+
const _retry = parseRetryAfter(message);
|
|
725
|
+
const _base = pool.cooldownMs ?? cooldownMs;
|
|
726
|
+
const _max = pool.maxCooldownMs ?? maxCooldownMs;
|
|
727
|
+
const _effBase = _retry !== undefined ? Math.max(_base, Math.min(_retry, _max ?? _base * 8)) : _base;
|
|
728
|
+
const _b = recordFailure(pool, pool.state.lastUsed, Date.now(), _effBase, _max);
|
|
729
|
+
pushEvent(pool, pool.state.lastUsed, code ?? 'UNKNOWN', _b);
|
|
730
|
+
// authFailCounts/brokenUntil: lazy-init if state was created by an older plugin version
|
|
731
|
+
if (!pool.state.authFailCounts) pool.state.authFailCounts = new Map();
|
|
732
|
+
if (!pool.state.brokenUntil) pool.state.brokenUntil = new Map();
|
|
733
|
+
const _code2 = String(code ?? '');
|
|
734
|
+
if (_code2 === 'AUTH' || /auth/i.test(message)) {
|
|
735
|
+
const _c2 = (pool.state.authFailCounts.get(pool.state.lastUsed) ?? 0) + 1;
|
|
736
|
+
pool.state.authFailCounts.set(pool.state.lastUsed, _c2);
|
|
737
|
+
if (_c2 >= 3) {
|
|
738
|
+
pool.state.brokenUntil.set(pool.state.lastUsed, Date.now() + 86400000*30);
|
|
739
|
+
pool.state.failedUntil.set(pool.state.lastUsed, Date.now() + 86400000*30);
|
|
740
|
+
}
|
|
741
|
+
} else {
|
|
742
|
+
pool.state.authFailCounts.delete(pool.state.lastUsed);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
708
745
|
pool.state.switches = (pool.state.switches ?? 0) + 1;
|
|
709
746
|
pool.state.lastReason = String(code ?? 'UNKNOWN');
|
|
710
747
|
pool.state.lastSwitchAt = Date.now();
|
|
@@ -716,7 +753,10 @@ export function apply(ctx, config = {}) {
|
|
|
716
753
|
// cost tracking if provider returns usage.cost
|
|
717
754
|
if (chunk.usage?.cost != null && pool.state.lastUsed) {
|
|
718
755
|
const c = Number(chunk.usage.cost);
|
|
719
|
-
if (!isNaN(c))
|
|
756
|
+
if (!isNaN(c)) {
|
|
757
|
+
if (!pool.state.costPerKey) pool.state.costPerKey = new Map();
|
|
758
|
+
pool.state.costPerKey.set(pool.state.lastUsed, (pool.state.costPerKey.get(pool.state.lastUsed) ?? 0) + c);
|
|
759
|
+
}
|
|
720
760
|
}
|
|
721
761
|
// Usage by day (#119)
|
|
722
762
|
if (pool.state.lastUsed) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-key-rotation",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.25",
|
|
4
4
|
"description": "Per-provider API key rotation for DeepSeek Harness: a key pool per provider, auto-created clone routes, and switching to the next key on quota/rate-limit errors. Includes a Settings section (Key Rotation) to edit the key pools, cooldown and switch codes.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|