@juspay/neurolink 7.24.0 → 7.25.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 (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/cli/index.js +8 -66
  3. package/dist/core/baseProvider.d.ts +13 -0
  4. package/dist/core/baseProvider.js +79 -1
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.js +2 -0
  7. package/dist/lib/core/baseProvider.d.ts +13 -0
  8. package/dist/lib/core/baseProvider.js +79 -1
  9. package/dist/lib/index.d.ts +3 -0
  10. package/dist/lib/index.js +2 -0
  11. package/dist/lib/middleware/builtin/analytics.d.ts +16 -0
  12. package/dist/lib/middleware/builtin/analytics.js +130 -0
  13. package/dist/lib/middleware/factory.d.ts +54 -0
  14. package/dist/lib/middleware/factory.js +289 -0
  15. package/dist/lib/middleware/index.d.ts +58 -0
  16. package/dist/lib/middleware/index.js +67 -0
  17. package/dist/lib/middleware/registry.d.ts +78 -0
  18. package/dist/lib/middleware/registry.js +283 -0
  19. package/dist/lib/middleware/types.d.ts +144 -0
  20. package/dist/lib/middleware/types.js +1 -0
  21. package/dist/lib/providers/anthropic.js +25 -33
  22. package/dist/lib/providers/googleAiStudio.js +10 -11
  23. package/dist/lib/providers/openAI.js +15 -6
  24. package/dist/lib/types/errors.d.ts +44 -0
  25. package/dist/lib/types/errors.js +60 -0
  26. package/dist/middleware/builtin/analytics.d.ts +16 -0
  27. package/dist/middleware/builtin/analytics.js +130 -0
  28. package/dist/middleware/factory.d.ts +54 -0
  29. package/dist/middleware/factory.js +289 -0
  30. package/dist/middleware/index.d.ts +58 -0
  31. package/dist/middleware/index.js +67 -0
  32. package/dist/middleware/registry.d.ts +78 -0
  33. package/dist/middleware/registry.js +283 -0
  34. package/dist/middleware/types.d.ts +144 -0
  35. package/dist/middleware/types.js +1 -0
  36. package/dist/providers/anthropic.js +25 -33
  37. package/dist/providers/googleAiStudio.js +10 -11
  38. package/dist/providers/openAI.js +15 -6
  39. package/dist/types/errors.d.ts +44 -0
  40. package/dist/types/errors.js +60 -0
  41. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [7.25.0](https://github.com/juspay/neurolink/compare/v7.24.1...v7.25.0) (2025-08-21)
2
+
3
+ ### Features
4
+
5
+ - **(middleware):** add custom middleware development guide ([ffd0343](https://github.com/juspay/neurolink/commit/ffd0343a589b267a5b8349a06cdfe2664a942e4c))
6
+
7
+ ## [7.24.1](https://github.com/juspay/neurolink/compare/v7.24.0...v7.24.1) (2025-08-21)
8
+
1
9
  ## [7.24.0](https://github.com/juspay/neurolink/compare/v7.23.0...v7.24.0) (2025-08-20)
2
10
 
3
11
  ### Features
package/dist/cli/index.js CHANGED
@@ -14,6 +14,7 @@ import { fileURLToPath } from "url";
14
14
  import { addOllamaCommands } from "./commands/ollama.js";
15
15
  import { addSageMakerCommands } from "./commands/sagemaker.js";
16
16
  import { CLICommandFactory } from "./factories/commandFactory.js";
17
+ import { AuthenticationError, AuthorizationError, NetworkError, RateLimitError, } from "../lib/types/errors.js";
17
18
  import { logger } from "../lib/utils/logger.js";
18
19
  // Get version from package.json
19
20
  const __filename = fileURLToPath(import.meta.url);
@@ -32,69 +33,8 @@ catch (error) {
32
33
  }
33
34
  // Utility Functions (Simple, Zero Maintenance)
34
35
  function handleError(error, context) {
35
- const specificErrorMessage = error.message;
36
- const originalErrorMessageLowerCase = error.message
37
- ? error.message.toLowerCase()
38
- : "";
39
- const errorStringLowerCase = String(error).toLowerCase();
40
- let isAuthError = false;
41
- let genericMessage = specificErrorMessage; // Initialize genericMessage with the specific one
42
- if (originalErrorMessageLowerCase.includes("api_key") ||
43
- originalErrorMessageLowerCase.includes("google_ai_api_key") ||
44
- originalErrorMessageLowerCase.includes("aws_access_key_id") ||
45
- originalErrorMessageLowerCase.includes("aws_secret_access_key") ||
46
- originalErrorMessageLowerCase.includes("aws_session_token") ||
47
- originalErrorMessageLowerCase.includes("google_application_credentials") ||
48
- originalErrorMessageLowerCase.includes("google_service_account_key") ||
49
- originalErrorMessageLowerCase.includes("google_auth_client_email") ||
50
- originalErrorMessageLowerCase.includes("anthropic_api_key") ||
51
- originalErrorMessageLowerCase.includes("azure_openai_api_key")) {
52
- isAuthError = true;
53
- }
54
- else if (
55
- // Fallback to checking the full stringified error if direct message didn't match
56
- errorStringLowerCase.includes("api_key") ||
57
- errorStringLowerCase.includes("google_ai_api_key") ||
58
- errorStringLowerCase.includes("aws_access_key_id") ||
59
- errorStringLowerCase.includes("aws_secret_access_key") ||
60
- errorStringLowerCase.includes("aws_session_token") ||
61
- errorStringLowerCase.includes("google_application_credentials") ||
62
- errorStringLowerCase.includes("google_service_account_key") ||
63
- errorStringLowerCase.includes("google_auth_client_email") ||
64
- errorStringLowerCase.includes("anthropic_api_key") ||
65
- errorStringLowerCase.includes("azure_openai_api_key")) {
66
- isAuthError = true;
67
- }
68
- if (isAuthError) {
69
- genericMessage =
70
- "Authentication error: Missing or invalid API key/credentials for the selected provider.";
71
- }
72
- else if (originalErrorMessageLowerCase.includes("enotfound") || // Prefer direct message checks
73
- originalErrorMessageLowerCase.includes("econnrefused") ||
74
- originalErrorMessageLowerCase.includes("invalid-endpoint") ||
75
- originalErrorMessageLowerCase.includes("network error") ||
76
- originalErrorMessageLowerCase.includes("could not connect") ||
77
- originalErrorMessageLowerCase.includes("timeout") ||
78
- errorStringLowerCase.includes("enotfound") || // Fallback to full string
79
- errorStringLowerCase.includes("econnrefused") ||
80
- errorStringLowerCase.includes("invalid-endpoint") ||
81
- errorStringLowerCase.includes("network error") ||
82
- errorStringLowerCase.includes("could not connect") ||
83
- errorStringLowerCase.includes("timeout") // General timeout
84
- ) {
85
- genericMessage =
86
- "Network error: Could not connect to the API endpoint or the request timed out.";
87
- }
88
- else if (errorStringLowerCase.includes("not authorized") ||
89
- errorStringLowerCase.includes("permission denied")) {
90
- genericMessage =
91
- "Authorization error: You are not authorized to perform this action or access this resource.";
92
- }
93
- // If no specific condition matched, genericMessage remains error.message
94
- logger.error(chalk.red(`❌ ${context} failed: ${genericMessage}`));
95
- // Smart hints for common errors (just string matching!)
96
- if (genericMessage.toLowerCase().includes("api key") ||
97
- genericMessage.toLowerCase().includes("credential")) {
36
+ logger.error(chalk.red(`❌ ${context} failed: ${error.message}`));
37
+ if (error instanceof AuthenticationError) {
98
38
  logger.error(chalk.yellow("💡 Set Google AI Studio API key (RECOMMENDED): export GOOGLE_AI_API_KEY=AIza-..."));
99
39
  logger.error(chalk.yellow("💡 Or set OpenAI API key: export OPENAI_API_KEY=sk-..."));
100
40
  logger.error(chalk.yellow("💡 Or set AWS Bedrock credentials: export AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_REGION=us-east-1"));
@@ -102,14 +42,16 @@ function handleError(error, context) {
102
42
  logger.error(chalk.yellow("💡 Or set Anthropic API key: export ANTHROPIC_API_KEY=sk-ant-..."));
103
43
  logger.error(chalk.yellow("💡 Or set Azure OpenAI credentials: export AZURE_OPENAI_API_KEY=... AZURE_OPENAI_ENDPOINT=..."));
104
44
  }
105
- if (error.message.toLowerCase().includes("rate limit")) {
45
+ else if (error instanceof RateLimitError) {
106
46
  logger.error(chalk.yellow("💡 Try again in a few moments or use --provider vertex"));
107
47
  }
108
- if (error.message.toLowerCase().includes("not authorized") ||
109
- error.message.toLowerCase().includes("permission denied")) {
48
+ else if (error instanceof AuthorizationError) {
110
49
  logger.error(chalk.yellow("💡 Check your account permissions for the selected model/service."));
111
50
  logger.error(chalk.yellow("💡 For AWS Bedrock, ensure you have permissions for the specific model and consider using inference profile ARNs."));
112
51
  }
52
+ else if (error instanceof NetworkError) {
53
+ logger.error(chalk.yellow("💡 Check your internet connection and the provider's status page."));
54
+ }
113
55
  process.exit(1);
114
56
  }
115
57
  // Manual pre-validation for unknown flags
@@ -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";
@@ -357,6 +358,83 @@ export class BaseProvider {
357
358
  evaluation: result.evaluation,
358
359
  };
359
360
  }
361
+ /**
362
+ * Get AI SDK model with middleware applied
363
+ * This method wraps the base model with any configured middleware
364
+ */
365
+ async getAISDKModelWithMiddleware(options = {}) {
366
+ // Get the base model
367
+ const baseModel = await this.getAISDKModel();
368
+ // Check if middleware should be applied
369
+ const middlewareOptions = this.extractMiddlewareOptions(options);
370
+ if (!middlewareOptions || this.shouldSkipMiddleware(options)) {
371
+ return baseModel;
372
+ }
373
+ try {
374
+ // Create middleware context
375
+ const context = MiddlewareFactory.createContext(this.providerName, this.modelName, options, {
376
+ sessionId: this.sessionId,
377
+ userId: this.userId,
378
+ });
379
+ // Apply middleware to the model
380
+ const wrappedModel = MiddlewareFactory.applyMiddleware(baseModel, context, middlewareOptions);
381
+ logger.debug(`Applied middleware to ${this.providerName} model`, {
382
+ provider: this.providerName,
383
+ model: this.modelName,
384
+ hasMiddleware: true,
385
+ });
386
+ return wrappedModel;
387
+ }
388
+ catch (error) {
389
+ logger.warn(`Failed to apply middleware to ${this.providerName}, using base model`, {
390
+ error: error instanceof Error ? error.message : String(error),
391
+ });
392
+ // Return base model on middleware failure to maintain functionality
393
+ return baseModel;
394
+ }
395
+ }
396
+ /**
397
+ * Extract middleware options from generation options
398
+ */
399
+ extractMiddlewareOptions(options) {
400
+ // Check for middleware configuration in options
401
+ const optionsRecord = options;
402
+ const middlewareConfig = optionsRecord.middlewareConfig;
403
+ const enabledMiddleware = optionsRecord.enabledMiddleware;
404
+ const disabledMiddleware = optionsRecord.disabledMiddleware;
405
+ const preset = optionsRecord.middlewarePreset;
406
+ // If no middleware configuration is present, return null
407
+ if (!middlewareConfig &&
408
+ !enabledMiddleware &&
409
+ !disabledMiddleware &&
410
+ !preset) {
411
+ return null;
412
+ }
413
+ return {
414
+ middlewareConfig,
415
+ enabledMiddleware,
416
+ disabledMiddleware,
417
+ preset,
418
+ global: {
419
+ collectStats: true,
420
+ continueOnError: true,
421
+ },
422
+ };
423
+ }
424
+ /**
425
+ * Determine if middleware should be skipped for this request
426
+ */
427
+ shouldSkipMiddleware(options) {
428
+ // Skip middleware if explicitly disabled
429
+ if (options.disableMiddleware === true) {
430
+ return true;
431
+ }
432
+ // Skip middleware for tool-disabled requests to avoid conflicts
433
+ if (options.disableTools === true) {
434
+ return true;
435
+ }
436
+ return false;
437
+ }
360
438
  // ===================
361
439
  // TOOL MANAGEMENT
362
440
  // ===================
@@ -428,7 +506,7 @@ export class BaseProvider {
428
506
  timestamp: new Date().toISOString(),
429
507
  params: params,
430
508
  // Keep it simple - just indicate an error occurred
431
- message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`
509
+ message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`,
432
510
  };
433
511
  }
434
512
  },
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";
@@ -357,6 +358,83 @@ export class BaseProvider {
357
358
  evaluation: result.evaluation,
358
359
  };
359
360
  }
361
+ /**
362
+ * Get AI SDK model with middleware applied
363
+ * This method wraps the base model with any configured middleware
364
+ */
365
+ async getAISDKModelWithMiddleware(options = {}) {
366
+ // Get the base model
367
+ const baseModel = await this.getAISDKModel();
368
+ // Check if middleware should be applied
369
+ const middlewareOptions = this.extractMiddlewareOptions(options);
370
+ if (!middlewareOptions || this.shouldSkipMiddleware(options)) {
371
+ return baseModel;
372
+ }
373
+ try {
374
+ // Create middleware context
375
+ const context = MiddlewareFactory.createContext(this.providerName, this.modelName, options, {
376
+ sessionId: this.sessionId,
377
+ userId: this.userId,
378
+ });
379
+ // Apply middleware to the model
380
+ const wrappedModel = MiddlewareFactory.applyMiddleware(baseModel, context, middlewareOptions);
381
+ logger.debug(`Applied middleware to ${this.providerName} model`, {
382
+ provider: this.providerName,
383
+ model: this.modelName,
384
+ hasMiddleware: true,
385
+ });
386
+ return wrappedModel;
387
+ }
388
+ catch (error) {
389
+ logger.warn(`Failed to apply middleware to ${this.providerName}, using base model`, {
390
+ error: error instanceof Error ? error.message : String(error),
391
+ });
392
+ // Return base model on middleware failure to maintain functionality
393
+ return baseModel;
394
+ }
395
+ }
396
+ /**
397
+ * Extract middleware options from generation options
398
+ */
399
+ extractMiddlewareOptions(options) {
400
+ // Check for middleware configuration in options
401
+ const optionsRecord = options;
402
+ const middlewareConfig = optionsRecord.middlewareConfig;
403
+ const enabledMiddleware = optionsRecord.enabledMiddleware;
404
+ const disabledMiddleware = optionsRecord.disabledMiddleware;
405
+ const preset = optionsRecord.middlewarePreset;
406
+ // If no middleware configuration is present, return null
407
+ if (!middlewareConfig &&
408
+ !enabledMiddleware &&
409
+ !disabledMiddleware &&
410
+ !preset) {
411
+ return null;
412
+ }
413
+ return {
414
+ middlewareConfig,
415
+ enabledMiddleware,
416
+ disabledMiddleware,
417
+ preset,
418
+ global: {
419
+ collectStats: true,
420
+ continueOnError: true,
421
+ },
422
+ };
423
+ }
424
+ /**
425
+ * Determine if middleware should be skipped for this request
426
+ */
427
+ shouldSkipMiddleware(options) {
428
+ // Skip middleware if explicitly disabled
429
+ if (options.disableMiddleware === true) {
430
+ return true;
431
+ }
432
+ // Skip middleware for tool-disabled requests to avoid conflicts
433
+ if (options.disableTools === true) {
434
+ return true;
435
+ }
436
+ return false;
437
+ }
360
438
  // ===================
361
439
  // TOOL MANAGEMENT
362
440
  // ===================
@@ -428,7 +506,7 @@ export class BaseProvider {
428
506
  timestamp: new Date().toISOString(),
429
507
  params: params,
430
508
  // Keep it simple - just indicate an error occurred
431
- message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`
509
+ message: `Error calling ${toolName}: ${error instanceof Error ? error.message : String(error)}`,
432
510
  };
433
511
  }
434
512
  },
@@ -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/lib/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
  /**
@@ -0,0 +1,16 @@
1
+ import type { NeuroLinkMiddleware } from "../types.js";
2
+ /**
3
+ * Create analytics middleware for tracking AI model usage
4
+ * Collects metrics on token usage, response times, and model performance
5
+ */
6
+ export declare function createAnalyticsMiddleware(): NeuroLinkMiddleware;
7
+ /**
8
+ * Get collected metrics from analytics middleware
9
+ * Note: This is a utility function for accessing metrics
10
+ */
11
+ export declare function getAnalyticsMetrics(): Map<string, Record<string, unknown>>;
12
+ /**
13
+ * Clear collected metrics from analytics middleware
14
+ * Note: This is a utility function for clearing metrics
15
+ */
16
+ export declare function clearAnalyticsMetrics(): void;
@@ -0,0 +1,130 @@
1
+ import { logger } from "../../utils/logger.js";
2
+ /**
3
+ * Create analytics middleware for tracking AI model usage
4
+ * Collects metrics on token usage, response times, and model performance
5
+ */
6
+ export function createAnalyticsMiddleware() {
7
+ const requestMetrics = new Map();
8
+ const metadata = {
9
+ id: "analytics",
10
+ name: "Analytics Tracking",
11
+ description: "Tracks token usage, response times, and model performance metrics",
12
+ priority: 100, // High priority to ensure analytics are captured
13
+ defaultEnabled: true,
14
+ };
15
+ const middleware = {
16
+ wrapGenerate: async ({ doGenerate, params }) => {
17
+ const requestId = `analytics-${Date.now()}`;
18
+ const startTime = Date.now();
19
+ logger.debug(`[AnalyticsMiddleware] Starting request tracking`, {
20
+ requestId,
21
+ hasPrompt: !!params.prompt,
22
+ });
23
+ try {
24
+ // Execute the generation
25
+ const result = await doGenerate();
26
+ // Calculate metrics
27
+ const responseTime = Date.now() - startTime;
28
+ const analytics = {
29
+ requestId,
30
+ responseTime,
31
+ timestamp: new Date().toISOString(),
32
+ usage: {
33
+ inputTokens: result.usage?.promptTokens || 0,
34
+ outputTokens: result.usage?.completionTokens || 0,
35
+ totalTokens: (result.usage?.promptTokens || 0) +
36
+ (result.usage?.completionTokens || 0),
37
+ },
38
+ };
39
+ // Store metrics for potential retrieval
40
+ requestMetrics.set(requestId, analytics);
41
+ logger.debug(`[AnalyticsMiddleware] Request completed`, analytics);
42
+ // Add analytics to the result
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ const updatedResult = { ...result };
45
+ if (!updatedResult.experimental_providerMetadata) {
46
+ updatedResult.experimental_providerMetadata = {};
47
+ }
48
+ if (!updatedResult.experimental_providerMetadata.neurolink) {
49
+ updatedResult.experimental_providerMetadata.neurolink = {};
50
+ }
51
+ updatedResult.experimental_providerMetadata.neurolink.analytics =
52
+ analytics;
53
+ return updatedResult;
54
+ }
55
+ catch (error) {
56
+ const responseTime = Date.now() - startTime;
57
+ logger.error(`[AnalyticsMiddleware] Request failed`, {
58
+ requestId,
59
+ responseTime,
60
+ error: error instanceof Error ? error.message : String(error),
61
+ });
62
+ throw error;
63
+ }
64
+ },
65
+ wrapStream: async ({ doStream, params }) => {
66
+ const requestId = `analytics-stream-${Date.now()}`;
67
+ const startTime = Date.now();
68
+ logger.debug(`[AnalyticsMiddleware] Starting stream tracking`, {
69
+ requestId,
70
+ hasPrompt: !!params.prompt,
71
+ });
72
+ try {
73
+ const result = await doStream();
74
+ const streamAnalytics = {
75
+ requestId,
76
+ startTime,
77
+ timestamp: new Date().toISOString(),
78
+ streamingMode: true,
79
+ };
80
+ requestMetrics.set(requestId, streamAnalytics);
81
+ // The 'result' is a stream, so we can't directly modify it.
82
+ // Analytics for streams are typically handled after the stream is consumed.
83
+ // For this middleware, we'll log the start and rely on other mechanisms
84
+ // to capture the end-to-end stream metrics if needed.
85
+ // We will pass a new property in the `rawResponse`
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ const updatedResult = { ...result };
88
+ if (!updatedResult.rawResponse) {
89
+ updatedResult.rawResponse = {};
90
+ }
91
+ if (!updatedResult.rawResponse.neurolink) {
92
+ updatedResult.rawResponse.neurolink = {};
93
+ }
94
+ updatedResult.rawResponse.neurolink.analytics = streamAnalytics;
95
+ return updatedResult;
96
+ }
97
+ catch (error) {
98
+ const responseTime = Date.now() - startTime;
99
+ logger.error(`[AnalyticsMiddleware] Stream failed`, {
100
+ requestId,
101
+ responseTime,
102
+ error: error instanceof Error ? error.message : String(error),
103
+ });
104
+ throw error;
105
+ }
106
+ },
107
+ };
108
+ // Return the NeuroLinkMiddleware with metadata
109
+ return {
110
+ ...middleware,
111
+ metadata,
112
+ };
113
+ }
114
+ /**
115
+ * Get collected metrics from analytics middleware
116
+ * Note: This is a utility function for accessing metrics
117
+ */
118
+ export function getAnalyticsMetrics() {
119
+ // This would need to be implemented with a global registry
120
+ // For now, return empty map
121
+ return new Map();
122
+ }
123
+ /**
124
+ * Clear collected metrics from analytics middleware
125
+ * Note: This is a utility function for clearing metrics
126
+ */
127
+ export function clearAnalyticsMetrics() {
128
+ // This would need to be implemented with a global registry
129
+ // For now, do nothing
130
+ }
@@ -0,0 +1,54 @@
1
+ import type { LanguageModelV1 } from "ai";
2
+ import type { MiddlewareContext, MiddlewareConfig, MiddlewareFactoryOptions, MiddlewareChainStats } from "./types.js";
3
+ /**
4
+ * Middleware factory for creating and applying middleware chains
5
+ */
6
+ export declare class MiddlewareFactory {
7
+ /**
8
+ * Apply middleware to a language model
9
+ */
10
+ static applyMiddleware(model: LanguageModelV1, context: MiddlewareContext, options?: MiddlewareFactoryOptions): LanguageModelV1;
11
+ /**
12
+ * Build middleware configuration from factory options
13
+ */
14
+ private static buildMiddlewareConfig;
15
+ /**
16
+ * Get preset configuration
17
+ */
18
+ private static getPresetConfig;
19
+ /**
20
+ * Get built-in preset configurations
21
+ */
22
+ private static getBuiltInPresets;
23
+ /**
24
+ * Create middleware context from provider and options
25
+ */
26
+ static createContext(provider: string, model: string, options?: Record<string, unknown>, session?: {
27
+ sessionId?: string;
28
+ userId?: string;
29
+ }): MiddlewareContext;
30
+ /**
31
+ * Validate middleware configuration
32
+ */
33
+ static validateConfig(config: Record<string, MiddlewareConfig>): {
34
+ isValid: boolean;
35
+ errors: string[];
36
+ warnings: string[];
37
+ };
38
+ /**
39
+ * Get available presets
40
+ */
41
+ static getAvailablePresets(): Array<{
42
+ name: string;
43
+ description: string;
44
+ middleware: string[];
45
+ }>;
46
+ /**
47
+ * Get middleware chain statistics
48
+ */
49
+ static getChainStats(context: MiddlewareContext, config: Record<string, MiddlewareConfig>): MiddlewareChainStats;
50
+ /**
51
+ * Create a middleware-enabled model factory function
52
+ */
53
+ static createModelFactory(baseModelFactory: () => Promise<LanguageModelV1>, defaultOptions?: MiddlewareFactoryOptions): (context: MiddlewareContext, options?: MiddlewareFactoryOptions) => Promise<LanguageModelV1>;
54
+ }