@kb-labs/core-resource-broker 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.
package/dist/index.js ADDED
@@ -0,0 +1,1418 @@
1
+ import { randomUUID } from 'crypto';
2
+
3
+ // src/types.ts
4
+ var DEFAULT_RETRY_CONFIG = {
5
+ maxRetries: 3,
6
+ baseDelay: 1e3,
7
+ maxDelay: 3e4,
8
+ jitter: 0.1,
9
+ retryableErrors: ["rate_limit", "server_error", "timeout", "network"]
10
+ };
11
+ var DEFAULT_RATE_LIMIT_CONFIG = {
12
+ safetyMargin: 0.9
13
+ };
14
+
15
+ // src/queue/priority-queue.ts
16
+ var PriorityQueue = class {
17
+ high = [];
18
+ normal = [];
19
+ low = [];
20
+ /**
21
+ * Add an item to the queue.
22
+ *
23
+ * @param item - Queue item to add
24
+ */
25
+ enqueue(item) {
26
+ const priority = item.request.priority;
27
+ switch (priority) {
28
+ case "high":
29
+ this.high.push(item);
30
+ break;
31
+ case "low":
32
+ this.low.push(item);
33
+ break;
34
+ default:
35
+ this.normal.push(item);
36
+ }
37
+ }
38
+ /**
39
+ * Remove and return the highest priority item.
40
+ *
41
+ * @returns The next item or undefined if queue is empty
42
+ */
43
+ dequeue() {
44
+ if (this.high.length > 0) {
45
+ return this.high.shift();
46
+ }
47
+ if (this.normal.length > 0) {
48
+ return this.normal.shift();
49
+ }
50
+ if (this.low.length > 0) {
51
+ return this.low.shift();
52
+ }
53
+ return void 0;
54
+ }
55
+ /**
56
+ * Peek at the highest priority item without removing it.
57
+ *
58
+ * @returns The next item or undefined if queue is empty
59
+ */
60
+ peek() {
61
+ if (this.high.length > 0) {
62
+ return this.high[0];
63
+ }
64
+ if (this.normal.length > 0) {
65
+ return this.normal[0];
66
+ }
67
+ if (this.low.length > 0) {
68
+ return this.low[0];
69
+ }
70
+ return void 0;
71
+ }
72
+ /**
73
+ * Get total queue size.
74
+ */
75
+ size() {
76
+ return this.high.length + this.normal.length + this.low.length;
77
+ }
78
+ /**
79
+ * Get queue size by priority.
80
+ */
81
+ sizeByPriority() {
82
+ return {
83
+ high: this.high.length,
84
+ normal: this.normal.length,
85
+ low: this.low.length
86
+ };
87
+ }
88
+ /**
89
+ * Check if queue is empty.
90
+ */
91
+ isEmpty() {
92
+ return this.size() === 0;
93
+ }
94
+ /**
95
+ * Clear all items from the queue.
96
+ *
97
+ * @returns All removed items (for cleanup/rejection)
98
+ */
99
+ clear() {
100
+ const all = [...this.high, ...this.normal, ...this.low];
101
+ this.high = [];
102
+ this.normal = [];
103
+ this.low = [];
104
+ return all;
105
+ }
106
+ /**
107
+ * Remove a specific item by request ID.
108
+ *
109
+ * @param requestId - ID of the request to remove
110
+ * @returns The removed item or undefined if not found
111
+ */
112
+ remove(requestId) {
113
+ const highIndex = this.high.findIndex((item) => item.request.id === requestId);
114
+ if (highIndex !== -1) {
115
+ return this.high.splice(highIndex, 1)[0];
116
+ }
117
+ const normalIndex = this.normal.findIndex((item) => item.request.id === requestId);
118
+ if (normalIndex !== -1) {
119
+ return this.normal.splice(normalIndex, 1)[0];
120
+ }
121
+ const lowIndex = this.low.findIndex((item) => item.request.id === requestId);
122
+ if (lowIndex !== -1) {
123
+ return this.low.splice(lowIndex, 1)[0];
124
+ }
125
+ return void 0;
126
+ }
127
+ /**
128
+ * Get all items for a specific resource.
129
+ *
130
+ * @param resource - Resource identifier
131
+ * @returns Items matching the resource
132
+ */
133
+ getByResource(resource) {
134
+ return [
135
+ ...this.high.filter((item) => item.request.resource === resource),
136
+ ...this.normal.filter((item) => item.request.resource === resource),
137
+ ...this.low.filter((item) => item.request.resource === resource)
138
+ ];
139
+ }
140
+ /**
141
+ * Get queue size for a specific resource.
142
+ *
143
+ * @param resource - Resource identifier
144
+ */
145
+ sizeByResource(resource) {
146
+ return this.high.filter((item) => item.request.resource === resource).length + this.normal.filter((item) => item.request.resource === resource).length + this.low.filter((item) => item.request.resource === resource).length;
147
+ }
148
+ /**
149
+ * Iterate over all items (for inspection, not removal).
150
+ */
151
+ *[Symbol.iterator]() {
152
+ yield* this.high;
153
+ yield* this.normal;
154
+ yield* this.low;
155
+ }
156
+ };
157
+
158
+ // src/rate-limit/presets.ts
159
+ var RATE_LIMIT_PRESETS = {
160
+ // ═══════════════════════════════════════════════════════════════════════════
161
+ // OPENAI
162
+ // https://platform.openai.com/docs/guides/rate-limits
163
+ // ═══════════════════════════════════════════════════════════════════════════
164
+ /**
165
+ * OpenAI Tier 1 (paid accounts, entry level)
166
+ * Updated Nov 2024: Tier 1 now has 1M TPM for embeddings
167
+ */
168
+ "openai-tier-1": {
169
+ tokensPerMinute: 1e6,
170
+ requestsPerMinute: 3e3,
171
+ maxTokensPerRequest: 8191,
172
+ safetyMargin: 0.85
173
+ // More conservative to avoid edge cases
174
+ },
175
+ /**
176
+ * OpenAI Tier 2 (after $50+ spent)
177
+ */
178
+ "openai-tier-2": {
179
+ tokensPerMinute: 2e6,
180
+ requestsPerMinute: 5e3,
181
+ maxTokensPerRequest: 8191,
182
+ safetyMargin: 0.9
183
+ },
184
+ /**
185
+ * OpenAI Tier 3
186
+ */
187
+ "openai-tier-3": {
188
+ tokensPerMinute: 5e6,
189
+ requestsPerMinute: 5e3,
190
+ maxTokensPerRequest: 8191,
191
+ safetyMargin: 0.9
192
+ },
193
+ /**
194
+ * OpenAI Tier 4
195
+ */
196
+ "openai-tier-4": {
197
+ tokensPerMinute: 1e7,
198
+ requestsPerMinute: 1e4,
199
+ maxTokensPerRequest: 8191,
200
+ safetyMargin: 0.9
201
+ },
202
+ /**
203
+ * OpenAI Tier 5 (enterprise)
204
+ */
205
+ "openai-tier-5": {
206
+ tokensPerMinute: 5e7,
207
+ requestsPerMinute: 1e4,
208
+ maxTokensPerRequest: 8191,
209
+ safetyMargin: 0.9
210
+ },
211
+ /**
212
+ * OpenAI GPT-4 specific limits (more restrictive)
213
+ */
214
+ "openai-gpt4": {
215
+ tokensPerMinute: 15e4,
216
+ requestsPerMinute: 500,
217
+ maxTokensPerRequest: 8192,
218
+ safetyMargin: 0.85
219
+ },
220
+ // ═══════════════════════════════════════════════════════════════════════════
221
+ // ANTHROPIC
222
+ // https://docs.anthropic.com/en/api/rate-limits
223
+ // ═══════════════════════════════════════════════════════════════════════════
224
+ /**
225
+ * Anthropic Tier 1 (default)
226
+ */
227
+ "anthropic-tier-1": {
228
+ tokensPerMinute: 4e4,
229
+ requestsPerMinute: 50,
230
+ maxTokensPerRequest: 4096,
231
+ safetyMargin: 0.85
232
+ },
233
+ /**
234
+ * Anthropic Tier 2
235
+ */
236
+ "anthropic-tier-2": {
237
+ tokensPerMinute: 8e4,
238
+ requestsPerMinute: 100,
239
+ maxTokensPerRequest: 4096,
240
+ safetyMargin: 0.9
241
+ },
242
+ /**
243
+ * Anthropic Tier 3
244
+ */
245
+ "anthropic-tier-3": {
246
+ tokensPerMinute: 16e4,
247
+ requestsPerMinute: 200,
248
+ maxTokensPerRequest: 4096,
249
+ safetyMargin: 0.9
250
+ },
251
+ /**
252
+ * Anthropic Tier 4
253
+ */
254
+ "anthropic-tier-4": {
255
+ tokensPerMinute: 4e5,
256
+ requestsPerMinute: 400,
257
+ maxTokensPerRequest: 4096,
258
+ safetyMargin: 0.9
259
+ },
260
+ // ═══════════════════════════════════════════════════════════════════════════
261
+ // RUSSIAN PROVIDERS
262
+ // ═══════════════════════════════════════════════════════════════════════════
263
+ /**
264
+ * Sber GigaChat API
265
+ * Conservative limits for typical access
266
+ */
267
+ "sber-gigachat": {
268
+ requestsPerMinute: 100,
269
+ requestsPerSecond: 5,
270
+ safetyMargin: 0.8
271
+ },
272
+ /**
273
+ * Yandex GPT API
274
+ */
275
+ "yandex-gpt": {
276
+ requestsPerMinute: 100,
277
+ requestsPerSecond: 10,
278
+ safetyMargin: 0.8
279
+ },
280
+ // ═══════════════════════════════════════════════════════════════════════════
281
+ // LOCAL MODELS
282
+ // ═══════════════════════════════════════════════════════════════════════════
283
+ /**
284
+ * Local Ollama
285
+ * No external rate limits, only GPU concurrency
286
+ */
287
+ "ollama-local": {
288
+ maxConcurrentRequests: 4
289
+ },
290
+ /**
291
+ * Self-hosted vLLM
292
+ */
293
+ "vllm-local": {
294
+ maxConcurrentRequests: 8,
295
+ requestsPerSecond: 100
296
+ },
297
+ /**
298
+ * Self-hosted text-embeddings-inference
299
+ */
300
+ "tei-local": {
301
+ maxConcurrentRequests: 16
302
+ },
303
+ // ═══════════════════════════════════════════════════════════════════════════
304
+ // SPECIAL
305
+ // ═══════════════════════════════════════════════════════════════════════════
306
+ /**
307
+ * No rate limiting (for testing or unlimited APIs)
308
+ */
309
+ unlimited: {},
310
+ /**
311
+ * Very conservative (for debugging rate limit issues)
312
+ */
313
+ debug: {
314
+ tokensPerMinute: 1e4,
315
+ requestsPerMinute: 10,
316
+ maxConcurrentRequests: 1,
317
+ safetyMargin: 0.5
318
+ }
319
+ };
320
+ function getRateLimitConfig(configOrPreset) {
321
+ if (!configOrPreset) {
322
+ return RATE_LIMIT_PRESETS["openai-tier-2"];
323
+ }
324
+ if (typeof configOrPreset === "string") {
325
+ const preset = RATE_LIMIT_PRESETS[configOrPreset];
326
+ if (!preset) {
327
+ throw new Error(`Unknown rate limit preset: ${configOrPreset}`);
328
+ }
329
+ return preset;
330
+ }
331
+ return configOrPreset;
332
+ }
333
+ function estimateTokens(text) {
334
+ return Math.ceil(text.length / 3.5);
335
+ }
336
+ function estimateBatchTokens(texts) {
337
+ return texts.reduce((sum, text) => sum + estimateTokens(text), 0);
338
+ }
339
+
340
+ // src/retry/error-classifier.ts
341
+ function classifyError(error) {
342
+ if (!error) {
343
+ return "unknown";
344
+ }
345
+ if (error instanceof Error) {
346
+ const message = error.message.toLowerCase();
347
+ const name = error.name.toLowerCase();
348
+ if (message.includes("429") || message.includes("rate limit") || message.includes("too many requests") || message.includes("quota exceeded") || name.includes("ratelimit")) {
349
+ return "rate_limit";
350
+ }
351
+ if (message.includes("timeout") || message.includes("timed out") || message.includes("etimedout") || message.includes("deadline exceeded") || name.includes("timeout")) {
352
+ return "timeout";
353
+ }
354
+ if (message.includes("econnrefused") || message.includes("econnreset") || message.includes("enotfound") || message.includes("network") || message.includes("socket") || message.includes("dns") || name.includes("fetch") || name.includes("network")) {
355
+ return "network";
356
+ }
357
+ if (message.includes("500") || message.includes("502") || message.includes("503") || message.includes("504") || message.includes("internal server") || message.includes("bad gateway") || message.includes("service unavailable") || message.includes("gateway timeout")) {
358
+ return "server_error";
359
+ }
360
+ if (message.includes("400") || message.includes("401") || message.includes("403") || message.includes("404") || message.includes("bad request") || message.includes("unauthorized") || message.includes("forbidden") || message.includes("not found")) {
361
+ return "client_error";
362
+ }
363
+ }
364
+ if (typeof error === "object" && error !== null) {
365
+ const obj = error;
366
+ const status = obj.status ?? obj.statusCode ?? obj.code;
367
+ if (typeof status === "number") {
368
+ if (status === 429) {
369
+ return "rate_limit";
370
+ }
371
+ if (status >= 500) {
372
+ return "server_error";
373
+ }
374
+ if (status >= 400) {
375
+ return "client_error";
376
+ }
377
+ }
378
+ const code = obj.code;
379
+ if (typeof code === "string") {
380
+ const lowerCode = code.toLowerCase();
381
+ if (lowerCode.includes("timeout")) {
382
+ return "timeout";
383
+ }
384
+ if (lowerCode.includes("econnrefused")) {
385
+ return "network";
386
+ }
387
+ if (lowerCode.includes("econnreset")) {
388
+ return "network";
389
+ }
390
+ if (lowerCode.includes("enotfound")) {
391
+ return "network";
392
+ }
393
+ }
394
+ }
395
+ return "unknown";
396
+ }
397
+ function isRateLimitError(error) {
398
+ return classifyError(error) === "rate_limit";
399
+ }
400
+ function isRetryableError(error, retryableTypes = ["rate_limit", "server_error", "timeout", "network"]) {
401
+ const errorType = classifyError(error);
402
+ return retryableTypes.includes(errorType);
403
+ }
404
+ function extractRetryAfter(error) {
405
+ if (!error || typeof error !== "object") {
406
+ return void 0;
407
+ }
408
+ const obj = error;
409
+ const retryAfter = obj.retryAfter ?? obj["retry-after"] ?? obj.retryAfterMs ?? obj.headers?.["retry-after"];
410
+ if (typeof retryAfter === "number") {
411
+ return retryAfter < 1e3 ? retryAfter * 1e3 : retryAfter;
412
+ }
413
+ if (typeof retryAfter === "string") {
414
+ const parsed = parseInt(retryAfter, 10);
415
+ if (!isNaN(parsed)) {
416
+ return parsed < 1e3 ? parsed * 1e3 : parsed;
417
+ }
418
+ }
419
+ return void 0;
420
+ }
421
+
422
+ // src/retry/retry-strategy.ts
423
+ function calculateBackoffDelay(attempt, config, retryAfterHint) {
424
+ if (retryAfterHint !== void 0 && retryAfterHint > 0) {
425
+ return Math.min(retryAfterHint, config.maxDelay);
426
+ }
427
+ const exponentialDelay = config.baseDelay * Math.pow(2, attempt);
428
+ const cappedDelay = Math.min(exponentialDelay, config.maxDelay);
429
+ const jitterMultiplier = 1 + Math.random() * config.jitter;
430
+ return Math.floor(cappedDelay * jitterMultiplier);
431
+ }
432
+ function shouldRetry(error, attempt, config = {}) {
433
+ const fullConfig = {
434
+ ...DEFAULT_RETRY_CONFIG,
435
+ ...config
436
+ };
437
+ const errorType = classifyError(error);
438
+ const isRetryable = isRetryableError(error, fullConfig.retryableErrors);
439
+ const hasAttemptsLeft = attempt < fullConfig.maxRetries;
440
+ const shouldRetryNow = isRetryable && hasAttemptsLeft;
441
+ let delayMs = 0;
442
+ if (shouldRetryNow) {
443
+ const retryAfterHint = extractRetryAfter(error);
444
+ if (errorType === "rate_limit") {
445
+ const rateLimitConfig = {
446
+ ...fullConfig,
447
+ baseDelay: Math.max(fullConfig.baseDelay, 5e3)
448
+ // At least 5s for rate limits
449
+ };
450
+ delayMs = calculateBackoffDelay(attempt, rateLimitConfig, retryAfterHint);
451
+ } else {
452
+ delayMs = calculateBackoffDelay(attempt, fullConfig, retryAfterHint);
453
+ }
454
+ }
455
+ return {
456
+ shouldRetry: shouldRetryNow,
457
+ delayMs,
458
+ errorType,
459
+ attempt,
460
+ maxAttempts: fullConfig.maxRetries
461
+ };
462
+ }
463
+ async function withRetry(fn, config = {}) {
464
+ const fullConfig = {
465
+ ...DEFAULT_RETRY_CONFIG,
466
+ ...config
467
+ };
468
+ let lastError;
469
+ let attempts = 0;
470
+ for (let attempt = 0; attempt <= fullConfig.maxRetries; attempt++) {
471
+ attempts = attempt + 1;
472
+ try {
473
+ const result = await fn();
474
+ return { result, attempts };
475
+ } catch (error) {
476
+ lastError = error;
477
+ const decision = shouldRetry(error, attempt, fullConfig);
478
+ if (!decision.shouldRetry) {
479
+ break;
480
+ }
481
+ await sleep(decision.delayMs);
482
+ }
483
+ }
484
+ throw lastError;
485
+ }
486
+ function sleep(ms) {
487
+ return new Promise((resolve) => {
488
+ setTimeout(resolve, ms);
489
+ });
490
+ }
491
+ function createRateLimitRetryConfig(maxRetries = 5) {
492
+ return {
493
+ maxRetries,
494
+ baseDelay: 5e3,
495
+ // Start with 5s for rate limits
496
+ maxDelay: 6e4,
497
+ // Cap at 1 minute
498
+ jitter: 0.2,
499
+ // 20% jitter for distributed systems
500
+ retryableErrors: ["rate_limit", "server_error", "timeout", "network"]
501
+ };
502
+ }
503
+ function createQuickRetryConfig(maxRetries = 3) {
504
+ return {
505
+ maxRetries,
506
+ baseDelay: 500,
507
+ // Start with 500ms
508
+ maxDelay: 5e3,
509
+ // Cap at 5s
510
+ jitter: 0.1,
511
+ // 10% jitter
512
+ retryableErrors: ["server_error", "timeout", "network"]
513
+ };
514
+ }
515
+
516
+ // src/broker/resource-broker.ts
517
+ var ResourceBroker = class {
518
+ constructor(rateLimitBackend) {
519
+ this.rateLimitBackend = rateLimitBackend;
520
+ }
521
+ resources = /* @__PURE__ */ new Map();
522
+ queue = new PriorityQueue();
523
+ processing = false;
524
+ shuttingDown = false;
525
+ startTime = Date.now();
526
+ /**
527
+ * Active processing count per resource (for concurrent limit tracking).
528
+ */
529
+ activeProcessing = /* @__PURE__ */ new Map();
530
+ /**
531
+ * Register a resource with its configuration.
532
+ *
533
+ * @param resource - Resource identifier ('llm', 'embeddings', 'vectorStore')
534
+ * @param config - Resource configuration including executor
535
+ */
536
+ register(resource, config) {
537
+ const rateLimits = typeof config.rateLimits === "string" ? getRateLimitConfig(config.rateLimits) : config.rateLimits ?? DEFAULT_RATE_LIMIT_CONFIG;
538
+ this.resources.set(resource, {
539
+ config,
540
+ rateLimits,
541
+ stats: {
542
+ totalRequests: 0,
543
+ totalSuccess: 0,
544
+ totalErrors: 0,
545
+ totalWaitTime: 0,
546
+ totalProcessingTime: 0
547
+ }
548
+ });
549
+ this.activeProcessing.set(resource, 0);
550
+ }
551
+ /**
552
+ * Enqueue a request for execution.
553
+ *
554
+ * @param request - Resource request (without id and createdAt)
555
+ * @returns Promise that resolves with response when execution completes
556
+ */
557
+ enqueue(request) {
558
+ if (this.shuttingDown) {
559
+ return Promise.resolve({
560
+ success: false,
561
+ error: new Error("ResourceBroker is shutting down"),
562
+ retries: 0,
563
+ waitTime: 0,
564
+ processingTime: 0,
565
+ totalTime: 0
566
+ });
567
+ }
568
+ const registered = this.resources.get(request.resource);
569
+ if (!registered) {
570
+ return Promise.resolve({
571
+ success: false,
572
+ error: new Error(`Resource not registered: ${request.resource}`),
573
+ retries: 0,
574
+ waitTime: 0,
575
+ processingTime: 0,
576
+ totalTime: 0
577
+ });
578
+ }
579
+ const fullRequest = {
580
+ ...request,
581
+ id: randomUUID(),
582
+ createdAt: Date.now(),
583
+ timeout: request.timeout ?? registered.config.timeout ?? 6e4,
584
+ maxRetries: request.maxRetries ?? registered.config.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries
585
+ };
586
+ return new Promise((resolve, reject) => {
587
+ const item = {
588
+ request: fullRequest,
589
+ resolve,
590
+ reject,
591
+ enqueuedAt: Date.now()
592
+ };
593
+ this.queue.enqueue(item);
594
+ registered.stats.totalRequests++;
595
+ this.processQueue();
596
+ });
597
+ }
598
+ /**
599
+ * Process queue items continuously.
600
+ */
601
+ async processQueue() {
602
+ if (this.processing) {
603
+ return;
604
+ }
605
+ this.processing = true;
606
+ try {
607
+ while (!this.queue.isEmpty() && !this.shuttingDown) {
608
+ const item = this.queue.peek();
609
+ if (!item) {
610
+ break;
611
+ }
612
+ const registered = this.resources.get(item.request.resource);
613
+ if (!registered) {
614
+ this.queue.dequeue();
615
+ item.reject(new Error(`Resource not registered: ${item.request.resource}`));
616
+ continue;
617
+ }
618
+ const tokens = item.request.estimatedTokens ?? 0;
619
+ const acquireResult = await this.rateLimitBackend.acquire(
620
+ item.request.resource,
621
+ tokens,
622
+ registered.rateLimits
623
+ );
624
+ if (!acquireResult.allowed) {
625
+ await sleep(acquireResult.waitTimeMs ?? 100);
626
+ continue;
627
+ }
628
+ this.queue.dequeue();
629
+ const currentActive = this.activeProcessing.get(item.request.resource) ?? 0;
630
+ this.activeProcessing.set(item.request.resource, currentActive + 1);
631
+ this.executeItem(item, registered).catch(() => {
632
+ });
633
+ }
634
+ } finally {
635
+ this.processing = false;
636
+ if (!this.queue.isEmpty() && !this.shuttingDown) {
637
+ setImmediate(() => this.processQueue());
638
+ }
639
+ }
640
+ }
641
+ /**
642
+ * Execute a single queue item with retry logic.
643
+ */
644
+ async executeItem(item, registered) {
645
+ const startTime = Date.now();
646
+ const waitTime = startTime - item.enqueuedAt;
647
+ let retries = 0;
648
+ let lastError;
649
+ const retryConfig = {
650
+ maxRetries: item.request.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries,
651
+ baseDelay: registered.config.baseDelay ?? DEFAULT_RETRY_CONFIG.baseDelay,
652
+ maxDelay: registered.config.maxDelay ?? DEFAULT_RETRY_CONFIG.maxDelay,
653
+ jitter: DEFAULT_RETRY_CONFIG.jitter,
654
+ retryableErrors: DEFAULT_RETRY_CONFIG.retryableErrors
655
+ };
656
+ try {
657
+ for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
658
+ try {
659
+ const timeoutMs = item.request.timeout ?? 6e4;
660
+ const timeoutPromise = new Promise((_, reject) => {
661
+ setTimeout(() => reject(new Error(`Request timeout after ${timeoutMs}ms`)), timeoutMs);
662
+ });
663
+ const executionPromise = registered.config.executor(
664
+ item.request.operation,
665
+ item.request.args
666
+ );
667
+ const result = await Promise.race([executionPromise, timeoutPromise]);
668
+ const endTime2 = Date.now();
669
+ const processingTime2 = endTime2 - startTime;
670
+ registered.stats.totalSuccess++;
671
+ registered.stats.totalWaitTime += waitTime;
672
+ registered.stats.totalProcessingTime += processingTime2;
673
+ item.resolve({
674
+ success: true,
675
+ data: result,
676
+ retries,
677
+ waitTime,
678
+ processingTime: processingTime2,
679
+ totalTime: endTime2 - item.enqueuedAt
680
+ });
681
+ return;
682
+ } catch (error) {
683
+ lastError = error instanceof Error ? error : new Error(String(error));
684
+ retries = attempt;
685
+ const decision = shouldRetry(error, attempt, retryConfig);
686
+ if (!decision.shouldRetry) {
687
+ break;
688
+ }
689
+ await sleep(decision.delayMs);
690
+ const tokens = item.request.estimatedTokens ?? 0;
691
+ const acquireResult = await this.rateLimitBackend.acquire(
692
+ item.request.resource,
693
+ tokens,
694
+ registered.rateLimits
695
+ );
696
+ if (!acquireResult.allowed && acquireResult.waitTimeMs) {
697
+ await sleep(acquireResult.waitTimeMs);
698
+ }
699
+ }
700
+ }
701
+ const endTime = Date.now();
702
+ const processingTime = endTime - startTime;
703
+ registered.stats.totalErrors++;
704
+ registered.stats.totalWaitTime += waitTime;
705
+ registered.stats.totalProcessingTime += processingTime;
706
+ item.resolve({
707
+ success: false,
708
+ error: lastError,
709
+ retries,
710
+ waitTime,
711
+ processingTime,
712
+ totalTime: endTime - item.enqueuedAt
713
+ });
714
+ } finally {
715
+ await this.rateLimitBackend.release(item.request.resource);
716
+ const currentActive = this.activeProcessing.get(item.request.resource) ?? 1;
717
+ this.activeProcessing.set(item.request.resource, Math.max(0, currentActive - 1));
718
+ }
719
+ }
720
+ /**
721
+ * Get broker statistics.
722
+ */
723
+ getStats() {
724
+ const resources = {};
725
+ let totalRequests = 0;
726
+ let totalSuccess = 0;
727
+ let totalErrors = 0;
728
+ for (const [resourceName, registered] of this.resources) {
729
+ const queueByPriority = this.queue.sizeByPriority();
730
+ const queueSize = this.queue.sizeByResource(resourceName);
731
+ const activeRequests = this.activeProcessing.get(resourceName) ?? 0;
732
+ const rateLimitStats = {
733
+ resource: resourceName,
734
+ tokensThisMinute: 0,
735
+ requestsThisMinute: 0,
736
+ requestsThisSecond: 0,
737
+ activeRequests,
738
+ totalRequests: registered.stats.totalRequests,
739
+ totalTokens: 0,
740
+ waitCount: 0,
741
+ totalWaitTime: registered.stats.totalWaitTime
742
+ };
743
+ const avgWaitTime = registered.stats.totalRequests > 0 ? registered.stats.totalWaitTime / registered.stats.totalRequests : 0;
744
+ const avgProcessingTime = registered.stats.totalRequests > 0 ? registered.stats.totalProcessingTime / registered.stats.totalRequests : 0;
745
+ resources[resourceName] = {
746
+ rateLimits: rateLimitStats,
747
+ queueSize,
748
+ queueByPriority,
749
+ totalRequests: registered.stats.totalRequests,
750
+ totalSuccess: registered.stats.totalSuccess,
751
+ totalErrors: registered.stats.totalErrors,
752
+ avgWaitTime,
753
+ avgProcessingTime
754
+ };
755
+ totalRequests += registered.stats.totalRequests;
756
+ totalSuccess += registered.stats.totalSuccess;
757
+ totalErrors += registered.stats.totalErrors;
758
+ }
759
+ return {
760
+ resources,
761
+ totalRequests,
762
+ totalSuccess,
763
+ totalErrors,
764
+ queueSize: this.queue.size(),
765
+ uptime: Date.now() - this.startTime
766
+ };
767
+ }
768
+ /**
769
+ * Graceful shutdown - drain queues and stop processing.
770
+ *
771
+ * @param timeoutMs - Maximum time to wait for drain (default: 30000)
772
+ */
773
+ async shutdown(timeoutMs = 3e4) {
774
+ this.shuttingDown = true;
775
+ const startTime = Date.now();
776
+ while (!this.queue.isEmpty() && Date.now() - startTime < timeoutMs) {
777
+ await sleep(100);
778
+ }
779
+ const remaining = this.queue.clear();
780
+ for (const item of remaining) {
781
+ item.resolve({
782
+ success: false,
783
+ error: new Error("ResourceBroker shutdown"),
784
+ retries: 0,
785
+ waitTime: Date.now() - item.enqueuedAt,
786
+ processingTime: 0,
787
+ totalTime: Date.now() - item.enqueuedAt
788
+ });
789
+ }
790
+ }
791
+ /**
792
+ * Check if broker is shutting down.
793
+ */
794
+ isShuttingDown() {
795
+ return this.shuttingDown;
796
+ }
797
+ /**
798
+ * Get registered resource names.
799
+ */
800
+ getRegisteredResources() {
801
+ return Array.from(this.resources.keys());
802
+ }
803
+ /**
804
+ * Check if a resource is registered.
805
+ */
806
+ hasResource(resource) {
807
+ return this.resources.has(resource);
808
+ }
809
+ /**
810
+ * Unregister a resource.
811
+ * Note: Pending requests for this resource will fail.
812
+ */
813
+ unregister(resource) {
814
+ this.resources.delete(resource);
815
+ this.activeProcessing.delete(resource);
816
+ }
817
+ };
818
+
819
+ // src/rate-limit/in-memory-backend.ts
820
+ var InMemoryRateLimitBackend = class {
821
+ states = /* @__PURE__ */ new Map();
822
+ /**
823
+ * Get or create state for a resource.
824
+ */
825
+ getState(resource) {
826
+ let state = this.states.get(resource);
827
+ if (!state) {
828
+ const now = Date.now();
829
+ state = {
830
+ tokensThisMinute: 0,
831
+ requestsThisMinute: 0,
832
+ requestsThisSecond: 0,
833
+ activeRequests: 0,
834
+ minuteWindowStart: now,
835
+ secondWindowStart: now,
836
+ totalRequests: 0,
837
+ totalTokens: 0,
838
+ waitCount: 0,
839
+ totalWaitTime: 0
840
+ };
841
+ this.states.set(resource, state);
842
+ }
843
+ return state;
844
+ }
845
+ /**
846
+ * Reset windows if time has passed.
847
+ */
848
+ resetWindowsIfNeeded(state) {
849
+ const now = Date.now();
850
+ if (now - state.minuteWindowStart >= 6e4) {
851
+ state.tokensThisMinute = 0;
852
+ state.requestsThisMinute = 0;
853
+ state.minuteWindowStart = now;
854
+ }
855
+ if (now - state.secondWindowStart >= 1e3) {
856
+ state.requestsThisSecond = 0;
857
+ state.secondWindowStart = now;
858
+ }
859
+ }
860
+ /**
861
+ * Check if all limits allow proceeding.
862
+ */
863
+ checkLimits(state, tokens, config) {
864
+ const now = Date.now();
865
+ const safetyMargin = config.safetyMargin ?? 0.9;
866
+ const effectiveTPM = config.tokensPerMinute ? Math.floor(config.tokensPerMinute * safetyMargin) : void 0;
867
+ const effectiveRPM = config.requestsPerMinute ? Math.floor(config.requestsPerMinute * safetyMargin) : void 0;
868
+ const effectiveRPS = config.requestsPerSecond ? Math.floor(config.requestsPerSecond * safetyMargin) : void 0;
869
+ const delays = [];
870
+ if (effectiveTPM !== void 0 && state.tokensThisMinute + tokens > effectiveTPM) {
871
+ const timeUntilMinuteReset = 6e4 - (now - state.minuteWindowStart);
872
+ delays.push(Math.max(100, timeUntilMinuteReset + 100));
873
+ }
874
+ if (effectiveRPM !== void 0 && state.requestsThisMinute >= effectiveRPM) {
875
+ const timeUntilMinuteReset = 6e4 - (now - state.minuteWindowStart);
876
+ delays.push(Math.max(100, timeUntilMinuteReset + 100));
877
+ }
878
+ if (effectiveRPS !== void 0 && state.requestsThisSecond >= effectiveRPS) {
879
+ const timeUntilSecondReset = 1e3 - (now - state.secondWindowStart);
880
+ delays.push(Math.max(50, timeUntilSecondReset + 50));
881
+ }
882
+ if (config.maxConcurrentRequests !== void 0 && state.activeRequests >= config.maxConcurrentRequests) {
883
+ delays.push(100);
884
+ }
885
+ if (delays.length > 0) {
886
+ return { allowed: false, waitTimeMs: Math.min(...delays) };
887
+ }
888
+ return { allowed: true };
889
+ }
890
+ /**
891
+ * @inheritdoc
892
+ */
893
+ async acquire(resource, tokens, config) {
894
+ const state = this.getState(resource);
895
+ this.resetWindowsIfNeeded(state);
896
+ const check = this.checkLimits(state, tokens, config);
897
+ if (!check.allowed) {
898
+ state.waitCount++;
899
+ state.totalWaitTime += check.waitTimeMs ?? 0;
900
+ const safetyMargin2 = config.safetyMargin ?? 0.9;
901
+ const effectiveTPM2 = config.tokensPerMinute ? Math.floor(config.tokensPerMinute * safetyMargin2) : void 0;
902
+ const effectiveRPM2 = config.requestsPerMinute ? Math.floor(config.requestsPerMinute * safetyMargin2) : void 0;
903
+ return {
904
+ allowed: false,
905
+ waitTimeMs: check.waitTimeMs,
906
+ tokensRemaining: effectiveTPM2 ? Math.max(0, effectiveTPM2 - state.tokensThisMinute) : void 0,
907
+ requestsRemaining: effectiveRPM2 ? Math.max(0, effectiveRPM2 - state.requestsThisMinute) : void 0,
908
+ activeRequests: state.activeRequests
909
+ };
910
+ }
911
+ state.tokensThisMinute += tokens;
912
+ state.requestsThisMinute++;
913
+ state.requestsThisSecond++;
914
+ state.activeRequests++;
915
+ state.totalRequests++;
916
+ state.totalTokens += tokens;
917
+ const safetyMargin = config.safetyMargin ?? 0.9;
918
+ const effectiveTPM = config.tokensPerMinute ? Math.floor(config.tokensPerMinute * safetyMargin) : void 0;
919
+ const effectiveRPM = config.requestsPerMinute ? Math.floor(config.requestsPerMinute * safetyMargin) : void 0;
920
+ return {
921
+ allowed: true,
922
+ tokensRemaining: effectiveTPM ? Math.max(0, effectiveTPM - state.tokensThisMinute) : void 0,
923
+ requestsRemaining: effectiveRPM ? Math.max(0, effectiveRPM - state.requestsThisMinute) : void 0,
924
+ activeRequests: state.activeRequests
925
+ };
926
+ }
927
+ /**
928
+ * @inheritdoc
929
+ */
930
+ async release(resource) {
931
+ const state = this.states.get(resource);
932
+ if (state) {
933
+ state.activeRequests = Math.max(0, state.activeRequests - 1);
934
+ }
935
+ }
936
+ /**
937
+ * @inheritdoc
938
+ */
939
+ async getStats(resource) {
940
+ const state = this.getState(resource);
941
+ this.resetWindowsIfNeeded(state);
942
+ return {
943
+ resource,
944
+ tokensThisMinute: state.tokensThisMinute,
945
+ requestsThisMinute: state.requestsThisMinute,
946
+ requestsThisSecond: state.requestsThisSecond,
947
+ activeRequests: state.activeRequests,
948
+ totalRequests: state.totalRequests,
949
+ totalTokens: state.totalTokens,
950
+ waitCount: state.waitCount,
951
+ totalWaitTime: state.totalWaitTime
952
+ };
953
+ }
954
+ /**
955
+ * @inheritdoc
956
+ */
957
+ async reset(resource) {
958
+ this.states.delete(resource);
959
+ }
960
+ /**
961
+ * Reset all resources.
962
+ */
963
+ resetAll() {
964
+ this.states.clear();
965
+ }
966
+ };
967
+
968
+ // src/rate-limit/state-broker-backend.ts
969
+ var StateBrokerRateLimitBackend = class {
970
+ constructor(broker) {
971
+ this.broker = broker;
972
+ }
973
+ /**
974
+ * Get current minute window key.
975
+ */
976
+ getMinuteKey(resource) {
977
+ const window = (/* @__PURE__ */ new Date()).toISOString().slice(0, 16);
978
+ return `ratelimit:${resource}:minute:${window}`;
979
+ }
980
+ /**
981
+ * Get current second window key.
982
+ */
983
+ getSecondKey(resource) {
984
+ const window = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19);
985
+ return `ratelimit:${resource}:second:${window}`;
986
+ }
987
+ /**
988
+ * Get active requests key.
989
+ */
990
+ getActiveKey(resource) {
991
+ return `ratelimit:${resource}:active`;
992
+ }
993
+ /**
994
+ * Get stats key.
995
+ */
996
+ getStatsKey(resource) {
997
+ return `ratelimit:${resource}:stats`;
998
+ }
999
+ /**
1000
+ * Get or initialize window state.
1001
+ */
1002
+ async getWindowState(key) {
1003
+ const state = await this.broker.get(key);
1004
+ return state ?? { tokens: 0, requests: 0, activeRequests: 0, updatedAt: Date.now() };
1005
+ }
1006
+ /**
1007
+ * Get or initialize stats.
1008
+ */
1009
+ async getCumulativeStats(resource) {
1010
+ const stats = await this.broker.get(this.getStatsKey(resource));
1011
+ return stats ?? { totalRequests: 0, totalTokens: 0, waitCount: 0, totalWaitTime: 0 };
1012
+ }
1013
+ /**
1014
+ * @inheritdoc
1015
+ */
1016
+ async acquire(resource, tokens, config) {
1017
+ const safetyMargin = config.safetyMargin ?? 0.9;
1018
+ const [minuteState, secondState, activeCount] = await Promise.all([
1019
+ this.getWindowState(this.getMinuteKey(resource)),
1020
+ this.getWindowState(this.getSecondKey(resource)),
1021
+ this.broker.get(this.getActiveKey(resource)).then((v) => v ?? 0)
1022
+ ]);
1023
+ const effectiveTPM = config.tokensPerMinute ? Math.floor(config.tokensPerMinute * safetyMargin) : void 0;
1024
+ const effectiveRPM = config.requestsPerMinute ? Math.floor(config.requestsPerMinute * safetyMargin) : void 0;
1025
+ const effectiveRPS = config.requestsPerSecond ? Math.floor(config.requestsPerSecond * safetyMargin) : void 0;
1026
+ const delays = [];
1027
+ const now = Date.now();
1028
+ if (effectiveTPM && minuteState.tokens + tokens > effectiveTPM) {
1029
+ const secondsRemaining = 60 - (/* @__PURE__ */ new Date()).getSeconds();
1030
+ delays.push(secondsRemaining * 1e3 + 100);
1031
+ }
1032
+ if (effectiveRPM && minuteState.requests >= effectiveRPM) {
1033
+ const secondsRemaining = 60 - (/* @__PURE__ */ new Date()).getSeconds();
1034
+ delays.push(secondsRemaining * 1e3 + 100);
1035
+ }
1036
+ if (effectiveRPS && secondState.requests >= effectiveRPS) {
1037
+ const msRemaining = 1e3 - (/* @__PURE__ */ new Date()).getMilliseconds();
1038
+ delays.push(msRemaining + 50);
1039
+ }
1040
+ if (config.maxConcurrentRequests && activeCount >= config.maxConcurrentRequests) {
1041
+ delays.push(100);
1042
+ }
1043
+ if (delays.length > 0) {
1044
+ const stats2 = await this.getCumulativeStats(resource);
1045
+ stats2.waitCount++;
1046
+ stats2.totalWaitTime += Math.min(...delays);
1047
+ await this.broker.set(this.getStatsKey(resource), stats2);
1048
+ return {
1049
+ allowed: false,
1050
+ waitTimeMs: Math.min(...delays),
1051
+ tokensRemaining: effectiveTPM ? Math.max(0, effectiveTPM - minuteState.tokens) : void 0,
1052
+ requestsRemaining: effectiveRPM ? Math.max(0, effectiveRPM - minuteState.requests) : void 0,
1053
+ activeRequests: activeCount
1054
+ };
1055
+ }
1056
+ const minuteKey = this.getMinuteKey(resource);
1057
+ const secondKey = this.getSecondKey(resource);
1058
+ const activeKey = this.getActiveKey(resource);
1059
+ const statsKey = this.getStatsKey(resource);
1060
+ const newMinuteState = {
1061
+ tokens: minuteState.tokens + tokens,
1062
+ requests: minuteState.requests + 1,
1063
+ activeRequests: activeCount + 1,
1064
+ updatedAt: now
1065
+ };
1066
+ await this.broker.set(minuteKey, newMinuteState, 12e4);
1067
+ const newSecondState = {
1068
+ tokens: secondState.tokens + tokens,
1069
+ requests: secondState.requests + 1,
1070
+ activeRequests: activeCount + 1,
1071
+ updatedAt: now
1072
+ };
1073
+ await this.broker.set(secondKey, newSecondState, 1e4);
1074
+ await this.broker.set(activeKey, activeCount + 1);
1075
+ const stats = await this.getCumulativeStats(resource);
1076
+ stats.totalRequests++;
1077
+ stats.totalTokens += tokens;
1078
+ await this.broker.set(statsKey, stats);
1079
+ return {
1080
+ allowed: true,
1081
+ tokensRemaining: effectiveTPM ? Math.max(0, effectiveTPM - newMinuteState.tokens) : void 0,
1082
+ requestsRemaining: effectiveRPM ? Math.max(0, effectiveRPM - newMinuteState.requests) : void 0,
1083
+ activeRequests: activeCount + 1
1084
+ };
1085
+ }
1086
+ /**
1087
+ * @inheritdoc
1088
+ */
1089
+ async release(resource) {
1090
+ const activeKey = this.getActiveKey(resource);
1091
+ const current = await this.broker.get(activeKey);
1092
+ if (current !== null && current > 0) {
1093
+ await this.broker.set(activeKey, current - 1);
1094
+ }
1095
+ }
1096
+ /**
1097
+ * @inheritdoc
1098
+ */
1099
+ async getStats(resource) {
1100
+ const [minuteState, secondState, activeCount, cumulativeStats] = await Promise.all([
1101
+ this.getWindowState(this.getMinuteKey(resource)),
1102
+ this.getWindowState(this.getSecondKey(resource)),
1103
+ this.broker.get(this.getActiveKey(resource)).then((v) => v ?? 0),
1104
+ this.getCumulativeStats(resource)
1105
+ ]);
1106
+ return {
1107
+ resource,
1108
+ tokensThisMinute: minuteState.tokens,
1109
+ requestsThisMinute: minuteState.requests,
1110
+ requestsThisSecond: secondState.requests,
1111
+ activeRequests: activeCount,
1112
+ totalRequests: cumulativeStats.totalRequests,
1113
+ totalTokens: cumulativeStats.totalTokens,
1114
+ waitCount: cumulativeStats.waitCount,
1115
+ totalWaitTime: cumulativeStats.totalWaitTime
1116
+ };
1117
+ }
1118
+ /**
1119
+ * @inheritdoc
1120
+ */
1121
+ async reset(resource) {
1122
+ await Promise.all([
1123
+ this.broker.clear(`ratelimit:${resource}:*`)
1124
+ ]);
1125
+ }
1126
+ };
1127
+
1128
+ // src/wrappers/queued-llm.ts
1129
+ var QueuedLLM = class {
1130
+ constructor(broker, realLLM) {
1131
+ this.broker = broker;
1132
+ this.realLLM = realLLM;
1133
+ }
1134
+ /**
1135
+ * Generate a completion through the queue.
1136
+ *
1137
+ * @param prompt - Text prompt
1138
+ * @param options - Optional generation options with priority
1139
+ * @returns LLM response
1140
+ * @throws Error if request fails after all retries
1141
+ */
1142
+ async complete(prompt, options) {
1143
+ const priority = options?.priority ?? "normal";
1144
+ const estimatedTokens = estimateTokens(prompt);
1145
+ const resource = options?.metadata?.resource ?? "llm";
1146
+ const response = await this.broker.enqueue({
1147
+ resource,
1148
+ operation: "complete",
1149
+ args: [prompt, options],
1150
+ priority,
1151
+ estimatedTokens
1152
+ });
1153
+ if (!response.success) {
1154
+ throw response.error ?? new Error("LLM request failed");
1155
+ }
1156
+ return response.data;
1157
+ }
1158
+ async getProtocolCapabilities() {
1159
+ if (!this.realLLM.getProtocolCapabilities) {
1160
+ return {
1161
+ cache: { supported: false },
1162
+ stream: { supported: true }
1163
+ };
1164
+ }
1165
+ return this.realLLM.getProtocolCapabilities();
1166
+ }
1167
+ /**
1168
+ * Stream a completion (bypasses queue for real-time UX).
1169
+ *
1170
+ * Streaming is passed through directly to the underlying LLM
1171
+ * because:
1172
+ * 1. Real-time user experience requires immediate response
1173
+ * 2. Token counting happens after streaming completes
1174
+ * 3. Rate limits are still enforced by the underlying adapter
1175
+ *
1176
+ * @param prompt - Text prompt
1177
+ * @param options - Optional generation options
1178
+ * @returns Async iterable of text chunks
1179
+ */
1180
+ stream(prompt, options) {
1181
+ return this.realLLM.stream(prompt, options);
1182
+ }
1183
+ /**
1184
+ * Chat with native tool calling support (proxies to underlying LLM).
1185
+ * Currently NOT queued - passes through directly for simplicity.
1186
+ * TODO: Add queueing support when needed.
1187
+ */
1188
+ async chatWithTools(messages, options) {
1189
+ if (!this.realLLM.chatWithTools) {
1190
+ throw new Error("Underlying LLM does not support chatWithTools");
1191
+ }
1192
+ return this.realLLM.chatWithTools(messages, options);
1193
+ }
1194
+ };
1195
+ function createQueuedLLM(broker, llm) {
1196
+ return new QueuedLLM(broker, llm);
1197
+ }
1198
+
1199
+ // src/wrappers/queued-embeddings.ts
1200
+ var QueuedEmbeddings = class {
1201
+ constructor(broker, realEmbeddings) {
1202
+ this.broker = broker;
1203
+ this.realEmbeddings = realEmbeddings;
1204
+ }
1205
+ _priority = "normal";
1206
+ /**
1207
+ * Get embedding dimensions from the underlying implementation.
1208
+ */
1209
+ get dimensions() {
1210
+ return this.realEmbeddings.dimensions;
1211
+ }
1212
+ /**
1213
+ * Set default priority for subsequent operations.
1214
+ *
1215
+ * @param priority - Priority level
1216
+ * @returns this for chaining
1217
+ */
1218
+ withPriority(priority) {
1219
+ this._priority = priority;
1220
+ return this;
1221
+ }
1222
+ /**
1223
+ * Generate embedding vector for a single text through the queue.
1224
+ *
1225
+ * @param text - Input text
1226
+ * @returns Embedding vector
1227
+ * @throws Error if request fails after all retries
1228
+ */
1229
+ async embed(text) {
1230
+ const estimatedTokens = estimateTokens(text);
1231
+ const response = await this.broker.enqueue({
1232
+ resource: "embeddings",
1233
+ operation: "embed",
1234
+ args: [text],
1235
+ priority: this._priority,
1236
+ estimatedTokens
1237
+ });
1238
+ if (!response.success) {
1239
+ throw response.error ?? new Error("Embeddings request failed");
1240
+ }
1241
+ return response.data;
1242
+ }
1243
+ /**
1244
+ * Generate embedding vectors for multiple texts through the queue.
1245
+ *
1246
+ * @param texts - Array of input texts
1247
+ * @returns Array of embedding vectors
1248
+ * @throws Error if request fails after all retries
1249
+ */
1250
+ async embedBatch(texts) {
1251
+ const estimatedTokens = estimateBatchTokens(texts);
1252
+ const response = await this.broker.enqueue({
1253
+ resource: "embeddings",
1254
+ operation: "embedBatch",
1255
+ args: [texts],
1256
+ priority: this._priority,
1257
+ estimatedTokens
1258
+ });
1259
+ if (!response.success) {
1260
+ throw response.error ?? new Error("Embeddings batch request failed");
1261
+ }
1262
+ return response.data;
1263
+ }
1264
+ /**
1265
+ * Get the dimensions of the embeddings.
1266
+ * This method is needed for IPC/Unix Socket transport to access the dimensions property.
1267
+ */
1268
+ async getDimensions() {
1269
+ return this.realEmbeddings.dimensions;
1270
+ }
1271
+ };
1272
+ function createQueuedEmbeddings(broker, embeddings) {
1273
+ return new QueuedEmbeddings(broker, embeddings);
1274
+ }
1275
+
1276
+ // src/wrappers/queued-vector-store.ts
1277
+ var QueuedVectorStore = class {
1278
+ constructor(broker, realVectorStore) {
1279
+ this.broker = broker;
1280
+ this.realVectorStore = realVectorStore;
1281
+ }
1282
+ _priority = "normal";
1283
+ /**
1284
+ * Set default priority for subsequent operations.
1285
+ *
1286
+ * @param priority - Priority level
1287
+ * @returns this for chaining
1288
+ */
1289
+ withPriority(priority) {
1290
+ this._priority = priority;
1291
+ return this;
1292
+ }
1293
+ /**
1294
+ * Search for similar vectors through the queue.
1295
+ *
1296
+ * @param query - Query embedding vector
1297
+ * @param limit - Maximum number of results
1298
+ * @param filter - Optional metadata filter
1299
+ * @returns Search results
1300
+ * @throws Error if request fails after all retries
1301
+ */
1302
+ async search(query, limit, filter) {
1303
+ const response = await this.broker.enqueue({
1304
+ resource: "vectorStore",
1305
+ operation: "search",
1306
+ args: [query, limit, filter],
1307
+ priority: this._priority
1308
+ // No token estimation for vector operations
1309
+ });
1310
+ if (!response.success) {
1311
+ throw response.error ?? new Error("VectorStore search failed");
1312
+ }
1313
+ return response.data;
1314
+ }
1315
+ /**
1316
+ * Upsert vectors through the queue.
1317
+ *
1318
+ * @param vectors - Array of vector records to upsert
1319
+ * @throws Error if request fails after all retries
1320
+ */
1321
+ async upsert(vectors) {
1322
+ const response = await this.broker.enqueue({
1323
+ resource: "vectorStore",
1324
+ operation: "upsert",
1325
+ args: [vectors],
1326
+ priority: this._priority
1327
+ });
1328
+ if (!response.success) {
1329
+ throw response.error ?? new Error("VectorStore upsert failed");
1330
+ }
1331
+ }
1332
+ /**
1333
+ * Delete vectors through the queue.
1334
+ *
1335
+ * @param ids - Array of vector IDs to delete
1336
+ * @throws Error if request fails after all retries
1337
+ */
1338
+ async delete(ids) {
1339
+ const response = await this.broker.enqueue({
1340
+ resource: "vectorStore",
1341
+ operation: "delete",
1342
+ args: [ids],
1343
+ priority: this._priority
1344
+ });
1345
+ if (!response.success) {
1346
+ throw response.error ?? new Error("VectorStore delete failed");
1347
+ }
1348
+ }
1349
+ /**
1350
+ * Get total count of vectors through the queue.
1351
+ *
1352
+ * @returns Vector count
1353
+ * @throws Error if request fails after all retries
1354
+ */
1355
+ async count() {
1356
+ const response = await this.broker.enqueue({
1357
+ resource: "vectorStore",
1358
+ operation: "count",
1359
+ args: [],
1360
+ priority: this._priority
1361
+ });
1362
+ if (!response.success) {
1363
+ throw response.error ?? new Error("VectorStore count failed");
1364
+ }
1365
+ return response.data;
1366
+ }
1367
+ /**
1368
+ * Get vectors by IDs through the queue (if supported).
1369
+ *
1370
+ * @param ids - Array of vector IDs to retrieve
1371
+ * @returns Array of vector records
1372
+ * @throws Error if request fails or method not supported
1373
+ */
1374
+ async get(ids) {
1375
+ if (!this.realVectorStore.get) {
1376
+ throw new Error("VectorStore.get() not supported by underlying implementation");
1377
+ }
1378
+ const response = await this.broker.enqueue({
1379
+ resource: "vectorStore",
1380
+ operation: "get",
1381
+ args: [ids],
1382
+ priority: this._priority
1383
+ });
1384
+ if (!response.success) {
1385
+ throw response.error ?? new Error("VectorStore get failed");
1386
+ }
1387
+ return response.data;
1388
+ }
1389
+ /**
1390
+ * Query vectors by filter through the queue (if supported).
1391
+ *
1392
+ * @param filter - Metadata filter to apply
1393
+ * @returns Array of matching vector records
1394
+ * @throws Error if request fails or method not supported
1395
+ */
1396
+ async query(filter) {
1397
+ if (!this.realVectorStore.query) {
1398
+ throw new Error("VectorStore.query() not supported by underlying implementation");
1399
+ }
1400
+ const response = await this.broker.enqueue({
1401
+ resource: "vectorStore",
1402
+ operation: "query",
1403
+ args: [filter],
1404
+ priority: this._priority
1405
+ });
1406
+ if (!response.success) {
1407
+ throw response.error ?? new Error("VectorStore query failed");
1408
+ }
1409
+ return response.data;
1410
+ }
1411
+ };
1412
+ function createQueuedVectorStore(broker, vectorStore) {
1413
+ return new QueuedVectorStore(broker, vectorStore);
1414
+ }
1415
+
1416
+ export { DEFAULT_RATE_LIMIT_CONFIG, DEFAULT_RETRY_CONFIG, InMemoryRateLimitBackend, PriorityQueue, QueuedEmbeddings, QueuedLLM, QueuedVectorStore, RATE_LIMIT_PRESETS, ResourceBroker, StateBrokerRateLimitBackend, calculateBackoffDelay, classifyError, createQueuedEmbeddings, createQueuedLLM, createQueuedVectorStore, createQuickRetryConfig, createRateLimitRetryConfig, estimateBatchTokens, estimateTokens, extractRetryAfter, getRateLimitConfig, isRateLimitError, isRetryableError, shouldRetry, sleep, withRetry };
1417
+ //# sourceMappingURL=index.js.map
1418
+ //# sourceMappingURL=index.js.map