@relayplane/proxy 1.8.40 → 1.9.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.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Multi-account token pool for Anthropic (and future providers).
3
+ *
4
+ * Supports two registration paths:
5
+ * 1. Auto-detected — tokens seen in incoming Authorization headers are
6
+ * registered automatically with lower priority (priority = 10).
7
+ * 2. Explicit config — accounts listed in ~/.relayplane/config.json under
8
+ * providers.anthropic.accounts[] are registered with the user-specified
9
+ * priority (default 0 = highest).
10
+ *
11
+ * Selection strategy:
12
+ * - Skip tokens that are currently rate-limited.
13
+ * - Skip tokens that have exceeded 90% of their known RPM limit (proactive
14
+ * throttling).
15
+ * - Among remaining candidates, pick the lowest priority number first;
16
+ * break ties by fewest requests this minute.
17
+ *
18
+ * 429 handling:
19
+ * - When the caller receives a 429 from upstream, it calls record429() with
20
+ * the token that was used. That token is marked as rate-limited until
21
+ * `retry-after` seconds have elapsed (default: 60 s).
22
+ * - The caller should then call selectToken() again to get the next
23
+ * available token.
24
+ */
25
+ export interface PoolAccountConfig {
26
+ /** Human-readable label, e.g. "work-max" */
27
+ label: string;
28
+ /** Raw API key or OAT token */
29
+ apiKey: string;
30
+ /**
31
+ * Priority: lower = tried first. Explicitly configured accounts use the
32
+ * value from config.json (default 0). Auto-detected tokens use 10.
33
+ */
34
+ priority: number;
35
+ }
36
+ export interface TokenState {
37
+ label: string;
38
+ apiKey: string;
39
+ priority: number;
40
+ /** How was this token registered? */
41
+ source: 'config' | 'auto-detect';
42
+ /** Whether the token is an OAT (Claude Max) subscription token */
43
+ isOat: boolean;
44
+ /** Unix ms timestamp until which this token is considered rate-limited */
45
+ rateLimitedUntil: number;
46
+ /** Number of requests dispatched in the current 1-minute window */
47
+ requestsThisMinute: number;
48
+ /** Start of the current 1-minute window (Unix ms) */
49
+ windowStart: number;
50
+ /** Best-known RPM limit learned from upstream headers */
51
+ knownRpmLimit: number;
52
+ }
53
+ export interface TokenPoolStatus {
54
+ accounts: Array<{
55
+ label: string;
56
+ priority: number;
57
+ source: 'config' | 'auto-detect';
58
+ isOat: boolean;
59
+ requestsThisMinute: number;
60
+ knownRpmLimit: number;
61
+ rateLimitedUntil: number | null;
62
+ available: boolean;
63
+ }>;
64
+ }
65
+ export declare class TokenPool {
66
+ private tokens;
67
+ /**
68
+ * Register explicit config accounts. Called once at startup.
69
+ * Replaces any existing config-sourced entry for the same apiKey.
70
+ */
71
+ registerConfigAccounts(accounts: PoolAccountConfig[]): void;
72
+ /**
73
+ * Auto-register a token seen in an incoming Authorization header.
74
+ * No-op if the token is already registered (from config or previous request).
75
+ */
76
+ autoDetect(apiKey: string): void;
77
+ /**
78
+ * Select the best available token.
79
+ * Returns `null` if all tokens are exhausted / rate-limited.
80
+ */
81
+ selectToken(now?: number): TokenState | null;
82
+ /**
83
+ * Record a 429 response for the given apiKey.
84
+ * Marks the token as rate-limited for `retryAfterSeconds`.
85
+ */
86
+ record429(apiKey: string, retryAfterSeconds?: number, now?: number): void;
87
+ /**
88
+ * Update the known RPM limit and remaining requests from upstream headers.
89
+ * Reads `anthropic-ratelimit-requests-remaining` /
90
+ * `x-ratelimit-remaining-requests` and `anthropic-ratelimit-requests-limit`.
91
+ */
92
+ recordResponseHeaders(apiKey: string, headers: Record<string, string | string[] | undefined>, now?: number): void;
93
+ getStatus(now?: number): TokenPoolStatus;
94
+ /** How many tokens are registered? */
95
+ size(): number;
96
+ private makeState;
97
+ /** Roll over any per-minute windows that have expired. */
98
+ private tickWindows;
99
+ private isAvailable;
100
+ }
101
+ export declare function getTokenPool(): TokenPool;
102
+ /** Reset the singleton (used in tests) */
103
+ export declare function resetTokenPool(): void;
104
+ //# sourceMappingURL=token-pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-pool.d.ts","sourceRoot":"","sources":["../src/token-pool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,MAAM,WAAW,iBAAiB;IAChC,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,GAAG,aAAa,CAAC;IACjC,kEAAkE;IAClE,KAAK,EAAE,OAAO,CAAC;IACf,0EAA0E;IAC1E,gBAAgB,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,KAAK,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,QAAQ,GAAG,aAAa,CAAC;QACjC,KAAK,EAAE,OAAO,CAAC;QACf,kBAAkB,EAAE,MAAM,CAAC;QAC3B,aAAa,EAAE,MAAM,CAAC;QACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;QAChC,SAAS,EAAE,OAAO,CAAC;KACpB,CAAC,CAAC;CACJ;AASD,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAsC;IAIpD;;;OAGG;IACH,sBAAsB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,IAAI;IAa3D;;;OAGG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAYhC;;;OAGG;IACH,WAAW,CAAC,GAAG,GAAE,MAAmB,GAAG,UAAU,GAAG,IAAI;IAsBxD;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,GAAE,MAAmB,GAAG,IAAI;IAOrF;;;;OAIG;IACH,qBAAqB,CACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,EACtD,GAAG,GAAE,MAAmB,GACvB,IAAI;IA8BP,SAAS,CAAC,GAAG,GAAE,MAAmB,GAAG,eAAe;IAiBpD,sCAAsC;IACtC,IAAI,IAAI,MAAM;IAMd,OAAO,CAAC,SAAS;IAoBjB,0DAA0D;IAC1D,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,WAAW;CAKpB;AAKD,wBAAgB,YAAY,IAAI,SAAS,CAGxC;AAED,0CAA0C;AAC1C,wBAAgB,cAAc,IAAI,IAAI,CAErC"}
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Multi-account token pool for Anthropic (and future providers).
4
+ *
5
+ * Supports two registration paths:
6
+ * 1. Auto-detected — tokens seen in incoming Authorization headers are
7
+ * registered automatically with lower priority (priority = 10).
8
+ * 2. Explicit config — accounts listed in ~/.relayplane/config.json under
9
+ * providers.anthropic.accounts[] are registered with the user-specified
10
+ * priority (default 0 = highest).
11
+ *
12
+ * Selection strategy:
13
+ * - Skip tokens that are currently rate-limited.
14
+ * - Skip tokens that have exceeded 90% of their known RPM limit (proactive
15
+ * throttling).
16
+ * - Among remaining candidates, pick the lowest priority number first;
17
+ * break ties by fewest requests this minute.
18
+ *
19
+ * 429 handling:
20
+ * - When the caller receives a 429 from upstream, it calls record429() with
21
+ * the token that was used. That token is marked as rate-limited until
22
+ * `retry-after` seconds have elapsed (default: 60 s).
23
+ * - The caller should then call selectToken() again to get the next
24
+ * available token.
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.TokenPool = void 0;
28
+ exports.getTokenPool = getTokenPool;
29
+ exports.resetTokenPool = resetTokenPool;
30
+ const provider_limits_js_1 = require("./provider-limits.js");
31
+ /** Tokens with requestsThisMinute >= throttleRatio * knownRpmLimit are skipped */
32
+ const THROTTLE_RATIO = 0.9;
33
+ /** Priority assigned to auto-detected tokens */
34
+ const AUTO_DETECT_PRIORITY = 10;
35
+ /** Default rate-limit window after a 429 when no Retry-After header is present */
36
+ const DEFAULT_RETRY_AFTER_S = 60;
37
+ class TokenPool {
38
+ tokens = new Map();
39
+ // ── Registration ──────────────────────────────────────────────────────────
40
+ /**
41
+ * Register explicit config accounts. Called once at startup.
42
+ * Replaces any existing config-sourced entry for the same apiKey.
43
+ */
44
+ registerConfigAccounts(accounts) {
45
+ for (const acct of accounts) {
46
+ const existing = this.tokens.get(acct.apiKey);
47
+ if (existing && existing.source === 'config') {
48
+ // Update priority/label in case config changed
49
+ existing.label = acct.label;
50
+ existing.priority = acct.priority;
51
+ }
52
+ else {
53
+ this.tokens.set(acct.apiKey, this.makeState(acct, 'config'));
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Auto-register a token seen in an incoming Authorization header.
59
+ * No-op if the token is already registered (from config or previous request).
60
+ */
61
+ autoDetect(apiKey) {
62
+ if (!apiKey || this.tokens.has(apiKey))
63
+ return;
64
+ const label = `auto-${apiKey.slice(-8)}`;
65
+ const state = this.makeState({ label, apiKey, priority: AUTO_DETECT_PRIORITY }, 'auto-detect');
66
+ this.tokens.set(apiKey, state);
67
+ }
68
+ // ── Selection ─────────────────────────────────────────────────────────────
69
+ /**
70
+ * Select the best available token.
71
+ * Returns `null` if all tokens are exhausted / rate-limited.
72
+ */
73
+ selectToken(now = Date.now()) {
74
+ this.tickWindows(now);
75
+ const candidates = Array.from(this.tokens.values()).filter((t) => this.isAvailable(t, now));
76
+ if (candidates.length === 0)
77
+ return null;
78
+ // Sort: lowest priority first, then fewest requests this minute
79
+ candidates.sort((a, b) => {
80
+ if (a.priority !== b.priority)
81
+ return a.priority - b.priority;
82
+ return a.requestsThisMinute - b.requestsThisMinute;
83
+ });
84
+ const selected = candidates[0];
85
+ selected.requestsThisMinute += 1;
86
+ return selected;
87
+ }
88
+ // ── Recording ─────────────────────────────────────────────────────────────
89
+ /**
90
+ * Record a 429 response for the given apiKey.
91
+ * Marks the token as rate-limited for `retryAfterSeconds`.
92
+ */
93
+ record429(apiKey, retryAfterSeconds, now = Date.now()) {
94
+ const state = this.tokens.get(apiKey);
95
+ if (!state)
96
+ return;
97
+ const waitS = retryAfterSeconds ?? DEFAULT_RETRY_AFTER_S;
98
+ state.rateLimitedUntil = now + waitS * 1000;
99
+ }
100
+ /**
101
+ * Update the known RPM limit and remaining requests from upstream headers.
102
+ * Reads `anthropic-ratelimit-requests-remaining` /
103
+ * `x-ratelimit-remaining-requests` and `anthropic-ratelimit-requests-limit`.
104
+ */
105
+ recordResponseHeaders(apiKey, headers, now = Date.now()) {
106
+ const state = this.tokens.get(apiKey);
107
+ if (!state)
108
+ return;
109
+ const h = (name) => {
110
+ const v = headers[name] ?? headers[name.toLowerCase()];
111
+ return Array.isArray(v) ? v[0] : v;
112
+ };
113
+ // Learn the limit
114
+ const limit = h('anthropic-ratelimit-requests-limit') ??
115
+ h('x-ratelimit-limit-requests');
116
+ if (limit) {
117
+ const n = parseInt(limit, 10);
118
+ if (!isNaN(n) && n > 0)
119
+ state.knownRpmLimit = n;
120
+ }
121
+ // If retry-after is present and we haven't already recorded a 429, clear it
122
+ const retryAfter = h('retry-after');
123
+ if (retryAfter && state.rateLimitedUntil <= now) {
124
+ const waitS = parseInt(retryAfter, 10);
125
+ if (!isNaN(waitS) && waitS > 0) {
126
+ state.rateLimitedUntil = now + waitS * 1000;
127
+ }
128
+ }
129
+ }
130
+ // ── Status ────────────────────────────────────────────────────────────────
131
+ getStatus(now = Date.now()) {
132
+ this.tickWindows(now);
133
+ const accounts = Array.from(this.tokens.values())
134
+ .sort((a, b) => a.priority - b.priority)
135
+ .map((t) => ({
136
+ label: t.label,
137
+ priority: t.priority,
138
+ source: t.source,
139
+ isOat: t.isOat,
140
+ requestsThisMinute: t.requestsThisMinute,
141
+ knownRpmLimit: t.knownRpmLimit,
142
+ rateLimitedUntil: t.rateLimitedUntil > now ? t.rateLimitedUntil : null,
143
+ available: this.isAvailable(t, now),
144
+ }));
145
+ return { accounts };
146
+ }
147
+ /** How many tokens are registered? */
148
+ size() {
149
+ return this.tokens.size;
150
+ }
151
+ // ── Internals ─────────────────────────────────────────────────────────────
152
+ makeState(acct, source) {
153
+ const isOat = acct.apiKey.startsWith('sk-ant-oat');
154
+ return {
155
+ label: acct.label,
156
+ apiKey: acct.apiKey,
157
+ priority: acct.priority,
158
+ source,
159
+ isOat,
160
+ rateLimitedUntil: 0,
161
+ requestsThisMinute: 0,
162
+ // windowStart=0 so the first tickWindows() call always initialises it to
163
+ // the real current time rather than creation time (which may differ in tests).
164
+ windowStart: 0,
165
+ knownRpmLimit: (0, provider_limits_js_1.getDefaultRpm)('anthropic', isOat),
166
+ };
167
+ }
168
+ /** Roll over any per-minute windows that have expired. */
169
+ tickWindows(now) {
170
+ for (const state of this.tokens.values()) {
171
+ if (now - state.windowStart >= 60_000) {
172
+ state.requestsThisMinute = 0;
173
+ state.windowStart = now;
174
+ }
175
+ }
176
+ }
177
+ isAvailable(t, now) {
178
+ if (t.rateLimitedUntil > now)
179
+ return false;
180
+ if (t.requestsThisMinute >= t.knownRpmLimit * THROTTLE_RATIO)
181
+ return false;
182
+ return true;
183
+ }
184
+ }
185
+ exports.TokenPool = TokenPool;
186
+ /** Singleton pool instance shared across all requests */
187
+ let _pool = null;
188
+ function getTokenPool() {
189
+ if (!_pool)
190
+ _pool = new TokenPool();
191
+ return _pool;
192
+ }
193
+ /** Reset the singleton (used in tests) */
194
+ function resetTokenPool() {
195
+ _pool = null;
196
+ }
197
+ //# sourceMappingURL=token-pool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"token-pool.js","sourceRoot":"","sources":["../src/token-pool.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@relayplane/proxy",
3
- "version": "1.8.40",
3
+ "version": "1.9.0",
4
4
  "description": "Open source cost intelligence proxy for AI agents. Cut LLM costs ~80% with smart model routing. Dashboard, policy engine, 11 providers. MIT licensed.",
5
5
  "homepage": "https://relayplane.com",
6
6
  "repository": {