@juspay/neurolink 7.24.1 → 7.26.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 (34) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/core/baseProvider.d.ts +13 -0
  3. package/dist/core/baseProvider.js +121 -1
  4. package/dist/core/evaluationProviders.d.ts +26 -0
  5. package/dist/core/evaluationProviders.js +160 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +2 -0
  8. package/dist/lib/core/baseProvider.d.ts +13 -0
  9. package/dist/lib/core/baseProvider.js +121 -1
  10. package/dist/lib/core/evaluationProviders.d.ts +26 -0
  11. package/dist/lib/core/evaluationProviders.js +160 -0
  12. package/dist/lib/index.d.ts +3 -0
  13. package/dist/lib/index.js +2 -0
  14. package/dist/lib/middleware/builtin/analytics.d.ts +16 -0
  15. package/dist/lib/middleware/builtin/analytics.js +130 -0
  16. package/dist/lib/middleware/factory.d.ts +54 -0
  17. package/dist/lib/middleware/factory.js +289 -0
  18. package/dist/lib/middleware/index.d.ts +58 -0
  19. package/dist/lib/middleware/index.js +67 -0
  20. package/dist/lib/middleware/registry.d.ts +78 -0
  21. package/dist/lib/middleware/registry.js +283 -0
  22. package/dist/lib/middleware/types.d.ts +144 -0
  23. package/dist/lib/middleware/types.js +1 -0
  24. package/dist/middleware/builtin/analytics.d.ts +16 -0
  25. package/dist/middleware/builtin/analytics.js +130 -0
  26. package/dist/middleware/factory.d.ts +54 -0
  27. package/dist/middleware/factory.js +289 -0
  28. package/dist/middleware/index.d.ts +58 -0
  29. package/dist/middleware/index.js +67 -0
  30. package/dist/middleware/registry.d.ts +78 -0
  31. package/dist/middleware/registry.js +283 -0
  32. package/dist/middleware/types.d.ts +144 -0
  33. package/dist/middleware/types.js +1 -0
  34. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## [7.26.0](https://github.com/juspay/neurolink/compare/v7.25.0...v7.26.0) (2025-08-21)
