@juspay/neurolink 9.41.0 → 9.42.1

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 (212) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +7 -1
  3. package/dist/auth/anthropicOAuth.d.ts +18 -3
  4. package/dist/auth/anthropicOAuth.js +149 -4
  5. package/dist/auth/providers/firebase.js +5 -1
  6. package/dist/auth/providers/jwt.js +5 -1
  7. package/dist/auth/providers/workos.js +5 -1
  8. package/dist/auth/sessionManager.d.ts +1 -1
  9. package/dist/auth/sessionManager.js +58 -27
  10. package/dist/browser/neurolink.min.js +354 -334
  11. package/dist/cli/commands/mcp.d.ts +6 -0
  12. package/dist/cli/commands/mcp.js +188 -181
  13. package/dist/cli/commands/proxy.d.ts +2 -1
  14. package/dist/cli/commands/proxy.js +713 -431
  15. package/dist/cli/commands/task.js +3 -0
  16. package/dist/cli/factories/commandFactory.d.ts +2 -0
  17. package/dist/cli/factories/commandFactory.js +38 -0
  18. package/dist/cli/parser.js +4 -3
  19. package/dist/client/aiSdkAdapter.js +3 -0
  20. package/dist/client/streamingClient.js +30 -10
  21. package/dist/core/baseProvider.d.ts +6 -1
  22. package/dist/core/baseProvider.js +208 -230
  23. package/dist/core/factory.d.ts +3 -0
  24. package/dist/core/factory.js +138 -188
  25. package/dist/core/modules/GenerationHandler.js +3 -2
  26. package/dist/core/redisConversationMemoryManager.js +7 -3
  27. package/dist/evaluation/BatchEvaluator.js +4 -1
  28. package/dist/evaluation/hooks/observabilityHooks.js +5 -3
  29. package/dist/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  30. package/dist/evaluation/pipeline/evaluationPipeline.js +24 -9
  31. package/dist/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  32. package/dist/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  33. package/dist/evaluation/scorers/scorerRegistry.d.ts +3 -0
  34. package/dist/evaluation/scorers/scorerRegistry.js +353 -282
  35. package/dist/lib/auth/anthropicOAuth.d.ts +18 -3
  36. package/dist/lib/auth/anthropicOAuth.js +149 -4
  37. package/dist/lib/auth/providers/firebase.js +5 -1
  38. package/dist/lib/auth/providers/jwt.js +5 -1
  39. package/dist/lib/auth/providers/workos.js +5 -1
  40. package/dist/lib/auth/sessionManager.d.ts +1 -1
  41. package/dist/lib/auth/sessionManager.js +58 -27
  42. package/dist/lib/client/aiSdkAdapter.js +3 -0
  43. package/dist/lib/client/streamingClient.js +30 -10
  44. package/dist/lib/core/baseProvider.d.ts +6 -1
  45. package/dist/lib/core/baseProvider.js +208 -230
  46. package/dist/lib/core/factory.d.ts +3 -0
  47. package/dist/lib/core/factory.js +138 -188
  48. package/dist/lib/core/modules/GenerationHandler.js +3 -2
  49. package/dist/lib/core/redisConversationMemoryManager.js +7 -3
  50. package/dist/lib/evaluation/BatchEvaluator.js +4 -1
  51. package/dist/lib/evaluation/hooks/observabilityHooks.js +5 -3
  52. package/dist/lib/evaluation/pipeline/evaluationPipeline.d.ts +3 -2
  53. package/dist/lib/evaluation/pipeline/evaluationPipeline.js +24 -9
  54. package/dist/lib/evaluation/pipeline/strategies/batchStrategy.js +6 -3
  55. package/dist/lib/evaluation/pipeline/strategies/samplingStrategy.js +18 -10
  56. package/dist/lib/evaluation/scorers/scorerRegistry.d.ts +3 -0
  57. package/dist/lib/evaluation/scorers/scorerRegistry.js +353 -282
  58. package/dist/lib/mcp/toolRegistry.d.ts +2 -0
  59. package/dist/lib/mcp/toolRegistry.js +32 -31
  60. package/dist/lib/neurolink.d.ts +41 -2
  61. package/dist/lib/neurolink.js +1616 -1681
  62. package/dist/lib/observability/otelBridge.d.ts +2 -2
  63. package/dist/lib/observability/otelBridge.js +12 -3
  64. package/dist/lib/providers/amazonBedrock.js +2 -4
  65. package/dist/lib/providers/anthropic.d.ts +9 -5
  66. package/dist/lib/providers/anthropic.js +19 -14
  67. package/dist/lib/providers/anthropicBaseProvider.d.ts +3 -3
  68. package/dist/lib/providers/anthropicBaseProvider.js +5 -4
  69. package/dist/lib/providers/azureOpenai.d.ts +1 -1
  70. package/dist/lib/providers/azureOpenai.js +5 -4
  71. package/dist/lib/providers/googleAiStudio.js +30 -6
  72. package/dist/lib/providers/googleVertex.d.ts +10 -0
  73. package/dist/lib/providers/googleVertex.js +437 -423
  74. package/dist/lib/providers/huggingFace.d.ts +3 -3
  75. package/dist/lib/providers/huggingFace.js +6 -8
  76. package/dist/lib/providers/litellm.d.ts +1 -0
  77. package/dist/lib/providers/litellm.js +76 -55
  78. package/dist/lib/providers/mistral.js +2 -1
  79. package/dist/lib/providers/ollama.js +93 -23
  80. package/dist/lib/providers/openAI.d.ts +2 -0
  81. package/dist/lib/providers/openAI.js +141 -141
  82. package/dist/lib/providers/openRouter.js +2 -1
  83. package/dist/lib/providers/openaiCompatible.d.ts +4 -4
  84. package/dist/lib/providers/openaiCompatible.js +4 -4
  85. package/dist/lib/proxy/claudeFormat.d.ts +3 -2
  86. package/dist/lib/proxy/claudeFormat.js +27 -14
  87. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  88. package/dist/lib/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  89. package/dist/lib/proxy/modelRouter.js +3 -0
  90. package/dist/lib/proxy/oauthFetch.d.ts +1 -1
  91. package/dist/lib/proxy/oauthFetch.js +289 -316
  92. package/dist/lib/proxy/proxyConfig.js +46 -24
  93. package/dist/lib/proxy/proxyEnv.d.ts +19 -0
  94. package/dist/lib/proxy/proxyEnv.js +73 -0
  95. package/dist/lib/proxy/proxyFetch.js +291 -217
  96. package/dist/lib/proxy/proxyTracer.d.ts +133 -0
  97. package/dist/lib/proxy/proxyTracer.js +645 -0
  98. package/dist/lib/proxy/rawStreamCapture.d.ts +10 -0
  99. package/dist/lib/proxy/rawStreamCapture.js +83 -0
  100. package/dist/lib/proxy/requestLogger.d.ts +32 -5
  101. package/dist/lib/proxy/requestLogger.js +503 -47
  102. package/dist/lib/proxy/sseInterceptor.d.ts +97 -0
  103. package/dist/lib/proxy/sseInterceptor.js +427 -0
  104. package/dist/lib/proxy/usageStats.d.ts +4 -3
  105. package/dist/lib/proxy/usageStats.js +25 -12
  106. package/dist/lib/rag/chunkers/MarkdownChunker.js +13 -5
  107. package/dist/lib/rag/chunking/markdownChunker.js +15 -6
  108. package/dist/lib/server/routes/claudeProxyRoutes.d.ts +17 -3
  109. package/dist/lib/server/routes/claudeProxyRoutes.js +3032 -1349
  110. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +7 -1
  111. package/dist/lib/services/server/ai/observability/instrumentation.js +337 -161
  112. package/dist/lib/tasks/backends/bullmqBackend.d.ts +1 -0
  113. package/dist/lib/tasks/backends/bullmqBackend.js +35 -22
  114. package/dist/lib/tasks/store/redisTaskStore.d.ts +1 -0
  115. package/dist/lib/tasks/store/redisTaskStore.js +54 -39
  116. package/dist/lib/tasks/taskManager.d.ts +5 -0
  117. package/dist/lib/tasks/taskManager.js +158 -30
  118. package/dist/lib/telemetry/index.d.ts +2 -1
  119. package/dist/lib/telemetry/index.js +2 -1
  120. package/dist/lib/telemetry/telemetryService.d.ts +3 -0
  121. package/dist/lib/telemetry/telemetryService.js +69 -5
  122. package/dist/lib/types/cli.d.ts +10 -0
  123. package/dist/lib/types/proxyTypes.d.ts +160 -5
  124. package/dist/lib/types/streamTypes.d.ts +25 -3
  125. package/dist/lib/utils/messageBuilder.js +3 -2
  126. package/dist/lib/utils/providerHealth.d.ts +19 -0
  127. package/dist/lib/utils/providerHealth.js +279 -33
  128. package/dist/lib/utils/providerUtils.js +17 -22
  129. package/dist/lib/utils/toolChoice.d.ts +4 -0
  130. package/dist/lib/utils/toolChoice.js +7 -0
  131. package/dist/mcp/toolRegistry.d.ts +2 -0
  132. package/dist/mcp/toolRegistry.js +32 -31
  133. package/dist/neurolink.d.ts +41 -2
  134. package/dist/neurolink.js +1616 -1681
  135. package/dist/observability/otelBridge.d.ts +2 -2
  136. package/dist/observability/otelBridge.js +12 -3
  137. package/dist/providers/amazonBedrock.js +2 -4
  138. package/dist/providers/anthropic.d.ts +9 -5
  139. package/dist/providers/anthropic.js +19 -14
  140. package/dist/providers/anthropicBaseProvider.d.ts +3 -3
  141. package/dist/providers/anthropicBaseProvider.js +5 -4
  142. package/dist/providers/azureOpenai.d.ts +1 -1
  143. package/dist/providers/azureOpenai.js +5 -4
  144. package/dist/providers/googleAiStudio.js +30 -6
  145. package/dist/providers/googleVertex.d.ts +10 -0
  146. package/dist/providers/googleVertex.js +437 -423
  147. package/dist/providers/huggingFace.d.ts +3 -3
  148. package/dist/providers/huggingFace.js +6 -7
  149. package/dist/providers/litellm.d.ts +1 -0
  150. package/dist/providers/litellm.js +76 -55
  151. package/dist/providers/mistral.js +2 -1
  152. package/dist/providers/ollama.js +93 -23
  153. package/dist/providers/openAI.d.ts +2 -0
  154. package/dist/providers/openAI.js +141 -141
  155. package/dist/providers/openRouter.js +2 -1
  156. package/dist/providers/openaiCompatible.d.ts +4 -4
  157. package/dist/providers/openaiCompatible.js +4 -3
  158. package/dist/proxy/claudeFormat.d.ts +3 -2
  159. package/dist/proxy/claudeFormat.js +27 -14
  160. package/dist/proxy/cloaking/plugins/sessionIdentity.d.ts +2 -6
  161. package/dist/proxy/cloaking/plugins/sessionIdentity.js +9 -33
  162. package/dist/proxy/modelRouter.js +3 -0
  163. package/dist/proxy/oauthFetch.d.ts +1 -1
  164. package/dist/proxy/oauthFetch.js +289 -316
  165. package/dist/proxy/proxyConfig.js +46 -24
  166. package/dist/proxy/proxyEnv.d.ts +19 -0
  167. package/dist/proxy/proxyEnv.js +72 -0
  168. package/dist/proxy/proxyFetch.js +291 -217
  169. package/dist/proxy/proxyTracer.d.ts +133 -0
  170. package/dist/proxy/proxyTracer.js +644 -0
  171. package/dist/proxy/rawStreamCapture.d.ts +10 -0
  172. package/dist/proxy/rawStreamCapture.js +82 -0
  173. package/dist/proxy/requestLogger.d.ts +32 -5
  174. package/dist/proxy/requestLogger.js +503 -47
  175. package/dist/proxy/sseInterceptor.d.ts +97 -0
  176. package/dist/proxy/sseInterceptor.js +426 -0
  177. package/dist/proxy/usageStats.d.ts +4 -3
  178. package/dist/proxy/usageStats.js +25 -12
  179. package/dist/rag/chunkers/MarkdownChunker.js +13 -5
  180. package/dist/rag/chunking/markdownChunker.js +15 -6
  181. package/dist/server/routes/claudeProxyRoutes.d.ts +17 -3
  182. package/dist/server/routes/claudeProxyRoutes.js +3032 -1349
  183. package/dist/services/server/ai/observability/instrumentation.d.ts +7 -1
  184. package/dist/services/server/ai/observability/instrumentation.js +337 -161
  185. package/dist/tasks/backends/bullmqBackend.d.ts +1 -0
  186. package/dist/tasks/backends/bullmqBackend.js +35 -22
  187. package/dist/tasks/store/redisTaskStore.d.ts +1 -0
  188. package/dist/tasks/store/redisTaskStore.js +54 -39
  189. package/dist/tasks/taskManager.d.ts +5 -0
  190. package/dist/tasks/taskManager.js +158 -30
  191. package/dist/telemetry/index.d.ts +2 -1
  192. package/dist/telemetry/index.js +2 -1
  193. package/dist/telemetry/telemetryService.d.ts +3 -0
  194. package/dist/telemetry/telemetryService.js +69 -5
  195. package/dist/types/cli.d.ts +10 -0
  196. package/dist/types/proxyTypes.d.ts +160 -5
  197. package/dist/types/streamTypes.d.ts +25 -3
  198. package/dist/utils/messageBuilder.js +3 -2
  199. package/dist/utils/providerHealth.d.ts +19 -0
  200. package/dist/utils/providerHealth.js +279 -33
  201. package/dist/utils/providerUtils.js +18 -22
  202. package/dist/utils/toolChoice.d.ts +4 -0
  203. package/dist/utils/toolChoice.js +6 -0
  204. package/docs/assets/dashboards/neurolink-proxy-observability-dashboard.json +6609 -0
  205. package/docs/changelog.md +252 -0
  206. package/package.json +19 -2
  207. package/scripts/observability/check-proxy-telemetry.mjs +235 -0
  208. package/scripts/observability/docker-compose.proxy-observability.yaml +55 -0
  209. package/scripts/observability/import-openobserve-dashboard.mjs +240 -0
  210. package/scripts/observability/manage-local-openobserve.sh +215 -0
  211. package/scripts/observability/otel-collector.proxy-observability.yaml +78 -0
  212. package/scripts/observability/proxy-observability.env.example +23 -0
