ai.matey.patterns 0.2.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/LICENSE +21 -0
  3. package/dist/cjs/batch-processor.js +136 -0
  4. package/dist/cjs/batch-processor.js.map +1 -0
  5. package/dist/cjs/complexity-router.js +85 -0
  6. package/dist/cjs/complexity-router.js.map +1 -0
  7. package/dist/cjs/cost-optimizer.js +66 -0
  8. package/dist/cjs/cost-optimizer.js.map +1 -0
  9. package/dist/cjs/failover.js +55 -0
  10. package/dist/cjs/failover.js.map +1 -0
  11. package/dist/cjs/index.js +23 -0
  12. package/dist/cjs/index.js.map +1 -0
  13. package/dist/cjs/parallel-aggregator.js +68 -0
  14. package/dist/cjs/parallel-aggregator.js.map +1 -0
  15. package/dist/esm/batch-processor.js +133 -0
  16. package/dist/esm/batch-processor.js.map +1 -0
  17. package/dist/esm/complexity-router.js +81 -0
  18. package/dist/esm/complexity-router.js.map +1 -0
  19. package/dist/esm/cost-optimizer.js +63 -0
  20. package/dist/esm/cost-optimizer.js.map +1 -0
  21. package/dist/esm/failover.js +52 -0
  22. package/dist/esm/failover.js.map +1 -0
  23. package/dist/esm/index.js +14 -0
  24. package/dist/esm/index.js.map +1 -0
  25. package/dist/esm/parallel-aggregator.js +65 -0
  26. package/dist/esm/parallel-aggregator.js.map +1 -0
  27. package/dist/types/batch-processor.d.ts +66 -0
  28. package/dist/types/batch-processor.d.ts.map +1 -0
  29. package/dist/types/complexity-router.d.ts +55 -0
  30. package/dist/types/complexity-router.d.ts.map +1 -0
  31. package/dist/types/cost-optimizer.d.ts +57 -0
  32. package/dist/types/cost-optimizer.d.ts.map +1 -0
  33. package/dist/types/failover.d.ts +40 -0
  34. package/dist/types/failover.d.ts.map +1 -0
  35. package/dist/types/index.d.ts +14 -0
  36. package/dist/types/index.d.ts.map +1 -0
  37. package/dist/types/parallel-aggregator.d.ts +45 -0
  38. package/dist/types/parallel-aggregator.d.ts.map +1 -0
  39. package/package.json +71 -0
  40. package/readme.md +54 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # ai.matey.patterns
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - aef9f4a: New `ai.matey.patterns` package: complexity routing, parallel aggregation, failover middleware,
8
+ cost optimization with budget windows, and batch processing. Router's `dispatchParallel` now
9
+ actually honors the `fastest` strategy (previously returned the first-registered success).
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [dae4d01]
14
+ - Updated dependencies [e7df1d0]
15
+ - Updated dependencies [f227db2]
16
+ - Updated dependencies [2912b7d]
17
+ - Updated dependencies [aef9f4a]
18
+ - Updated dependencies [78731bb]
19
+ - Updated dependencies [b7e2312]
20
+ - ai.matey.types@0.3.0
21
+ - ai.matey.core@0.3.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 AI Matey Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Batch Processing with Rate Limiting
4
+ *
5
+ * High-throughput request processing with bounded concurrency, an optional
6
+ * token-bucket rate limit, retries, and progress reporting. Zero-dependency.
7
+ * Extracted from docs/PATTERNS.md §6.
8
+ *
9
+ * @module
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createBatchProcessor = createBatchProcessor;
13
+ /**
14
+ * Create a batch processor.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const processor = createBatchProcessor({
19
+ * execute: (request) => bridge.chat(request),
20
+ * concurrency: 5,
21
+ * requestsPerSecond: 10,
22
+ * retries: 2,
23
+ * });
24
+ * const results = await processor.addAll(requests);
25
+ * ```
26
+ */
27
+ function createBatchProcessor(config) {
28
+ const concurrency = config.concurrency ?? 5;
29
+ const maxQueueSize = config.maxQueueSize ?? Infinity;
30
+ const retries = config.retries ?? 0;
31
+ const queue = [];
32
+ let inFlight = 0;
33
+ let completed = 0;
34
+ let failed = 0;
35
+ let disposed = false;
36
+ const drainWaiters = [];
37
+ // Token bucket (only when rate limiting is configured)
38
+ let tokens = config.requestsPerSecond ?? Infinity;
39
+ let refillTimer;
40
+ if (config.requestsPerSecond) {
41
+ const perTick = config.requestsPerSecond / 10;
42
+ refillTimer = setInterval(() => {
43
+ tokens = Math.min(config.requestsPerSecond, tokens + perTick);
44
+ pump();
45
+ }, 100);
46
+ // Do not hold the process open for an idle processor
47
+ if (typeof refillTimer === 'object' && 'unref' in refillTimer) {
48
+ refillTimer.unref();
49
+ }
50
+ }
51
+ const stats = () => ({
52
+ completed,
53
+ failed,
54
+ pending: queue.length,
55
+ inFlight,
56
+ });
57
+ const notifyDrainWaiters = () => {
58
+ if (queue.length === 0 && inFlight === 0) {
59
+ while (drainWaiters.length > 0) {
60
+ drainWaiters.shift()?.();
61
+ }
62
+ }
63
+ };
64
+ const runItem = (item) => {
65
+ inFlight++;
66
+ item.attempts++;
67
+ config
68
+ .execute(item.request)
69
+ .then((result) => {
70
+ completed++;
71
+ item.resolve(result);
72
+ })
73
+ .catch((error) => {
74
+ if (item.attempts <= retries && !disposed) {
75
+ // Requeue for retry (does not consume a fresh queue slot check)
76
+ queue.push(item);
77
+ return;
78
+ }
79
+ failed++;
80
+ item.reject(error);
81
+ })
82
+ .finally(() => {
83
+ inFlight--;
84
+ config.onProgress?.(stats());
85
+ pump();
86
+ notifyDrainWaiters();
87
+ });
88
+ };
89
+ const pump = () => {
90
+ while (!disposed && inFlight < concurrency && queue.length > 0 && tokens >= 1) {
91
+ if (config.requestsPerSecond) {
92
+ tokens -= 1;
93
+ }
94
+ const item = queue.shift();
95
+ if (item) {
96
+ runItem(item);
97
+ }
98
+ }
99
+ };
100
+ return {
101
+ add(request) {
102
+ if (disposed) {
103
+ return Promise.reject(new Error('Batch processor disposed'));
104
+ }
105
+ if (queue.length >= maxQueueSize) {
106
+ return Promise.reject(new Error(`Queue full (max ${maxQueueSize})`));
107
+ }
108
+ return new Promise((resolve, reject) => {
109
+ queue.push({ request, resolve, reject, attempts: 0 });
110
+ pump();
111
+ });
112
+ },
113
+ addAll(requests) {
114
+ return Promise.allSettled(requests.map((request) => this.add(request)));
115
+ },
116
+ drain() {
117
+ if (queue.length === 0 && inFlight === 0) {
118
+ return Promise.resolve();
119
+ }
120
+ return new Promise((resolve) => {
121
+ drainWaiters.push(resolve);
122
+ });
123
+ },
124
+ stats,
125
+ dispose() {
126
+ disposed = true;
127
+ if (refillTimer) {
128
+ clearInterval(refillTimer);
129
+ }
130
+ while (queue.length > 0) {
131
+ queue.shift()?.reject(new Error('Batch processor disposed'));
132
+ }
133
+ },
134
+ };
135
+ }
136
+ //# sourceMappingURL=batch-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-processor.js","sourceRoot":"","sources":["../../src/batch-processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Complexity-Based Routing
4
+ *
5
+ * Route requests to different backends by estimated query complexity —
6
+ * cheap/fast models for simple queries, capable models for hard ones.
7
+ * Extracted from docs/PATTERNS.md §1.
8
+ *
9
+ * @module
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.defaultComplexityAnalyzer = defaultComplexityAnalyzer;
13
+ exports.createComplexityRouter = createComplexityRouter;
14
+ const ai_matey_core_1 = require("ai.matey.core");
15
+ /**
16
+ * Heuristic complexity score (0-100) from message length, question depth,
17
+ * and reasoning keywords.
18
+ */
19
+ function defaultComplexityAnalyzer(request) {
20
+ const text = request.messages
21
+ .map((message) => typeof message.content === 'string'
22
+ ? message.content
23
+ : message.content.map((block) => (block.type === 'text' ? block.text : '')).join(' '))
24
+ .join(' ');
25
+ let score = 0;
26
+ // Length: up to 40 points at ~2000 chars
27
+ score += Math.min(40, text.length / 50);
28
+ // Reasoning indicators: up to 40 points
29
+ const reasoningKeywords = [
30
+ 'why',
31
+ 'how',
32
+ 'explain',
33
+ 'analyze',
34
+ 'compare',
35
+ 'evaluate',
36
+ 'design',
37
+ 'architect',
38
+ 'prove',
39
+ 'derive',
40
+ 'step by step',
41
+ 'trade-off',
42
+ 'tradeoff',
43
+ ];
44
+ const lower = text.toLowerCase();
45
+ const hits = reasoningKeywords.filter((keyword) => lower.includes(keyword)).length;
46
+ score += Math.min(40, hits * 10);
47
+ // Code blocks / structured content: up to 20 points
48
+ if (lower.includes('```') || lower.includes('function ') || lower.includes('class ')) {
49
+ score += 20;
50
+ }
51
+ return Math.min(100, Math.round(score));
52
+ }
53
+ /**
54
+ * Create a Router that picks its backend by query complexity.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const router = createComplexityRouter({
59
+ * tiers: [
60
+ * { backend: 'fast', maxComplexity: 30 },
61
+ * { backend: 'balanced', maxComplexity: 70 },
62
+ * { backend: 'powerful', maxComplexity: 100 },
63
+ * ],
64
+ * backends: { fast, balanced, powerful },
65
+ * });
66
+ * ```
67
+ */
68
+ function createComplexityRouter(config) {
69
+ const analyzer = config.analyzer ?? defaultComplexityAnalyzer;
70
+ const tiers = [...config.tiers].sort((a, b) => a.maxComplexity - b.maxComplexity);
71
+ const router = new ai_matey_core_1.Router({
72
+ ...config.routerConfig,
73
+ routingStrategy: 'custom',
74
+ customRouter: (request, availableBackends) => {
75
+ const complexity = analyzer(request);
76
+ const tier = tiers.find((candidate) => complexity <= candidate.maxComplexity && availableBackends.includes(candidate.backend)) ?? tiers[tiers.length - 1];
77
+ return Promise.resolve(tier?.backend ?? null);
78
+ },
79
+ });
80
+ for (const [name, adapter] of Object.entries(config.backends)) {
81
+ router.register(name, adapter);
82
+ }
83
+ return router;
84
+ }
85
+ //# sourceMappingURL=complexity-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity-router.js","sourceRoot":"","sources":["../../src/complexity-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * Cost-Optimized Provider Selection
4
+ *
5
+ * A cost-optimizing Router plus a budget-window middleware: route to the
6
+ * cheapest capable backend and enforce a spending ceiling. Extracted from
7
+ * docs/PATTERNS.md §4.
8
+ *
9
+ * @module
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createCostOptimizer = createCostOptimizer;
13
+ const ai_matey_core_1 = require("ai.matey.core");
14
+ const ai_matey_errors_1 = require("ai.matey.errors");
15
+ /**
16
+ * Create a cost-optimized router with an optional budget window.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const { router, middleware, recordSpend } = createCostOptimizer({
21
+ * backends: { cheap, premium },
22
+ * budget: { limitUSD: 10, windowMs: 3600_000 },
23
+ * });
24
+ * const bridge = new Bridge(frontend, router).use(middleware);
25
+ * ```
26
+ */
27
+ function createCostOptimizer(config) {
28
+ const router = new ai_matey_core_1.Router({
29
+ trackCost: true,
30
+ capabilityBasedRouting: true,
31
+ optimization: 'cost',
32
+ ...config.routerConfig,
33
+ });
34
+ for (const [name, adapter] of Object.entries(config.backends)) {
35
+ router.register(name, adapter);
36
+ }
37
+ // Sliding-window spend ledger
38
+ const windowMs = config.budget?.windowMs ?? 3_600_000;
39
+ let entries = [];
40
+ const prune = () => {
41
+ const cutoff = Date.now() - windowMs;
42
+ entries = entries.filter((entry) => entry.at >= cutoff);
43
+ };
44
+ const getSpend = () => {
45
+ prune();
46
+ return entries.reduce((sum, entry) => sum + entry.costUSD, 0);
47
+ };
48
+ const recordSpend = (costUSD) => {
49
+ entries.push({ at: Date.now(), costUSD });
50
+ };
51
+ const middleware = async (_context, next) => {
52
+ if (config.budget && getSpend() >= config.budget.limitUSD) {
53
+ if ((config.budget.onExceeded ?? 'error') === 'error') {
54
+ throw new ai_matey_errors_1.AdapterError({
55
+ code: ai_matey_errors_1.ErrorCode.RATE_LIMIT_EXCEEDED,
56
+ message: `Budget of $${config.budget.limitUSD} exceeded for the current window`,
57
+ isRetryable: true,
58
+ provenance: { router: router.metadata.name },
59
+ });
60
+ }
61
+ }
62
+ return next();
63
+ };
64
+ return { router, middleware, getSpend, recordSpend };
65
+ }
66
+ //# sourceMappingURL=cost-optimizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-optimizer.js","sourceRoot":"","sources":["../../src/cost-optimizer.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAyDH,kDA6CC;AApGD,iDAAuC;AACvC,qDAA0D;AA0C1D;;;;;;;;;;;GAWG;AACH,SAAgB,mBAAmB,CAAC,MAA2B;IAC7D,MAAM,MAAM,GAAG,IAAI,sBAAM,CAAC;QACxB,SAAS,EAAE,IAAI;QACf,sBAAsB,EAAE,IAAI;QAC5B,YAAY,EAAE,MAAM;QACpB,GAAG,MAAM,CAAC,YAAY;KACvB,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,8BAA8B;IAC9B,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,IAAI,SAAS,CAAC;IACtD,IAAI,OAAO,GAA2C,EAAE,CAAC;IAEzD,MAAM,KAAK,GAAG,GAAS,EAAE;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC;QACrC,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,MAAM,CAAC,CAAC;IAC1D,CAAC,CAAC;IAEF,MAAM,QAAQ,GAAG,GAAW,EAAE;QAC5B,KAAK,EAAE,CAAC;QACR,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,OAAe,EAAQ,EAAE;QAC5C,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC;IAEF,MAAM,UAAU,GAAe,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QACtD,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,EAAE,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,IAAI,OAAO,CAAC,KAAK,OAAO,EAAE,CAAC;gBACtD,MAAM,IAAI,8BAAY,CAAC;oBACrB,IAAI,EAAE,2BAAS,CAAC,mBAAmB;oBACnC,OAAO,EAAE,cAAc,MAAM,CAAC,MAAM,CAAC,QAAQ,kCAAkC;oBAC/E,WAAW,EAAE,IAAI;oBACjB,UAAU,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;iBAC7C,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,IAAI,EAAE,CAAC;IAChB,CAAC,CAAC;IAEF,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC;AACvD,CAAC"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ /**
3
+ * Automatic Failover Middleware
4
+ *
5
+ * Bridge-level failover: when the primary backend fails, retry the request
6
+ * against fallback adapters in order. For Router users, prefer the Router's
7
+ * built-in fallback chains — this pattern serves plain-Bridge setups.
8
+ * Extracted from docs/PATTERNS.md §3.
9
+ *
10
+ * @module
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.createFailoverMiddleware = createFailoverMiddleware;
14
+ const ai_matey_errors_1 = require("ai.matey.errors");
15
+ /**
16
+ * Create failover middleware for a Bridge.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * bridge.use(createFailoverMiddleware({
21
+ * fallbacks: [new AnthropicBackendAdapter({ apiKey }), new GroqBackendAdapter({ apiKey })],
22
+ * }));
23
+ * ```
24
+ */
25
+ function createFailoverMiddleware(config) {
26
+ const shouldFailover = config.shouldFailover ??
27
+ ((error) => (error instanceof ai_matey_errors_1.AdapterError ? error.isRetryable : true));
28
+ return async (context, next) => {
29
+ try {
30
+ return await next();
31
+ }
32
+ catch (primaryError) {
33
+ const error = primaryError instanceof Error ? primaryError : new Error(String(primaryError));
34
+ if (!shouldFailover(error)) {
35
+ throw error;
36
+ }
37
+ let lastError = error;
38
+ for (const fallback of config.fallbacks) {
39
+ config.onFailover?.({ to: fallback.metadata.name, error: lastError });
40
+ try {
41
+ return await fallback.execute(context.request, context.signal);
42
+ }
43
+ catch (fallbackError) {
44
+ lastError =
45
+ fallbackError instanceof Error ? fallbackError : new Error(String(fallbackError));
46
+ if (!shouldFailover(lastError)) {
47
+ throw lastError;
48
+ }
49
+ }
50
+ }
51
+ throw lastError;
52
+ }
53
+ };
54
+ }
55
+ //# sourceMappingURL=failover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failover.js","sourceRoot":"","sources":["../../src/failover.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;AAgCH,4DAgCC;AA9DD,qDAA+C;AAoB/C;;;;;;;;;GASG;AACH,SAAgB,wBAAwB,CAAC,MAAsB;IAC7D,MAAM,cAAc,GAClB,MAAM,CAAC,cAAc;QACrB,CAAC,CAAC,KAAY,EAAE,EAAE,CAAC,CAAC,KAAK,YAAY,8BAAY,CAAC,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAEjF,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC7B,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAC;QACtB,CAAC;QAAC,OAAO,YAAY,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;YAE7F,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC3B,MAAM,KAAK,CAAC;YACd,CAAC;YAED,IAAI,SAAS,GAAU,KAAK,CAAC;YAC7B,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;gBACxC,MAAM,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;gBACtE,IAAI,CAAC;oBACH,OAAO,MAAM,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;gBACjE,CAAC;gBAAC,OAAO,aAAa,EAAE,CAAC;oBACvB,SAAS;wBACP,aAAa,YAAY,KAAK,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;oBACpF,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC/B,MAAM,SAAS,CAAC;oBAClB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,MAAM,SAAS,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /**
3
+ * ai.matey.patterns
4
+ *
5
+ * Production integration patterns, extracted from the validated pattern
6
+ * library (docs/PATTERNS.md) into importable utilities.
7
+ *
8
+ * @module
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.createBatchProcessor = exports.createCostOptimizer = exports.createFailoverMiddleware = exports.createParallelAggregator = exports.defaultComplexityAnalyzer = exports.createComplexityRouter = void 0;
12
+ var complexity_router_js_1 = require("./complexity-router.js");
13
+ Object.defineProperty(exports, "createComplexityRouter", { enumerable: true, get: function () { return complexity_router_js_1.createComplexityRouter; } });
14
+ Object.defineProperty(exports, "defaultComplexityAnalyzer", { enumerable: true, get: function () { return complexity_router_js_1.defaultComplexityAnalyzer; } });
15
+ var parallel_aggregator_js_1 = require("./parallel-aggregator.js");
16
+ Object.defineProperty(exports, "createParallelAggregator", { enumerable: true, get: function () { return parallel_aggregator_js_1.createParallelAggregator; } });
17
+ var failover_js_1 = require("./failover.js");
18
+ Object.defineProperty(exports, "createFailoverMiddleware", { enumerable: true, get: function () { return failover_js_1.createFailoverMiddleware; } });
19
+ var cost_optimizer_js_1 = require("./cost-optimizer.js");
20
+ Object.defineProperty(exports, "createCostOptimizer", { enumerable: true, get: function () { return cost_optimizer_js_1.createCostOptimizer; } });
21
+ var batch_processor_js_1 = require("./batch-processor.js");
22
+ Object.defineProperty(exports, "createBatchProcessor", { enumerable: true, get: function () { return batch_processor_js_1.createBatchProcessor; } });
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,+DAKgC;AAJ9B,8HAAA,sBAAsB,OAAA;AACtB,iIAAA,yBAAyB,OAAA;AAK3B,mEAIkC;AAHhC,kIAAA,wBAAwB,OAAA;AAK1B,6CAA8E;AAArE,uHAAA,wBAAwB,OAAA;AAEjC,yDAI6B;AAH3B,wHAAA,mBAAmB,OAAA;AAKrB,2DAK8B;AAJ5B,0HAAA,oBAAoB,OAAA"}
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Parallel Provider Aggregation
4
+ *
5
+ * Query several backends at once and aggregate: fastest-wins, all-results,
6
+ * or a custom judge. The aggregator is itself a BackendAdapter, so it plugs
7
+ * into a Bridge like any single backend. Extracted from docs/PATTERNS.md §2.
8
+ *
9
+ * @module
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createParallelAggregator = createParallelAggregator;
13
+ const ai_matey_core_1 = require("ai.matey.core");
14
+ /**
15
+ * Create a BackendAdapter that fans requests out to all configured
16
+ * backends in parallel.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const consensus = createParallelAggregator({
21
+ * backends: { openai, anthropic, gemini },
22
+ * strategy: (results) => results[0].response, // custom judge
23
+ * });
24
+ * const bridge = new Bridge(new OpenAIFrontendAdapter(), consensus);
25
+ * ```
26
+ */
27
+ function createParallelAggregator(config) {
28
+ const strategy = config.strategy ?? 'fastest';
29
+ const router = new ai_matey_core_1.Router();
30
+ for (const [name, adapter] of Object.entries(config.backends)) {
31
+ router.register(name, adapter);
32
+ }
33
+ const metadata = {
34
+ name: 'parallel-aggregator',
35
+ version: '1.0.0',
36
+ provider: 'ai.matey.patterns',
37
+ capabilities: {
38
+ streaming: false,
39
+ multiModal: true,
40
+ tools: true,
41
+ systemMessageStrategy: 'in-messages',
42
+ supportsMultipleSystemMessages: true,
43
+ },
44
+ };
45
+ return {
46
+ metadata,
47
+ fromIR: (request) => request,
48
+ toIR: () => {
49
+ throw new Error('toIR() not applicable for parallel aggregator');
50
+ },
51
+ async execute(request, signal) {
52
+ if (typeof strategy === 'function') {
53
+ const result = await router.dispatchParallel(request, { strategy: 'all', timeout: config.timeout }, signal);
54
+ return strategy([...(result.allResponses ?? [])]);
55
+ }
56
+ const result = await router.dispatchParallel(request, {
57
+ strategy: strategy === 'fastest' ? 'fastest' : 'all',
58
+ timeout: config.timeout,
59
+ }, signal);
60
+ return result.response;
61
+ },
62
+ // eslint-disable-next-line @typescript-eslint/require-await, require-yield -- adapter interface requires an async generator; aggregation cannot stream
63
+ async *executeStream() {
64
+ throw new Error('Parallel aggregation does not support streaming; use a single backend for streams');
65
+ },
66
+ };
67
+ }
68
+ //# sourceMappingURL=parallel-aggregator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parallel-aggregator.js","sourceRoot":"","sources":["../../src/parallel-aggregator.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;AAiDH,4DAyDC;AAxGD,iDAAuC;AAkCvC;;;;;;;;;;;;GAYG;AACH,SAAgB,wBAAwB,CAAC,MAAgC;IACvE,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,SAAS,CAAC;IAE9C,MAAM,MAAM,GAAG,IAAI,sBAAM,EAAE,CAAC;IAC5B,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9D,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,QAAQ,GAAoB;QAChC,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE,mBAAmB;QAC7B,YAAY,EAAE;YACZ,SAAS,EAAE,KAAK;YAChB,UAAU,EAAE,IAAI;YAChB,KAAK,EAAE,IAAI;YACX,qBAAqB,EAAE,aAAa;YACpC,8BAA8B,EAAE,IAAI;SACrC;KACF,CAAC;IAEF,OAAO;QACL,QAAQ;QAER,MAAM,EAAE,CAAC,OAAsB,EAAE,EAAE,CAAC,OAAO;QAC3C,IAAI,EAAE,GAAG,EAAE;YACT,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAED,KAAK,CAAC,OAAO,CAAC,OAAsB,EAAE,MAAoB;YACxD,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAC1C,OAAO,EACP,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAC5C,MAAM,CACP,CAAC;gBACF,OAAO,QAAQ,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACpD,CAAC;YAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAC1C,OAAO,EACP;gBACE,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK;gBACpD,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,EACD,MAAM,CACP,CAAC;YACF,OAAO,MAAM,CAAC,QAAQ,CAAC;QACzB,CAAC;QAED,uJAAuJ;QACvJ,KAAK,CAAC,CAAC,aAAa;YAClB,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;QACJ,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Batch Processing with Rate Limiting
3
+ *
4
+ * High-throughput request processing with bounded concurrency, an optional
5
+ * token-bucket rate limit, retries, and progress reporting. Zero-dependency.
6
+ * Extracted from docs/PATTERNS.md §6.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Create a batch processor.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const processor = createBatchProcessor({
16
+ * execute: (request) => bridge.chat(request),
17
+ * concurrency: 5,
18
+ * requestsPerSecond: 10,
19
+ * retries: 2,
20
+ * });
21
+ * const results = await processor.addAll(requests);
22
+ * ```
23
+ */
24
+ export function createBatchProcessor(config) {
25
+ const concurrency = config.concurrency ?? 5;
26
+ const maxQueueSize = config.maxQueueSize ?? Infinity;
27
+ const retries = config.retries ?? 0;
28
+ const queue = [];
29
+ let inFlight = 0;
30
+ let completed = 0;
31
+ let failed = 0;
32
+ let disposed = false;
33
+ const drainWaiters = [];
34
+ // Token bucket (only when rate limiting is configured)
35
+ let tokens = config.requestsPerSecond ?? Infinity;
36
+ let refillTimer;
37
+ if (config.requestsPerSecond) {
38
+ const perTick = config.requestsPerSecond / 10;
39
+ refillTimer = setInterval(() => {
40
+ tokens = Math.min(config.requestsPerSecond, tokens + perTick);
41
+ pump();
42
+ }, 100);
43
+ // Do not hold the process open for an idle processor
44
+ if (typeof refillTimer === 'object' && 'unref' in refillTimer) {
45
+ refillTimer.unref();
46
+ }
47
+ }
48
+ const stats = () => ({
49
+ completed,
50
+ failed,
51
+ pending: queue.length,
52
+ inFlight,
53
+ });
54
+ const notifyDrainWaiters = () => {
55
+ if (queue.length === 0 && inFlight === 0) {
56
+ while (drainWaiters.length > 0) {
57
+ drainWaiters.shift()?.();
58
+ }
59
+ }
60
+ };
61
+ const runItem = (item) => {
62
+ inFlight++;
63
+ item.attempts++;
64
+ config
65
+ .execute(item.request)
66
+ .then((result) => {
67
+ completed++;
68
+ item.resolve(result);
69
+ })
70
+ .catch((error) => {
71
+ if (item.attempts <= retries && !disposed) {
72
+ // Requeue for retry (does not consume a fresh queue slot check)
73
+ queue.push(item);
74
+ return;
75
+ }
76
+ failed++;
77
+ item.reject(error);
78
+ })
79
+ .finally(() => {
80
+ inFlight--;
81
+ config.onProgress?.(stats());
82
+ pump();
83
+ notifyDrainWaiters();
84
+ });
85
+ };
86
+ const pump = () => {
87
+ while (!disposed && inFlight < concurrency && queue.length > 0 && tokens >= 1) {
88
+ if (config.requestsPerSecond) {
89
+ tokens -= 1;
90
+ }
91
+ const item = queue.shift();
92
+ if (item) {
93
+ runItem(item);
94
+ }
95
+ }
96
+ };
97
+ return {
98
+ add(request) {
99
+ if (disposed) {
100
+ return Promise.reject(new Error('Batch processor disposed'));
101
+ }
102
+ if (queue.length >= maxQueueSize) {
103
+ return Promise.reject(new Error(`Queue full (max ${maxQueueSize})`));
104
+ }
105
+ return new Promise((resolve, reject) => {
106
+ queue.push({ request, resolve, reject, attempts: 0 });
107
+ pump();
108
+ });
109
+ },
110
+ addAll(requests) {
111
+ return Promise.allSettled(requests.map((request) => this.add(request)));
112
+ },
113
+ drain() {
114
+ if (queue.length === 0 && inFlight === 0) {
115
+ return Promise.resolve();
116
+ }
117
+ return new Promise((resolve) => {
118
+ drainWaiters.push(resolve);
119
+ });
120
+ },
121
+ stats,
122
+ dispose() {
123
+ disposed = true;
124
+ if (refillTimer) {
125
+ clearInterval(refillTimer);
126
+ }
127
+ while (queue.length > 0) {
128
+ queue.shift()?.reject(new Error('Batch processor disposed'));
129
+ }
130
+ },
131
+ };
132
+ }
133
+ //# sourceMappingURL=batch-processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch-processor.js","sourceRoot":"","sources":["../../src/batch-processor.ts"],"names":[],"mappings":"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"}