@mayank3238/keymux 1.0.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,859 @@
1
+ export { NvidiaStreamTranslator, OpenRouterStreamTranslator, startProxyServer, stopProxyServer, translateAnthropicToOpenAI, translateOpenAIToAnthropic } from './proxy/index.js';
2
+ import 'http';
3
+
4
+ /**
5
+ * Core type definitions for keymux
6
+ * Public API types exported for consumers
7
+ */
8
+ /**
9
+ * Configuration for a single API key in the pool
10
+ */
11
+ interface KeyConfig {
12
+ /** Unique identifier - each key has its own ID */
13
+ id: string;
14
+ /** Actual API key string */
15
+ key: string;
16
+ /** RPM limit - how many requests can run in one minute (default: 40) */
17
+ rpmLimit?: number;
18
+ /** Weight - increase weight for more important keys (default: 1) */
19
+ weight?: number;
20
+ /** Base URL - custom URL if you don't want to use the provider's default URL */
21
+ baseURL?: string;
22
+ /** Custom metadata - for storing extra info */
23
+ metadata?: Record<string, unknown>;
24
+ }
25
+ /**
26
+ * Internal state tracking for a key
27
+ */
28
+ interface KeyState {
29
+ config: KeyConfig;
30
+ /** Current RPM - how many requests have been made in the current minute */
31
+ rpm: number;
32
+ /** Last request timestamp */
33
+ lastUsed: number;
34
+ /** Circuit breaker state - HEALTHY, DEGRADED or OPEN */
35
+ circuitState: CircuitState;
36
+ /** Continuous failures - circuit opens if this gets too high */
37
+ failures: number;
38
+ /** Cooldown until - how long to wait before retrying */
39
+ cooldownUntil: number;
40
+ /** Average latency - calculated from last 25 requests */
41
+ avgLatencyMs: number;
42
+ /** Raw TTFT history - sliding window of last 25 readings for true average calculation */
43
+ latencyHistory: number[];
44
+ /** Total requests - how many requests processed so far */
45
+ totalRequests: number;
46
+ /** Total errors - how many errors encountered so far */
47
+ totalErrors: number;
48
+ /** Timestamps of requests in the current window */
49
+ requestTimestamps: number[];
50
+ }
51
+ /**
52
+ * Circuit breaker states
53
+ * HEALTHY -> DEGRADED -> OPEN (recovers after cooldown)
54
+ */
55
+ declare enum CircuitState {
56
+ HEALTHY = "healthy",// Everything is fine, can be used
57
+ DEGRADED = "degraded",// Some issues, use carefully
58
+ OPEN = "open"
59
+ }
60
+ /**
61
+ * Configuration for the key router
62
+ */
63
+ interface KeyRouterConfig {
64
+ /** All API keys to manage */
65
+ keys: KeyConfig[];
66
+ /** Default RPM limit - used if key-specific limit is not provided (default: 40) */
67
+ defaultRpmLimit?: number;
68
+ /** Default weight - equal importance for all keys? Or some more important? (default: 1) */
69
+ defaultWeight?: number;
70
+ /** Circuit breaker - block key after this many failures (default: 3) */
71
+ failureThreshold?: number;
72
+ /** Circuit breaker - recovery time in milliseconds after being blocked (default: 30000 = 30sec) */
73
+ cooldownMs?: number;
74
+ /** Sliding window - RPM is counted within a 1 minute (60000ms) window */
75
+ windowMs?: number;
76
+ /** Latency tracking - track how fast requests complete? (default: true) */
77
+ trackLatency?: boolean;
78
+ /** Callback - called when a key's state changes (e.g. healthy to degraded) */
79
+ onStateChange?: (keyId: string, state: CircuitState) => void;
80
+ /** Debug callback - for understanding what is happening in development */
81
+ onDebug?: (event: DebugEvent) => void;
82
+ }
83
+ /**
84
+ * Debug events - for logging and monitoring
85
+ * What event occurred (selected, failed, recovered), which key, when, and extra details
86
+ */
87
+ interface DebugEvent {
88
+ type: 'key_selected' | 'key_failed' | 'key_recovered' | 'circuit_opened' | 'all_exhausted';
89
+ keyId?: string;
90
+ keyIndex?: number;
91
+ timestamp: number;
92
+ details?: Record<string, unknown>;
93
+ }
94
+ /**
95
+ * Result of key selection
96
+ */
97
+ interface KeySelectionResult {
98
+ /** The selected API key */
99
+ key: string;
100
+ /** Key configuration */
101
+ config: KeyConfig;
102
+ /** Internal key ID */
103
+ keyId: string;
104
+ /** Current utilization (0-1) */
105
+ utilization: number;
106
+ }
107
+ /**
108
+ * Statistics for a single key
109
+ */
110
+ interface KeyStats {
111
+ id: string;
112
+ key: string;
113
+ rpm: number;
114
+ rpmLimit: number;
115
+ utilization: number;
116
+ circuitState: CircuitState;
117
+ failures: number;
118
+ avgLatencyMs: number;
119
+ totalRequests: number;
120
+ totalErrors: number;
121
+ isHealthy: boolean;
122
+ cooldownRemainingMs?: number;
123
+ }
124
+ /**
125
+ * Overall router statistics
126
+ */
127
+ interface RouterStats {
128
+ totalKeys: number;
129
+ healthyKeys: number;
130
+ totalRpm: number;
131
+ totalRpmLimit: number;
132
+ overallUtilization: number;
133
+ keys: KeyStats[];
134
+ }
135
+ /**
136
+ * Options for key selection
137
+ */
138
+ interface SelectionOptions {
139
+ /** Preferred key IDs (will try these first if healthy) */
140
+ preferredKeys?: string[];
141
+ /** Minimum utilization threshold (0-1) - skip keys above this */
142
+ maxUtilization?: number;
143
+ /** Custom selection strategy name */
144
+ strategy?: string;
145
+ }
146
+ /**
147
+ * Error types for better error handling
148
+ */
149
+ declare class KeyRouterError extends Error {
150
+ readonly code: 'ALL_KEYS_EXHAUSTED' | 'NO_KEYS_CONFIGURED' | 'KEY_NOT_FOUND' | 'CIRCUIT_OPEN' | 'INVALID_CONFIG';
151
+ readonly keyId?: string | undefined;
152
+ constructor(message: string, code: 'ALL_KEYS_EXHAUSTED' | 'NO_KEYS_CONFIGURED' | 'KEY_NOT_FOUND' | 'CIRCUIT_OPEN' | 'INVALID_CONFIG', keyId?: string | undefined);
153
+ }
154
+ declare class RateLimitError extends KeyRouterError {
155
+ constructor(keyId: string);
156
+ }
157
+ /**
158
+ * Provider preset configurations
159
+ */
160
+ interface ProviderPreset {
161
+ name: string;
162
+ baseURL: string;
163
+ defaultRpmLimit: number;
164
+ models: string[];
165
+ }
166
+ declare const PROVIDER_PRESETS: Record<string, ProviderPreset>;
167
+ /**
168
+ * Configuration for one provider in a multi-provider pool
169
+ * Each provider can have multiple API keys
170
+ */
171
+ interface ProviderPoolEntry {
172
+ /** Provider name - use preset name ('nvidia', 'gemini', etc.) or any custom string */
173
+ provider: string;
174
+ /** API keys for this provider (multiple keys = more RPM capacity) */
175
+ keys: string[];
176
+ /** Base URL - auto-filled from PROVIDER_PRESETS for known providers */
177
+ baseURL?: string;
178
+ /** Models this provider serves - first model is the default */
179
+ models?: string[];
180
+ /** Per-key RPM limit - auto-filled from PROVIDER_PRESETS for known providers */
181
+ rpmLimit?: number;
182
+ /** Provider priority weight - higher weight = more requests routed here (default: 1) */
183
+ weight?: number;
184
+ /** Custom metadata - for storing extra info per provider */
185
+ metadata?: Record<string, unknown>;
186
+ }
187
+ /**
188
+ * Result of getEndpoint() — everything needed to make an API call
189
+ * Unlike getKey() which returns just a string, this returns the full endpoint info
190
+ */
191
+ interface EndpointResult {
192
+ /** The API key to use */
193
+ key: string;
194
+ /** Provider's base URL for API calls */
195
+ baseURL: string;
196
+ /** Model to use for this request */
197
+ model: string;
198
+ /** Provider name (e.g. 'nvidia', 'gemini', 'openrouter') */
199
+ provider: string;
200
+ /** Unique endpoint ID for reporting success/failure */
201
+ endpointId: string;
202
+ /** Current utilization of this endpoint (0-1) */
203
+ utilization: number;
204
+ }
205
+ /**
206
+ * Options for getEndpoint() - filter and control which endpoint is selected
207
+ */
208
+ interface EndpointOptions {
209
+ /** Try these providers first if healthy (e.g. ['gemini', 'nvidia']) */
210
+ preferProviders?: string[];
211
+ /** Never use these providers for this request */
212
+ excludeProviders?: string[];
213
+ /** Request a specific model - router picks the provider that has it */
214
+ model?: string;
215
+ /** Skip endpoints above this utilization (0-1) */
216
+ maxUtilization?: number;
217
+ /** Override selection strategy for this request */
218
+ strategy?: string;
219
+ }
220
+ /**
221
+ * Configuration options for MultiProviderRouter
222
+ */
223
+ interface MultiProviderConfig {
224
+ /** Circuit breaker - block key after this many failures (default: 3) */
225
+ failureThreshold?: number;
226
+ /** Circuit breaker - recovery time in ms after being blocked (default: 30000) */
227
+ cooldownMs?: number;
228
+ /** Sliding window for RPM counting in ms (default: 60000) */
229
+ windowMs?: number;
230
+ /** Track latency per endpoint? (default: true) */
231
+ trackLatency?: boolean;
232
+ /** Default selection strategy (default: 'weighted-least-utilization') */
233
+ strategy?: string;
234
+ /** Callback when an endpoint's state changes */
235
+ onStateChange?: (endpointId: string, state: CircuitState) => void;
236
+ /** Debug callback for monitoring */
237
+ onDebug?: (event: DebugEvent) => void;
238
+ }
239
+
240
+ /**
241
+ * Key Selection Strategies
242
+ * Pure functions that select the best key from the available keys
243
+ */
244
+
245
+ /**
246
+ * Strategy interface
247
+ */
248
+ interface SelectionStrategy {
249
+ name: string;
250
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
251
+ }
252
+ /**
253
+ * Weighted Least Utilization Strategy (RECOMMENDED)
254
+ * Picks key with lowest (rpm / rpmLimit) ratio, weighted by key weight
255
+ * Handles different RPM limits per key fairly
256
+ */
257
+ declare class WeightedLeastUtilizationStrategy implements SelectionStrategy {
258
+ name: string;
259
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
260
+ }
261
+ /**
262
+ * Least Requests Strategy
263
+ * Picks key with absolute lowest RPM count
264
+ * Good when all keys have same limits
265
+ */
266
+ declare class LeastRequestsStrategy implements SelectionStrategy {
267
+ name: string;
268
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
269
+ }
270
+ /**
271
+ * Smart Routing Strategy
272
+ * 1. Filters out keys exceeding max utilization.
273
+ * 2. Forms a "fast pool" of keys within 100ms of the fastest key. Always includes keys with no latency data (e.g., avgLatencyMs === 0) so they don't get starved.
274
+ * 3. Sorts the pool by RPM (ascending) and then totalRequests (ascending) to balance load (Least Used).
275
+ */
276
+ declare class SmartRoutingStrategy implements SelectionStrategy {
277
+ name: string;
278
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
279
+ }
280
+ /**
281
+ * Least Latency Strategy
282
+ * Picks key with lowest average latency
283
+ * Requires latency tracking to be enabled
284
+ * Falls back to LeastRequestsStrategy if no latency data is available
285
+ */
286
+ declare class LeastLatencyStrategy implements SelectionStrategy {
287
+ name: string;
288
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
289
+ }
290
+ /**
291
+ * Preferred Keys Strategy
292
+ * Tries preferred keys first, falls back to another strategy
293
+ */
294
+ declare class PreferredKeysStrategy implements SelectionStrategy {
295
+ private fallbackStrategy;
296
+ name: string;
297
+ constructor(fallbackStrategy: SelectionStrategy);
298
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
299
+ }
300
+ /**
301
+ * Random Strategy
302
+ * Selects a key randomly - good for distributing load evenly over time
303
+ * Applies utilization filter so overloaded keys are not selected
304
+ */
305
+ declare class RandomStrategy implements SelectionStrategy {
306
+ name: string;
307
+ select(keys: KeyState[], options?: SelectionOptions): KeyState | null;
308
+ }
309
+ /**
310
+ * Strategy Factory - creates strategy by name
311
+ */
312
+ declare function createStrategy(name: string, fallback?: SelectionStrategy): SelectionStrategy;
313
+ /**
314
+ * Default strategy instance (singleton)
315
+ */
316
+ declare const defaultStrategy: WeightedLeastUtilizationStrategy;
317
+
318
+ /**
319
+ * KeyRouter - Main entry point of the keymux library
320
+ * Manages keys, tracks state, and distributes requests
321
+ * Uses a selection strategy to pick the best key
322
+ */
323
+
324
+ declare class KeyRouter {
325
+ private tracker;
326
+ private strategy;
327
+ private config;
328
+ private initialized;
329
+ constructor(config: KeyRouterConfig);
330
+ /**
331
+ * Initialize the router with keys
332
+ * Must be called before using getKey()
333
+ */
334
+ initialize(): void;
335
+ /**
336
+ * Get the next available API key
337
+ * Returns key string ready to use with OpenAI/LangChain SDKs
338
+ */
339
+ getKey(options?: SelectionOptions): Promise<string>;
340
+ /**
341
+ * Get key with full metadata (for debugging/logging)
342
+ */
343
+ getKeyWithMeta(options?: SelectionOptions): Promise<KeySelectionResult>;
344
+ /**
345
+ * Report a failed request for a specific key or key ID
346
+ * Call this when you catch a 429/5xx error from the API
347
+ */
348
+ reportFailure(keyOrId: string, isRateLimit?: boolean): void;
349
+ /**
350
+ * Report a successful request for a specific key or key ID
351
+ * Optional: call this if you want to track latency manually
352
+ */
353
+ reportSuccess(keyOrId: string, latencyMs?: number): void;
354
+ /**
355
+ * Manually mark a key as recovered
356
+ */
357
+ reportRecovery(keyOrId: string): void;
358
+ /**
359
+ * Get statistics for all keys
360
+ */
361
+ getStats(): KeyStats[];
362
+ /**
363
+ * Get overall router statistics
364
+ */
365
+ getOverallStats(): {
366
+ totalKeys: number;
367
+ healthyKeys: number;
368
+ totalRpm: number;
369
+ totalRpmLimit: number;
370
+ overallUtilization: number;
371
+ keys: KeyStats[];
372
+ };
373
+ /**
374
+ * Get a specific key's stats by ID
375
+ */
376
+ getKeyStats(keyId: string): KeyStats | undefined;
377
+ /**
378
+ * Check if a specific key is healthy
379
+ */
380
+ isKeyHealthy(keyId: string): boolean;
381
+ /**
382
+ * Get all available (healthy + under limit) key IDs
383
+ */
384
+ getAvailableKeyIds(): string[];
385
+ /**
386
+ * Set custom selection strategy
387
+ */
388
+ setStrategy(strategy: SelectionStrategy | string): void;
389
+ /**
390
+ * Add a new key at runtime
391
+ */
392
+ addKey(keyConfig: KeyConfig): void;
393
+ /**
394
+ * Remove a key at runtime
395
+ */
396
+ removeKey(keyId: string): boolean;
397
+ /**
398
+ * Update key configuration at runtime
399
+ */
400
+ updateKey(keyId: string, updates: Partial<KeyConfig>): boolean;
401
+ /**
402
+ * Reset all tracking (useful for testing)
403
+ */
404
+ reset(): void;
405
+ /**
406
+ * Shutdown and cleanup
407
+ */
408
+ destroy(): void;
409
+ /**
410
+ * Create a router from a provider preset
411
+ */
412
+ static fromProvider(provider: keyof typeof PROVIDER_PRESETS, keys: string[], options?: Partial<KeyRouterConfig>): KeyRouter;
413
+ /**
414
+ * Create a router for NVIDIA specifically (most common use case)
415
+ */
416
+ static forNvidia(keys: string[], options?: Partial<KeyRouterConfig>): KeyRouter;
417
+ /**
418
+ * Create router from environment variable (comma-separated keys)
419
+ */
420
+ static fromEnv(envVar: string, options?: Partial<KeyRouterConfig>): KeyRouter;
421
+ private ensureInitialized;
422
+ private findKeyOrId;
423
+ }
424
+
425
+ /**
426
+ * KeyTracker - Tracks per-key state: RPM, circuit breaker, latency
427
+ * Single responsibility: tracks and updates key metrics
428
+ */
429
+
430
+ interface KeyTrackerOptions {
431
+ defaultRpmLimit: number;
432
+ defaultWeight: number;
433
+ failureThreshold: number;
434
+ cooldownMs: number;
435
+ windowMs: number;
436
+ trackLatency: boolean;
437
+ onStateChange?: (keyId: string, state: CircuitState) => void;
438
+ onDebug?: (event: DebugEvent) => void;
439
+ }
440
+ declare class KeyTracker {
441
+ private keys;
442
+ private options;
443
+ private cleanupInterval?;
444
+ constructor(options: KeyTrackerOptions);
445
+ initialize(keyConfigs: KeyConfig[]): void;
446
+ /**
447
+ * Get all key states - for debugging or stats
448
+ */
449
+ getAllStates(): KeyState[];
450
+ /**
451
+ * Get state of a specific key
452
+ */
453
+ getState(keyId: string): KeyState | undefined;
454
+ /**
455
+ * Get only healthy and available keys
456
+ * Checks circuit breaker state and RPM limits
457
+ */
458
+ getAvailableKeys(): KeyState[];
459
+ /**
460
+ * Record a request attempt (reservation)
461
+ * Increments RPM count without touching failures or circuit state.
462
+ */
463
+ recordAttempt(keyId: string): void;
464
+ /**
465
+ * Record a successful request completion
466
+ * Updates latency and resets failures (RPM already incremented by recordAttempt).
467
+ */
468
+ recordSuccess(keyId: string, latencyMs?: number): void;
469
+ /**
470
+ * Record a failed request (rate limit, server error, etc.)
471
+ * Triggers circuit breaker logic (RPM already incremented by recordAttempt).
472
+ */
473
+ recordFailure(keyId: string, isRateLimit?: boolean): void;
474
+ /**
475
+ * Manually report a key as recovered (for external health checks)
476
+ */
477
+ reportRecovery(keyId: string): void;
478
+ /**
479
+ * Get statistics for all keys
480
+ */
481
+ getStats(): KeyStats[];
482
+ /**
483
+ * Get overall router statistics
484
+ */
485
+ getOverallStats(): {
486
+ totalKeys: number;
487
+ healthyKeys: number;
488
+ totalRpm: number;
489
+ totalRpmLimit: number;
490
+ overallUtilization: number;
491
+ keys: KeyStats[];
492
+ };
493
+ /**
494
+ * Reset all tracking (useful for testing)
495
+ */
496
+ reset(): void;
497
+ /**
498
+ * Shutdown cleanup
499
+ */
500
+ destroy(): void;
501
+ private transitionToHealthy;
502
+ private openCircuit;
503
+ private toStats;
504
+ private maskKey;
505
+ private emitDebug;
506
+ private startCleanupLoop;
507
+ private cleanupExpiredWindows;
508
+ private checkCircuitRecovery;
509
+ }
510
+
511
+ /**
512
+ * Utility helpers for keymux
513
+ */
514
+ /**
515
+ * Check if an error is a rate limit error (429)
516
+ */
517
+ declare function isRateLimitError(error: unknown, seen?: WeakSet<object>): boolean;
518
+ /**
519
+ * Check if an error is a server error (5xx)
520
+ */
521
+ declare function isServerError(error: unknown, seen?: WeakSet<object>): boolean;
522
+ /**
523
+ * Check if an error is retryable (rate limit or server error)
524
+ */
525
+ declare function isRetryableError(error: unknown): boolean;
526
+ /**
527
+ * Calculate exponential backoff delay with jitter
528
+ */
529
+ declare function calculateBackoff(attempt: number, baseMs?: number, maxMs?: number): number;
530
+ /**
531
+ * Mask API key for logging (show only first 4 and last 4 chars)
532
+ * Example: sk-abcdef1234567890 -> sk-ab****7890
533
+ */
534
+ declare function maskKey(key: string): string;
535
+ /**
536
+ * Parse comma-separated keys from string
537
+ * Reads keys from environment variables - splits by comma to create an array
538
+ * Filters out empty strings
539
+ */
540
+ declare function parseKeys(keysString: string): string[];
541
+ /**
542
+ * Create a simple async key getter for OpenAI SDK / LangChain
543
+ * Usage: new ChatOpenAI({ apiKey: createKeyGetter(router) })
544
+ * This function returns an async getter that calls router.getKey()
545
+ */
546
+ declare function createKeyGetter(router: {
547
+ getKey(): Promise<string>;
548
+ }): () => Promise<string>;
549
+ /**
550
+ * Create a key getter with automatic failover
551
+ * Usage: new ChatOpenAI({ apiKey: createFailoverKeyGetter(router, 3) })
552
+ */
553
+ declare function createFailoverKeyGetter(router: {
554
+ getKey(): Promise<string>;
555
+ reportFailure(key: string, isRateLimit?: boolean): void;
556
+ reportSuccess(key: string): void;
557
+ }, maxRetries?: number): () => Promise<string>;
558
+ /**
559
+ * Sleep utility
560
+ */
561
+ declare function sleep(ms: number): Promise<void>;
562
+ /**
563
+ * Format router stats for pretty logging
564
+ */
565
+ declare function formatStats(stats: ReturnType<KeyRouter['getOverallStats']>): string;
566
+ /**
567
+ * Create a periodic stats logger
568
+ */
569
+ declare function createStatsLogger(router: {
570
+ getOverallStats(): ReturnType<KeyRouter['getOverallStats']>;
571
+ }, intervalMs?: number): NodeJS.Timeout;
572
+
573
+ /**
574
+ * MultiProviderRouter - Routes requests across multiple AI providers
575
+ *
576
+ * Internally uses KeyTracker + SelectionStrategy directly (same engine as KeyRouter)
577
+ * but adds provider-awareness: baseURL, model, and provider-level filtering.
578
+ *
579
+ * Architecture:
580
+ * - Flattens all providers' keys into a single pool of KeyConfig entries
581
+ * - Each entry gets a unique ID: `${provider}-${keyIndex}` (e.g. 'nvidia-0', 'gemini-1')
582
+ * - An endpointMap stores the metadata (baseURL, model, provider) for each key ID
583
+ * - A providerGroups map tracks which key IDs belong to which provider
584
+ * - Selection uses existing KeyTracker (circuit breakers, RPM) + SelectionStrategy
585
+ */
586
+
587
+ /**
588
+ * Per-provider statistics
589
+ */
590
+ interface ProviderStats {
591
+ provider: string;
592
+ totalKeys: number;
593
+ healthyKeys: number;
594
+ totalRpm: number;
595
+ totalRpmLimit: number;
596
+ utilization: number;
597
+ keys: KeyStats[];
598
+ }
599
+ declare class MultiProviderRouter {
600
+ private tracker;
601
+ private strategy;
602
+ private endpointMap;
603
+ private providerGroups;
604
+ lastRoute: {
605
+ provider: string;
606
+ model: string;
607
+ key: string;
608
+ time: number;
609
+ } | null;
610
+ private keyConfigs;
611
+ private providers;
612
+ private config;
613
+ constructor(providers: ProviderPoolEntry[], config?: MultiProviderConfig);
614
+ /**
615
+ * Get the next best endpoint for making an API call
616
+ * Returns key + baseURL + model + provider — everything needed for a request
617
+ *
618
+ * Supports filtering:
619
+ * - preferProviders: try these first
620
+ * - excludeProviders: skip these entirely
621
+ * - model: pick a provider that has this model
622
+ * - maxUtilization: skip overloaded endpoints
623
+ */
624
+ getEndpoint(options?: EndpointOptions): Promise<EndpointResult>;
625
+ /**
626
+ * Report a successful request for an endpoint
627
+ * Resets failure count, updates latency (EMA), recovers degraded state
628
+ */
629
+ reportSuccess(endpointId: string, latencyMs?: number): void;
630
+ /**
631
+ * Report a failed request for an endpoint
632
+ * Increments failure count, may open circuit breaker
633
+ * Next getEndpoint() call will automatically route to a different provider
634
+ */
635
+ reportFailure(endpointId: string, isRateLimit?: boolean): void;
636
+ /**
637
+ * Manually recover an endpoint (e.g. after external health check)
638
+ */
639
+ reportRecovery(endpointId: string): void;
640
+ /**
641
+ * Get overall stats across all providers (same familiar format as KeyRouter)
642
+ */
643
+ getOverallStats(): {
644
+ totalKeys: number;
645
+ healthyKeys: number;
646
+ totalRpm: number;
647
+ totalRpmLimit: number;
648
+ overallUtilization: number;
649
+ keys: KeyStats[];
650
+ };
651
+ /**
652
+ * Get stats for all endpoints
653
+ */
654
+ getStats(): KeyStats[];
655
+ /**
656
+ * Get stats for a specific provider
657
+ */
658
+ getProviderStats(provider: string): ProviderStats;
659
+ /**
660
+ * Get list of all configured provider names
661
+ */
662
+ getProviderNames(): string[];
663
+ /**
664
+ * Get all available (healthy + under limit) endpoint IDs
665
+ */
666
+ getLastRoute(): {
667
+ provider: string;
668
+ model: string;
669
+ key: string;
670
+ time: number;
671
+ } | null;
672
+ getAvailableEndpointIds(): string[];
673
+ /**
674
+ * Check if a specific endpoint is healthy
675
+ */
676
+ isEndpointHealthy(endpointId: string): boolean;
677
+ /**
678
+ * Check if a provider has any healthy endpoints
679
+ */
680
+ isProviderHealthy(provider: string): boolean;
681
+ /**
682
+ * Add a new provider at runtime
683
+ */
684
+ addProvider(entry: ProviderPoolEntry): void;
685
+ /**
686
+ * Add a key to an existing provider at runtime
687
+ */
688
+ addKeyToProvider(provider: string, key: string): void;
689
+ /**
690
+ * Remove a provider entirely at runtime
691
+ */
692
+ removeProvider(provider: string): boolean;
693
+ /**
694
+ * Remove a specific key from a provider at runtime
695
+ */
696
+ removeKey(provider: string, key: string): boolean;
697
+ /**
698
+ * Set the selection strategy
699
+ */
700
+ setStrategy(strategy: SelectionStrategy | string): void;
701
+ /**
702
+ * Reset all tracking data (useful for testing)
703
+ */
704
+ reset(): void;
705
+ /**
706
+ * Shutdown and cleanup (stops background cleanup timer)
707
+ */
708
+ destroy(): void;
709
+ /**
710
+ * Flatten all providers into a single key pool
711
+ * Each key gets a unique ID: `${provider}-${keyIndex}`
712
+ * Provider metadata (baseURL, model) is stored in endpointMap
713
+ */
714
+ private buildPool;
715
+ /**
716
+ * Rebuild the pool from current providers (used after dynamic changes)
717
+ */
718
+ private rebuildPool;
719
+ }
720
+
721
+ interface FetchConfig {
722
+ router: MultiProviderRouter;
723
+ maxRetries?: number;
724
+ preferProviders?: string[];
725
+ excludeProviders?: string[];
726
+ model?: string;
727
+ }
728
+ declare function fetchWithFailover(urlBuilder: (ep: EndpointResult) => string | URL, fetchOptionsBuilder: (ep: EndpointResult) => RequestInit, config: FetchConfig): Promise<Response>;
729
+
730
+ interface DailyUsage {
731
+ date: string;
732
+ inputTokens: number;
733
+ outputTokens: number;
734
+ cacheTokens: number;
735
+ requests: number;
736
+ providerUsage?: Record<string, ProviderUsage>;
737
+ }
738
+ interface ProviderUsage {
739
+ requests: number;
740
+ tokens: number;
741
+ }
742
+ interface UsageData {
743
+ totalRequests: number;
744
+ totalInputTokens: number;
745
+ totalOutputTokens: number;
746
+ totalCacheTokens: number;
747
+ providerUsage: Record<string, number | ProviderUsage>;
748
+ daily: Record<string, DailyUsage>;
749
+ sessions: number;
750
+ firstUsed: number;
751
+ }
752
+ declare class UsageTracker {
753
+ private data;
754
+ private filePath;
755
+ private saveTimeout;
756
+ private sessionProviderUsage;
757
+ private initialized;
758
+ constructor();
759
+ private ensureInitialized;
760
+ private loadData;
761
+ private saveData;
762
+ private scheduleSave;
763
+ recordRequest(provider: string, inputTokens: number, outputTokens: number, cacheTokens?: number): void;
764
+ getSessionUsage(): Record<string, ProviderUsage>;
765
+ getTodayUsage(): Record<string, ProviderUsage>;
766
+ getData(): UsageData;
767
+ }
768
+ declare const globalUsageTracker: UsageTracker;
769
+
770
+ /**
771
+ * Create a KeyRouter for NVIDIA keys - the most common use case
772
+ * NVIDIA preset settings will be automatically applied
773
+ *
774
+ * @example
775
+ * ```typescript
776
+ * import { createNvidiaRouter } from 'keymux';
777
+ *
778
+ * const router = createNvidiaRouter([
779
+ * 'nvapi-key-1',
780
+ * 'nvapi-key-2',
781
+ * 'nvapi-key-3'
782
+ * ]);
783
+ *
784
+ * // Use with OpenAI SDK
785
+ * import OpenAI from 'openai';
786
+ * const client = new OpenAI({
787
+ * apiKey: async () => await router.getKey(),
788
+ * baseURL: 'https://integrate.api.nvidia.com/v1'
789
+ * });
790
+ * ```
791
+ */
792
+ declare function createNvidiaRouter(keys: string[], options?: Partial<KeyRouterConfig>): KeyRouter;
793
+ /**
794
+ * Create a KeyRouter from a provider preset
795
+ * Examples: mistral, openai, nvidia, etc.
796
+ *
797
+ * @example
798
+ * ```typescript
799
+ * import { createRouter } from 'keymux';
800
+ *
801
+ * const router = createRouter('mistral', [
802
+ * 'mistral-key-1',
803
+ * 'mistral-key-2'
804
+ * ]);
805
+ * ```
806
+ */
807
+ declare function createRouter(provider: keyof typeof PROVIDER_PRESETS, keys: string[], options?: Partial<KeyRouterConfig>): KeyRouter;
808
+ /**
809
+ * Create a KeyRouter from an environment variable
810
+ * Read keys from .env file or system env - comma separated
811
+ *
812
+ * @example
813
+ * ```typescript
814
+ * import { createRouterFromEnv } from 'keymux';
815
+ *
816
+ * // Set NVIDIA_KEYS="key1,key2,key3" in .env
817
+ * const router = createRouterFromEnv('NVIDIA_KEYS');
818
+ * ```
819
+ */
820
+ declare function createRouterFromEnv(envVar: string, options?: Partial<KeyRouterConfig>): KeyRouter;
821
+
822
+ /**
823
+ * Create a MultiProviderRouter from provider configs
824
+ * Routes requests across multiple AI providers with automatic failover
825
+ *
826
+ * @example
827
+ * ```typescript
828
+ * import { createMultiProviderRouter } from 'keymux';
829
+ *
830
+ * const router = createMultiProviderRouter([
831
+ * { provider: 'gemini', keys: ['AIza-key1'] },
832
+ * { provider: 'nvidia', keys: ['nvapi-key1', 'nvapi-key2'] },
833
+ * { provider: 'openrouter', keys: ['sk-or-key1'] }
834
+ * ]);
835
+ *
836
+ * const ep = await router.getEndpoint();
837
+ * // ep = { key, baseURL, model, provider, endpointId, utilization }
838
+ * ```
839
+ */
840
+ declare function createMultiProviderRouter(providers: ProviderPoolEntry[], config?: MultiProviderConfig): MultiProviderRouter;
841
+ /**
842
+ * Create a MultiProviderRouter from environment variables
843
+ * Each provider's keys are read from a comma-separated env var
844
+ *
845
+ * @example
846
+ * ```typescript
847
+ * import { createMultiProviderRouterFromEnv } from 'keymux';
848
+ *
849
+ * // process.env.NVIDIA_KEYS = "nvapi-key1,nvapi-key2"
850
+ * // process.env.GEMINI_KEYS = "AIza-key1"
851
+ * const router = createMultiProviderRouterFromEnv({
852
+ * nvidia: 'NVIDIA_KEYS',
853
+ * gemini: 'GEMINI_KEYS',
854
+ * });
855
+ * ```
856
+ */
857
+ declare function createMultiProviderRouterFromEnv(envMap: Record<string, string>, overrides?: Record<string, Partial<ProviderPoolEntry>>, config?: MultiProviderConfig): MultiProviderRouter;
858
+
859
+ export { CircuitState, type DailyUsage, type DebugEvent, type EndpointOptions, type EndpointResult, type KeyConfig, KeyRouter, type KeyRouterConfig, KeyRouterError, type KeySelectionResult, type KeyState, type KeyStats, KeyTracker, LeastLatencyStrategy, LeastRequestsStrategy, type MultiProviderConfig, MultiProviderRouter, PROVIDER_PRESETS, PreferredKeysStrategy, type ProviderPoolEntry, type ProviderPreset, type ProviderStats, type ProviderUsage, RandomStrategy, RateLimitError, type RouterStats, type SelectionOptions, type SelectionStrategy, SmartRoutingStrategy, type UsageData, UsageTracker, WeightedLeastUtilizationStrategy, calculateBackoff, createFailoverKeyGetter, createKeyGetter, createMultiProviderRouter, createMultiProviderRouterFromEnv, createNvidiaRouter, createRouter, createRouterFromEnv, createStatsLogger, createStrategy, defaultStrategy, fetchWithFailover, formatStats, globalUsageTracker, isRateLimitError, isRetryableError, isServerError, maskKey, parseKeys, sleep };