@@ -47,6 +47,141 @@ export class AIProviderFactory {
47
47
  // This ensures the factory continues to work even if dynamic models fail
48
48
  }
49
49
  }
50
+ static resolveModelFromEnvironment(providerName, modelName, functionTag) {
51
+ if (modelName && modelName !== "default") {
52
+ logger.debug(`[${functionTag}] Skipping environment variable check - explicit model provided: ${modelName}`);
53
+ return modelName;
54
+ }
55
+ logger.debug(`[${functionTag}] Checking environment variables for provider: ${providerName}`);
56
+ const normalizedProvider = providerName.toLowerCase();
57
+ const envConfigs = [
58
+ {
59
+ match: ["bedrock"],
60
+ label: "Bedrock",
61
+ vars: ["BEDROCK_MODEL", "BEDROCK_MODEL_ID"],
62
+ },
63
+ {
64
+ match: ["vertex"],
65
+ label: "Vertex",
66
+ vars: ["VERTEX_MODEL"],
67
+ },
68
+ {
69
+ match: ["azure"],
70
+ label: "Azure",
71
+ vars: [
72
+ "AZURE_OPENAI_MODEL",
73
+ "AZURE_OPENAI_DEPLOYMENT",
74
+ "AZURE_OPENAI_DEPLOYMENT_ID",
75
+ ],
76
+ },
77
+ {
78
+ match: ["openai", "gpt", "chatgpt", "openai-compat"],
79
+ label: "OpenAI",
80
+ vars: ["OPENAI_MODEL"],
81
+ },
82
+ {
83
+ match: ["anthropic", "claude", "anthropic-claude"],
84
+ label: "Anthropic",
85
+ vars: ["ANTHROPIC_MODEL"],
86
+ },
87
+ {
88
+ match: ["google", "gemini"],
89
+ label: "Google AI",
90
+ vars: ["GOOGLE_AI_MODEL"],
91
+ },
92
+ {
93
+ match: ["mistral"],
94
+ label: "Mistral",
95
+ vars: ["MISTRAL_MODEL"],
96
+ },
97
+ {
98
+ match: ["ollama"],
99
+ label: "Ollama",
100
+ vars: ["OLLAMA_MODEL"],
101
+ },
102
+ {
103
+ match: ["litellm"],
104
+ label: "LiteLLM",
105
+ vars: ["LITELLM_MODEL"],
106
+ },
107
+ ];
108
+ const envConfig = envConfigs.find((config) => config.match.some((match) => normalizedProvider.includes(match)));
109
+ if (!envConfig) {
110
+ logger.debug(`[${functionTag}] Provider ${providerName} - no environment variable check implemented`);
111
+ return modelName;
112
+ }
113
+ const matchedVar = envConfig.vars.find((name) => process.env[name]);
114
+ const envModel = matchedVar ? process.env[matchedVar] : undefined;
115
+ if (envModel) {
116
+ logger.debug(`[${functionTag}] Environment variable found for ${envConfig.label}`, {
117
+ envVariable: matchedVar,
118
+ resolvedModel: envModel,
119
+ });
120
+ return envModel;
121
+ }
122
+ logger.debug(`[${functionTag}] No ${envConfig.label} environment variables found (${envConfig.vars.join(", ")})`);
123
+ return modelName;
124
+ }
125
+ static async resolveDynamicModelName(providerName, modelName, resolvedModelName, functionTag) {
126
+ if ((resolvedModelName && resolvedModelName !== "default") ||
127
+ (modelName && modelName !== "default")) {
128
+ logger.debug(`[${functionTag}] Skipping dynamic model resolution`, {
129
+ resolvedModelName: resolvedModelName || "none",
130
+ reason: "Model already resolved from environment variables or explicit parameter",
131
+ });
132
+ return resolvedModelName;
133
+ }
134
+ logger.debug(`[${functionTag}] Attempting dynamic model resolution`, {
135
+ currentResolvedModel: resolvedModelName || "none",
136
+ reason: "No environment variable found and no explicit model provided",
137
+ });
138
+ try {
139
+ const normalizedProvider = this.normalizeProviderName(providerName);
140
+ if (dynamicModelProvider.needsRefresh()) {
141
+ logger.debug(`[${functionTag}] Dynamic model provider needs refresh - initializing`);
142
+ await this.initializeDynamicProviderWithTimeout();
143
+ }
144
+ const dynamicModel = dynamicModelProvider.resolveModel(normalizedProvider, modelName || undefined);
145
+ if (!dynamicModel) {
146
+ logger.debug(`[${functionTag}] Dynamic model resolution returned null`, {
147
+ provider: normalizedProvider,
148
+ requestedModel: modelName || "default",
149
+ });
150
+ return resolvedModelName;
151
+ }
152
+ logger.debug(`[${functionTag}] Resolved dynamic model`, {
153
+ provider: normalizedProvider,
154
+ requestedModel: modelName || "default",
155
+ resolvedModel: dynamicModel.id,
156
+ displayName: dynamicModel.displayName,
157
+ pricing: dynamicModel.pricing.input,
158
+ });
159
+ return dynamicModel.id;
160
+ }
161
+ catch (resolveError) {
162
+ logger.debug(`[${functionTag}] Dynamic model resolution failed, using static fallback`, {
163
+ error: resolveError instanceof Error
164
+ ? resolveError.message
165
+ : String(resolveError),
166
+ });
167
+ return resolvedModelName;
168
+ }
169
+ }
170
+ static async createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag) {
171
+ await withTimeout(ProviderRegistry.registerAllProviders(), 30_000, new Error("Provider registration timed out"));
172
+ const normalizedName = this.normalizeProviderName(providerName);
173
+ const finalModelName = resolvedModelName === "default" || resolvedModelName === null
174
+ ? undefined
175
+ : resolvedModelName;
176
+ logger.debug(`[${functionTag}] Final provider configuration`, {
177
+ originalProviderName: providerName,
178
+ normalizedProviderName: normalizedName,
179
+ resolvedModelName: resolvedModelName || "not resolved",
180
+ finalModelName: finalModelName || "using provider default",
181
+ });
182
+ const provider = await withTimeout(ProviderFactory.createProvider(normalizedName, finalModelName, sdk, region), 30_000, new Error(`Provider creation timed out for ${normalizedName}`));
183
+ return { normalizedName, finalModelName, provider };
184
+ }
50
185
  /**
51
186
  * Create a provider instance for the specified provider type
52
187
  * @param providerName - Name of the provider ('vertex', 'bedrock', 'openai')
@@ -88,194 +223,9 @@ export class AIProviderFactory {
88
223
  // - Enhanced error handling and logging for debugging
89
224
  //
90
225
  // The dynamic model provider now provides reliable functionality without hanging
91
- let resolvedModelName = modelName;
92
- // PRIORITY 1: Check environment variables BEFORE dynamic resolution
93
- if (!modelName || modelName === "default") {
94
- logger.debug(`[${functionTag}] Checking environment variables for provider: ${providerName}`);
95
- // Check for provider-specific environment variables first
96
- if (providerName.toLowerCase().includes("bedrock")) {
97
- const envModel = process.env.BEDROCK_MODEL || process.env.BEDROCK_MODEL_ID;
98
- if (envModel) {
99
- resolvedModelName = envModel;
100
- logger.debug(`[${functionTag}] Environment variable found for Bedrock`, {
101
- envVariable: process.env.BEDROCK_MODEL
102
- ? "BEDROCK_MODEL"
103
- : "BEDROCK_MODEL_ID",
104
- resolvedModel: envModel,
105
- });
106
- }
107
- else {
108
- logger.debug(`[${functionTag}] No Bedrock environment variables found (BEDROCK_MODEL, BEDROCK_MODEL_ID)`);
109
- }
110
- }
111
- else if (providerName.toLowerCase().includes("vertex")) {
112
- const envModel = process.env.VERTEX_MODEL;
113
- if (envModel) {
114
- resolvedModelName = envModel;
115
- logger.debug(`[${functionTag}] Environment variable found for Vertex`, {
116
- envVariable: "VERTEX_MODEL",
117
- resolvedModel: envModel,
118
- });
119
- }
120
- else {
121
- logger.debug(`[${functionTag}] No Vertex environment variables found (VERTEX_MODEL)`);
122
- }
123
- }
124
- else if (providerName.toLowerCase().includes("azure")) {
125
- const envModel = process.env.AZURE_OPENAI_MODEL ||
126
- process.env.AZURE_OPENAI_DEPLOYMENT ||
127
- process.env.AZURE_OPENAI_DEPLOYMENT_ID;
128
- if (envModel) {
129
- resolvedModelName = envModel;
130
- logger.debug(`[${functionTag}] Environment variable found for Azure`, {
131
- envVariable: process.env.AZURE_OPENAI_MODEL
132
- ? "AZURE_OPENAI_MODEL"
133
- : process.env.AZURE_OPENAI_DEPLOYMENT
134
- ? "AZURE_OPENAI_DEPLOYMENT"
135
- : "AZURE_OPENAI_DEPLOYMENT_ID",
136
- resolvedModel: envModel,
137
- });
138
- }
139
- else {
140
- logger.debug(`[${functionTag}] No Azure environment variables found (AZURE_OPENAI_MODEL, AZURE_OPENAI_DEPLOYMENT, AZURE_OPENAI_DEPLOYMENT_ID)`);
141
- }
142
- }
143
- else if (providerName.toLowerCase().includes("openai")) {
144
- const envModel = process.env.OPENAI_MODEL;
145
- if (envModel) {
146
- resolvedModelName = envModel;
147
- logger.debug(`[${functionTag}] Environment variable found for OpenAI`, {
148
- envVariable: "OPENAI_MODEL",
149
- resolvedModel: envModel,
150
- });
151
- }
152
- else {
153
- logger.debug(`[${functionTag}] No OpenAI environment variables found (OPENAI_MODEL)`);
154
- }
155
- }
156
- else if (providerName.toLowerCase().includes("anthropic")) {
157
- const envModel = process.env.ANTHROPIC_MODEL;
158
- if (envModel) {
159
- resolvedModelName = envModel;
160
- logger.debug(`[${functionTag}] Environment variable found for Anthropic`, {
161
- envVariable: "ANTHROPIC_MODEL",
162
- resolvedModel: envModel,
163
- });
164
- }
165
- else {
166
- logger.debug(`[${functionTag}] No Anthropic environment variables found (ANTHROPIC_MODEL)`);
167
- }
168
- }
169
- else if (providerName.toLowerCase().includes("google") ||
170
- providerName.toLowerCase().includes("gemini")) {
171
- const envModel = process.env.GOOGLE_AI_MODEL;
172
- if (envModel) {
173
- resolvedModelName = envModel;
174
- logger.debug(`[${functionTag}] Environment variable found for Google AI`, {
175
- envVariable: "GOOGLE_AI_MODEL",
176
- resolvedModel: envModel,
177
- });
178
- }
179
- else {
180
- logger.debug(`[${functionTag}] No Google AI environment variables found (GOOGLE_AI_MODEL)`);
181
- }
182
- }
183
- else if (providerName.toLowerCase().includes("mistral")) {
184
- const envModel = process.env.MISTRAL_MODEL;
185
- if (envModel) {
186
- resolvedModelName = envModel;
187
- logger.debug(`[${functionTag}] Environment variable found for Mistral`, {
188
- envVariable: "MISTRAL_MODEL",
189
- resolvedModel: envModel,
190
- });
191
- }
192
- else {
193
- logger.debug(`[${functionTag}] No Mistral environment variables found (MISTRAL_MODEL)`);
194
- }
195
- }
196
- else if (providerName.toLowerCase().includes("ollama")) {
197
- const envModel = process.env.OLLAMA_MODEL;
198
- if (envModel) {
199
- resolvedModelName = envModel;
200
- logger.debug(`[${functionTag}] Environment variable found for Ollama`, {
201
- envVariable: "OLLAMA_MODEL",
202
- resolvedModel: envModel,
203
- });
204
- }
205
- else {
206
- logger.debug(`[${functionTag}] No Ollama environment variables found (OLLAMA_MODEL)`);
207
- }
208
- }
209
- else {
210
- logger.debug(`[${functionTag}] Provider ${providerName} - no environment variable check implemented`);
211
- }
212
- }
213
- else {
214
- logger.debug(`[${functionTag}] Skipping environment variable check - explicit model provided: ${modelName}`);
215
- }
216
- // PRIORITY 2: Enable dynamic model resolution only if no env var found
217
- if ((!resolvedModelName || resolvedModelName === "default") &&
218
- (!modelName || modelName === "default")) {
219
- logger.debug(`[${functionTag}] Attempting dynamic model resolution`, {
220
- currentResolvedModel: resolvedModelName || "none",
221
- reason: "No environment variable found and no explicit model provided",
222
- });
223
- try {
224
- const normalizedProvider = this.normalizeProviderName(providerName);
225
- // Initialize with timeout protection - won't hang anymore
226
- if (dynamicModelProvider.needsRefresh()) {
227
- logger.debug(`[${functionTag}] Dynamic model provider needs refresh - initializing`);
228
- await this.initializeDynamicProviderWithTimeout();
229
- }
230
- const dynamicModel = dynamicModelProvider.resolveModel(normalizedProvider, modelName || undefined);
231
- if (dynamicModel) {
232
- resolvedModelName = dynamicModel.id;
233
- logger.debug(`[${functionTag}] Resolved dynamic model`, {
234
- provider: normalizedProvider,
235
- requestedModel: modelName || "default",
236
- resolvedModel: resolvedModelName,
237
- displayName: dynamicModel.displayName,
238
- pricing: dynamicModel.pricing.input,
239
- });
240
- }
241
- else {
242
- logger.debug(`[${functionTag}] Dynamic model resolution returned null`, {
243
- provider: normalizedProvider,
244
- requestedModel: modelName || "default",
245
- });
246
- }
247
- }
248
- catch (resolveError) {
249
- logger.debug(`[${functionTag}] Dynamic model resolution failed, using static fallback`, {
250
- error: resolveError instanceof Error
251
- ? resolveError.message
252
- : String(resolveError),
253
- });
254
- // Continue with static model name - no functionality loss
255
- }
256
- }
257
- else {
258
- logger.debug(`[${functionTag}] Skipping dynamic model resolution`, {
259
- resolvedModelName: resolvedModelName || "none",
260
- reason: "Model already resolved from environment variables or explicit parameter",
261
- });
262
- }
263
- // CRITICAL FIX: Initialize providers before using them
264
- await withTimeout(ProviderRegistry.registerAllProviders(), 30_000, new Error("Provider registration timed out"));
265
- // PURE FACTORY PATTERN: No switch statements - use ProviderFactory exclusively
266
- const normalizedName = this.normalizeProviderName(providerName);
267
- const finalModelName = resolvedModelName === "default" || resolvedModelName === null
268
- ? undefined
269
- : resolvedModelName;
270
- logger.debug(`[${functionTag}] Final provider configuration`, {
271
- originalProviderName: providerName,
272
- normalizedProviderName: normalizedName,
273
- originalModelName: modelName || "not provided",
274
- resolvedModelName: resolvedModelName || "not resolved",
275
- finalModelName: finalModelName || "using provider default",
276
- });
277
- // Create provider with enhanced SDK and region support
278
- const provider = await withTimeout(ProviderFactory.createProvider(normalizedName, finalModelName, sdk, region), 30_000, new Error(`Provider creation timed out for ${normalizedName}`));
226
+ let resolvedModelName = this.resolveModelFromEnvironment(providerName, modelName, functionTag);
227
+ resolvedModelName = await this.resolveDynamicModelName(providerName, modelName, resolvedModelName, functionTag);
228
+ const { normalizedName, finalModelName, provider } = await this.createResolvedProvider(providerName, resolvedModelName, sdk, region, functionTag);
279
229
  // Summary logging in format expected by debugging tools
280
230
  logger.debug(`[AIProviderFactory] Provider creation completed { providerName: '${normalizedName}', modelName: '${finalModelName}' }`);
281
231
  logger.debug(`[AIProviderFactory] Resolved model: ${finalModelName}`);
@@ -95,6 +95,7 @@ export class GenerationHandler {
95
95
  };
96
96
  }
97
97
  }
98
+ const prepareStep = options.prepareStep;
98
99
  return await generateText({
99
100
  model,
100
101
  messages,
@@ -103,8 +104,8 @@ export class GenerationHandler {
103
104
  stopWhen: stepCountIs(options.maxSteps ?? DEFAULT_MAX_STEPS),
104
105
  ...(shouldUseTools &&
105
106
  options.toolChoice && { toolChoice: options.toolChoice }),
106
- ...(options.prepareStep && {
107
- experimental_prepareStep: ((stepOptions) => options.prepareStep({
107
+ ...(prepareStep && {
108
+ experimental_prepareStep: ((stepOptions) => prepareStep({
108
109
  ...stepOptions,
109
110
  maxSteps: options.maxSteps ?? DEFAULT_MAX_STEPS,
110
111
  })),
@@ -16,6 +16,7 @@ import { runWithCurrentLangfuseContext } from "../services/server/ai/observabili
16
16
  import { logger } from "../utils/logger.js";
17
17
  import { deserializeConversation, getNormalizedConfig, getPooledRedisClient, getSessionKey, getUserSessionsKey, releasePooledRedisClient, scanKeys, serializeConversation, } from "../utils/redis.js";
18
18
  const redisTracer = tracers.redis;
19
+ const REDIS_TIMEOUT_MS = 5000;
19
20
  /**
20
21
  * Redis-based implementation of the ConversationMemoryManager
21
22
  * Uses the same interface but stores data in Redis
@@ -130,13 +131,14 @@ export class RedisConversationMemoryManager {
130
131
  if (!this.redisClient) {
131
132
  return undefined;
132
133
  }
134
+ const redisClient = this.redisClient;
133
135
  return redisTracer.startActiveSpan("neurolink.memory.getSession", { kind: SpanKind.CLIENT, attributes: { "session.id": sessionId } }, async (span) => {
134
136
  if (userId) {
135
137
  span.setAttribute("user.id", userId);
136
138
  }
137
139
  try {
138
140
  const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
139
- const conversationData = await this.redisClient.get(redisKey);
141
+ const conversationData = await withTimeout(redisClient.get(redisKey), REDIS_TIMEOUT_MS);
140
142
  const conversation = deserializeConversation(conversationData || null);
141
143
  if (!conversation) {
142
144
  span.setAttribute("session.found", false);
@@ -630,6 +632,7 @@ export class RedisConversationMemoryManager {
630
632
  logger.warn("[RedisConversationMemoryManager] Redis client not available in buildContextMessages");
631
633
  return [];
632
634
  }
635
+ const redisClient = this.redisClient;
633
636
  // NLK-GAP-012: Add span for buildContext CRUD operation
634
637
  return redisTracer.startActiveSpan("neurolink.memory.buildContext", {
635
638
  kind: SpanKind.CLIENT,
@@ -645,7 +648,7 @@ export class RedisConversationMemoryManager {
645
648
  method: "buildContextMessages",
646
649
  });
647
650
  const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
648
- const conversationData = await this.redisClient.get(redisKey);
651
+ const conversationData = await withTimeout(redisClient.get(redisKey), REDIS_TIMEOUT_MS);
649
652
  const conversation = deserializeConversation(conversationData || null);
650
653
  if (!conversation) {
651
654
  span.setAttribute("session.found", false);
@@ -1080,6 +1083,7 @@ User message: "${userMessage}"`;
1080
1083
  if (!this.redisClient) {
1081
1084
  return false;
1082
1085
  }
1086
+ const redisClient = this.redisClient;
1083
1087
  // NLK-GAP-012: Add span for clearSession CRUD operation
1084
1088
  return redisTracer.startActiveSpan("neurolink.memory.clear", {
1085
1089
  kind: SpanKind.CLIENT,
@@ -1090,7 +1094,7 @@ User message: "${userMessage}"`;
1090
1094
  }, async (span) => {
1091
1095
  try {
1092
1096
  const redisKey = getSessionKey(this.redisConfig, sessionId, userId);
1093
- const result = await this.redisClient.del(redisKey);
1097
+ const result = await withTimeout(redisClient.del(redisKey), REDIS_TIMEOUT_MS);
1094
1098
  if (Number(result) > 0) {
1095
1099
  // Remove session from user's session set
1096
1100
  if (userId) {
@@ -6,6 +6,9 @@ import { Evaluator } from "./index.js";
6
6
  import { createBatchEvaluationError, isRetryableEvaluationError, } from "./errors/EvaluationError.js";
7
7
  import { logger } from "../utils/logger.js";
8
8
  import { NeuroLinkFeatureError } from "../core/infrastructure/index.js";
9
+ function hasEvaluationData(result) {
10
+ return result.success && result.data !== undefined;
11
+ }
9
12
  /**
10
13
  * BatchEvaluator - Performs evaluation on multiple items in parallel.
11
14
  * Supports configurable concurrency, retry logic, and progress tracking.
@@ -201,7 +204,7 @@ export class BatchEvaluator {
201
204
  }
202
205
  }
203
206
  // Calculate summary statistics
204
- const successfulResults = results.filter((r) => r.success && r.data);
207
+ const successfulResults = results.filter(hasEvaluationData);
205
208
  const scores = successfulResults.map((r) => r.data.overall);
206
209
  const passingScores = successfulResults.filter((r) => r.data.overall >=
207
210
  (autoEvalConfig.threshold || this.config.threshold || 7));
@@ -41,10 +41,12 @@ export class ObservabilityHooks {
41
41
  * Register an event handler
42
42
  */