2
+
3
+ ### Features
4
+
5
+ - **(core):** implement provider performance metrics and optimization system ([caa68e7](https://github.com/juspay/neurolink/commit/caa68e7fd44d5a15dd48605063b093279b6f82ae))
6
+
7
+ ## [7.25.0](https://github.com/juspay/neurolink/compare/v7.24.1...v7.25.0) (2025-08-21)
8
+
9
+ ### Features
10
+
11
+ - **(middleware):** add custom middleware development guide ([ffd0343](https://github.com/juspay/neurolink/commit/ffd0343a589b267a5b8349a06cdfe2664a942e4c))
12
+
1
13
  ## [7.24.1](https://github.com/juspay/neurolink/compare/v7.24.0...v7.24.1) (2025-08-21)
2
14
 
3
15
  ## [7.24.0](https://github.com/juspay/neurolink/compare/v7.23.0...v7.24.0) (2025-08-20)
@@ -63,6 +63,19 @@ export declare abstract class BaseProvider implements AIProvider {
63
63
  * Returns the Vercel AI SDK model instance for this provider
64
64
  */
65
65
  protected abstract getAISDKModel(): LanguageModelV1 | Promise<LanguageModelV1>;
66
+ /**
67
+ * Get AI SDK model with middleware applied
68
+ * This method wraps the base model with any configured middleware
69
+ */
70
+ protected getAISDKModelWithMiddleware(options?: TextGenerationOptions | StreamOptions): Promise<LanguageModelV1>;
71
+ /**
72
+ * Extract middleware options from generation options
73
+ */
74
+ private extractMiddlewareOptions;
75
+ /**
76
+ * Determine if middleware should be skipped for this request
77
+ */
78
+ private shouldSkipMiddleware;
66
79
  /**
67
80
  * Get all available tools - direct tools are ALWAYS available
68
81
  * MCP tools are added when available (without blocking)
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { MiddlewareFactory } from "../middleware/factory.js";
2
3
  import { logger } from "../utils/logger.js";
3
4
  import { DEFAULT_MAX_STEPS, STEP_LIMITS } from "../core/constants.js";
4
5
  import { directAgentTools } from "../agent/directTools.js";
@@ -8,6 +9,8 @@ import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
8
9
  import { buildMessagesArray } from "../utils/messageBuilder.js";
9
10
  import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
10
11
  import { validateStreamOptions as validateStreamOpts, validateTextGenerationOptions, ValidationError, createValidationSummary, } from "../utils/parameterValidation.js";
12
+ import { recordProviderPerformanceFromMetrics, getPerformanceOptimizedProvider, } from "./evaluationProviders.js";
13
+ import { modelConfig } from "./modelConfiguration.js";
11
14
  /**
12
15
  * Abstract base class for all AI providers
13
16
  * Tools are integrated as first-class citizens - always available by default
@@ -181,6 +184,46 @@ export class BaseProvider {
181
184
  temperature: options.temperature,
182
185
  maxTokens: options.maxTokens || 8192,
183
186
  });
187
+ const responseTime = Date.now() - startTime;
188
+ try {
189
+ // Calculate actual cost based on token usage and provider configuration
190
+ const calculateActualCost = () => {
191
+ try {
192
+ const costInfo = modelConfig.getCostInfo(this.providerName, this.modelName);
193
+ if (!costInfo) {
194
+ return 0; // No cost info available
195
+ }
196
+ const promptTokens = result.usage?.promptTokens || 0;
197
+ const completionTokens = result.usage?.completionTokens || 0;
198
+ // Calculate cost per 1K tokens
199
+ const inputCost = (promptTokens / 1000) * costInfo.input;
200
+ const outputCost = (completionTokens / 1000) * costInfo.output;
201
+ return inputCost + outputCost;
202
+ }
203
+ catch (error) {
204
+ logger.debug(`Cost calculation failed for ${this.providerName}:`, error);
205
+ return 0; // Fallback to 0 on any error
206
+ }
207
+ };
208
+ const actualCost = calculateActualCost();
209
+ recordProviderPerformanceFromMetrics(this.providerName, {
210
+ responseTime,
211
+ tokensGenerated: result.usage?.totalTokens || 0,
212
+ cost: actualCost,
213
+ success: true,
214
+ });
215
+ // Show what the system learned (updated to include cost)
216
+ const optimizedProvider = getPerformanceOptimizedProvider("speed");
217
+ logger.debug(`🚀 Performance recorded for ${this.providerName}:`, {
218
+ responseTime: `${responseTime}ms`,
219
+ tokens: result.usage?.totalTokens || 0,
220
+ estimatedCost: `$${actualCost.toFixed(6)}`,
221
+ recommendedSpeedProvider: optimizedProvider?.provider || "none",
222
+ });
223
+ }
224
+ catch (perfError) {
225
+ logger.warn("⚠️ Performance recording failed:", perfError);
226
+ }
184
227
  // Extract tool names from tool calls for tracking
185
228
  // AI SDK puts tool calls in steps array for multi-step generation
186
229
  const toolsUsed = [];
@@ -357,6 +400,83 @@ export class BaseProvider {
357
400
  evaluation: result.evaluation,
358
401
  };
359
402
  }
403
+ /**
404
+ * Get AI SDK model with middleware applied
405
+ * This method wraps the base model with any configured middleware
406
+ */
407
+ async getAISDKModelWithMiddleware(options = {}) {
408
+ // Get the base model
409
+ const baseModel = await this.getAISDKModel();
410
+ // Check if middleware should be applied
411
+ const middlewareOptions = this.extractMiddlewareOptions(options);
412
+ if (!middlewareOptions || this.shouldSkipMiddleware(options)) {
413
+ return baseModel;
414
+ }
415
+ try {
416
+ // Create middleware context
417
+ const context = MiddlewareFactory.createContext(this.providerName, this.modelName, options, {
418
+ sessionId: this.sessionId,
419
+ userId: this.userId,
420
+ });
421
+ // Apply middleware to the model
422
+ const wrappedModel = MiddlewareFactory.applyMiddleware(baseModel, context, middlewareOptions);
423
+ logger.debug(`Applied middleware to ${this.providerName} model`, {
424
+ provider: this.providerName,
425
+ model: this.modelName,
426
+ hasMiddleware: true,
427
+ });
428
+ return wrappedModel;
429
+ }
430
+ catch (error) {
431
+ logger.warn(`Failed to apply middleware to ${this.providerName}, using base model`, {
432
+ error: error instanceof Error ? error.message : String(error),
433
+ });
434
+ // Return base model on middleware failure to maintain functionality
435
+ return baseModel;
436
+ }
437
+ }
438
+ /**
439
+ * Extract middleware options from generation options
440
+ */
441
+ extractMiddlewareOptions(options) {
442
+ // Check for middleware configuration in options
443
+ const optionsRecord = options;
444
+ const middlewareConfig = optionsRecord.middlewareConfig;
445
+ const enabledMiddleware = optionsRecord.enabledMiddleware;
446
+ const disabledMiddleware = optionsRecord.disabledMiddleware;
447
+ const preset = optionsRecord.middlewarePreset;
448
+ // If no middleware configuration is present, return null
449
+ if (!middlewareConfig &&
450
+ !enabledMiddleware &&
451
+ !disabledMiddleware &&
452
+ !preset) {
453
+ return null;
454
+ }
455
+ return {
456
+ middlewareConfig,
457
+ enabledMiddleware,
458
+ disabledMiddleware,
459
+ preset,
460
+ global: {
461
+ collectStats: true,
462
+ continueOnError: true,
463
+ },
464
+ };
465
+ }
466
+ /**
467
+ * Determine if middleware should be skipped for this request
468
+ */
469
+ shouldSkipMiddleware(options) {
470
+ // Skip middleware if explicitly disabled
471
+ if (options.disableMiddleware === true) {
472
+ return true;
473
+ }
474
+ // Skip middleware for tool-disabled requests to avoid conflicts
475
+ if (options.disableTools === true) {
476
+ return true;
477
+ }
478
+ return false;
479
+ }
360
480
  // ===================
361
481
  // TOOL MANAGEMENT
362
482
  // ===================
@@ -428,7 +548,7 @@ export class BaseProvider {
428
548
  timestamp: new Date().toISOString(),
429
549
  params: params,
430
550
  // Keep it simple - just indicate an error occurred
431
- message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`
551
+ message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`,
432
552
  };
433
553
  }
434
554
  },
@@ -32,3 +32,29 @@ export declare function isProviderAvailable(providerName: string): boolean;
32
32
  * Get the best available provider based on preference
33
33
  */
34
34
  export declare function getBestAvailableProvider(preferCheap?: boolean): ProviderModelConfig | null;
35
+ /**
36
+ Record actual provider performance for optimization
37
+ */
38
+ export declare function recordProviderPerformanceFromMetrics(providerName: string, metrics: {
39
+ responseTime: number;
40
+ tokensGenerated: number;
41
+ cost: number;
42
+ success: boolean;
43
+ }): void;
44
+ /**
45
+ * Get performance-optimized provider based on real metrics
46
+ */
47
+ export declare function getPerformanceOptimizedProvider(priority?: "speed" | "cost" | "reliability"): ProviderModelConfig | null;
48
+ export declare function getProviderPerformanceAnalytics(): Record<string, {
49
+ avgResponseTime: number;
50
+ successRate: number;
51
+ tokenThroughput: number;
52
+ costEfficiency: number;
53
+ recommendation: string;
54
+ sampleCount: number;
55
+ }>;
56
+ /**
57
+ * Reset performance metrics for a provider or all providers.
58
+ * @param providerName - (Optional) The name of the provider to reset. If omitted, resets all providers.
59
+ */
60
+ export declare function resetProviderMetrics(providerName?: string): void;
@@ -1,4 +1,11 @@
1
1
  import { modelConfig } from "./modelConfiguration.js";
2
+ const PERFORMANCE_THRESHOLDS = {
3
+ EXCELLENT_SUCCESS_RATE: 0.95,
4
+ EXCELLENT_RESPONSE_TIME_MS: 2000,
5
+ GOOD_SUCCESS_RATE: 0.9,
6
+ FAIR_SUCCESS_RATE: 0.8,
7
+ };
8
+ const providerMetrics = new Map();
2
9
  /**
3
10
  * Convert new configuration format to legacy format for backwards compatibility
4
11
  */
@@ -101,3 +108,156 @@ export function getBestAvailableProvider(preferCheap = true) {
101
108
  const sortedProviders = sortProvidersByPreference(availableProviders, preferCheap);
102
109
  return sortedProviders[0];
103
110
  }
111
+ /**
112
+ Record actual provider performance for optimization
113
+ */
114
+ export function recordProviderPerformanceFromMetrics(providerName, metrics) {
115
+ const existing = providerMetrics.get(providerName) || {
116
+ responseTime: [],
117
+ successRate: 0,
118
+ tokenThroughput: 0,
119
+ costEfficiency: 0,
120
+ lastUpdated: new Date(),
121
+ sampleCount: 0,
122
+ };
123
+ // Keep rolling window of last 50 response times
124
+ existing.responseTime.push(metrics.responseTime);
125
+ if (existing.responseTime.length > 50) {
126
+ existing.responseTime.shift();
127
+ }
128
+ // Update success rate
129
+ const totalSamples = existing.sampleCount + 1;
130
+ existing.successRate =
131
+ (existing.successRate * existing.sampleCount + (metrics.success ? 1 : 0)) /
132
+ totalSamples;
133
+ // Update throughput (tokens per second)
134
+ if (metrics.responseTime > 0) {
135
+ const tokensPerSecond = metrics.tokensGenerated / (metrics.responseTime / 1000);
136
+ existing.tokenThroughput =
137
+ (existing.tokenThroughput * existing.sampleCount + tokensPerSecond) /
138
+ totalSamples;
139
+ }
140
+ // If responseTime is 0, skip updating tokenThroughput for this sample
141
+ // but still update other metrics
142
+ // Update cost efficiency (tokens per dollar)
143
+ if (metrics.cost > 0) {
144
+ const tokensPerDollar = metrics.tokensGenerated / metrics.cost;
145
+ existing.costEfficiency =
146
+ (existing.costEfficiency * existing.sampleCount + tokensPerDollar) /
147
+ totalSamples;
148
+ }
149
+ existing.sampleCount = totalSamples;
150
+ existing.lastUpdated = new Date();
151
+ providerMetrics.set(providerName, existing);
152
+ }
153
+ /**
154
+ * Get performance-optimized provider based on real metrics
155
+ */
156
+ export function getPerformanceOptimizedProvider(priority = "speed") {
157
+ const availableProviders = getAvailableProviders();
158
+ if (availableProviders.length === 0) {
159
+ return null;
160
+ }
161
+ // Score providers based on real performance data
162
+ const scoredProviders = availableProviders.map((provider) => {
163
+ const metrics = providerMetrics.get(provider.provider);
164
+ if (!metrics || metrics.sampleCount < 3) {
165
+ // Fall back to static performance ratings for providers without data
166
+ return {
167
+ provider,
168
+ score: getStaticPerformanceScore(provider, priority),
169
+ metrics: null,
170
+ };
171
+ }
172
+ let score = 0;
173
+ switch (priority) {
174
+ case "speed": {
175
+ const avgResponseTime = metrics.responseTime.reduce((a, b) => a + b, 0) /
176
+ metrics.responseTime.length;
177
+ score =
178
+ metrics.tokenThroughput * 0.6 +
179
+ (5000 / Math.max(avgResponseTime, 100)) * 0.4;
180
+ break;
181
+ }
182
+ case "cost": {
183
+ score = metrics.costEfficiency * 0.7 + metrics.successRate * 0.3;
184
+ break;
185
+ }
186
+ case "reliability": {
187
+ score = metrics.successRate * 0.8 + (metrics.sampleCount / 100) * 0.2;
188
+ break;
189
+ }
190
+ }
191
+ return { provider, score, metrics };
192
+ });
193
+ // Sort by score and return best
194
+ scoredProviders.sort((a, b) => b.score - a.score);
195
+ return scoredProviders[0].provider;
196
+ }
197
+ /**
198
+ * Helper function for providers without performance data
199
+ */
200
+ function getStaticPerformanceScore(provider, priority) {
201
+ switch (priority) {
202
+ case "speed": {
203
+ const speedScore = provider.performance?.speed || 1;
204
+ return speedScore;
205
+ }
206
+ case "cost": {
207
+ const costScore = provider.performance?.cost || 1;
208
+ return costScore;
209
+ }
210
+ case "reliability": {
211
+ const qualityScore = provider.performance?.quality || 1;
212
+ return qualityScore;
213
+ }
214
+ default: {
215
+ throw new Error(`Invalid priority: "${priority}". Must be one of: speed, cost, reliability`);
216
+ }
217
+ }
218
+ }
219
+ export function getProviderPerformanceAnalytics() {
220
+ const analytics = {};
221
+ for (const [providerName, metrics] of providerMetrics.entries()) {
222
+ if (metrics.sampleCount === 0) {
223
+ continue;
224
+ }
225
+ const avgResponseTime = metrics.responseTime.reduce((a, b) => a + b, 0) /
226
+ metrics.responseTime.length;
227
+ let recommendation = "";
228
+ if (metrics.successRate > PERFORMANCE_THRESHOLDS.EXCELLENT_SUCCESS_RATE &&
229
+ avgResponseTime < PERFORMANCE_THRESHOLDS.EXCELLENT_RESPONSE_TIME_MS) {
230
+ recommendation = "Excellent - Recommended for production";
231
+ }
232
+ else if (metrics.successRate > PERFORMANCE_THRESHOLDS.GOOD_SUCCESS_RATE) {
233
+ recommendation = "Good - Suitable for most tasks";
234
+ }
235
+ else if (metrics.successRate > PERFORMANCE_THRESHOLDS.FAIR_SUCCESS_RATE) {
236
+ recommendation = "Fair - Monitor closely";
237
+ }
238
+ else {
239
+ recommendation = "Poor - Consider alternative providers";
240
+ }
241
+ analytics[providerName] = {
242
+ avgResponseTime,
243
+ successRate: metrics.successRate,
244
+ tokenThroughput: metrics.tokenThroughput,
245
+ costEfficiency: metrics.costEfficiency,
246
+ recommendation,
247
+ sampleCount: metrics.sampleCount,
248
+ };
249
+ }
250
+ return analytics;
251
+ }
252
+ /**
253
+ * Reset performance metrics for a provider or all providers.
254
+ * @param providerName - (Optional) The name of the provider to reset. If omitted, resets all providers.
255
+ */
256
+ export function resetProviderMetrics(providerName) {
257
+ if (providerName) {
258
+ providerMetrics.delete(providerName);
259
+ }
260
+ else {
261
+ providerMetrics.clear();
262
+ }
263
+ }
package/dist/index.d.ts CHANGED
@@ -18,6 +18,9 @@ export { getBestProvider, getAvailableProviders, isValidProvider, } from "./util
18
18
  export { NeuroLink } from "./neurolink.js";
19
19
  export type { ProviderStatus, MCPStatus } from "./neurolink.js";
20
20
  export type { MCPServerInfo } from "./types/mcpTypes.js";
21
+ export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, } from "./middleware/types.js";
22
+ export { MiddlewareRegistry, MiddlewareFactory } from "./middleware/index.js";
23
+ export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
21
24
  export declare const VERSION = "1.0.0";
22
25
  /**
23
26
  * Quick start factory function
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
16
16
  export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
17
17
  // Main NeuroLink wrapper class and diagnostic types
18
18
  export { NeuroLink } from "./neurolink.js";
19
+ export { MiddlewareRegistry, MiddlewareFactory } from "./middleware/index.js";
20
+ export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
19
21
  // Version
20
22
  export const VERSION = "1.0.0";
21
23
  /**
@@ -63,6 +63,19 @@ export declare abstract class BaseProvider implements AIProvider {
63
63
  * Returns the Vercel AI SDK model instance for this provider
64
64
  */
65
65
  protected abstract getAISDKModel(): LanguageModelV1 | Promise<LanguageModelV1>;
66
+ /**
67
+ * Get AI SDK model with middleware applied
68
+ * This method wraps the base model with any configured middleware
69
+ */
70
+ protected getAISDKModelWithMiddleware(options?: TextGenerationOptions | StreamOptions): Promise<LanguageModelV1>;
71
+ /**
72
+ * Extract middleware options from generation options
73
+ */
74
+ private extractMiddlewareOptions;
75
+ /**
76
+ * Determine if middleware should be skipped for this request
77
+ */
78
+ private shouldSkipMiddleware;
66
79
  /**
67
80
  * Get all available tools - direct tools are ALWAYS available
68
81
  * MCP tools are added when available (without blocking)
@@ -1,4 +1,5 @@
1
1
  import { z } from "zod";
2
+ import { MiddlewareFactory } from "../middleware/factory.js";
2
3
  import { logger } from "../utils/logger.js";
3
4
  import { DEFAULT_MAX_STEPS, STEP_LIMITS } from "../core/constants.js";
4
5
  import { directAgentTools } from "../agent/directTools.js";
@@ -8,6 +9,8 @@ import { shouldDisableBuiltinTools } from "../utils/toolUtils.js";
8
9
  import { buildMessagesArray } from "../utils/messageBuilder.js";
9
10
  import { getKeysAsString, getKeyCount } from "../utils/transformationUtils.js";
10
11
  import { validateStreamOptions as validateStreamOpts, validateTextGenerationOptions, ValidationError, createValidationSummary, } from "../utils/parameterValidation.js";
12
+ import { recordProviderPerformanceFromMetrics, getPerformanceOptimizedProvider, } from "./evaluationProviders.js";
13
+ import { modelConfig } from "./modelConfiguration.js";
11
14
  /**
12
15
  * Abstract base class for all AI providers
13
16
  * Tools are integrated as first-class citizens - always available by default
@@ -181,6 +184,46 @@ export class BaseProvider {
181
184
  temperature: options.temperature,
182
185
  maxTokens: options.maxTokens || 8192,
183
186
  });
187
+ const responseTime = Date.now() - startTime;
188
+ try {
189
+ // Calculate actual cost based on token usage and provider configuration
190
+ const calculateActualCost = () => {
191
+ try {
192
+ const costInfo = modelConfig.getCostInfo(this.providerName, this.modelName);
193
+ if (!costInfo) {
194
+ return 0; // No cost info available
195
+ }
196
+ const promptTokens = result.usage?.promptTokens || 0;
197
+ const completionTokens = result.usage?.completionTokens || 0;
198
+ // Calculate cost per 1K tokens
199
+ const inputCost = (promptTokens / 1000) * costInfo.input;
200
+ const outputCost = (completionTokens / 1000) * costInfo.output;
201
+ return inputCost + outputCost;
202
+ }
203
+ catch (error) {
204
+ logger.debug(`Cost calculation failed for ${this.providerName}:`, error);
205
+ return 0; // Fallback to 0 on any error
206
+ }
207
+ };
208
+ const actualCost = calculateActualCost();
209
+ recordProviderPerformanceFromMetrics(this.providerName, {
210
+ responseTime,
211
+ tokensGenerated: result.usage?.totalTokens || 0,
212
+ cost: actualCost,
213
+ success: true,
214
+ });
215
+ // Show what the system learned (updated to include cost)
216
+ const optimizedProvider = getPerformanceOptimizedProvider("speed");
217
+ logger.debug(`🚀 Performance recorded for ${this.providerName}:`, {
218
+ responseTime: `${responseTime}ms`,
219
+ tokens: result.usage?.totalTokens || 0,
220
+ estimatedCost: `$${actualCost.toFixed(6)}`,
221
+ recommendedSpeedProvider: optimizedProvider?.provider || "none",
222
+ });
223
+ }
224
+ catch (perfError) {
225
+ logger.warn("⚠️ Performance recording failed:", perfError);
226
+ }
184
227
  // Extract tool names from tool calls for tracking
185
228
  // AI SDK puts tool calls in steps array for multi-step generation
186
229
  const toolsUsed = [];
@@ -357,6 +400,83 @@ export class BaseProvider {
357
400
  evaluation: result.evaluation,
358
401
  };
359
402
  }
403
+ /**
404
+ * Get AI SDK model with middleware applied
405
+ * This method wraps the base model with any configured middleware
406
+ */
407
+ async getAISDKModelWithMiddleware(options = {}) {
408
+ // Get the base model
409
+ const baseModel = await this.getAISDKModel();
410
+ // Check if middleware should be applied
411
+ const middlewareOptions = this.extractMiddlewareOptions(options);
412
+ if (!middlewareOptions || this.shouldSkipMiddleware(options)) {
413
+ return baseModel;
414
+ }
415
+ try {
416
+ // Create middleware context
417
+ const context = MiddlewareFactory.createContext(this.providerName, this.modelName, options, {
418
+ sessionId: this.sessionId,
419
+ userId: this.userId,
420
+ });
421
+ // Apply middleware to the model
422
+ const wrappedModel = MiddlewareFactory.applyMiddleware(baseModel, context, middlewareOptions);
423
+ logger.debug(`Applied middleware to ${this.providerName} model`, {
424
+ provider: this.providerName,
425
+ model: this.modelName,
426
+ hasMiddleware: true,
427
+ });
428
+ return wrappedModel;
429
+ }
430
+ catch (error) {
431
+ logger.warn(`Failed to apply middleware to ${this.providerName}, using base model`, {
432
+ error: error instanceof Error ? error.message : String(error),
433
+ });
434
+ // Return base model on middleware failure to maintain functionality
435
+ return baseModel;
436
+ }
437
+ }
438
+ /**
439
+ * Extract middleware options from generation options
440
+ */
441
+ extractMiddlewareOptions(options) {
442
+ // Check for middleware configuration in options
443
+ const optionsRecord = options;
444
+ const middlewareConfig = optionsRecord.middlewareConfig;
445
+ const enabledMiddleware = optionsRecord.enabledMiddleware;
446
+ const disabledMiddleware = optionsRecord.disabledMiddleware;
447
+ const preset = optionsRecord.middlewarePreset;
448
+ // If no middleware configuration is present, return null
449
+ if (!middlewareConfig &&
450
+ !enabledMiddleware &&
451
+ !disabledMiddleware &&
452
+ !preset) {
453
+ return null;
454
+ }
455
+ return {
456
+ middlewareConfig,
457
+ enabledMiddleware,
458
+ disabledMiddleware,
459
+ preset,
460
+ global: {
461
+ collectStats: true,
462
+ continueOnError: true,
463
+ },
464
+ };
465
+ }
466
+ /**
467
+ * Determine if middleware should be skipped for this request
468
+ */
469
+ shouldSkipMiddleware(options) {
470
+ // Skip middleware if explicitly disabled
471
+ if (options.disableMiddleware === true) {
472
+ return true;
473
+ }
474
+ // Skip middleware for tool-disabled requests to avoid conflicts
475
+ if (options.disableTools === true) {
476
+ return true;
477
+ }
478
+ return false;
479
+ }
360
480
  // ===================
361
481
  // TOOL MANAGEMENT
362
482
  // ===================
@@ -428,7 +548,7 @@ export class BaseProvider {
428
548
  timestamp: new Date().toISOString(),
429
549
  params: params,
430
550
  // Keep it simple - just indicate an error occurred
431
- message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`
551
+ message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`,
432
552
  };
433
553
  }
434
554
  },
