@relayplane/proxy 1.8.4 → 1.8.6

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/dist/cli.js CHANGED
File without changes
package/dist/config.d.ts CHANGED
@@ -14,6 +14,30 @@ export interface MeshConfigSection {
14
14
  sync_interval_ms: number;
15
15
  contribute: boolean;
16
16
  }
17
+ export interface RateLimitModelConfig {
18
+ /** Requests per minute for this model */
19
+ rpm: number;
20
+ }
21
+ export interface ProviderRateLimitConfig {
22
+ /**
23
+ * Requests per minute for ALL models from this provider.
24
+ * Applies when no model-specific override exists.
25
+ * Example: providers.anthropic.rateLimit.rpm = 100
26
+ */
27
+ rpm: number;
28
+ }
29
+ export interface ProviderConfig {
30
+ /** Provider-level rate limit. Applies to all models for this provider unless overridden per-model. */
31
+ rateLimit?: ProviderRateLimitConfig;
32
+ }
33
+ export interface RateLimitConfigSection {
34
+ /** Per-model RPM overrides. Keys are model names (e.g. "claude-sonnet-4-6"). */
35
+ models?: Record<string, RateLimitModelConfig>;
36
+ /** Max requests to queue when limit is hit (default: 50) */
37
+ maxQueueDepth?: number;
38
+ /** Max ms a queued request waits before getting a 429 (default: 30000) */
39
+ queueTimeoutMs?: number;
40
+ }
17
41
  export interface ProxyConfig {
18
42
  /** Anonymous device ID (generated on first run) */
19
43
  device_id: string;
@@ -31,6 +55,23 @@ export interface ProxyConfig {
31
55
  updated_at: string;
32
56
  /** Mesh (Osmosis) learning layer config */
33
57
  mesh?: MeshConfigSection;
58
+ /** Rate limiter configuration */
59
+ rateLimit?: RateLimitConfigSection;
60
+ /**
61
+ * Per-provider configuration.
62
+ * Supported providers: anthropic, openai, google, xai, groq, perplexity.
63
+ *
64
+ * Example ~/.relayplane/config.json:
65
+ * ```json
66
+ * {
67
+ * "providers": {
68
+ * "anthropic": { "rateLimit": { "rpm": 100 } },
69
+ * "openai": { "rateLimit": { "rpm": 60 } }
70
+ * }
71
+ * }
72
+ * ```
73
+ */
74
+ providers?: Record<string, ProviderConfig>;
34
75
  }
35
76
  /**
36
77
  * Check if credentials.json exists and has a valid apiKey
@@ -103,4 +144,13 @@ export declare function getMeshConfig(): MeshConfigSection;
103
144
  * Update mesh config section
104
145
  */
105
146
  export declare function updateMeshConfig(updates: Partial<MeshConfigSection>): void;
147
+ /**
148
+ * Get rate limit config section (with defaults)
149
+ */
150
+ export declare function getRateLimitConfig(): RateLimitConfigSection;
151
+ /**
152
+ * Get per-provider configurations (with defaults).
153
+ * Returns empty object if no providers section exists in config.
154
+ */
155
+ export declare function getProviderConfigs(): Record<string, ProviderConfig>;
106
156
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,kDAAkD;IAClD,kBAAkB,EAAE,OAAO,CAAC;IAE5B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IAEvB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IAEnB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IAEnB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAkDD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,WAAW,CAmExC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAiBpD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAKvE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAGpC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAa3C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,CAQjD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAI1E"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,yCAAyC;IACzC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,cAAc;IAC7B,sGAAsG;IACtG,SAAS,CAAC,EAAE,uBAAuB,CAAC;CACrC;AAED,MAAM,WAAW,sBAAsB;IACrC,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAC9C,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAElB,8BAA8B;IAC9B,iBAAiB,EAAE,OAAO,CAAC;IAE3B,kDAAkD;IAClD,kBAAkB,EAAE,OAAO,CAAC;IAE5B,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,oCAAoC;IACpC,cAAc,EAAE,MAAM,CAAC;IAEvB,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IAEnB,+BAA+B;IAC/B,UAAU,EAAE,MAAM,CAAC;IAEnB,2CAA2C;IAC3C,IAAI,CAAC,EAAE,iBAAiB,CAAC;IAEzB,iCAAiC;IACjC,SAAS,CAAC,EAAE,sBAAsB,CAAC;IAEnC;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC5C;AAkDD;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAED;;;;GAIG;AACH,wBAAgB,UAAU,IAAI,WAAW,CAmExC;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAiBpD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAKvE;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,OAAO,CAGpC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAG5C;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAGpC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAa3C;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,GAAG,SAAS,CAG9C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,iBAAiB,CAQjD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAI1E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,sBAAsB,CAG3D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAGnE"}
package/dist/config.js CHANGED
@@ -57,6 +57,8 @@ exports.getConfigPath = getConfigPath;
57
57
  exports.getCredentialsPath = getCredentialsPath;
