@juspay/neurolink 9.56.1 → 9.57.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 +12 -0
- package/dist/auth/AuthProviderFactory.d.ts +3 -3
- package/dist/auth/providers/BaseAuthProvider.d.ts +2 -2
- package/dist/auth/providers/BaseAuthProvider.js +1 -1
- package/dist/auth/serverBridge.d.ts +2 -2
- package/dist/browser/neurolink.min.js +306 -306
- package/dist/cli/factories/commandFactory.js +32 -8
- package/dist/cli/loop/optionsSchema.js +4 -0
- package/dist/cli/parser.js +3 -3
- package/dist/dynamic/dynamicResolver.d.ts +282 -0
- package/dist/dynamic/dynamicResolver.js +633 -0
- package/dist/dynamic/index.d.ts +10 -0
- package/dist/dynamic/index.js +12 -0
- package/dist/dynamic/resolution.d.ts +17 -0
- package/dist/dynamic/resolution.js +21 -0
- package/dist/evaluation/index.js +1 -1
- package/dist/files/fileReferenceRegistry.js +25 -10
- package/dist/index.js +19 -2
- package/dist/lib/auth/AuthProviderFactory.d.ts +3 -3
- package/dist/lib/auth/providers/BaseAuthProvider.d.ts +2 -2
- package/dist/lib/auth/providers/BaseAuthProvider.js +1 -1
- package/dist/lib/auth/serverBridge.d.ts +2 -2
- package/dist/lib/dynamic/dynamicResolver.d.ts +282 -0
- package/dist/lib/dynamic/dynamicResolver.js +634 -0
- package/dist/lib/dynamic/index.d.ts +10 -0
- package/dist/lib/dynamic/index.js +13 -0
- package/dist/lib/dynamic/resolution.d.ts +17 -0
- package/dist/lib/dynamic/resolution.js +22 -0
- package/dist/lib/evaluation/index.js +1 -1
- package/dist/lib/files/fileReferenceRegistry.js +25 -10
- package/dist/lib/index.js +19 -2
- package/dist/lib/mcp/mcpServerBase.d.ts +1 -1
- package/dist/lib/mcp/mcpServerBase.js +1 -1
- package/dist/lib/neurolink.d.ts +12 -4
- package/dist/lib/neurolink.js +79 -6
- package/dist/lib/observability/exporters/baseExporter.d.ts +1 -1
- package/dist/lib/observability/exporters/baseExporter.js +1 -1
- package/dist/lib/types/auth.d.ts +6 -6
- package/dist/lib/types/config.d.ts +4 -4
- package/dist/lib/types/dynamic.d.ts +98 -0
- package/dist/lib/types/dynamic.js +10 -0
- package/dist/lib/types/file.d.ts +10 -0
- package/dist/lib/types/fileReference.d.ts +9 -0
- package/dist/lib/types/generate.d.ts +29 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/index.js +2 -0
- package/dist/lib/types/scorer.d.ts +1 -1
- package/dist/lib/types/scorer.js +1 -1
- package/dist/lib/types/span.d.ts +1 -1
- package/dist/lib/types/span.js +1 -1
- package/dist/lib/types/stream.d.ts +6 -0
- package/dist/lib/utils/fileDetector.d.ts +7 -0
- package/dist/lib/utils/fileDetector.js +47 -0
- package/dist/lib/utils/messageBuilder.js +15 -1
- package/dist/lib/utils/mimeTypeHints.d.ts +40 -0
- package/dist/lib/utils/mimeTypeHints.js +122 -0
- package/dist/mcp/mcpServerBase.d.ts +1 -1
- package/dist/mcp/mcpServerBase.js +1 -1
- package/dist/neurolink.d.ts +12 -4
- package/dist/neurolink.js +79 -6
- package/dist/observability/exporters/baseExporter.d.ts +1 -1
- package/dist/observability/exporters/baseExporter.js +1 -1
- package/dist/types/auth.d.ts +6 -6
- package/dist/types/config.d.ts +4 -4
- package/dist/types/dynamic.d.ts +98 -0
- package/dist/types/dynamic.js +9 -0
- package/dist/types/file.d.ts +10 -0
- package/dist/types/fileReference.d.ts +9 -0
- package/dist/types/generate.d.ts +29 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/scorer.d.ts +1 -1
- package/dist/types/scorer.js +1 -1
- package/dist/types/span.d.ts +1 -1
- package/dist/types/span.js +1 -1
- package/dist/types/stream.d.ts +6 -0
- package/dist/utils/fileDetector.d.ts +7 -0
- package/dist/utils/fileDetector.js +47 -0
- package/dist/utils/messageBuilder.js +15 -1
- package/dist/utils/mimeTypeHints.d.ts +40 -0
- package/dist/utils/mimeTypeHints.js +121 -0
- package/package.json +1 -1
|
@@ -0,0 +1,633 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Argument Resolution Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for resolving dynamic arguments to their actual values,
|
|
5
|
+
* with support for caching, memoization, fallbacks, and conditional resolution.
|
|
6
|
+
*
|
|
7
|
+
* @module dynamic/dynamicResolver
|
|
8
|
+
*/
|
|
9
|
+
import { isDynamicFunction, isContextAwareFunction } from "./resolution.js";
|
|
10
|
+
import { logger } from "../utils/logger.js";
|
|
11
|
+
import { withTimeout } from "../utils/errorHandling.js";
|
|
12
|
+
/**
|
|
13
|
+
* Default resolution options
|
|
14
|
+
*/
|
|
15
|
+
const DEFAULT_RESOLUTION_OPTIONS = {
|
|
16
|
+
timeout: 5000,
|
|
17
|
+
cache: false,
|
|
18
|
+
cacheKey: "",
|
|
19
|
+
cacheTtl: 60000, // 1 minute
|
|
20
|
+
defaultValue: undefined,
|
|
21
|
+
throwOnError: true,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Resolution cache for dynamic arguments
|
|
25
|
+
*/
|
|
26
|
+
class ResolutionCache {
|
|
27
|
+
cache = new Map();
|
|
28
|
+
cleanupInterval = null;
|
|
29
|
+
constructor(cleanupIntervalMs = 60000) {
|
|
30
|
+
this.startCleanup(cleanupIntervalMs);
|
|
31
|
+
}
|
|
32
|
+
get(key) {
|
|
33
|
+
const entry = this.cache.get(key);
|
|
34
|
+
if (!entry) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
if (Date.now() > entry.expiresAt) {
|
|
38
|
+
this.cache.delete(key);
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return entry.value;
|
|
42
|
+
}
|
|
43
|
+
set(key, value, ttl) {
|
|
44
|
+
const now = Date.now();
|
|
45
|
+
this.cache.set(key, {
|
|
46
|
+
value,
|
|
47
|
+
resolvedAt: now,
|
|
48
|
+
expiresAt: now + ttl,
|
|
49
|
+
key,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
delete(key) {
|
|
53
|
+
return this.cache.delete(key);
|
|
54
|
+
}
|
|
55
|
+
clear() {
|
|
56
|
+
this.cache.clear();
|
|
57
|
+
}
|
|
58
|
+
size() {
|
|
59
|
+
return this.cache.size;
|
|
60
|
+
}
|
|
61
|
+
startCleanup(intervalMs) {
|
|
62
|
+
const timer = setInterval(() => {
|
|
63
|
+
const now = Date.now();
|
|
64
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
65
|
+
if (now > entry.expiresAt) {
|
|
66
|
+
this.cache.delete(key);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, intervalMs);
|
|
70
|
+
if (typeof timer.unref === "function") {
|
|
71
|
+
timer.unref();
|
|
72
|
+
}
|
|
73
|
+
this.cleanupInterval = timer;
|
|
74
|
+
}
|
|
75
|
+
destroy() {
|
|
76
|
+
if (this.cleanupInterval) {
|
|
77
|
+
clearInterval(this.cleanupInterval);
|
|
78
|
+
this.cleanupInterval = null;
|
|
79
|
+
}
|
|
80
|
+
this.cache.clear();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Global resolution cache
|
|
84
|
+
const globalCache = new ResolutionCache();
|
|
85
|
+
// Stable per-instance ids for function arguments — `String(fn)` is unsafe
|
|
86
|
+
// because two distinct closures with identical source text collide.
|
|
87
|
+
const functionIds = new WeakMap();
|
|
88
|
+
let nextFunctionId = 0;
|
|
89
|
+
function getArgumentId(argument) {
|
|
90
|
+
if (typeof argument !== "function") {
|
|
91
|
+
return String(argument);
|
|
92
|
+
}
|
|
93
|
+
let id = functionIds.get(argument);
|
|
94
|
+
if (id === undefined) {
|
|
95
|
+
id = `fn:${++nextFunctionId}`;
|
|
96
|
+
functionIds.set(argument, id);
|
|
97
|
+
}
|
|
98
|
+
return id;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Generate cache key for dynamic argument resolution
|
|
102
|
+
*/
|
|
103
|
+
function generateCacheKey(argumentId, context, options) {
|
|
104
|
+
if (options?.cacheKey) {
|
|
105
|
+
return options.cacheKey;
|
|
106
|
+
}
|
|
107
|
+
const parts = [argumentId];
|
|
108
|
+
if (context?.requestContext) {
|
|
109
|
+
// Extract cache-scoping ids from generic context (consumer-defined shape)
|
|
110
|
+
const rc = context.requestContext;
|
|
111
|
+
if (rc.user?.id) {
|
|
112
|
+
parts.push(`user:${rc.user.id}`);
|
|
113
|
+
}
|
|
114
|
+
if (rc.tenant?.id) {
|
|
115
|
+
parts.push(`tenant:${rc.tenant.id}`);
|
|
116
|
+
}
|
|
117
|
+
if (rc.session?.id) {
|
|
118
|
+
parts.push(`session:${rc.session.id}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return parts.join(":");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Resolve a dynamic argument to its actual value
|
|
125
|
+
*
|
|
126
|
+
* @template T - The expected resolved type
|
|
127
|
+
* @param argument - The dynamic argument to resolve
|
|
128
|
+
* @param context - Resolution context (optional for static values)
|
|
129
|
+
* @param options - Resolution options
|
|
130
|
+
* @returns Resolution result with value and metadata
|
|
131
|
+
*
|
|
132
|
+
* @example Resolve static value
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const result = await resolveDynamicArgument("gpt-4o");
|
|
135
|
+
* console.log(result.value); // "gpt-4o"
|
|
136
|
+
* console.log(result.resolutionType); // "static"
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @example Resolve context-aware function
|
|
140
|
+
* ```typescript
|
|
141
|
+
* const modelSelector = ({ requestContext }) =>
|
|
142
|
+
* requestContext.tenant?.plan === "enterprise" ? "claude-3-opus" : "claude-3-sonnet";
|
|
143
|
+
*
|
|
144
|
+
* const result = await resolveDynamicArgument(modelSelector, {
|
|
145
|
+
* requestContext: { requestId: "123", tenant: { id: "t1", plan: "enterprise" } }
|
|
146
|
+
* });
|
|
147
|
+
* console.log(result.value); // "claude-3-opus"
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export async function resolveDynamicArgument(argument, context, options) {
|
|
151
|
+
const startTime = Date.now();
|
|
152
|
+
const opts = { ...DEFAULT_RESOLUTION_OPTIONS, ...options };
|
|
153
|
+
// Check cache first
|
|
154
|
+
if (opts.cache) {
|
|
155
|
+
const cacheKey = generateCacheKey(getArgumentId(argument), context, options);
|
|
156
|
+
const cached = globalCache.get(cacheKey);
|
|
157
|
+
if (cached !== undefined) {
|
|
158
|
+
return {
|
|
159
|
+
value: cached,
|
|
160
|
+
fromCache: true,
|
|
161
|
+
resolutionTime: Date.now() - startTime,
|
|
162
|
+
resolutionType: "static", // Cached value, original type unknown
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
// Static value
|
|
168
|
+
if (!isDynamicFunction(argument)) {
|
|
169
|
+
const result = {
|
|
170
|
+
value: argument,
|
|
171
|
+
fromCache: false,
|
|
172
|
+
resolutionTime: Date.now() - startTime,
|
|
173
|
+
resolutionType: "static",
|
|
174
|
+
};
|
|
175
|
+
if (opts.cache) {
|
|
176
|
+
const cacheKey = generateCacheKey(getArgumentId(argument), context, options);
|
|
177
|
+
globalCache.set(cacheKey, argument, opts.cacheTtl);
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
// Function value
|
|
182
|
+
let resolvedValue;
|
|
183
|
+
let resolutionType = "async-function";
|
|
184
|
+
const resolutionPromise = (async () => {
|
|
185
|
+
if (isContextAwareFunction(argument)) {
|
|
186
|
+
// Context-aware function — pass an empty context if caller didn't
|
|
187
|
+
// provide one. Combinators (withFallback, conditional, etc.) return
|
|
188
|
+
// arity-1 functions even when their inputs don't need context, so
|
|
189
|
+
// requiring a context here would force callers to invent one.
|
|
190
|
+
resolutionType = "context-aware";
|
|
191
|
+
return argument(context ?? { requestContext: {} });
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
// No-argument function
|
|
195
|
+
const fn = argument;
|
|
196
|
+
const result = fn();
|
|
197
|
+
if (result instanceof Promise) {
|
|
198
|
+
resolutionType = "async-function";
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
resolutionType = "sync-function";
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
})();
|
|
207
|
+
// Apply timeout if specified
|
|
208
|
+
if (opts.timeout > 0) {
|
|
209
|
+
resolvedValue = await withTimeout(resolutionPromise, opts.timeout, new Error(`Dynamic argument resolution timed out after ${opts.timeout}ms`));
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
resolvedValue = await resolutionPromise;
|
|
213
|
+
}
|
|
214
|
+
const result = {
|
|
215
|
+
value: resolvedValue,
|
|
216
|
+
fromCache: false,
|
|
217
|
+
resolutionTime: Date.now() - startTime,
|
|
218
|
+
resolutionType: resolutionType,
|
|
219
|
+
};
|
|
220
|
+
// Cache if enabled
|
|
221
|
+
if (opts.cache) {
|
|
222
|
+
const cacheKey = generateCacheKey(getArgumentId(argument), context, options);
|
|
223
|
+
globalCache.set(cacheKey, resolvedValue, opts.cacheTtl);
|
|
224
|
+
}
|
|
225
|
+
return result;
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
logger.error("Dynamic argument resolution failed", {
|
|
229
|
+
error: error instanceof Error ? error.message : String(error),
|
|
230
|
+
resolutionTime: Date.now() - startTime,
|
|
231
|
+
});
|
|
232
|
+
if (opts.throwOnError) {
|
|
233
|
+
throw error;
|
|
234
|
+
}
|
|
235
|
+
// Return default value on error
|
|
236
|
+
return {
|
|
237
|
+
value: opts.defaultValue,
|
|
238
|
+
fromCache: false,
|
|
239
|
+
resolutionTime: Date.now() - startTime,
|
|
240
|
+
resolutionType: "static",
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Resolve multiple dynamic arguments in parallel
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* const [model, temperature] = await resolveDynamicArguments(
|
|
250
|
+
* [
|
|
251
|
+
* ({ requestContext }) => requestContext.user?.preferences?.preferredModel || "gpt-4o",
|
|
252
|
+
* 0.7,
|
|
253
|
+
* ],
|
|
254
|
+
* context
|
|
255
|
+
* );
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export async function resolveDynamicArguments(arguments_, context, options) {
|
|
259
|
+
const results = await Promise.all(arguments_.map((arg) => resolveDynamicArgument(arg, context, options)));
|
|
260
|
+
return results.map((r) => r.value);
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Resolve all properties of a dynamic configuration object
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```typescript
|
|
267
|
+
* const dynamicConfig = {
|
|
268
|
+
* model: ({ requestContext }) => requestContext.tenant?.settings?.defaultModel || "gpt-4o",
|
|
269
|
+
* temperature: 0.7,
|
|
270
|
+
* maxTokens: async () => (await fetchConfig()).maxTokens,
|
|
271
|
+
* };
|
|
272
|
+
*
|
|
273
|
+
* const resolved = await resolveDynamicConfig(dynamicConfig, context);
|
|
274
|
+
* // resolved.model, resolved.temperature, resolved.maxTokens are all resolved values
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
277
|
+
export async function resolveDynamicConfig(config, context, options) {
|
|
278
|
+
const entries = Object.entries(config);
|
|
279
|
+
const resolvedEntries = await Promise.all(entries.map(async ([key, value]) => {
|
|
280
|
+
const result = await resolveDynamicArgument(value, context, options);
|
|
281
|
+
return [key, result.value];
|
|
282
|
+
}));
|
|
283
|
+
return Object.fromEntries(resolvedEntries);
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Create a memoized dynamic argument that caches its result
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```typescript
|
|
290
|
+
* const expensiveModelSelector = memoizeDynamicArgument(
|
|
291
|
+
* async ({ requestContext }) => {
|
|
292
|
+
* const config = await fetchTenantConfig(requestContext.tenant?.id);
|
|
293
|
+
* return config.preferredModel;
|
|
294
|
+
* },
|
|
295
|
+
* { cacheTtl: 300000 } // Cache for 5 minutes
|
|
296
|
+
* );
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
export function memoizeDynamicArgument(argument, options) {
|
|
300
|
+
if (!isDynamicFunction(argument)) {
|
|
301
|
+
return argument; // Static values don't need memoization
|
|
302
|
+
}
|
|
303
|
+
const cache = new Map();
|
|
304
|
+
const ttl = options?.cacheTtl || 60000;
|
|
305
|
+
return async (context) => {
|
|
306
|
+
const key = options?.cacheKey ||
|
|
307
|
+
generateCacheKey("memoized", context, { cacheKey: options?.cacheKey });
|
|
308
|
+
const cached = cache.get(key);
|
|
309
|
+
if (cached && Date.now() < cached.expiresAt) {
|
|
310
|
+
return cached.value;
|
|
311
|
+
}
|
|
312
|
+
const result = await resolveDynamicArgument(argument, context);
|
|
313
|
+
cache.set(key, { value: result.value, expiresAt: Date.now() + ttl });
|
|
314
|
+
return result.value;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Create a dynamic argument with fallback chain
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* const modelWithFallback = withFallback(
|
|
323
|
+
* ({ requestContext }) => requestContext.user?.preferences?.preferredModel,
|
|
324
|
+
* ({ requestContext }) => requestContext.tenant?.settings?.defaultModel,
|
|
325
|
+
* "gpt-4o" // Final static fallback
|
|
326
|
+
* );
|
|
327
|
+
* ```
|
|
328
|
+
*/
|
|
329
|
+
export function withFallback(...arguments_) {
|
|
330
|
+
return async (context) => {
|
|
331
|
+
let lastError;
|
|
332
|
+
for (const arg of arguments_) {
|
|
333
|
+
try {
|
|
334
|
+
const result = await resolveDynamicArgument(arg, context, {
|
|
335
|
+
throwOnError: false,
|
|
336
|
+
});
|
|
337
|
+
if (result.value !== undefined && result.value !== null) {
|
|
338
|
+
return result.value;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
lastError = err;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
const msg = lastError instanceof Error ? lastError.message : String(lastError || "");
|
|
346
|
+
throw new Error(`All fallbacks failed${msg ? `: ${msg}` : ""}`, {
|
|
347
|
+
cause: lastError,
|
|
348
|
+
});
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Create a conditional dynamic argument
|
|
353
|
+
*
|
|
354
|
+
* @example
|
|
355
|
+
* ```typescript
|
|
356
|
+
* const conditionalModel = conditional(
|
|
357
|
+
* ({ requestContext }) => requestContext.tenant?.plan === "enterprise",
|
|
358
|
+
* "claude-3-opus", // If true
|
|
359
|
+
* "claude-3-sonnet" // If false
|
|
360
|
+
* );
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
363
|
+
export function conditional(condition, ifTrue, ifFalse) {
|
|
364
|
+
return async (context) => {
|
|
365
|
+
const conditionResult = await resolveDynamicArgument(condition, context);
|
|
366
|
+
if (conditionResult.value) {
|
|
367
|
+
return (await resolveDynamicArgument(ifTrue, context)).value;
|
|
368
|
+
}
|
|
369
|
+
return (await resolveDynamicArgument(ifFalse, context)).value;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Create a mapped dynamic argument that transforms the result
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* const upperCaseModel = mapDynamicArgument(
|
|
378
|
+
* ({ requestContext }) => requestContext.user?.preferences?.preferredModel,
|
|
379
|
+
* (model) => model?.toUpperCase()
|
|
380
|
+
* );
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
export function mapDynamicArgument(argument, transform) {
|
|
384
|
+
return async (context) => {
|
|
385
|
+
const result = await resolveDynamicArgument(argument, context);
|
|
386
|
+
return transform(result.value);
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Create a dynamic argument that combines multiple arguments
|
|
391
|
+
*
|
|
392
|
+
* @example
|
|
393
|
+
* ```typescript
|
|
394
|
+
* const combinedConfig = combineDynamicArguments(
|
|
395
|
+
* [
|
|
396
|
+
* ({ requestContext }) => requestContext.user?.preferences?.preferredModel,
|
|
397
|
+
* ({ requestContext }) => requestContext.tenant?.settings?.defaultTemperature,
|
|
398
|
+
* ],
|
|
399
|
+
* ([model, temperature]) => ({ model: model || "gpt-4o", temperature: temperature || 0.7 })
|
|
400
|
+
* );
|
|
401
|
+
* ```
|
|
402
|
+
*/
|
|
403
|
+
export function combineDynamicArguments(arguments_, combiner) {
|
|
404
|
+
return async (context) => {
|
|
405
|
+
const results = await resolveDynamicArguments(arguments_, context);
|
|
406
|
+
return combiner(results);
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Check if a value contains any dynamic arguments (is a function)
|
|
411
|
+
*/
|
|
412
|
+
export function hasDynamicArgument(value) {
|
|
413
|
+
return isDynamicFunction(value);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Check if an object has any dynamic properties
|
|
417
|
+
*/
|
|
418
|
+
export function hasDynamicProperties(config) {
|
|
419
|
+
return Object.values(config).some((value) => isDynamicFunction(value));
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Clear the global resolution cache
|
|
423
|
+
*/
|
|
424
|
+
export function clearResolutionCache() {
|
|
425
|
+
globalCache.clear();
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get resolution cache statistics
|
|
429
|
+
*/
|
|
430
|
+
export function getResolutionCacheStats() {
|
|
431
|
+
return { size: globalCache.size() };
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Destroy the resolver (cleanup intervals, etc.)
|
|
435
|
+
*/
|
|
436
|
+
export function destroyResolver() {
|
|
437
|
+
globalCache.destroy();
|
|
438
|
+
}
|
|
439
|
+
// ============================================================================
|
|
440
|
+
// Environment Variable Interpolation
|
|
441
|
+
// ============================================================================
|
|
442
|
+
/**
|
|
443
|
+
* Pattern for environment variable interpolation
|
|
444
|
+
* Supports: ${ENV_VAR}, ${ENV_VAR:-default}, ${ENV_VAR:+replacement}
|
|
445
|
+
*/
|
|
446
|
+
const ENV_VAR_PATTERN = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::([+-]?)([^}]*))?\}/g;
|
|
447
|
+
/**
|
|
448
|
+
* Interpolate environment variables in a string
|
|
449
|
+
*
|
|
450
|
+
* Supports syntax:
|
|
451
|
+
* - ${VAR} - Simple substitution
|
|
452
|
+
* - ${VAR:-default} - Use default if VAR is unset or empty
|
|
453
|
+
* - ${VAR:+replacement} - Use replacement if VAR is set and non-empty
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```typescript
|
|
457
|
+
* interpolateEnvVars("Model: ${DEFAULT_MODEL:-gpt-4o}");
|
|
458
|
+
* // Returns "Model: gpt-4o" if DEFAULT_MODEL is not set
|
|
459
|
+
*
|
|
460
|
+
* interpolateEnvVars("API Key: ${OPENAI_API_KEY}");
|
|
461
|
+
* // Returns "API Key: sk-xxx..." if OPENAI_API_KEY is set
|
|
462
|
+
*
|
|
463
|
+
* interpolateEnvVars("Debug: ${DEBUG:+enabled}");
|
|
464
|
+
* // Returns "Debug: enabled" if DEBUG is set, "Debug: " otherwise
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
export function interpolateEnvVars(input, customEnv) {
|
|
468
|
+
const env = customEnv || process.env;
|
|
469
|
+
return input.replace(ENV_VAR_PATTERN, (match, varName, modifier, value) => {
|
|
470
|
+
const envValue = env[varName];
|
|
471
|
+
const isSet = envValue !== undefined && envValue !== "";
|
|
472
|
+
if (modifier === "-") {
|
|
473
|
+
// ${VAR:-default} - Use default if unset or empty
|
|
474
|
+
return isSet ? envValue : value || "";
|
|
475
|
+
}
|
|
476
|
+
else if (modifier === "+") {
|
|
477
|
+
// ${VAR:+replacement} - Use replacement if set and non-empty
|
|
478
|
+
return isSet ? value || "" : "";
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
// ${VAR} - Simple substitution
|
|
482
|
+
return envValue || "";
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Create a dynamic argument that interpolates environment variables
|
|
488
|
+
*
|
|
489
|
+
* @example
|
|
490
|
+
* ```typescript
|
|
491
|
+
* const model = fromEnv("${PREFERRED_MODEL:-gpt-4o}");
|
|
492
|
+
* // Resolves to value of PREFERRED_MODEL or "gpt-4o" as fallback
|
|
493
|
+
* ```
|
|
494
|
+
*/
|
|
495
|
+
export function fromEnv(template) {
|
|
496
|
+
return () => interpolateEnvVars(template);
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Create a dynamic argument from a single environment variable
|
|
500
|
+
*
|
|
501
|
+
* @example
|
|
502
|
+
* ```typescript
|
|
503
|
+
* const apiKey = envVar("OPENAI_API_KEY");
|
|
504
|
+
* // Resolves to value of OPENAI_API_KEY or undefined
|
|
505
|
+
*
|
|
506
|
+
* const model = envVar("DEFAULT_MODEL", "gpt-4o");
|
|
507
|
+
* // Resolves to DEFAULT_MODEL value or "gpt-4o" as default
|
|
508
|
+
* ```
|
|
509
|
+
*/
|
|
510
|
+
export function envVar(name, defaultValue) {
|
|
511
|
+
return () => {
|
|
512
|
+
const value = process.env[name];
|
|
513
|
+
return (value !== undefined && value !== "" ? value : defaultValue);
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Create a dynamic argument that selects from environment-based configurations
|
|
518
|
+
*
|
|
519
|
+
* @example
|
|
520
|
+
* ```typescript
|
|
521
|
+
* const model = envSwitch("NODE_ENV", {
|
|
522
|
+
* development: "gpt-4o-mini",
|
|
523
|
+
* production: "gpt-4o",
|
|
524
|
+
* test: "gpt-3.5-turbo",
|
|
525
|
+
* }, "gpt-4o-mini");
|
|
526
|
+
* ```
|
|
527
|
+
*/
|
|
528
|
+
export function envSwitch(envVarName, options, defaultValue) {
|
|
529
|
+
return () => {
|
|
530
|
+
const envValue = process.env[envVarName];
|
|
531
|
+
if (envValue && envValue in options) {
|
|
532
|
+
return options[envValue];
|
|
533
|
+
}
|
|
534
|
+
return defaultValue;
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Create a dynamic argument that parses a JSON value from an environment variable
|
|
539
|
+
*
|
|
540
|
+
* @example
|
|
541
|
+
* ```typescript
|
|
542
|
+
* // If RATE_LIMITS='{"requestsPerMinute": 100, "tokensPerDay": 50000}'
|
|
543
|
+
* const rateLimits = envJson<RateLimits>("RATE_LIMITS", { requestsPerMinute: 10 });
|
|
544
|
+
* ```
|
|
545
|
+
*/
|
|
546
|
+
export function envJson(name, defaultValue) {
|
|
547
|
+
return () => {
|
|
548
|
+
const value = process.env[name];
|
|
549
|
+
if (!value) {
|
|
550
|
+
return defaultValue;
|
|
551
|
+
}
|
|
552
|
+
try {
|
|
553
|
+
return JSON.parse(value);
|
|
554
|
+
}
|
|
555
|
+
catch {
|
|
556
|
+
logger.warn(`Failed to parse JSON from environment variable ${name}`);
|
|
557
|
+
return defaultValue;
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Create a dynamic argument that reads a number from an environment variable
|
|
563
|
+
*
|
|
564
|
+
* @example
|
|
565
|
+
* ```typescript
|
|
566
|
+
* const maxTokens = envNumber("MAX_TOKENS", 1000);
|
|
567
|
+
* const temperature = envNumber("TEMPERATURE", 0.7);
|
|
568
|
+
* ```
|
|
569
|
+
*/
|
|
570
|
+
export function envNumber(name, defaultValue) {
|
|
571
|
+
return () => {
|
|
572
|
+
const value = process.env[name];
|
|
573
|
+
if (!value) {
|
|
574
|
+
return defaultValue;
|
|
575
|
+
}
|
|
576
|
+
const parsed = parseFloat(value);
|
|
577
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Create a dynamic argument that reads a boolean from an environment variable
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* const enableDebug = envBoolean("DEBUG", false);
|
|
586
|
+
* const enableTools = envBoolean("ENABLE_TOOLS", true);
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
export function envBoolean(name, defaultValue) {
|
|
590
|
+
return () => {
|
|
591
|
+
const value = process.env[name];
|
|
592
|
+
if (!value) {
|
|
593
|
+
return defaultValue;
|
|
594
|
+
}
|
|
595
|
+
const lower = value.toLowerCase();
|
|
596
|
+
if (lower === "true" ||
|
|
597
|
+
lower === "1" ||
|
|
598
|
+
lower === "yes" ||
|
|
599
|
+
lower === "on") {
|
|
600
|
+
return true;
|
|
601
|
+
}
|
|
602
|
+
if (lower === "false" ||
|
|
603
|
+
lower === "0" ||
|
|
604
|
+
lower === "no" ||
|
|
605
|
+
lower === "off") {
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return defaultValue;
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
/**
|
|
612
|
+
* Create a dynamic argument that reads a comma-separated list from an environment variable
|
|
613
|
+
*
|
|
614
|
+
* @example
|
|
615
|
+
* ```typescript
|
|
616
|
+
* // If ALLOWED_PROVIDERS='openai,anthropic,vertex'
|
|
617
|
+
* const providers = envList("ALLOWED_PROVIDERS", ["openai"]);
|
|
618
|
+
* // Returns ["openai", "anthropic", "vertex"]
|
|
619
|
+
* ```
|
|
620
|
+
*/
|
|
621
|
+
export function envList(name, defaultValue, separator = ",") {
|
|
622
|
+
return () => {
|
|
623
|
+
const value = process.env[name];
|
|
624
|
+
if (!value) {
|
|
625
|
+
return defaultValue;
|
|
626
|
+
}
|
|
627
|
+
return value
|
|
628
|
+
.split(separator)
|
|
629
|
+
.map((s) => s.trim())
|
|
630
|
+
.filter(Boolean);
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
export { globalCache as resolutionCache };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Arguments Module
|
|
3
|
+
*
|
|
4
|
+
* Resolves function-valued options to their actual values before
|
|
5
|
+
* provider dispatch. All type definitions live in `src/lib/types/dynamic.ts`.
|
|
6
|
+
*
|
|
7
|
+
* @module dynamic
|
|
8
|
+
*/
|
|
9
|
+
export { isDynamicFunction, isContextAwareFunction } from "./resolution.js";
|
|
10
|
+
export { resolveDynamicArgument, resolveDynamicArguments, resolveDynamicConfig, memoizeDynamicArgument, withFallback, conditional, mapDynamicArgument, combineDynamicArguments, hasDynamicArgument, hasDynamicProperties, clearResolutionCache, getResolutionCacheStats, destroyResolver, resolutionCache, interpolateEnvVars, fromEnv, envVar, envSwitch, envJson, envNumber, envBoolean, envList, } from "./dynamicResolver.js";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Arguments Module
|
|
3
|
+
*
|
|
4
|
+
* Resolves function-valued options to their actual values before
|
|
5
|
+
* provider dispatch. All type definitions live in `src/lib/types/dynamic.ts`.
|
|
6
|
+
*
|
|
7
|
+
* @module dynamic
|
|
8
|
+
*/
|
|
9
|
+
// Type guards
|
|
10
|
+
export { isDynamicFunction, isContextAwareFunction } from "./resolution.js";
|
|
11
|
+
// Resolution utilities
|
|
12
|
+
export { resolveDynamicArgument, resolveDynamicArguments, resolveDynamicConfig, memoizeDynamicArgument, withFallback, conditional, mapDynamicArgument, combineDynamicArguments, hasDynamicArgument, hasDynamicProperties, clearResolutionCache, getResolutionCacheStats, destroyResolver, resolutionCache, interpolateEnvVars, fromEnv, envVar, envSwitch, envJson, envNumber, envBoolean, envList, } from "./dynamicResolver.js";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Arguments Runtime Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides type guard functions for dynamic argument resolution.
|
|
5
|
+
* Type definitions live in src/lib/types/dynamic.ts (canonical location).
|
|
6
|
+
*
|
|
7
|
+
* @module dynamic/resolution
|
|
8
|
+
*/
|
|
9
|
+
import type { DynamicArgument, DynamicResolutionContext } from "../types/index.js";
|
|
10
|
+
/**
|
|
11
|
+
* Type guard to check if a value is a DynamicArgument function
|
|
12
|
+
*/
|
|
13
|
+
export declare function isDynamicFunction<T>(value: DynamicArgument<T>): value is (() => T) | (() => Promise<T>) | ((context: DynamicResolutionContext) => T) | ((context: DynamicResolutionContext) => Promise<T>);
|
|
14
|
+
/**
|
|
15
|
+
* Type guard to check if a function expects context
|
|
16
|
+
*/
|
|
17
|
+
export declare function isContextAwareFunction<T>(fn: Function): fn is ((context: DynamicResolutionContext) => T) | ((context: DynamicResolutionContext) => Promise<T>);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamic Arguments Runtime Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides type guard functions for dynamic argument resolution.
|
|
5
|
+
* Type definitions live in src/lib/types/dynamic.ts (canonical location).
|
|
6
|
+
*
|
|
7
|
+
* @module dynamic/resolution
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Type guard to check if a value is a DynamicArgument function
|
|
11
|
+
*/
|
|
12
|
+
export function isDynamicFunction(value) {
|
|
13
|
+
return typeof value === "function";
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Type guard to check if a function expects context
|
|
17
|
+
*/
|
|
18
|
+
export function isContextAwareFunction(fn) {
|
|
19
|
+
// Check function parameter count
|
|
20
|
+
return fn.length > 0;
|
|
21
|
+
}
|
package/dist/evaluation/index.js
CHANGED
|
@@ -14,7 +14,7 @@ export * from "./pipeline/index.js";
|
|
|
14
14
|
export * from "./reporting/index.js";
|
|
15
15
|
// Re-export scorers
|
|
16
16
|
export * from "./scorers/index.js";
|
|
17
|
-
// Re-export Factory and Registry
|
|
17
|
+
// Re-export Factory and Registry
|
|
18
18
|
export { BatchEvaluator } from "./BatchEvaluator.js";
|
|
19
19
|
export { EvaluationAggregator } from "./EvaluationAggregator.js";
|
|
20
20
|
export { EvaluatorFactory, getEvaluatorFactory } from "./EvaluatorFactory.js";
|