@@ -32,3 +32,29 @@ export declare function isProviderAvailable(providerName: string): boolean;
32
32
  * Get the best available provider based on preference
33
33
  */
34
34
  export declare function getBestAvailableProvider(preferCheap?: boolean): ProviderModelConfig | null;
35
+ /**
36
+ Record actual provider performance for optimization
37
+ */
38
+ export declare function recordProviderPerformanceFromMetrics(providerName: string, metrics: {
39
+ responseTime: number;
40
+ tokensGenerated: number;
41
+ cost: number;
42
+ success: boolean;
43
+ }): void;
44
+ /**
45
+ * Get performance-optimized provider based on real metrics
46
+ */
47
+ export declare function getPerformanceOptimizedProvider(priority?: "speed" | "cost" | "reliability"): ProviderModelConfig | null;
48
+ export declare function getProviderPerformanceAnalytics(): Record<string, {
49
+ avgResponseTime: number;
50
+ successRate: number;
51
+ tokenThroughput: number;
52
+ costEfficiency: number;
53
+ recommendation: string;
54
+ sampleCount: number;
55
+ }>;
56
+ /**
57
+ * Reset performance metrics for a provider or all providers.
58
+ * @param providerName - (Optional) The name of the provider to reset. If omitted, resets all providers.
59
+ */
60
+ export declare function resetProviderMetrics(providerName?: string): void;