58
58
  exports.getMeshConfig = getMeshConfig;
59
59
  exports.updateMeshConfig = updateMeshConfig;
60
+ exports.getRateLimitConfig = getRateLimitConfig;
61
+ exports.getProviderConfigs = getProviderConfigs;
60
62
  const fs = __importStar(require("fs"));
61
63
  const path = __importStar(require("path"));
62
64
  const os = __importStar(require("os"));
@@ -314,4 +316,19 @@ function updateMeshConfig(updates) {
314
316
  config.mesh = { ...getMeshConfig(), ...updates };
315
317
  saveConfig(config);
316
318
  }
319
+ /**
320
+ * Get rate limit config section (with defaults)
321
+ */
322
+ function getRateLimitConfig() {
323
+ const config = loadConfig();
324
+ return config.rateLimit ?? {};
325
+ }
326
+ /**
327
+ * Get per-provider configurations (with defaults).
328
+ * Returns empty object if no providers section exists in config.
329
+ */
330
+ function getProviderConfigs() {
331
+ const config = loadConfig();
332
+ return config.providers ?? {};
333
+ }
317
334
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,19 @@
1
1
  /**
2
2
  * Rate Limiter - In-memory rate limiting for RelayPlane Proxy
3
3
  *
4
- * Hard limits:
5
- * - Opus models: 10 requests per minute
6
- * - Other models: 60 requests per minute (default)
4
+ * Limits are configurable via ~/.relayplane/config.json under `rateLimit.models`.
5
+ * When a limit is hit, requests are queued (up to maxQueueDepth) instead of
6
+ * immediately returning 429. Queue overflow or timeout results in a 429.
7
7
  *
8
- * Auto-expires old entries every 5 minutes
8
+ * Defaults:
9
+ * - Sonnet models: 60 RPM
10
+ * - Opus models: 30 RPM
11
+ * - Haiku models: 60 RPM
12
+ * - Other models: 60 RPM
13
+ *
14
+ * Auto-expires old entries every 5 minutes.
9
15
  */
