@juspay/neurolink 12.33.0 → 12.34.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 (34) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/adapters/providerImageAdapter.js +13 -0
  3. package/dist/browser/neurolink.min.js +419 -419
  4. package/dist/constants/contextWindows.js +28 -0
  5. package/dist/constants/enums.d.ts +21 -0
  6. package/dist/constants/enums.js +30 -0
  7. package/dist/constants/tokens.d.ts +3 -0
  8. package/dist/constants/tokens.js +3 -0
  9. package/dist/evaluation/EvaluatorFactory.js +3 -3
  10. package/dist/evaluation/EvaluatorRegistry.js +1 -1
  11. package/dist/evaluation/ragasEvaluator.js +1 -1
  12. package/dist/mcp/externalServerManager.js +1 -1
  13. package/dist/mcp/mcpClientFactory.d.ts +1 -1
  14. package/dist/mcp/mcpClientFactory.js +5 -5
  15. package/dist/middleware/utils/guardrailsUtils.js +1 -1
  16. package/dist/models/manifests/anthropic.js +33 -1
  17. package/dist/neurolink.js +6 -2
  18. package/dist/providers/catalog/xai.json +17 -3
  19. package/dist/providers/googleVertex/client.js +0 -2
  20. package/dist/providers/litellm/client.js +2 -2
  21. package/dist/providers/openAI/client.js +1 -1
  22. package/dist/providers/openRouter/client.js +1 -1
  23. package/dist/proxy/vertexAnthropicFallback.d.ts +8 -0
  24. package/dist/proxy/vertexAnthropicFallback.js +30 -6
  25. package/dist/services/server/ai/observability/instrumentation.d.ts +7 -14
  26. package/dist/services/server/ai/observability/instrumentation.js +80 -32
  27. package/dist/utils/modelChoices.js +0 -9
  28. package/dist/utils/pricing.js +5 -0
  29. package/dist/utils/providerHealth.js +7 -9
  30. package/dist/utils/redis.js +3 -1
  31. package/dist/utils/tokenLimits.js +9 -0
  32. package/dist/workflow/workflows/multiJudgeWorkflow.js +2 -2
  33. package/docs-site/static/search-index.json +10 -10
  34. package/package.json +4 -2
@@ -7,23 +7,17 @@
7
7
  * Flow: Vercel AI SDK → OpenTelemetry Spans → LangfuseSpanProcessor → Langfuse Platform
8
8
  */
9
9
  import { metrics, SpanStatusCode, trace, } from "@opentelemetry/api";
10
- import { W3CTraceContextPropagator } from "@opentelemetry/core";
11
- import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-http";
12
- import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
13
- import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
14
- import { resourceFromAttributes } from "@opentelemetry/resources";
15
- import { MeterProvider, PeriodicExportingMetricReader, } from "@opentelemetry/sdk-metrics";
16
- import { BatchLogRecordProcessor, LoggerProvider, } from "@opentelemetry/sdk-logs";
17
- import { BatchSpanProcessor, } from "@opentelemetry/sdk-trace-base";
18
- import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
19
- import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions";
20
10
  import { AsyncLocalStorage } from "async_hooks";
21
11
  import { extractMcpErrorText } from "../../../../utils/mcpErrorText.js";
22
12
  import { logger } from "../../../../utils/logger.js";
23
13
  import { tryImport } from "../../../../utils/tryImport.js";
24
14
  import { LANGFUSE_ATTR } from "../../../../telemetry/attributes.js";
25
15
  const LOG_PREFIX = "[OpenTelemetry]";