43
43
  on(event, handler) {
44
- if (!this._handlers.has(event)) {
45
- this._handlers.set(event, new Set());
44
+ let handlers = this._handlers.get(event);
45
+ if (!handlers) {
46
+ handlers = new Set();
47
+ this._handlers.set(event, handlers);
46
48
  }
47
- this._handlers.get(event).add(handler);
49
+ handlers.add(handler);
48
50
  // Return unsubscribe function
49
51
  return () => {
50
52
  this._handlers.get(event)?.delete(handler);
@@ -12,9 +12,9 @@ export type PipelineExecutionOptions = {
12
12
  correlationId?: string;
13
13
  /** Custom timeout override */
14
14
  timeout?: number;
15
- /** Skip specific scorers */
15
+ /** Skip specific scorers. Mutually exclusive with onlyScorers. */
16
16
  skipScorers?: string[];
17
- /** Only run specific scorers */
17
+ /** Only run specific scorers. Mutually exclusive with skipScorers. */
18
18
  onlyScorers?: string[];
19
19
  /** Additional metadata to attach */
20
20
  metadata?: JsonObject;
@@ -59,6 +59,7 @@ export declare class EvaluationPipeline {
59
59
  * Execute the pipeline on input
60
60
  */
61
61
  execute(input: ScorerInput, options?: PipelineExecutionOptions): Promise<PipelineResult>;
62
+ private _validateExecutionOptions;
62
63
  /**
63
64
  * Get scorers to run based on options
64
65
  */
@@ -3,7 +3,7 @@
3
3
  * Multi-scorer orchestration with configurable execution
4
4
  */
5
5
  import { logger } from "../../utils/logger.js";
6
- import { withTimeout } from "../../utils/errorHandling.js";
6
+ import { ErrorFactory, withTimeout } from "../../utils/errorHandling.js";
7
7
  import { DEFAULT_SCORE_SCALE } from "../scorers/baseScorer.js";
8
8
  import { ScorerRegistry } from "../scorers/scorerRegistry.js";
9
9
  /**
@@ -91,6 +91,7 @@ export class EvaluationPipeline {
91
91
  if (!this._initialized) {
92
92
  await this.initialize();
93
93
  }
94
+ this._validateExecutionOptions(options);
94
95
  const startTime = Date.now();
95
96
  const correlationId = options?.correlationId ?? `pipeline-${Date.now()}`;
96
97
  logger.debug(`Executing pipeline: ${this._config.name ?? "unnamed"}`, {
@@ -173,16 +174,28 @@ export class EvaluationPipeline {
173
174
  skippedScorers,
174
175
  };
175
176
  }
177
+ _validateExecutionOptions(options) {
178
+ const hasOnlyScorers = !!options?.onlyScorers && options.onlyScorers.length > 0;
179
+ const hasSkipScorers = !!options?.skipScorers && options.skipScorers.length > 0;
180
+ if (hasOnlyScorers && hasSkipScorers) {
181
+ throw ErrorFactory.invalidConfiguration("evaluation pipeline execution options", "Cannot specify both 'onlyScorers' and 'skipScorers' options", {
182
+ onlyScorers: options?.onlyScorers,
183
+ skipScorers: options?.skipScorers,
184
+ });
185
+ }
186
+ }
176
187
  /**
177
188
  * Get scorers to run based on options
178
189
  */
179
190
  _getScorersToRun(options) {
180
191
  const allScorers = Array.from(this._scorers.entries());
181
- if (options?.onlyScorers && options.onlyScorers.length > 0) {
182
- return allScorers.filter(([id]) => options.onlyScorers.includes(id));
192
+ const onlyScorers = options?.onlyScorers;
193
+ const skipScorers = options?.skipScorers;
194
+ if (onlyScorers && onlyScorers.length > 0) {
195
+ return allScorers.filter(([id]) => onlyScorers.includes(id));
183
196
  }
184
- if (options?.skipScorers && options.skipScorers.length > 0) {
185
- return allScorers.filter(([id]) => !options.skipScorers.includes(id));
197
+ if (skipScorers && skipScorers.length > 0) {
198
+ return allScorers.filter(([id]) => !skipScorers.includes(id));
186
199
  }
187
200
  return allScorers;
188
201
  }
@@ -191,11 +204,13 @@ export class EvaluationPipeline {
191
204
  */
192
205
  _getSkippedScorers(options) {
193
206
  const allIds = Array.from(this._scorers.keys());
194
- if (options?.onlyScorers && options.onlyScorers.length > 0) {
195
- return allIds.filter((id) => !options.onlyScorers.includes(id));
207
+ const onlyScorers = options?.onlyScorers;
208
+ const skipScorers = options?.skipScorers;
209
+ if (onlyScorers && onlyScorers.length > 0) {
210
+ return allIds.filter((id) => !onlyScorers.includes(id));
196
211
  }
197
- if (options?.skipScorers && options.skipScorers.length > 0) {
198
- return options.skipScorers.filter((id) => allIds.includes(id));
212
+ if (skipScorers && skipScorers.length > 0) {
213
+ return skipScorers.filter((id) => allIds.includes(id));
199
214
  }
200
215
  return [];
201
216
  }
@@ -2,6 +2,9 @@
2
2
  * @file Batch Strategy
3
3
  * Batch processing for evaluation pipelines
4
4
  */
5
+ function hasPipelineResult(result) {
6
+ return !result.error && result.result !== undefined;
7
+ }
5
8
  /**
6
9
  * Default batch configuration
7
10
  */
@@ -66,7 +69,7 @@ export class BatchStrategy {
66
69
  }
67
70
  // Calculate summary
68
71
  const totalDuration = Date.now() - startTime;
69
- const successfulResults = results.filter((r) => !r.error && r.result);
72
+ const successfulResults = results.filter(hasPipelineResult);
70
73
  const scores = successfulResults.map((r) => r.result.overallScore);
71
74
  const passed = successfulResults.filter((r) => r.result.passed);
72
75
  return {
@@ -192,7 +195,7 @@ export async function* streamBatchEvaluation(pipeline, inputs, config) {
192
195
  durations.push(result.duration);
193
196
  // If continueOnError is false and this result has an error, abort and return summary
194
197
  if (result.error && batchConfig.continueOnError === false) {
195
- const successfulResults = results.filter((r) => !r.error && r.result);
198
+ const successfulResults = results.filter(hasPipelineResult);
196
199
  const earlyScores = successfulResults.map((r) => r.result.overallScore);
197
200
  const earlyPassed = successfulResults.filter((r) => r.result.passed);
198
201
  return {
@@ -219,7 +222,7 @@ export async function* streamBatchEvaluation(pipeline, inputs, config) {
219
222
  }
220
223
  }
221
224
  // Return summary
222
- const successfulResults = results.filter((r) => !r.error && r.result);
225
+ const successfulResults = results.filter(hasPipelineResult);
223
226
  const scores = successfulResults.map((r) => r.result.overallScore);
224
227
  const passed = successfulResults.filter((r) => r.result.passed);
225
228
  return {
@@ -17,6 +17,12 @@ export const DEFAULT_SAMPLING_CONFIG = {
17
17
  maxRate: 1.0,
18
18
  },
19
19
  };
20
+ const DEFAULT_ADAPTIVE_CONFIG = {
21
+ enabled: false,
22
+ qualityThreshold: 0.7,
23
+ minRate: 0.1,
24
+ maxRate: 1.0,
25
+ };
20
26
  /**
21
27
  * Sampling strategy for evaluation
22
28
  */
@@ -27,9 +33,10 @@ export class SamplingStrategy {
27
33
  _maxRecentScores = 100;
28
34
  constructor(config = {}) {
29
35
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
36
+ const defaultAdaptive = DEFAULT_SAMPLING_CONFIG.adaptive ?? DEFAULT_ADAPTIVE_CONFIG;
30
37
  const rawRate = config.rate ?? DEFAULT_SAMPLING_CONFIG.rate;
31
- const rawMinRate = config.adaptive?.minRate ?? DEFAULT_SAMPLING_CONFIG.adaptive.minRate;
32
- const rawMaxRate = config.adaptive?.maxRate ?? DEFAULT_SAMPLING_CONFIG.adaptive.maxRate;
38
+ const rawMinRate = config.adaptive?.minRate ?? defaultAdaptive.minRate;
39
+ const rawMaxRate = config.adaptive?.maxRate ?? defaultAdaptive.maxRate;
33
40
  const minRate = clamp01(Math.min(rawMinRate, rawMaxRate));
34
41
  const maxRate = clamp01(Math.max(rawMinRate, rawMaxRate));
35
42
  this._config = {
@@ -41,9 +48,8 @@ export class SamplingStrategy {
41
48
  ...(config.alwaysEvaluate ?? {}),
42
49
  },
43
50
  adaptive: {
44
- enabled: config.adaptive?.enabled ?? DEFAULT_SAMPLING_CONFIG.adaptive.enabled,
45
- qualityThreshold: clamp01(config.adaptive?.qualityThreshold ??
46
- DEFAULT_SAMPLING_CONFIG.adaptive.qualityThreshold),
51
+ enabled: config.adaptive?.enabled ?? defaultAdaptive.enabled,
52
+ qualityThreshold: clamp01(config.adaptive?.qualityThreshold ?? defaultAdaptive.qualityThreshold),
47
53
  minRate,
48
54
  maxRate,
49
55
  },
@@ -156,9 +162,12 @@ export class SamplingStrategy {
156
162
  */
157
163
  configure(config) {
158
164
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
165
+ const currentAdaptive = this._config.adaptive ??
166
+ DEFAULT_SAMPLING_CONFIG.adaptive ??
167
+ DEFAULT_ADAPTIVE_CONFIG;
159
168
  const rawRate = config.rate ?? this._config.rate;
160
- const rawMinRate = config.adaptive?.minRate ?? this._config.adaptive.minRate;
161
- const rawMaxRate = config.adaptive?.maxRate ?? this._config.adaptive.maxRate;
169
+ const rawMinRate = config.adaptive?.minRate ?? currentAdaptive.minRate;
170
+ const rawMaxRate = config.adaptive?.maxRate ?? currentAdaptive.maxRate;
162
171
  const minRate = clamp01(Math.min(rawMinRate, rawMaxRate));
163
172
  const maxRate = clamp01(Math.max(rawMinRate, rawMaxRate));
164
173
  this._config = {
@@ -170,9 +179,8 @@ export class SamplingStrategy {
170
179
  ...(config.alwaysEvaluate ?? {}),
171
180
  },
172
181
  adaptive: {
173
- enabled: config.adaptive?.enabled ?? this._config.adaptive.enabled,
174
- qualityThreshold: clamp01(config.adaptive?.qualityThreshold ??
175
- this._config.adaptive.qualityThreshold),
182
+ enabled: config.adaptive?.enabled ?? currentAdaptive.enabled,
183
+ qualityThreshold: clamp01(config.adaptive?.qualityThreshold ?? currentAdaptive.qualityThreshold),
176
184
  minRate,
177
185
  maxRate,
178
186
  },
@@ -19,6 +19,9 @@ export declare class ScorerRegistry {
19
19
  * Register a scorer using a simple configuration
20
20
  */
21
21
  static registerScorer(metadata: ScorerMetadata, factory: ScorerFactory, aliases?: string[]): void;
22
+ private static registerScorerDefinitions;
23
+ private static registerBuiltInLLMScorers;
24
+ private static registerBuiltInRuleScorers;
22
25
  /**
23
26
  * Register built-in scorers using dynamic imports
24
27
  */