16
+ import type { RateLimitConfigSection, ProviderConfig } from './config.js';
10
17
  export interface RateLimitConfig {
11
18
  rpm: number;
12
19
  maxTokens?: number;
@@ -18,15 +25,77 @@ export interface RateLimitCheck {
18
25
  limit: number;
19
26
  retryAfter?: number;
20
27
  }
28
+ export declare class RateLimitError extends Error {
29
+ readonly code: 'QUEUE_FULL' | 'QUEUE_TIMEOUT';
30
+ readonly retryAfter: number;
31
+ readonly limit: number;
32
+ readonly resetAt: number;
33
+ constructor(message: string, opts: {
34
+ code: 'QUEUE_FULL' | 'QUEUE_TIMEOUT';
35
+ retryAfter: number;
36
+ limit: number;
37
+ resetAt: number;
38
+ });
39
+ }
21
40
  export declare const DEFAULT_LIMITS: Record<string, RateLimitConfig>;
22
- declare class RateLimiter {
41
+ export declare class RateLimiter {
23
42
  private buckets;
43
+ private queues;
44
+ private drainTimers;
24
45
  private lastCleanup;
25
46
  private readonly CLEANUP_INTERVAL;
47
+ private modelOverrides;
48
+ /**
49
+ * Provider-level overrides. Key is provider name (e.g. "anthropic").
50
+ * Applied when no model-specific override exists.
51
+ * Set via config.json `providers.{name}.rateLimit.rpm`.
52
+ */
53
+ private providerOverrides;
54
+ private maxQueueDepth;
55
+ private queueTimeoutMs;
56
+ constructor(opts?: {
57
+ maxQueueDepth?: number;
58
+ queueTimeoutMs?: number;
59
+ });
60
+ /**
61
+ * Apply configuration from ~/.relayplane/config.json rateLimit section.
62
+ * Call once at proxy startup.
63
+ */
64
+ configure(cfg: RateLimitConfigSection): void;
26
65
  /**
27
- * Check if a request is allowed under rate limits
66
+ * Apply per-provider configuration from ~/.relayplane/config.json `providers` section.
67
+ * Call once at proxy startup, after configure().
68
+ *
69
+ * Each provider's rateLimit.rpm becomes the fallback for ALL models from that provider
70
+ * when no model-specific override exists.
71
+ *
72
+ * Example config.json:
73
+ * ```json
74
+ * { "providers": { "anthropic": { "rateLimit": { "rpm": 100 } } } }
75
+ * ```
28
76
  */
29
- checkLimit(workspaceId: string, model: string): RateLimitCheck;
77
+ configureProviders(providers: Record<string, ProviderConfig>): void;
78
+ /**
79
+ * Synchronous check — returns immediately without queuing.
80
+ * Increments counter if allowed.
81
+ *
82
+ * @param provider Optional provider name (e.g. "anthropic"). Used to look up
83
+ * provider-level RPM limits when no model-specific override exists.
84
+ * Limits are isolated per-provider — one provider hitting its cap
85
+ * does NOT affect other providers.
86
+ */
87
+ checkLimit(workspaceId: string, model: string, provider?: string): RateLimitCheck;
88
+ /**
89
+ * Async slot acquisition with queuing.
90
+ *
91
+ * - Resolves immediately if a slot is available.
92
+ * - Queues the request if the limit is hit (up to maxQueueDepth).
93
+ * - Throws RateLimitError with code QUEUE_FULL if the queue is full.
94
+ * - Throws RateLimitError with code QUEUE_TIMEOUT if the request waits too long.
95
+ */
96
+ acquireSlot(workspaceId: string, model: string, provider?: string): Promise<void>;
97
+ private scheduleDrain;
98
+ private drainQueue;
30
99
  /**
31
100
  * Get current usage for a workspace/model
32
101
  */
@@ -39,6 +108,10 @@ declare class RateLimiter {
39
108
  * Reset limit for a specific workspace/model (emergency use)
40
109
  */
41
110
  resetLimit(workspaceId: string, model?: string): void;
111
+ /**
112
+ * Get current queue depth for a workspace/model
113
+ */
114
+ getQueueDepth(workspaceId: string, model: string): number;
42
115
  /**
43
116
  * Get all active limits (for debugging)
44
117
  */
@@ -53,12 +126,17 @@ declare class RateLimiter {
53
126
  private maybeCleanup;
54
127
  }
55
128
  export declare const rateLimiter: RateLimiter;
56
- export declare const checkLimit: (workspaceId: string, model: string) => RateLimitCheck;
129
+ /**
130
+ * Load rateLimit + providers config from ~/.relayplane/config.json and apply to the singleton.
131
+ * Call once at proxy startup.
132
+ */
133
+ export declare function configureRateLimiter(): void;
134
+ export declare const checkLimit: (workspaceId: string, model: string, provider?: string) => RateLimitCheck;
135
+ export declare const acquireSlot: (workspaceId: string, model: string, provider?: string) => Promise<void>;
57
136
  export declare const getUsage: (workspaceId: string, model: string) => {
58
137
  used: number;
59
138
  limit: number;
60
139
  resetAt: number;
61
140
  };
62
141
  export declare const resetLimit: (workspaceId: string, model?: string) => void;
63
- export {};
64
142
  //# sourceMappingURL=rate-limiter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAe1D,CAAC;AAOF,cAAM,WAAW;IACf,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAiB;IAElD;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,cAAc;IAsC9D;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAY9F;;OAEG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI;IAYrD;;OAEG;IACH,eAAe,IAAI,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAQzE,OAAO,CAAC,SAAS;IAmBjB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,YAAY;CAerB;AAGD,eAAO,MAAM,WAAW,aAAoB,CAAC;AAG7C,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM,mBACjB,CAAC;AAE7C,eAAO,MAAM,QAAQ,GAAI,aAAa,MAAM,EAAE,OAAO,MAAM;UAjGH,MAAM;WAAS,MAAM;aAAW,MAAM;CAkGpD,CAAC;AAE3C,eAAO,MAAM,UAAU,GAAI,aAAa,MAAM,EAAE,QAAQ,MAAM,SAClB,CAAC"}
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -2,20 +2,77 @@
2
2
  /**
3
3
  * Rate Limiter - In-memory rate limiting for RelayPlane Proxy
4
4
  *
5
- * Hard limits:
6
- * - Opus models: 10 requests per minute
7
- * - Other models: 60 requests per minute (default)
5
+ * Limits are configurable via ~/.relayplane/config.json under `rateLimit.models`.
6
+ * When a limit is hit, requests are queued (up to maxQueueDepth) instead of
7
+ * immediately returning 429. Queue overflow or timeout results in a 429.
8
8
  *
9
- * Auto-expires old entries every 5 minutes
9
+ * Defaults:
10
+ * - Sonnet models: 60 RPM
11
+ * - Opus models: 30 RPM
12
+ * - Haiku models: 60 RPM
13
+ * - Other models: 60 RPM
14
+ *
15
+ * Auto-expires old entries every 5 minutes.
10
16
  */
11
17
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.resetLimit = exports.getUsage = exports.checkLimit = exports.rateLimiter = exports.DEFAULT_LIMITS = void 0;
13
- // Hardcoded limits - Opus is expensive, be conservative
18
+ exports.resetLimit = exports.getUsage = exports.acquireSlot = exports.checkLimit = exports.rateLimiter = exports.RateLimiter = exports.DEFAULT_LIMITS = exports.RateLimitError = void 0;
19
+ exports.configureRateLimiter = configureRateLimiter;
20
+ // ── Sanitizers (defence against Infinity / NaN / negative values) ────────────
21
+ /** Maximum configurable RPM — prevents Infinity from bypassing the limiter. */
22
+ const MAX_RPM = 10_000;
23
+ /** Maximum configurable queue depth. */
24
+ const MAX_QUEUE_DEPTH = 10_000;
25
+ /** Maximum configurable queue timeout (10 minutes). */
26
+ const MAX_QUEUE_TIMEOUT_MS = 10 * 60 * 1_000;
27
+ /**
28
+ * Clamp an RPM value to a safe finite integer in [1, MAX_RPM].
29
+ * Returns the safe default (60) when the input is 0, negative, NaN, or Infinity.
30
+ */
31
+ function sanitizeRpm(value, safeDefault = 60) {
32
+ const num = Number(value);
33
+ if (!Number.isFinite(num) || num < 1)
34
+ return safeDefault;
35
+ return Math.min(MAX_RPM, Math.floor(num));
36
+ }
37
+ /**
38
+ * Clamp a maxQueueDepth value to a safe finite integer in [0, MAX_QUEUE_DEPTH].
39
+ */
40
+ function sanitizeQueueDepth(value, safeDefault = 50) {
41
+ const num = Number(value);
42
+ if (!Number.isFinite(num) || num < 0)
43
+ return safeDefault;
44
+ return Math.min(MAX_QUEUE_DEPTH, Math.floor(num));
45
+ }
46
+ /**
47
+ * Clamp a queueTimeoutMs value to a safe finite integer in [100, MAX_QUEUE_TIMEOUT_MS].
48
+ */
49
+ function sanitizeTimeoutMs(value, safeDefault = 30_000) {
50
+ const num = Number(value);
51
+ if (!Number.isFinite(num) || num < 100)
52
+ return safeDefault;
53
+ return Math.min(MAX_QUEUE_TIMEOUT_MS, Math.floor(num));
54
+ }
55
+ class RateLimitError extends Error {
56
+ code;
57
+ retryAfter;
58
+ limit;
59
+ resetAt;
60
+ constructor(message, opts) {
61
+ super(message);
62
+ this.name = 'RateLimitError';
63
+ this.code = opts.code;
64
+ this.retryAfter = opts.retryAfter;
65
+ this.limit = opts.limit;
66
+ this.resetAt = opts.resetAt;
67
+ }
68
+ }
69
+ exports.RateLimitError = RateLimitError;
70
+ // Default limits. Sonnet bumped to 60 RPM, Opus bumped to 30 RPM (GH #39).
14
71
  exports.DEFAULT_LIMITS = {
15
72
  // Anthropic models
16
- 'claude-opus-4-6': { rpm: 10, maxTokens: 4096 },
17
- 'claude-opus': { rpm: 10, maxTokens: 4096 },
18
- 'claude-sonnet-4-6': { rpm: 30 },
73
+ 'claude-opus-4-6': { rpm: 30, maxTokens: 4096 },
74
+ 'claude-opus': { rpm: 30, maxTokens: 4096 },
75
+ 'claude-sonnet-4-6': { rpm: 60 },
19
76
  'claude-haiku-4-5': { rpm: 60 },
20
77
  // OpenAI models
21
78
  'gpt-4o': { rpm: 30 },
@@ -23,28 +80,85 @@ exports.DEFAULT_LIMITS = {
23
80
  'o1': { rpm: 10, maxTokens: 4096 },
24
81
  'o3-mini': { rpm: 30 },
25
82
  // Default for unknown models
26
- 'default': { rpm: 60 }
83
+ 'default': { rpm: 60 },
27
84
  };
28
85
  class RateLimiter {
29
86
  buckets = new Map();
87
+ queues = new Map();
88
+ drainTimers = new Map();
30
89
  lastCleanup = Date.now();
31
90
  CLEANUP_INTERVAL = 5 * 60 * 1000; // 5 minutes
91
+ modelOverrides = {};
92
+ /**
93
+ * Provider-level overrides. Key is provider name (e.g. "anthropic").
94
+ * Applied when no model-specific override exists.
95
+ * Set via config.json `providers.{name}.rateLimit.rpm`.
96
+ */
97
+ providerOverrides = {};
98
+ maxQueueDepth;
99
+ queueTimeoutMs;
100
+ constructor(opts) {
101
+ this.maxQueueDepth = opts?.maxQueueDepth ?? 50;
102
+ this.queueTimeoutMs = opts?.queueTimeoutMs ?? 30_000;
103
+ }
104
+ /**
105
+ * Apply configuration from ~/.relayplane/config.json rateLimit section.
106
+ * Call once at proxy startup.
107
+ */
108
+ configure(cfg) {
109
+ if (cfg.models) {
110
+ for (const [model, modelCfg] of Object.entries(cfg.models)) {
111
+ this.modelOverrides[model.toLowerCase()] = { rpm: sanitizeRpm(modelCfg.rpm) };
112
+ }
113
+ }
114
+ if (cfg.maxQueueDepth !== undefined) {
115
+ this.maxQueueDepth = sanitizeQueueDepth(cfg.maxQueueDepth);
116
+ }
117
+ if (cfg.queueTimeoutMs !== undefined) {
118
+ this.queueTimeoutMs = sanitizeTimeoutMs(cfg.queueTimeoutMs);
119
+ }
120
+ }
121
+ /**
122
+ * Apply per-provider configuration from ~/.relayplane/config.json `providers` section.
123
+ * Call once at proxy startup, after configure().
124
+ *
125
+ * Each provider's rateLimit.rpm becomes the fallback for ALL models from that provider
126
+ * when no model-specific override exists.
127
+ *
128
+ * Example config.json:
129
+ * ```json
130
+ * { "providers": { "anthropic": { "rateLimit": { "rpm": 100 } } } }
131
+ * ```
132
+ */
133
+ configureProviders(providers) {
134
+ for (const [provider, cfg] of Object.entries(providers)) {
135
+ if (cfg.rateLimit?.rpm !== undefined) {
136
+ this.providerOverrides[provider.toLowerCase()] = { rpm: sanitizeRpm(cfg.rateLimit.rpm) };
137
+ }
138
+ }
139
+ }
32
140
  /**
33
- * Check if a request is allowed under rate limits
141
+ * Synchronous check returns immediately without queuing.
142
+ * Increments counter if allowed.
143
+ *
144
+ * @param provider Optional provider name (e.g. "anthropic"). Used to look up
145
+ * provider-level RPM limits when no model-specific override exists.
146
+ * Limits are isolated per-provider — one provider hitting its cap
147
+ * does NOT affect other providers.
34
148
  */
35
- checkLimit(workspaceId, model) {
149
+ checkLimit(workspaceId, model, provider) {
36
150
  this.maybeCleanup();
37
- const config = this.getConfig(model);
38
- const key = `${workspaceId}:${this.getModelKey(model)}:${this.getCurrentMinute()}`;
151
+ const config = this.getConfig(model, provider);
152
+ const providerSeg = provider ? `:${provider.toLowerCase()}` : '';
153
+ const key = `${workspaceId}${providerSeg}:${this.getModelKey(model)}:${this.getCurrentMinute()}`;
39
154
  const now = Date.now();
40
- const windowMs = 60 * 1000; // 1 minute window
155
+ const windowMs = 60 * 1000;
41
156
  const resetAt = this.getCurrentMinute() + windowMs;
42
157
  let entry = this.buckets.get(key);
43
158
  if (!entry) {
44
159
  entry = { count: 0, resetAt };
45
160
  this.buckets.set(key, entry);
46
161
  }
47
- // Check if window expired and reset
48
162
  if (now > entry.resetAt) {
49
163
  entry.count = 0;
50
164
  entry.resetAt = resetAt;
@@ -59,9 +173,91 @@ class RateLimiter {
59
173
  remaining,
60
174
  resetAt,
61
175
  limit: config.rpm,
62
- retryAfter: allowed ? undefined : Math.ceil((resetAt - now) / 1000)
176
+ retryAfter: allowed ? undefined : Math.ceil((resetAt - now) / 1000),
63
177
  };
64
178
  }
179
+ /**
180
+ * Async slot acquisition with queuing.
181
+ *
182
+ * - Resolves immediately if a slot is available.
183
+ * - Queues the request if the limit is hit (up to maxQueueDepth).
184
+ * - Throws RateLimitError with code QUEUE_FULL if the queue is full.
185
+ * - Throws RateLimitError with code QUEUE_TIMEOUT if the request waits too long.
186
+ */
187
+ async acquireSlot(workspaceId, model, provider) {
188
+ const check = this.checkLimit(workspaceId, model, provider);
189
+ if (check.allowed)
190
+ return;
191
+ const providerSeg = provider ? `:${provider.toLowerCase()}` : '';
192
+ const queueKey = `${workspaceId}${providerSeg}:${this.getModelKey(model)}`;
193
+ if (!this.queues.has(queueKey)) {
194
+ this.queues.set(queueKey, []);
195
+ }
196
+ const queue = this.queues.get(queueKey);
197
+ if (queue.length >= this.maxQueueDepth) {
198
+ throw new RateLimitError(`Rate limit queue full for ${model}. Max queue depth (${this.maxQueueDepth}) reached. ` +
199
+ `Retry after ${check.retryAfter ?? 60}s.`, {
200
+ code: 'QUEUE_FULL',
201
+ retryAfter: check.retryAfter ?? 60,
202
+ limit: check.limit,
203
+ resetAt: check.resetAt,
204
+ });
205
+ }
206
+ // Schedule drain at window reset (only one timer per queue key)
207
+ this.scheduleDrain(queueKey, workspaceId, model, check.resetAt, provider);
208
+ return new Promise((resolve, reject) => {
209
+ const timeoutId = setTimeout(() => {
210
+ const idx = queue.indexOf(entry);
211
+ if (idx >= 0)
212
+ queue.splice(idx, 1);
213
+ reject(new RateLimitError(`Rate limit queue timeout for ${model} after ${this.queueTimeoutMs}ms. ` +
214
+ `Retry after ${entry.check.retryAfter ?? 60}s.`, {
215
+ code: 'QUEUE_TIMEOUT',
216
+ retryAfter: entry.check.retryAfter ?? 60,
217
+ limit: entry.check.limit,
218
+ resetAt: entry.check.resetAt,
219
+ }));
220
+ }, this.queueTimeoutMs);
221
+ const entry = {
222
+ resolve: () => {
223
+ clearTimeout(timeoutId);
224
+ resolve();
225
+ },
226
+ reject: (err) => {
227
+ clearTimeout(timeoutId);
228
+ reject(err);
229
+ },
230
+ timeoutId,
231
+ check,
232
+ };
233
+ queue.push(entry);
234
+ });
235
+ }
236
+ scheduleDrain(queueKey, workspaceId, model, resetAt, provider) {
237
+ if (this.drainTimers.has(queueKey))
238
+ return; // already scheduled
239
+ const delay = Math.max(0, resetAt - Date.now());
240
+ const timer = setTimeout(() => {
241
+ this.drainTimers.delete(queueKey);
242
+ this.drainQueue(queueKey, workspaceId, model, provider);
243
+ }, delay);
244
+ this.drainTimers.set(queueKey, timer);
245
+ }
246
+ drainQueue(queueKey, workspaceId, model, provider) {
247
+ const queue = this.queues.get(queueKey);
248
+ if (!queue || queue.length === 0)
249
+ return;
250
+ while (queue.length > 0) {
251
+ const check = this.checkLimit(workspaceId, model, provider);
252
+ if (!check.allowed) {
253
+ // Window filled up — schedule next drain at next window reset
254
+ this.scheduleDrain(queueKey, workspaceId, model, check.resetAt, provider);
255
+ return;
256
+ }
257
+ const entry = queue.shift();
258
+ entry.resolve();
259
+ }
260
+ }
65
261
  /**
66
262
  * Get current usage for a workspace/model
67
263
  */
@@ -72,7 +268,7 @@ class RateLimiter {
72
268
  return {
73
269
  used: entry?.count || 0,
74
270
  limit: config.rpm,
75
- resetAt: entry?.resetAt || this.getCurrentMinute() + 60 * 1000
271
+ resetAt: entry?.resetAt || this.getCurrentMinute() + 60 * 1000,
76
272
  };
77
273
  }
78
274
  /**
@@ -88,6 +284,13 @@ class RateLimiter {
88
284
  }
89
285
  }
90
286
  }
287
+ /**
288
+ * Get current queue depth for a workspace/model
289
+ */
290
+ getQueueDepth(workspaceId, model) {
291
+ const queueKey = `${workspaceId}:${this.getModelKey(model)}`;
292
+ return this.queues.get(queueKey)?.length ?? 0;
293
+ }
91
294
  /**
92
295
  * Get all active limits (for debugging)
93
296
  */
@@ -95,18 +298,32 @@ class RateLimiter {
95
298
  return Array.from(this.buckets.entries()).map(([key, entry]) => ({
96
299
  key,
97
300
  count: entry.count,
98
- resetAt: entry.resetAt
301
+ resetAt: entry.resetAt,
99
302
  }));
100
303
  }
101
- getConfig(model) {
102
- // Normalize model name
304
+ getConfig(model, provider) {
103
305
  const normalized = model.toLowerCase().replace(/[^a-z0-9-]/g, '');
104
- // Check exact match first
306
+ // 1. Model-specific override takes highest priority
307
+ if (this.modelOverrides[normalized]) {
308
+ return this.modelOverrides[normalized];
309
+ }
310
+ // 2. Provider-level override (e.g. providers.anthropic.rateLimit.rpm)
311
+ // Applies to ALL models from this provider when no model-specific override exists.
312
+ // Each provider maintains its own isolated bucket — limits don't cascade across providers.
313
+ if (provider) {
314
+ const providerNorm = provider.toLowerCase();
315
+ if (this.providerOverrides[providerNorm]) {
316
+ return this.providerOverrides[providerNorm];
317
+ }
318
+ }
319
+ // 3. Check exact match in built-in defaults
105
320
  if (exports.DEFAULT_LIMITS[normalized]) {
106
321
  return exports.DEFAULT_LIMITS[normalized];
107
322
  }
108
- // Check partial match
323
+ // 4. Partial match (e.g. "claude-sonnet" matches "claude-sonnet-4-6")
109
324
  for (const [key, config] of Object.entries(exports.DEFAULT_LIMITS)) {
325
+ if (key === 'default')
326
+ continue;
110
327
  if (normalized.includes(key) || key.includes(normalized)) {
111
328
  return config;
112
329
  }
@@ -135,23 +352,40 @@ class RateLimiter {
135
352
  }
136
353
  maybeCleanup() {
137
354
  const now = Date.now();
138
- if (now - this.lastCleanup < this.CLEANUP_INTERVAL) {
355
+ if (now - this.lastCleanup < this.CLEANUP_INTERVAL)
139
356
  return;
140
- }
141
- // Remove expired entries
142
357
  for (const [key, entry] of this.buckets) {
143
- if (now > entry.resetAt + 60000) { // Keep 1 minute grace period
358
+ if (now > entry.resetAt + 60000) {
144
359
  this.buckets.delete(key);
145
360
  }
146
361
  }
147
362
  this.lastCleanup = now;
148
363
  }
149
364
  }
365
+ exports.RateLimiter = RateLimiter;
150
366
  // Singleton instance
151
367
  exports.rateLimiter = new RateLimiter();
368
+ /**
369
+ * Load rateLimit + providers config from ~/.relayplane/config.json and apply to the singleton.
370
+ * Call once at proxy startup.
371
+ */
372
+ function configureRateLimiter() {
373
+ try {
374
+ // Dynamic require to avoid circular deps at module init time
375
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
376
+ const cfg = require('./config.js');
377
+ exports.rateLimiter.configure(cfg.getRateLimitConfig());
378
+ exports.rateLimiter.configureProviders(cfg.getProviderConfigs());
379
+ }
380
+ catch {
381
+ // Ignore — use defaults
382
+ }
383
+ }
152
384
  // Convenience exports
153
- const checkLimit = (workspaceId, model) => exports.rateLimiter.checkLimit(workspaceId, model);
385
+ const checkLimit = (workspaceId, model, provider) => exports.rateLimiter.checkLimit(workspaceId, model, provider);
154
386
  exports.checkLimit = checkLimit;
387
+ const acquireSlot = (workspaceId, model, provider) => exports.rateLimiter.acquireSlot(workspaceId, model, provider);
388
+ exports.acquireSlot = acquireSlot;
155
389
  const getUsage = (workspaceId, model) => exports.rateLimiter.getUsage(workspaceId, model);
156
390
  exports.getUsage = getUsage;
157
391
  const resetLimit = (workspaceId, model) => exports.rateLimiter.resetLimit(workspaceId, model);