26
- function createOtelResource(config, serviceName) {
16
+ async function createOtelResource(config, serviceName) {
17
+ const [{ resourceFromAttributes }, { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION },] = await Promise.all([
18
+ import("@opentelemetry/resources"),
19
+ import("@opentelemetry/semantic-conventions"),
20
+ ]);
27
21
  return resourceFromAttributes({
28
22
  [ATTR_SERVICE_NAME]: serviceName,
29
23
  [ATTR_SERVICE_VERSION]: config.release || "v1.0.0",
@@ -32,13 +26,19 @@ function createOtelResource(config, serviceName) {
32
26
  }
33
27
  /**
34
28
  * Configure OTLP metrics and logs with bounded serialization batches for
35
- * request-serving processes.
29
+ * request-serving processes. Initialization awaits this so a caller may flush
30
+ * or shut down immediately after it returns without racing providers that have
31
+ * not been constructed yet. Signal-specific failures stay non-fatal here.
36
32
  */
37
- function initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName) {
33
+ async function initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName) {
38
34
  if (!otlpEndpoint) {
39
35
  return;
40
36
  }
41
37
  try {
38
+ const [{ OTLPMetricExporter }, { MeterProvider, PeriodicExportingMetricReader },] = await Promise.all([
39
+ import("@opentelemetry/exporter-metrics-otlp-http"),
40
+ import("@opentelemetry/sdk-metrics"),
41
+ ]);
42
42
  const metricExporter = new OTLPMetricExporter({
43
43
  url: `${otlpEndpoint}/v1/metrics`,
44
44
  });
@@ -68,6 +68,10 @@ function initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName) {
68
68
  });
69
69
  }
70
70
  try {
71
+ const [{ OTLPLogExporter }, { BatchLogRecordProcessor, LoggerProvider }] = await Promise.all([
72
+ import("@opentelemetry/exporter-logs-otlp-http"),
73
+ import("@opentelemetry/sdk-logs"),
74
+ ]);
71
75
  const logExporter = new OTLPLogExporter({
72
76
  url: `${otlpEndpoint}/v1/logs`,
73
77
  });
@@ -101,6 +105,7 @@ let meterProvider = null;
101
105
  let loggerProvider = null;
102
106
  let langfuseProcessor = null;
103
107
  let isInitialized = false;
108
+ let initializationPromise = null;
104
109
  let isCredentialsValid = false;
105
110
  let currentConfig = null;
106
111
  let usingExternalProvider = false;
@@ -637,8 +642,8 @@ async function initializeExternalOpenTelemetryMode(config, resource, otlpEndpoin
637
642
  ? await createLangfuseProcessor(config)
638
643
  : null;
639
644
  usingExternalProvider = true;
645
+ await initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
640
646
  isInitialized = true;
641
- initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
642
647
  try {
643
648
  const globalProvider = trace.getTracerProvider();
644
649
  const provider = globalProvider;
@@ -759,6 +764,10 @@ async function initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpo
759
764
  }
760
765
  if (otlpEndpoint) {
761
766
  try {
767
+ const [{ OTLPTraceExporter }, { BatchSpanProcessor }] = await Promise.all([
768
+ import("@opentelemetry/exporter-trace-otlp-http"),
769
+ import("@opentelemetry/sdk-trace-base"),
770
+ ]);
762
771
  const otlpExporter = new OTLPTraceExporter({
763
772
  url: `${otlpEndpoint}/v1/traces`,
764
773
  });
@@ -782,13 +791,17 @@ async function initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpo
782
791
  });
783
792
  }
784
793
  }
794
+ const [{ NodeTracerProvider }, { W3CTraceContextPropagator }] = await Promise.all([
795
+ import("@opentelemetry/sdk-trace-node"),
796
+ import("@opentelemetry/core"),
797
+ ]);
785
798
  tracerProvider = new NodeTracerProvider({ resource, spanProcessors });
786
799
  tracerProvider.register({
787
800
  propagator: new W3CTraceContextPropagator(),
788
801
  });
789
802
  usingExternalProvider = false;
803
+ await initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
790
804
  isInitialized = true;
791
- initializeOtlpMetricsAndLogs(resource, otlpEndpoint, serviceName);
792
805
  logger.info(`${LOG_PREFIX} Observability initialized`, {
793
806
  baseUrl: config.baseUrl || "https://cloud.langfuse.com",
794
807
  environment: config.environment || "dev",
@@ -820,6 +833,39 @@ async function initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpo
820
833
  throw error;
821
834
  }
822
835
  }
836
+ /**
837
+ * Initialize OpenTelemetry once and let concurrent callers share the work.
838
+ *
839
+ * NeuroLink constructors intentionally start this asynchronously. Keeping the
840
+ * in-flight promise here lets another constructor join it and lets an immediate
841
+ * flush or shutdown wait for the same initialization before touching providers.
842
+ */
843
+ export async function initializeOpenTelemetry(config) {
844
+ if (isInitialized) {
845
+ currentConfig = config;
846
+ logger.debug(`${LOG_PREFIX} Already initialized, config updated`, {
847
+ usingExternalProvider,
848
+ hasLangfuseProcessor: !!langfuseProcessor,
849
+ hasTraceNameFormat: typeof config.traceNameFormat === "function",
850
+ });
851
+ return;
852
+ }
853
+ if (initializationPromise) {
854
+ await initializationPromise;
855
+ currentConfig = config;
856
+ return;
857
+ }
858
+ const pending = initializeOpenTelemetryOnce(config);
859
+ initializationPromise = pending;
860
+ try {
861
+ await pending;
862
+ }
863
+ finally {
864
+ if (initializationPromise === pending) {
865
+ initializationPromise = null;
866
+ }
867
+ }
868
+ }
823
869
  /**
824
870
  * Initialize OpenTelemetry with Langfuse span processor
825
871
  *
@@ -834,20 +880,7 @@ async function initializeStandaloneOpenTelemetryMode(config, resource, otlpEndpo
834
880
  *
835
881
  * @param config - Langfuse configuration passed from parent application
836
882
  */
837
- export async function initializeOpenTelemetry(config) {
838
- // Guard against multiple initializations — but always update config
839
- // so that later NeuroLink instances can change traceNameFormat,
840
- // autoDetectOperationName, and other configuration preferences
841
- // without re-initializing the OTEL infrastructure.
842
- if (isInitialized) {
843
- currentConfig = config;
844
- logger.debug(`${LOG_PREFIX} Already initialized, config updated`, {
845
- usingExternalProvider,
846
- hasLangfuseProcessor: !!langfuseProcessor,
847
- hasTraceNameFormat: typeof config.traceNameFormat === "function",
848
- });
849
- return;
850
- }
883
+ async function initializeOpenTelemetryOnce(config) {
851
884
  // FIRST: Check for external provider mode - bypasses enabled check
852
885
  // NOTE: When autoDetectExternalProvider is true, we trust the flag directly rather than
853
886
  // calling hasExternalTracerProvider(). This is because Neurolink may bundle its own copy
@@ -862,7 +895,7 @@ export async function initializeOpenTelemetry(config) {
862
895
  const langfuseRequested = config?.enabled === true;
863
896
  const hasLangfuseCreds = !!config.publicKey && !!config.secretKey;
864
897
  const serviceName = process.env.OTEL_SERVICE_NAME || "neurolink";
865
- const resource = createOtelResource(config, serviceName);
898
+ const resource = await createOtelResource(config, serviceName);
866
899
  if (shouldUseExternal) {
867
900
  await initializeExternalOpenTelemetryMode(config, resource, otlpEndpoint, serviceName, langfuseRequested, hasLangfuseCreds);
868
901
  return;
@@ -873,6 +906,15 @@ export async function initializeOpenTelemetry(config) {
873
906
  * Flush all pending spans to Langfuse
874
907
  */
875
908
  export async function flushOpenTelemetry() {
909
+ if (initializationPromise) {
910
+ try {
911
+ await initializationPromise;
912
+ }
913
+ catch {
914
+ // Initialization owns its error logging; a failed init leaves nothing
915
+ // initialized, so the check below skips the flush as it always has.
916
+ }
917
+ }
876
918
  if (!isInitialized) {
877
919
  logger.debug(`${LOG_PREFIX} Not initialized, skipping flush`);
878
920
  return;
@@ -951,8 +993,14 @@ export async function flushOpenTelemetry() {
951
993
  * Shutdown OpenTelemetry and Langfuse span processor
952
994
  */
953
995
  export async function shutdownOpenTelemetry() {
954
- if (!isInitialized) {
955
- return;
996
+ if (initializationPromise) {
997
+ try {
998
+ await initializationPromise;
999
+ }
1000
+ catch {
1001
+ // Initialization owns its error logging. Continue so any provider it
1002
+ // created before failing is still shut down below.
1003
+ }
956
1004
  }
957
1005
  try {
958
1006
  // Only shutdown tracerProvider if we created it
@@ -103,14 +103,6 @@ const TOP_MODELS_CONFIG = {
103
103
  model: GoogleAIModels.GEMINI_3_PRO_PREVIEW,
104
104
  description: "Latest preview",
105
105
  },
106
- {
107
- model: GoogleAIModels.GEMINI_1_5_PRO,
108
- description: "Previous generation",
109
- },
110
- {
111
- model: GoogleAIModels.GEMINI_1_5_FLASH,
112
- description: "Legacy fast model",
113
- },
114
106
  ],
115
107
  [AIProviderName.VERTEX]: [
116
108
  {
@@ -126,7 +118,6 @@ const TOP_MODELS_CONFIG = {
126
118
  model: VertexModels.GEMINI_2_0_FLASH,
127
119
  description: "Stable production model",
128
120
  },
129
- { model: VertexModels.GEMINI_1_5_PRO, description: "Previous generation" },
130
121
  {
131
122
  model: VertexModels.CLAUDE_3_5_SONNET,
132
123
  description: "Claude 3.5 on Vertex",
@@ -685,6 +685,11 @@ const PRICING = {
685
685
  // Voyage bills per million input tokens — output dimension is the
686
686
  // embedding vector, not generated tokens. We charge to input only.
687
687
  _default: { input: 0.18 / 1_000_000, output: 0 },
688
+ // Voyage 4 family — docs.voyageai.com/docs/embeddings pricing table
689
+ "voyage-4": { input: 0.06 / 1_000_000, output: 0 },
690
+ "voyage-4-large": { input: 0.12 / 1_000_000, output: 0 },
691
+ "voyage-4-lite": { input: 0.02 / 1_000_000, output: 0 },
692
+ "voyage-code-4": { input: 0.12 / 1_000_000, output: 0 },
688
693
  "voyage-3.5": { input: 0.06 / 1_000_000, output: 0 },
689
694
  "voyage-3.5-lite": { input: 0.02 / 1_000_000, output: 0 },
690
695
  "voyage-3-large": { input: 0.18 / 1_000_000, output: 0 },
@@ -423,7 +423,7 @@ export class ProviderHealthChecker {
423
423
  healthStatus.recommendations.push(`Available models for ${providerName} (using dual provider architecture):\n` +
424
424
  ` Google Models (via vertex provider):\n` +
425
425
  ` • gemini-2.5-pro, gemini-2.5-flash, gemini-2.5-flash-lite\n` +
426
- ` • gemini-2.0-flash-001, gemini-1.5-pro, gemini-1.5-flash\n` +
426
+ ` • gemini-2.0-flash-001, gemini-2.0-flash-lite\n` +
427
427
  ` Anthropic Models (via vertexAnthropic provider):\n` +
428
428
  ` • claude-sonnet-4@20250514, claude-opus-4@20250514\n` +
429
429
  ` • claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022\n` +
@@ -771,7 +771,7 @@ export class ProviderHealthChecker {
771
771
  return new URL("/v1/models", this.getLiteLLMBaseUrl()).toString();
772
772
  }
773
773
  static getConfiguredLiteLLMModel() {
774
- return process.env.LITELLM_MODEL || "openai/gpt-4o-mini";
774
+ return process.env.LITELLM_MODEL || "openai/gpt-5.4-mini";
775
775
  }
776
776
  static getOllamaBaseUrl() {
777
777
  return (process.env.OLLAMA_BASE_URL ||
@@ -914,7 +914,7 @@ export class ProviderHealthChecker {
914
914
  return { ran: false, failed: false };
915
915
  }
916
916
  // Only pin the availability check to a specific model when the user
917
- // explicitly configured one. The fallback default ("openai/gpt-4o-mini")
917
+ // explicitly configured one. The fallback default ("openai/gpt-5.4-mini")
918
918
  // is a guess, not configuration — proxies that serve a different model
919
919
  // set (every self-hosted gateway) were reported "Not configured" here
920
920
  // while generate/stream against them worked fine with explicit models.
@@ -1002,9 +1002,9 @@ export class ProviderHealthChecker {
1002
1002
  ];
1003
1003
  case AIProviderName.GOOGLE_AI:
1004
1004
  return [
1005
- GoogleAIModels.GEMINI_1_5_PRO,
1006
- GoogleAIModels.GEMINI_1_5_FLASH,
1007
1005
  GoogleAIModels.GEMINI_2_5_PRO,
1006
+ GoogleAIModels.GEMINI_2_5_FLASH,
1007
+ GoogleAIModels.GEMINI_2_0_FLASH_001,
1008
1008
  ];
1009
1009
  case AIProviderName.VERTEX:
1010
1010
  return [
@@ -1013,8 +1013,6 @@ export class ProviderHealthChecker {
1013
1013
  GoogleAIModels.GEMINI_2_5_FLASH,
1014
1014
  GoogleAIModels.GEMINI_2_5_FLASH_LITE,
1015
1015
  GoogleAIModels.GEMINI_2_0_FLASH_001,
1016
- GoogleAIModels.GEMINI_1_5_PRO,
1017
- GoogleAIModels.GEMINI_1_5_FLASH,
1018
1016
  // Anthropic models (via vertexAnthropic provider)
1019
1017
  "claude-sonnet-4@20250514",
1020
1018
  "claude-opus-4@20250514",
@@ -1030,8 +1028,8 @@ export class ProviderHealthChecker {
1030
1028
  return [OpenAIModels.GPT_4O, OpenAIModels.GPT_4O_MINI, "gpt-35-turbo"];
1031
1029
  case AIProviderName.LITELLM:
1032
1030
  return [
1033
- "openai/gpt-4o-mini",
1034
- "anthropic/claude-3-haiku",
1031
+ "openai/gpt-5.4-mini",
1032
+ "anthropic/claude-haiku-4-5-20251001",
1035
1033
  "google/gemini-2.5-flash",
1036
1034
  ];
1037
1035
  case AIProviderName.OLLAMA: {
@@ -3,7 +3,6 @@
3
3
  * Helper functions for Redis storage operations
4
4
  */
5
5
  import { randomUUID } from "crypto";
6
- import { createClient } from "redis";
7
6
  import { logger } from "./logger.js";
8
7
  const SESSION_ONLY_PREFIX = "session-only:";
9
8
  /** Characters that end the authority (`user:pass@host:port`) of a URL in text. */
@@ -195,6 +194,9 @@ export async function createRedisClient(config) {
195
194
  if (config.password && !config.url) {
196
195
  clientOptions.password = config.password;
197
196
  }
197
+ // Imported here, not at the top: the redis client is ~590 modules, a quarter
198
+ // of everything importing the SDK loaded, and most callers never connect.
199
+ const { createClient } = await import("redis");
198
200
  // Create client with secured options
199
201
  const client = createClient(clientOptions);
200
202
  client.on("error", (err) => {
@@ -119,6 +119,15 @@ export function getClaudeMaxOutputTokens(model) {
119
119
  if (/sonnet[-_.]?4/.test(m) || /haiku[-_.]?4/.test(m)) {
120
120
  return 64000;
121
121
  }
122
+ // Claude 5.x family (Opus 5 / Opus 5.5 / Sonnet 5 / Fable 5.1 / Mythos):
123
+ // all confirmed at 128K standard max output on
124
+ // platform.claude.com/docs/en/about-claude/models/overview. The
125
+ // (?![0-9]) guard keeps this off dated 4.x ids like "opus-4-5-20251101"
126
+ // (Opus 4.5, not Opus 5), where the family word sits before the "4", not
127
+ // directly before this "5".
128
+ if (/(?:opus|sonnet|fable|mythos)[-_.]?5(?![0-9])/.test(m)) {
129
+ return 128000;
130
+ }
122
131
  // Claude 3.7 Sonnet supports 64K output.
123
132
  if (/3[-_.]?7[-_.]?sonnet/.test(m)) {
124
133
  return 64000;
@@ -277,8 +277,8 @@ export function createMultiJudgeWorkflow(modelCount, judgeCount) {
277
277
  },
278
278
  {
279
279
  provider: AIProviderName.GOOGLE_AI,
280
- model: "gemini-1.5-flash",
281
- label: "Gemini 1.5 Flash",
280
+ model: "gemini-2.5-flash",
281
+ label: "Gemini 2.5 Flash",
282
282
  weight: 0.8,
283
283
  temperature: 0.7,
284
284
  },