@juspay/neurolink 7.6.1 → 7.7.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/CHANGELOG.md +9 -4
- package/README.md +78 -3
- package/dist/cli/commands/config.d.ts +275 -3
- package/dist/cli/commands/config.js +121 -0
- package/dist/cli/commands/mcp.js +77 -28
- package/dist/cli/factories/commandFactory.js +359 -6
- package/dist/core/analytics.js +7 -27
- package/dist/core/baseProvider.js +43 -4
- package/dist/core/constants.d.ts +46 -0
- package/dist/core/constants.js +47 -0
- package/dist/core/dynamicModels.d.ts +16 -4
- package/dist/core/dynamicModels.js +130 -26
- package/dist/core/evaluation.js +5 -1
- package/dist/core/evaluationProviders.d.ts +6 -2
- package/dist/core/evaluationProviders.js +41 -125
- package/dist/core/factory.d.ts +5 -0
- package/dist/core/factory.js +62 -50
- package/dist/core/modelConfiguration.d.ts +246 -0
- package/dist/core/modelConfiguration.js +775 -0
- package/dist/core/types.d.ts +22 -3
- package/dist/core/types.js +5 -1
- package/dist/factories/providerRegistry.js +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/core/analytics.js +7 -27
- package/dist/lib/core/baseProvider.js +43 -4
- package/dist/lib/core/constants.d.ts +46 -0
- package/dist/lib/core/constants.js +47 -0
- package/dist/lib/core/dynamicModels.d.ts +16 -4
- package/dist/lib/core/dynamicModels.js +130 -26
- package/dist/lib/core/evaluation.js +5 -1
- package/dist/lib/core/evaluationProviders.d.ts +6 -2
- package/dist/lib/core/evaluationProviders.js +41 -125
- package/dist/lib/core/factory.d.ts +5 -0
- package/dist/lib/core/factory.js +63 -50
- package/dist/lib/core/modelConfiguration.d.ts +246 -0
- package/dist/lib/core/modelConfiguration.js +775 -0
- package/dist/lib/core/types.d.ts +22 -3
- package/dist/lib/core/types.js +5 -1
- package/dist/lib/factories/providerRegistry.js +3 -3
- package/dist/lib/index.d.ts +1 -1
- package/dist/lib/index.js +1 -1
- package/dist/lib/mcp/factory.d.ts +5 -5
- package/dist/lib/mcp/factory.js +2 -2
- package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/lib/mcp/servers/utilities/utilityServer.js +1 -1
- package/dist/lib/mcp/toolRegistry.js +2 -2
- package/dist/lib/neurolink.d.ts +168 -12
- package/dist/lib/neurolink.js +685 -123
- package/dist/lib/providers/anthropic.js +52 -2
- package/dist/lib/providers/googleAiStudio.js +4 -0
- package/dist/lib/providers/googleVertex.d.ts +75 -9
- package/dist/lib/providers/googleVertex.js +365 -46
- package/dist/lib/providers/huggingFace.d.ts +52 -11
- package/dist/lib/providers/huggingFace.js +180 -42
- package/dist/lib/providers/litellm.d.ts +9 -9
- package/dist/lib/providers/litellm.js +103 -16
- package/dist/lib/providers/ollama.d.ts +52 -17
- package/dist/lib/providers/ollama.js +276 -68
- package/dist/lib/sdk/toolRegistration.d.ts +42 -0
- package/dist/lib/sdk/toolRegistration.js +269 -27
- package/dist/lib/telemetry/telemetryService.d.ts +6 -0
- package/dist/lib/telemetry/telemetryService.js +38 -3
- package/dist/lib/types/contextTypes.d.ts +75 -11
- package/dist/lib/types/contextTypes.js +227 -1
- package/dist/lib/types/domainTypes.d.ts +62 -0
- package/dist/lib/types/domainTypes.js +5 -0
- package/dist/lib/types/generateTypes.d.ts +52 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/mcpTypes.d.ts +1 -1
- package/dist/lib/types/mcpTypes.js +1 -1
- package/dist/lib/types/streamTypes.d.ts +14 -0
- package/dist/lib/types/universalProviderOptions.d.ts +1 -1
- package/dist/lib/utils/errorHandling.d.ts +142 -0
- package/dist/lib/utils/errorHandling.js +316 -0
- package/dist/lib/utils/factoryProcessing.d.ts +74 -0
- package/dist/lib/utils/factoryProcessing.js +588 -0
- package/dist/lib/utils/optionsConversion.d.ts +54 -0
- package/dist/lib/utils/optionsConversion.js +126 -0
- package/dist/lib/utils/optionsUtils.d.ts +246 -0
- package/dist/lib/utils/optionsUtils.js +960 -0
- package/dist/lib/utils/providerHealth.d.ts +107 -0
- package/dist/lib/utils/providerHealth.js +507 -0
- package/dist/lib/utils/providerUtils.d.ts +17 -0
- package/dist/lib/utils/providerUtils.js +271 -16
- package/dist/lib/utils/timeout.js +1 -1
- package/dist/lib/utils/tokenLimits.d.ts +33 -0
- package/dist/lib/utils/tokenLimits.js +118 -0
- package/dist/mcp/factory.d.ts +5 -5
- package/dist/mcp/factory.js +2 -2
- package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/mcp/servers/utilities/utilityServer.js +1 -1
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/neurolink.d.ts +168 -12
- package/dist/neurolink.js +685 -123
- package/dist/providers/anthropic.js +52 -2
- package/dist/providers/googleAiStudio.js +4 -0
- package/dist/providers/googleVertex.d.ts +75 -9
- package/dist/providers/googleVertex.js +365 -46
- package/dist/providers/huggingFace.d.ts +52 -11
- package/dist/providers/huggingFace.js +181 -43
- package/dist/providers/litellm.d.ts +9 -9
- package/dist/providers/litellm.js +103 -16
- package/dist/providers/ollama.d.ts +52 -17
- package/dist/providers/ollama.js +276 -68
- package/dist/sdk/toolRegistration.d.ts +42 -0
- package/dist/sdk/toolRegistration.js +269 -27
- package/dist/telemetry/telemetryService.d.ts +6 -0
- package/dist/telemetry/telemetryService.js +38 -3
- package/dist/types/contextTypes.d.ts +75 -11
- package/dist/types/contextTypes.js +227 -2
- package/dist/types/domainTypes.d.ts +62 -0
- package/dist/types/domainTypes.js +5 -0
- package/dist/types/generateTypes.d.ts +52 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/mcpTypes.d.ts +1 -1
- package/dist/types/mcpTypes.js +1 -1
- package/dist/types/streamTypes.d.ts +14 -0
- package/dist/types/universalProviderOptions.d.ts +1 -1
- package/dist/types/universalProviderOptions.js +0 -1
- package/dist/utils/errorHandling.d.ts +142 -0
- package/dist/utils/errorHandling.js +316 -0
- package/dist/utils/factoryProcessing.d.ts +74 -0
- package/dist/utils/factoryProcessing.js +588 -0
- package/dist/utils/optionsConversion.d.ts +54 -0
- package/dist/utils/optionsConversion.js +126 -0
- package/dist/utils/optionsUtils.d.ts +246 -0
- package/dist/utils/optionsUtils.js +960 -0
- package/dist/utils/providerHealth.d.ts +107 -0
- package/dist/utils/providerHealth.js +507 -0
- package/dist/utils/providerUtils.d.ts +17 -0
- package/dist/utils/providerUtils.js +271 -16
- package/dist/utils/timeout.js +1 -1
- package/dist/utils/tokenLimits.d.ts +33 -0
- package/dist/utils/tokenLimits.js +118 -0
- package/package.json +2 -2
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory options processing utilities
|
|
3
|
+
*
|
|
4
|
+
* Processes factory configuration and ensures it flows through to AI providers
|
|
5
|
+
*/
|
|
6
|
+
import { logger } from "./logger.js";
|
|
7
|
+
// Removed crypto import - using faster string-based hash instead
|
|
8
|
+
/**
|
|
9
|
+
* LRU Cache for factory processing results
|
|
10
|
+
* Addresses GitHub Copilot review comment about adding caching for factory processing results
|
|
11
|
+
*/
|
|
12
|
+
class FactoryProcessingCache {
|
|
13
|
+
cache = new Map();
|
|
14
|
+
// Object identity cache to avoid recomputing cache keys for same object
|
|
15
|
+
// Using WeakMap to prevent memory leaks - entries are auto-collected when objects are GC'd
|
|
16
|
+
objectKeyCache = new WeakMap();
|
|
17
|
+
maxSize;
|
|
18
|
+
stats = {
|
|
19
|
+
hits: 0,
|
|
20
|
+
misses: 0,
|
|
21
|
+
evictions: 0,
|
|
22
|
+
totalRequests: 0,
|
|
23
|
+
keysCacheHits: 0, // New stat for object identity cache hits
|
|
24
|
+
};
|
|
25
|
+
constructor() {
|
|
26
|
+
// Configurable cache size via environment variable with bounds checking
|
|
27
|
+
const envCacheSize = process.env.NEUROLINK_FACTORY_CACHE_SIZE || "100";
|
|
28
|
+
const parsedSize = parseInt(envCacheSize, 10);
|
|
29
|
+
// Add bounds checking: min 1, max 10000 to prevent memory issues
|
|
30
|
+
if (isNaN(parsedSize) || parsedSize < 1) {
|
|
31
|
+
this.maxSize = 1;
|
|
32
|
+
logger.warn(`Invalid cache size '${envCacheSize}', using minimum value: 1`);
|
|
33
|
+
}
|
|
34
|
+
else if (parsedSize > 10000) {
|
|
35
|
+
this.maxSize = 10000;
|
|
36
|
+
logger.warn(`Cache size '${parsedSize}' exceeds maximum, using maximum value: 10000`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.maxSize = parsedSize;
|
|
40
|
+
}
|
|
41
|
+
logger.debug(`FactoryProcessingCache initialized with max size: ${this.maxSize}`);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate cache key from options using fast non-cryptographic hash
|
|
45
|
+
* Optimized for large options objects by extracting only key fields
|
|
46
|
+
* Uses numeric hash combination to avoid string concatenation in hot paths
|
|
47
|
+
* Implements object identity cache to avoid recomputation for same objects
|
|
48
|
+
*/
|
|
49
|
+
generateCacheKey(options) {
|
|
50
|
+
// Use object identity cache if possible for performance
|
|
51
|
+
if (typeof options === "object" && options !== null) {
|
|
52
|
+
const cachedKey = this.objectKeyCache.get(options);
|
|
53
|
+
if (cachedKey) {
|
|
54
|
+
this.stats.keysCacheHits++;
|
|
55
|
+
return cachedKey;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
// Extract key field hashes directly without string operations
|
|
60
|
+
const factoryConfigHash = this.extractKeyFieldsHash(options.factoryConfig);
|
|
61
|
+
const contextHash = this.extractKeyFieldsHash(options.context);
|
|
62
|
+
// Combine hashes numerically instead of string concatenation
|
|
63
|
+
const combinedHash = this.combineHashes(factoryConfigHash, contextHash);
|
|
64
|
+
const key = combinedHash.toString(16);
|
|
65
|
+
// Cache the computed key for future use (WeakMap auto-cleans on GC)
|
|
66
|
+
if (typeof options === "object" && options !== null) {
|
|
67
|
+
this.objectKeyCache.set(options, key);
|
|
68
|
+
}
|
|
69
|
+
return key;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
logger.warn("Failed to generate cache key, using deterministic fallback", { error });
|
|
73
|
+
return "fallback_" + this.stableStringify(options);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create deterministic string representation for fallback cache keys
|
|
78
|
+
* Ensures identical options always produce the same cache key
|
|
79
|
+
*/
|
|
80
|
+
stableStringify(obj) {
|
|
81
|
+
try {
|
|
82
|
+
if (obj === null || obj === undefined) {
|
|
83
|
+
return String(obj);
|
|
84
|
+
}
|
|
85
|
+
if (typeof obj !== "object") {
|
|
86
|
+
return String(obj);
|
|
87
|
+
}
|
|
88
|
+
// For objects, create a stable representation by sorting keys
|
|
89
|
+
const record = obj;
|
|
90
|
+
const sortedKeys = Object.keys(record).sort();
|
|
91
|
+
const pairs = sortedKeys.map((key) => `${key}:${this.stableStringify(record[key])}`);
|
|
92
|
+
return `{${pairs.join(",")}}`;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Ultimate fallback - use object type
|
|
96
|
+
return `[${typeof obj}]`;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Extract key field hash from objects without string concatenation
|
|
101
|
+
* Uses numeric hash combination for maximum performance in hot paths
|
|
102
|
+
*/
|
|
103
|
+
extractKeyFieldsHash(obj) {
|
|
104
|
+
if (!obj || typeof obj !== "object") {
|
|
105
|
+
return this.hashValue(obj);
|
|
106
|
+
}
|
|
107
|
+
const record = obj;
|
|
108
|
+
let hash = 0;
|
|
109
|
+
// Extract only the most identifying fields - ordered by importance
|
|
110
|
+
const importantFields = [
|
|
111
|
+
"domainType",
|
|
112
|
+
"enhancementType",
|
|
113
|
+
"domainConfig",
|
|
114
|
+
"id",
|
|
115
|
+
"type",
|
|
116
|
+
"name",
|
|
117
|
+
];
|
|
118
|
+
// Use numeric hash combination instead of string operations
|
|
119
|
+
for (let i = 0; i < importantFields.length; i++) {
|
|
120
|
+
const field = importantFields[i];
|
|
121
|
+
const value = record[field];
|
|
122
|
+
if (value !== undefined) {
|
|
123
|
+
// Combine field name hash and value hash numerically
|
|
124
|
+
const fieldHash = this.hashString(field);
|
|
125
|
+
const valueHash = this.hashValue(value);
|
|
126
|
+
hash = this.combineHashes(hash, this.combineHashes(fieldHash, valueHash));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// If no important fields found, use object structure hash
|
|
130
|
+
if (hash === 0) {
|
|
131
|
+
hash = Object.keys(record).length;
|
|
132
|
+
}
|
|
133
|
+
return hash;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Extract key identifying fields from objects for cache key generation
|
|
137
|
+
* Optimized to avoid expensive operations on large objects
|
|
138
|
+
* @deprecated Use extractKeyFieldsHash for better performance
|
|
139
|
+
*/
|
|
140
|
+
extractKeyFields(obj) {
|
|
141
|
+
if (!obj || typeof obj !== "object") {
|
|
142
|
+
return String(obj || "");
|
|
143
|
+
}
|
|
144
|
+
const record = obj;
|
|
145
|
+
// Pre-allocate array with known maximum size for better performance
|
|
146
|
+
const keyFields = [];
|
|
147
|
+
keyFields.length = 0; // Reset but keep allocated memory
|
|
148
|
+
// Extract only the most identifying fields - ordered by importance for early exit
|
|
149
|
+
const importantFields = [
|
|
150
|
+
"domainType",
|
|
151
|
+
"enhancementType",
|
|
152
|
+
"domainConfig",
|
|
153
|
+
"id",
|
|
154
|
+
"type",
|
|
155
|
+
"name",
|
|
156
|
+
];
|
|
157
|
+
// Use direct property access instead of 'in' operator for better performance
|
|
158
|
+
for (let i = 0; i < importantFields.length; i++) {
|
|
159
|
+
const field = importantFields[i];
|
|
160
|
+
const value = record[field];
|
|
161
|
+
if (value !== undefined) {
|
|
162
|
+
// Avoid template literals in hot path - use direct concatenation
|
|
163
|
+
keyFields.push(field + ":" + String(value));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// If no important fields found, use a more efficient fallback
|
|
167
|
+
if (keyFields.length === 0) {
|
|
168
|
+
// Cache the keys.length to avoid repeated Object.keys calls
|
|
169
|
+
keyFields.push("keys:" + Object.keys(record).length.toString());
|
|
170
|
+
}
|
|
171
|
+
return keyFields.join(",");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Fast non-cryptographic string hash function
|
|
175
|
+
* Much faster than MD5 for cache key generation
|
|
176
|
+
*/
|
|
177
|
+
fastStringHash(str) {
|
|
178
|
+
let hash = 0;
|
|
179
|
+
if (str.length === 0) {
|
|
180
|
+
return "0";
|
|
181
|
+
}
|
|
182
|
+
for (let i = 0; i < str.length; i++) {
|
|
183
|
+
const char = str.charCodeAt(i);
|
|
184
|
+
hash = (hash << 5) - hash + char;
|
|
185
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
186
|
+
}
|
|
187
|
+
// Convert to unsigned 32-bit integer hex string to avoid hash collisions
|
|
188
|
+
return (hash >>> 0).toString(16);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Fast numeric hash function for strings
|
|
192
|
+
* Returns numeric hash instead of string for performance
|
|
193
|
+
*/
|
|
194
|
+
hashString(str) {
|
|
195
|
+
let hash = 0;
|
|
196
|
+
if (str.length === 0) {
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
for (let i = 0; i < str.length; i++) {
|
|
200
|
+
const char = str.charCodeAt(i);
|
|
201
|
+
hash = (hash << 5) - hash + char;
|
|
202
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
203
|
+
}
|
|
204
|
+
return hash >>> 0;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Hash any value to a numeric hash
|
|
208
|
+
* Optimized for different value types
|
|
209
|
+
*/
|
|
210
|
+
hashValue(value) {
|
|
211
|
+
if (value === null) {
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
if (value === undefined) {
|
|
215
|
+
return 1;
|
|
216
|
+
}
|
|
217
|
+
const type = typeof value;
|
|
218
|
+
switch (type) {
|
|
219
|
+
case "string":
|
|
220
|
+
return this.hashString(value);
|
|
221
|
+
case "number":
|
|
222
|
+
return (Math.floor(value) >>> 0) % 2147483647;
|
|
223
|
+
case "boolean":
|
|
224
|
+
return value ? 1 : 0;
|
|
225
|
+
case "object":
|
|
226
|
+
// For objects, use a simple structural hash
|
|
227
|
+
try {
|
|
228
|
+
return this.hashString(JSON.stringify(value));
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return this.hashString(String(value));
|
|
232
|
+
}
|
|
233
|
+
default:
|
|
234
|
+
return this.hashString(String(value));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Combine two numeric hashes efficiently
|
|
239
|
+
* Uses bitwise operations for maximum performance
|
|
240
|
+
*/
|
|
241
|
+
combineHashes(hash1, hash2) {
|
|
242
|
+
// Use a variation of hash combination that minimizes collisions
|
|
243
|
+
return ((hash1 << 5) + hash1 + hash2) & 0x7fffffff;
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get cached result if available
|
|
247
|
+
*/
|
|
248
|
+
get(options) {
|
|
249
|
+
this.stats.totalRequests++;
|
|
250
|
+
const key = this.generateCacheKey(options);
|
|
251
|
+
const cached = this.cache.get(key);
|
|
252
|
+
if (cached) {
|
|
253
|
+
// Update access info for LRU
|
|
254
|
+
cached.accessCount++;
|
|
255
|
+
cached.timestamp = Date.now();
|
|
256
|
+
// Move to end (most recently used)
|
|
257
|
+
this.cache.delete(key);
|
|
258
|
+
this.cache.set(key, cached);
|
|
259
|
+
this.stats.hits++;
|
|
260
|
+
logger.debug("Factory processing cache hit", {
|
|
261
|
+
key: key.substring(0, 8),
|
|
262
|
+
});
|
|
263
|
+
return cached.result;
|
|
264
|
+
}
|
|
265
|
+
this.stats.misses++;
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Store result in cache
|
|
270
|
+
*/
|
|
271
|
+
set(options, result) {
|
|
272
|
+
try {
|
|
273
|
+
const key = this.generateCacheKey(options);
|
|
274
|
+
// Evict oldest entry if at capacity
|
|
275
|
+
if (this.cache.size >= this.maxSize) {
|
|
276
|
+
const oldestKey = this.cache.keys().next().value;
|
|
277
|
+
if (oldestKey !== undefined) {
|
|
278
|
+
this.cache.delete(oldestKey);
|
|
279
|
+
this.stats.evictions++;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
this.cache.set(key, {
|
|
283
|
+
result: { ...result }, // Deep copy to prevent mutations
|
|
284
|
+
timestamp: Date.now(),
|
|
285
|
+
accessCount: 1,
|
|
286
|
+
});
|
|
287
|
+
logger.debug("Factory processing result cached", {
|
|
288
|
+
key: key.substring(0, 8),
|
|
289
|
+
cacheSize: this.cache.size,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
logger.warn("Failed to cache factory processing result", { error });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Clear cache (useful for testing or memory management)
|
|
298
|
+
*/
|
|
299
|
+
clear() {
|
|
300
|
+
const size = this.cache.size;
|
|
301
|
+
this.cache.clear();
|
|
302
|
+
logger.debug(`Factory processing cache cleared (${size} entries removed)`);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Get cache statistics
|
|
306
|
+
*/
|
|
307
|
+
getStats() {
|
|
308
|
+
const hitRate = this.stats.totalRequests > 0
|
|
309
|
+
? Math.round((this.stats.hits / this.stats.totalRequests) * 100 * 100) /
|
|
310
|
+
100
|
|
311
|
+
: 0;
|
|
312
|
+
return {
|
|
313
|
+
...this.stats,
|
|
314
|
+
size: this.cache.size,
|
|
315
|
+
hitRate,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Remove entries older than specified age (in milliseconds)
|
|
320
|
+
*/
|
|
321
|
+
evictOld(maxAge = 5 * 60 * 1000) {
|
|
322
|
+
// Default: 5 minutes
|
|
323
|
+
const now = Date.now();
|
|
324
|
+
let evicted = 0;
|
|
325
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
326
|
+
if (now - entry.timestamp > maxAge) {
|
|
327
|
+
this.cache.delete(key);
|
|
328
|
+
evicted++;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (evicted > 0) {
|
|
332
|
+
logger.debug(`Evicted ${evicted} old cache entries`);
|
|
333
|
+
this.stats.evictions += evicted;
|
|
334
|
+
}
|
|
335
|
+
return evicted;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// Global cache instance
|
|
339
|
+
const factoryProcessingCache = new FactoryProcessingCache();
|
|
340
|
+
/**
|
|
341
|
+
* Validates if a value conforms to JsonValue type
|
|
342
|
+
* JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }
|
|
343
|
+
*/
|
|
344
|
+
function isJsonValue(value) {
|
|
345
|
+
if (value === null ||
|
|
346
|
+
typeof value === "string" ||
|
|
347
|
+
typeof value === "number" ||
|
|
348
|
+
typeof value === "boolean") {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
if (Array.isArray(value)) {
|
|
352
|
+
return value.every((item) => isJsonValue(item));
|
|
353
|
+
}
|
|
354
|
+
if (typeof value === "object" && value !== null) {
|
|
355
|
+
const obj = value;
|
|
356
|
+
return Object.values(obj).every((val) => isJsonValue(val));
|
|
357
|
+
}
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Safely converts unknown context values to JsonValue-compliant Record
|
|
362
|
+
* Filters out non-JsonValue compliant values and logs warnings
|
|
363
|
+
*/
|
|
364
|
+
function validateAndConvertContext(context) {
|
|
365
|
+
if (!context || typeof context !== "object" || Array.isArray(context)) {
|
|
366
|
+
logger.warn("Context must be a plain object, ignoring invalid context");
|
|
367
|
+
return {};
|
|
368
|
+
}
|
|
369
|
+
const validatedContext = {};
|
|
370
|
+
const obj = context;
|
|
371
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
372
|
+
if (isJsonValue(value)) {
|
|
373
|
+
validatedContext[key] = value;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
logger.warn(`Context value for key "${key}" is not JsonValue compliant, excluding from context`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return validatedContext;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Internal factory processing function (for caching)
|
|
383
|
+
*/
|
|
384
|
+
function processFactoryOptionsInternal(options) {
|
|
385
|
+
const functionTag = "processFactoryOptionsInternal";
|
|
386
|
+
try {
|
|
387
|
+
const factoryConfig = options.factoryConfig;
|
|
388
|
+
if (!factoryConfig) {
|
|
389
|
+
return { hasFactoryConfig: false };
|
|
390
|
+
}
|
|
391
|
+
logger.debug(`[${functionTag}] Processing factory configuration`, {
|
|
392
|
+
domainType: factoryConfig.domainType,
|
|
393
|
+
enhancementType: factoryConfig.enhancementType,
|
|
394
|
+
validateDomainData: factoryConfig.validateDomainData,
|
|
395
|
+
});
|
|
396
|
+
// Extract domain configuration
|
|
397
|
+
const domainType = factoryConfig.domainType;
|
|
398
|
+
const domainConfig = factoryConfig.domainConfig;
|
|
399
|
+
const enhancementType = factoryConfig.enhancementType;
|
|
400
|
+
// Create processed context that includes domain information with validation
|
|
401
|
+
const processedContext = {
|
|
402
|
+
...validateAndConvertContext(options.context),
|
|
403
|
+
};
|
|
404
|
+
// Add domain information to context if available
|
|
405
|
+
if (domainType) {
|
|
406
|
+
processedContext.domainType = domainType;
|
|
407
|
+
}
|
|
408
|
+
if (domainConfig) {
|
|
409
|
+
if (isJsonValue(domainConfig)) {
|
|
410
|
+
processedContext.domainConfig = domainConfig;
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
logger.warn("Domain config is not JsonValue compliant, excluding from context");
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (enhancementType) {
|
|
417
|
+
processedContext.enhancementType = enhancementType;
|
|
418
|
+
}
|
|
419
|
+
// Add factory metadata
|
|
420
|
+
processedContext.factoryEnhanced = true;
|
|
421
|
+
processedContext.factoryProcessedAt = Date.now();
|
|
422
|
+
logger.debug(`[${functionTag}] Factory configuration processed successfully`, {
|
|
423
|
+
domainType,
|
|
424
|
+
enhancementType,
|
|
425
|
+
contextKeys: Object.keys(processedContext),
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
hasFactoryConfig: true,
|
|
429
|
+
domainType,
|
|
430
|
+
domainConfig,
|
|
431
|
+
enhancementType,
|
|
432
|
+
processedContext,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
catch (error) {
|
|
436
|
+
logger.warn(`[${functionTag}] Failed to process factory configuration`, {
|
|
437
|
+
error: error instanceof Error ? error.message : String(error),
|
|
438
|
+
});
|
|
439
|
+
return { hasFactoryConfig: false };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Process factory configuration from enhanced options (with caching)
|
|
444
|
+
* Extracts and validates factory config for provider integration
|
|
445
|
+
*/
|
|
446
|
+
export function processFactoryOptions(options) {
|
|
447
|
+
// Try to get result from cache first
|
|
448
|
+
const cachedResult = factoryProcessingCache.get(options);
|
|
449
|
+
if (cachedResult) {
|
|
450
|
+
return cachedResult;
|
|
451
|
+
}
|
|
452
|
+
// Process and cache the result
|
|
453
|
+
const result = processFactoryOptionsInternal(options);
|
|
454
|
+
factoryProcessingCache.set(options, result);
|
|
455
|
+
return result;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Enhance TextGenerationOptions with factory configuration
|
|
459
|
+
* Converts enhanced GenerateOptions/StreamOptions to internal format
|
|
460
|
+
*/
|
|
461
|
+
export function enhanceTextGenerationOptions(baseOptions, factoryResult) {
|
|
462
|
+
if (!factoryResult.hasFactoryConfig) {
|
|
463
|
+
return baseOptions;
|
|
464
|
+
}
|
|
465
|
+
// Validate and merge contexts to ensure JsonValue compliance
|
|
466
|
+
const validatedBaseContext = validateAndConvertContext(baseOptions.context);
|
|
467
|
+
const factoryProcessedContext = factoryResult.processedContext || {};
|
|
468
|
+
const enhanced = {
|
|
469
|
+
...baseOptions,
|
|
470
|
+
// Merge contexts with proper validation to prevent runtime type errors
|
|
471
|
+
context: {
|
|
472
|
+
...validatedBaseContext,
|
|
473
|
+
...factoryProcessedContext,
|
|
474
|
+
},
|
|
475
|
+
// Ensure evaluation is enabled when using factory patterns
|
|
476
|
+
enableEvaluation: baseOptions.enableEvaluation ?? true,
|
|
477
|
+
// Use domain type for evaluation if available
|
|
478
|
+
evaluationDomain: factoryResult.domainType || baseOptions.evaluationDomain,
|
|
479
|
+
};
|
|
480
|
+
logger.debug("Enhanced TextGenerationOptions with factory configuration", {
|
|
481
|
+
domainType: factoryResult.domainType,
|
|
482
|
+
enhancementType: factoryResult.enhancementType,
|
|
483
|
+
hasProcessedContext: !!factoryResult.processedContext,
|
|
484
|
+
});
|
|
485
|
+
return enhanced;
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Check if options require factory processing
|
|
489
|
+
* Quick check to determine if factory enhancement is needed
|
|
490
|
+
*/
|
|
491
|
+
export function requiresFactoryProcessing(options) {
|
|
492
|
+
return !!options?.factoryConfig;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Extract streaming configuration for factory processing
|
|
496
|
+
* Handles streaming-specific factory enhancements
|
|
497
|
+
*/
|
|
498
|
+
export function processStreamingFactoryOptions(options) {
|
|
499
|
+
const streamingConfig = options.streaming;
|
|
500
|
+
if (!streamingConfig) {
|
|
501
|
+
return { hasStreamingConfig: false };
|
|
502
|
+
}
|
|
503
|
+
logger.debug("Processing streaming factory configuration", {
|
|
504
|
+
enabled: streamingConfig.enabled,
|
|
505
|
+
chunkSize: streamingConfig.chunkSize,
|
|
506
|
+
enableProgress: streamingConfig.enableProgress,
|
|
507
|
+
});
|
|
508
|
+
return {
|
|
509
|
+
hasStreamingConfig: true,
|
|
510
|
+
streamingEnabled: streamingConfig.enabled,
|
|
511
|
+
enhancedConfig: streamingConfig,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Convert enhanced StreamOptions back to clean StreamOptions
|
|
516
|
+
* Strips factory configuration while preserving enhanced context
|
|
517
|
+
*/
|
|
518
|
+
export function createCleanStreamOptions(enhancedOptions) {
|
|
519
|
+
const { factoryConfig, ...cleanOptions } = enhancedOptions;
|
|
520
|
+
// Return clean options without factoryConfig
|
|
521
|
+
return cleanOptions;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Validate factory configuration
|
|
525
|
+
* Ensures factory config is valid before processing
|
|
526
|
+
*/
|
|
527
|
+
export function validateFactoryConfig(factoryConfig) {
|
|
528
|
+
const errors = [];
|
|
529
|
+
if (!factoryConfig) {
|
|
530
|
+
return { isValid: true, errors: [] }; // No config is valid
|
|
531
|
+
}
|
|
532
|
+
// Validate domain type if present
|
|
533
|
+
if (factoryConfig.domainType !== undefined) {
|
|
534
|
+
if (typeof factoryConfig.domainType !== "string") {
|
|
535
|
+
errors.push("domainType must be a string");
|
|
536
|
+
}
|
|
537
|
+
else if (factoryConfig.domainType.length === 0) {
|
|
538
|
+
// Empty string is allowed (will be converted to "generic")
|
|
539
|
+
logger.debug("Empty domainType will be converted to 'generic'");
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
// Validate domain config if present
|
|
543
|
+
if (factoryConfig.domainConfig !== undefined) {
|
|
544
|
+
if (typeof factoryConfig.domainConfig !== "object" ||
|
|
545
|
+
factoryConfig.domainConfig === null) {
|
|
546
|
+
errors.push("domainConfig must be an object");
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
// Validate enhancement type if present
|
|
550
|
+
if (factoryConfig.enhancementType !== undefined) {
|
|
551
|
+
const validTypes = [
|
|
552
|
+
"domain-configuration",
|
|
553
|
+
"streaming-optimization",
|
|
554
|
+
"mcp-integration",
|
|
555
|
+
"legacy-migration",
|
|
556
|
+
"context-conversion",
|
|
557
|
+
];
|
|
558
|
+
if (!validTypes.includes(factoryConfig.enhancementType)) {
|
|
559
|
+
errors.push(`enhancementType must be one of: ${validTypes.join(", ")}`);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const isValid = errors.length === 0;
|
|
563
|
+
if (!isValid) {
|
|
564
|
+
logger.warn("Factory configuration validation failed", { errors });
|
|
565
|
+
}
|
|
566
|
+
return { isValid, errors };
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* Get factory processing cache statistics
|
|
570
|
+
* Useful for monitoring cache performance and debugging
|
|
571
|
+
*/
|
|
572
|
+
export function getFactoryProcessingCacheStats() {
|
|
573
|
+
return factoryProcessingCache.getStats();
|
|
574
|
+
}
|
|
575
|
+
/**
|
|
576
|
+
* Clear factory processing cache
|
|
577
|
+
* Useful for testing or memory management
|
|
578
|
+
*/
|
|
579
|
+
export function clearFactoryProcessingCache() {
|
|
580
|
+
factoryProcessingCache.clear();
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Evict old entries from factory processing cache
|
|
584
|
+
* Useful for periodic cleanup
|
|
585
|
+
*/
|
|
586
|
+
export function evictOldFactoryProcessingCache(maxAge) {
|
|
587
|
+
return factoryProcessingCache.evictOld(maxAge);
|
|
588
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type conversion utilities between GenerateOptions and StreamOptions
|
|
3
|
+
*
|
|
4
|
+
* 🔧 FIX: Addresses Issue #2 - Type System Mismatch
|
|
5
|
+
* Factory patterns need to work with both generate() and stream() methods
|
|
6
|
+
*/
|
|
7
|
+
import type { GenerateOptions } from "../types/generateTypes.js";
|
|
8
|
+
import type { StreamOptions } from "../types/streamTypes.js";
|
|
9
|
+
import type { UnknownRecord } from "../types/common.js";
|
|
10
|
+
/**
|
|
11
|
+
* Convert GenerateOptions to StreamOptions
|
|
12
|
+
* Preserves all factory configuration and enhancement data
|
|
13
|
+
*/
|
|
14
|
+
export declare function convertGenerateToStreamOptions(generateOptions: GenerateOptions): StreamOptions;
|
|
15
|
+
/**
|
|
16
|
+
* Convert StreamOptions to GenerateOptions
|
|
17
|
+
* Useful for fallback scenarios and unified processing
|
|
18
|
+
*/
|
|
19
|
+
export declare function convertStreamToGenerateOptions(streamOptions: StreamOptions): GenerateOptions;
|
|
20
|
+
/**
|
|
21
|
+
* Create a unified options interface that works with both methods
|
|
22
|
+
* Useful for factory utilities that need to work with either type
|
|
23
|
+
*/
|
|
24
|
+
export type UnifiedOptions = GenerateOptions & StreamOptions;
|
|
25
|
+
/**
|
|
26
|
+
* Check if options object has factory configuration
|
|
27
|
+
* Useful for determining if enhanced processing is needed
|
|
28
|
+
*/
|
|
29
|
+
export declare function hasFactoryConfig(options: GenerateOptions | StreamOptions | UnknownRecord): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Extract factory configuration from either options type
|
|
32
|
+
* Returns null if no factory config is present
|
|
33
|
+
*/
|
|
34
|
+
export declare function extractFactoryConfig(options: GenerateOptions | StreamOptions | UnknownRecord): GenerateOptions["factoryConfig"] | null;
|
|
35
|
+
/**
|
|
36
|
+
* Check if options object has streaming configuration
|
|
37
|
+
* Useful for determining if streaming enhancements are needed
|
|
38
|
+
*/
|
|
39
|
+
export declare function hasStreamingConfig(options: GenerateOptions | StreamOptions | UnknownRecord): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Extract streaming configuration from either options type
|
|
42
|
+
* Returns null if no streaming config is present
|
|
43
|
+
*/
|
|
44
|
+
export declare function extractStreamingConfig(options: GenerateOptions | StreamOptions | UnknownRecord): GenerateOptions["streaming"] | null;
|
|
45
|
+
/**
|
|
46
|
+
* Create factory-enhanced StreamOptions from domain configuration
|
|
47
|
+
* This is the key function that addresses Issue #2
|
|
48
|
+
*/
|
|
49
|
+
export declare function createFactoryAwareStreamOptions(baseOptions: Partial<StreamOptions>, factoryConfig: GenerateOptions["factoryConfig"]): StreamOptions;
|
|
50
|
+
/**
|
|
51
|
+
* Create factory-enhanced GenerateOptions from domain configuration
|
|
52
|
+
* Parallel function for generate() method
|
|
53
|
+
*/
|
|
54
|
+
export declare function createFactoryAwareGenerateOptions(baseOptions: Partial<GenerateOptions>, factoryConfig: GenerateOptions["factoryConfig"]): GenerateOptions;
|