@askalf/dario 6.15.0 → 6.17.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/README.md +19 -21
- package/dist/analytics-ui.html +835 -0
- package/dist/analytics.d.ts +26 -1
- package/dist/analytics.js +51 -3
- package/dist/body-identity.d.ts +25 -0
- package/dist/body-identity.js +68 -0
- package/dist/cc-template.js +3 -14
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +44 -7
- package/dist/codex-backend.js +12 -4
- package/dist/config-file.d.ts +1 -1
- package/dist/config-file.js +2 -2
- package/dist/credits-rung.d.ts +72 -0
- package/dist/credits-rung.js +133 -0
- package/dist/doctor-core.d.ts +2 -0
- package/dist/doctor-core.js +47 -10
- package/dist/donuts.d.ts +18 -17
- package/dist/donuts.js +46 -54
- package/dist/ledger.d.ts +20 -0
- package/dist/ledger.js +40 -1
- package/dist/metrics.js +2 -1
- package/dist/midstream.js +6 -2
- package/dist/openai-backend.js +8 -3
- package/dist/pool.d.ts +18 -1
- package/dist/pool.js +31 -10
- package/dist/proxy.d.ts +14 -1
- package/dist/proxy.js +351 -105
- package/dist/relaunch-signals.d.ts +35 -0
- package/dist/relaunch-signals.js +41 -0
- package/dist/stream-drain.d.ts +4 -3
- package/dist/stream-drain.js +4 -3
- package/dist/tui/tabs/config.js +2 -2
- package/dist/upstream-deadline.d.ts +25 -0
- package/dist/upstream-deadline.js +76 -0
- package/docs/analytics.md +10 -8
- package/docs/api-equivalent-spend.md +9 -2
- package/docs/commands.md +1 -1
- package/docs/configuration.md +1 -0
- package/docs/faq.md +5 -0
- package/docs/multi-account-pool.md +3 -1
- package/docs/pool-keys-and-analytics.md +2 -0
- package/docs/two-plans.md +2 -0
- package/docs/who-its-for.md +2 -2
- package/package.json +8 -4
package/dist/pool.d.ts
CHANGED
|
@@ -396,8 +396,24 @@ export interface PoolStatus {
|
|
|
396
396
|
* order is the operator's knob — name seats `1-main` / `2-overflow` to
|
|
397
397
|
* pick the fill order. Sticky bindings behave identically in both modes;
|
|
398
398
|
* strategy only decides where UNBOUND (new) conversations land.
|
|
399
|
+
*
|
|
400
|
+
* `headroom-by-plan` (opt-in): headroom, with each seat's headroom counted
|
|
401
|
+
* by its plan size (see planWeight). Never the default: utilization is a
|
|
402
|
+
* ratio of an account-wide budget that also carries the owner's own use, no
|
|
403
|
+
* absolute capacity reaches the wire, and the window that binds (5h or 7d)
|
|
404
|
+
* matters more than the plan name. A smaller plan regularly has more real
|
|
405
|
+
* room than a bigger one. It is for operators who know their seats are
|
|
406
|
+
* otherwise idle and want the bigger plan to carry proportionally more.
|
|
407
|
+
*/
|
|
408
|
+
export type PoolStrategy = 'headroom' | 'headroom-by-plan' | 'fill-first' | 'expiring-first';
|
|
409
|
+
/**
|
|
410
|
+
* How much a seat's headroom counts under `headroom-by-plan`: N for a Max Nx
|
|
411
|
+
* plan (from the profile's `rate_limit_tier`, e.g. `default_claude_max_20x`),
|
|
412
|
+
* 1 for Pro, and 1 for anything unrecognised or unknown, so a seat whose plan
|
|
413
|
+
* dario cannot read is never favoured. Only the whole `default_claude_max_Nx`
|
|
414
|
+
* tier name counts; a tier that merely contains `max_Nx` is unrecognised.
|
|
399
415
|
*/
|
|
400
|
-
export
|
|
416
|
+
export declare function planWeight(rateLimitTier: string | undefined): number;
|
|
401
417
|
/**
|
|
402
418
|
* `expiring-first` — fill-first, ordered by WHEN each seat's capacity expires
|
|
403
419
|
* instead of by alias: new conversations fill the seat whose 7-day window
|
|
@@ -602,6 +618,7 @@ export declare function headroomFloorProblem(value: string | number | null | und
|
|
|
602
618
|
*/
|
|
603
619
|
export declare function headroomFloorFlagProblem(value: string): string;
|
|
604
620
|
export declare function resolvePoolHeadroomFloor(explicit?: string | number | null, env?: NodeJS.ProcessEnv): number;
|
|
621
|
+
export declare function pickMaxHeadroom(accounts: PoolAccount[], family?: string | null, byPlan?: boolean): PoolAccount;
|
|
605
622
|
/**
|
|
606
623
|
* The 7-day reset `expiring-first` orders by, in epoch ms: the seat's own
|
|
607
624
|
* reading while it is still ahead of `now`, else +Infinity (never read, or the
|
package/dist/pool.js
CHANGED
|
@@ -408,6 +408,18 @@ export function accountAction(account, now = Date.now()) {
|
|
|
408
408
|
return 'wait';
|
|
409
409
|
return 'none';
|
|
410
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* How much a seat's headroom counts under `headroom-by-plan`: N for a Max Nx
|
|
413
|
+
* plan (from the profile's `rate_limit_tier`, e.g. `default_claude_max_20x`),
|
|
414
|
+
* 1 for Pro, and 1 for anything unrecognised or unknown, so a seat whose plan
|
|
415
|
+
* dario cannot read is never favoured. Only the whole `default_claude_max_Nx`
|
|
416
|
+
* tier name counts; a tier that merely contains `max_Nx` is unrecognised.
|
|
417
|
+
*/
|
|
418
|
+
export function planWeight(rateLimitTier) {
|
|
419
|
+
const m = /^default_claude_max_(\d+)x$/i.exec(rateLimitTier ?? '');
|
|
420
|
+
const n = m ? Number(m[1]) : 1;
|
|
421
|
+
return Number.isFinite(n) && n >= 1 ? n : 1;
|
|
422
|
+
}
|
|
411
423
|
/**
|
|
412
424
|
* `expiring-first` — fill-first, ordered by WHEN each seat's capacity expires
|
|
413
425
|
* instead of by alias: new conversations fill the seat whose 7-day window
|
|
@@ -431,7 +443,7 @@ export function resolvePoolStrategy(explicit, env = process.env) {
|
|
|
431
443
|
if (typeof c !== 'string')
|
|
432
444
|
continue;
|
|
433
445
|
const s = c.trim().toLowerCase();
|
|
434
|
-
if (s === 'headroom' || s === 'fill-first' || s === 'expiring-first')
|
|
446
|
+
if (s === 'headroom' || s === 'headroom-by-plan' || s === 'fill-first' || s === 'expiring-first')
|
|
435
447
|
return s;
|
|
436
448
|
}
|
|
437
449
|
return 'headroom';
|
|
@@ -446,6 +458,8 @@ export function resolvePoolStrategy(explicit, env = process.env) {
|
|
|
446
458
|
export function describePoolStrategy(strategy, floor = DEFAULT_POOL_HEADROOM_FLOOR) {
|
|
447
459
|
const pct = `${Math.round(floor * 100)}% floor`;
|
|
448
460
|
switch (strategy) {
|
|
461
|
+
case 'headroom-by-plan':
|
|
462
|
+
return `headroom-by-plan: new conversations go to the seat with the most headroom counted by plan size (Max Nx counts N, Pro 1); ${pct}`;
|
|
449
463
|
case 'fill-first':
|
|
450
464
|
return `fill-first — new conversations fill the alphabetically-first seat until the ${pct}, then spill`;
|
|
451
465
|
case 'expiring-first':
|
|
@@ -866,14 +880,21 @@ export function resolvePoolHeadroomFloor(explicit, env = process.env) {
|
|
|
866
880
|
// Pick the account with the most headroom in a single pass. The prior
|
|
867
881
|
// `.reduce()` form recomputed the incumbent's headroom every iteration
|
|
868
882
|
// (~2n computeHeadroom calls); this computes each once (#642-audit).
|
|
869
|
-
|
|
883
|
+
// `byPlan` scales positive headroom by planWeight; zero and negative headroom
|
|
884
|
+
// stay unscaled, so a bigger plan that is over its limit never ranks lower
|
|
885
|
+
// than a smaller one that is over by the same amount.
|
|
886
|
+
export function pickMaxHeadroom(accounts, family, byPlan = false) {
|
|
887
|
+
const score = (a) => {
|
|
888
|
+
const h = computeHeadroom(a.rateLimit, family);
|
|
889
|
+
return byPlan && h > 0 ? h * planWeight(a.rateLimitTier) : h;
|
|
890
|
+
};
|
|
870
891
|
let best = accounts[0];
|
|
871
|
-
let
|
|
892
|
+
let bestScore = score(best);
|
|
872
893
|
for (let i = 1; i < accounts.length; i++) {
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
894
|
+
const s = score(accounts[i]);
|
|
895
|
+
if (s > bestScore) {
|
|
875
896
|
best = accounts[i];
|
|
876
|
-
|
|
897
|
+
bestScore = s;
|
|
877
898
|
}
|
|
878
899
|
}
|
|
879
900
|
return best;
|
|
@@ -1107,10 +1128,10 @@ export class AccountPool {
|
|
|
1107
1128
|
return first;
|
|
1108
1129
|
}
|
|
1109
1130
|
if (!this.hardHeadroomFloor)
|
|
1110
|
-
return pickMaxHeadroom(eligible, family);
|
|
1131
|
+
return pickMaxHeadroom(eligible, family, this.strategy === 'headroom-by-plan');
|
|
1111
1132
|
const aboveFloor = eligible.filter(a => isAboveHeadroomFloor(computeHeadroom(a.rateLimit, family, now), this.headroomFloor));
|
|
1112
1133
|
return aboveFloor.length > 0
|
|
1113
|
-
? pickMaxHeadroom(aboveFloor, family)
|
|
1134
|
+
? pickMaxHeadroom(aboveFloor, family, this.strategy === 'headroom-by-plan')
|
|
1114
1135
|
: pickFloorProbe(eligible, family, this.headroomFloor, now);
|
|
1115
1136
|
}
|
|
1116
1137
|
// No seat is eligible. A seat parked inside a live window is not
|
|
@@ -1287,10 +1308,10 @@ export class AccountPool {
|
|
|
1287
1308
|
return first;
|
|
1288
1309
|
}
|
|
1289
1310
|
if (!this.hardHeadroomFloor)
|
|
1290
|
-
return pickMaxHeadroom(eligible, family);
|
|
1311
|
+
return pickMaxHeadroom(eligible, family, this.strategy === 'headroom-by-plan');
|
|
1291
1312
|
const aboveFloor = eligible.filter(a => isAboveHeadroomFloor(computeHeadroom(a.rateLimit, family, now), this.headroomFloor));
|
|
1292
1313
|
return aboveFloor.length > 0
|
|
1293
|
-
? pickMaxHeadroom(aboveFloor, family)
|
|
1314
|
+
? pickMaxHeadroom(aboveFloor, family, this.strategy === 'headroom-by-plan')
|
|
1294
1315
|
: pickFloorProbe(eligible, family, this.headroomFloor, now);
|
|
1295
1316
|
}
|
|
1296
1317
|
// Mid-flight: the seats a 429 could still hand this request to. A seat
|
package/dist/proxy.d.ts
CHANGED
|
@@ -503,7 +503,10 @@ interface ProxyOptions {
|
|
|
503
503
|
maxConcurrentPerConsumer?: number;
|
|
504
504
|
/**
|
|
505
505
|
* Max ms before the upstream fetch is aborted. Default 300000 (5 min,
|
|
506
|
-
* matching the Anthropic SDK).
|
|
506
|
+
* matching the Anthropic SDK). It caps the wait until the response
|
|
507
|
+
* streams; once a 2xx body is being read it is the longest allowed gap
|
|
508
|
+
* between two chunks, so a live stream is never cut. `--upstream-timeout`
|
|
509
|
+
* / `DARIO_UPSTREAM_TIMEOUT_MS`. Injectable so tests can exercise the
|
|
507
510
|
* timeout → slot-release path without waiting 5 minutes (dario#905).
|
|
508
511
|
*/
|
|
509
512
|
upstreamTimeoutMs?: number;
|
|
@@ -668,6 +671,16 @@ interface ProxyOptions {
|
|
|
668
671
|
* a CLI flag, so the key never lands in `ps`/argv).
|
|
669
672
|
*/
|
|
670
673
|
upstreamApiKey?: string;
|
|
674
|
+
/**
|
|
675
|
+
* The key behind a `credits:<model>` entry of `--pool-fallback`
|
|
676
|
+
* (credits-rung.ts): a Console API key of the organization a Max plan is
|
|
677
|
+
* linked to, spending the monthly API credits that plan includes. Used
|
|
678
|
+
* only when every seat is drained or cooling and no subscription rung can
|
|
679
|
+
* serve; the pool stays the first choice. Sourced from
|
|
680
|
+
* DARIO_CREDITS_API_KEY (env-only, like `upstreamApiKey`, so it never lands
|
|
681
|
+
* in argv). A chain entry without the key is inert and said so at startup.
|
|
682
|
+
*/
|
|
683
|
+
creditsApiKey?: string;
|
|
671
684
|
/**
|
|
672
685
|
* Overage-guard — halt the proxy on the first response that bills to
|
|
673
686
|
* anything other than the subscription pool (`overage`, `api`, or a new
|