@juspay/neurolink 12.59.0 → 13.0.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 (143) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/dist/auth/anthropicOAuth.js +3 -3
  3. package/dist/auth/middleware/AuthMiddleware.d.ts +1 -1
  4. package/dist/auth/sessionManager.js +4 -4
  5. package/dist/auth/tokenStore.js +4 -4
  6. package/dist/browser/neurolink.min.js +474 -473
  7. package/dist/cli/commands/observability.d.ts +1 -6
  8. package/dist/cli/commands/observability.js +0 -106
  9. package/dist/cli/commands/proxy.js +9 -18
  10. package/dist/cli/commands/telemetry.d.ts +3 -13
  11. package/dist/cli/commands/telemetry.js +7 -354
  12. package/dist/cli/loop/session.js +1 -1
  13. package/dist/client/httpClient.js +1 -1
  14. package/dist/client/sseClient.js +1 -1
  15. package/dist/client/streamingClient.js +2 -2
  16. package/dist/client/wsClient.js +1 -1
  17. package/dist/constants/langfuse.d.ts +25 -0
  18. package/dist/constants/langfuse.js +38 -0
  19. package/dist/constants/telemetry.d.ts +20 -0
  20. package/dist/constants/telemetry.js +34 -0
  21. package/dist/core/baseProvider.d.ts +2 -1
  22. package/dist/core/baseProvider.js +4 -11
  23. package/dist/core/conversationMemoryManager.js +5 -8
  24. package/dist/core/factory.js +1 -1
  25. package/dist/core/geminiLoopAdapter.d.ts +8 -1
  26. package/dist/core/geminiLoopAdapter.js +25 -1
  27. package/dist/core/loopEngine.js +69 -40
  28. package/dist/core/modules/MessageBuilder.js +2 -2
  29. package/dist/core/modules/StreamHandler.js +2 -2
  30. package/dist/core/modules/TelemetryHandler.d.ts +2 -18
  31. package/dist/core/modules/TelemetryHandler.js +1 -66
  32. package/dist/core/modules/ToolsManager.js +40 -31
  33. package/dist/core/nativeGenerateLoop.js +78 -8
  34. package/dist/core/redisConversationMemoryManager.js +8 -15
  35. package/dist/core/toolExecutionGuards.js +1 -13
  36. package/dist/index.d.ts +5 -12
  37. package/dist/index.js +5 -37
  38. package/dist/mcp/batching/requestBatcher.js +1 -1
  39. package/dist/mcp/externalServerManager.js +3 -21
  40. package/dist/mcp/toolDiscoveryService.js +4 -16
  41. package/dist/mcp/toolRegistry.js +2 -5
  42. package/dist/memory/memoryRetrievalTools.js +1 -1
  43. package/dist/neurolink.d.ts +16 -27
  44. package/dist/neurolink.js +788 -792
  45. package/dist/observability/exporterSpanProcessor.d.ts +50 -0
  46. package/dist/observability/exporterSpanProcessor.js +181 -0
  47. package/dist/observability/exporters/langfuseExporter.d.ts +13 -34
  48. package/dist/observability/exporters/langfuseExporter.js +101 -154
  49. package/dist/observability/index.d.ts +1 -0
  50. package/dist/observability/index.js +1 -0
  51. package/dist/observability/langfuseMasking.d.ts +8 -0
  52. package/dist/observability/langfuseMasking.js +47 -0
  53. package/dist/observability/otlpEncoding.d.ts +20 -0
  54. package/dist/observability/otlpEncoding.js +85 -0
  55. package/dist/providers/amazonBedrock/client.js +142 -176
  56. package/dist/providers/amazonBedrock/loopAdapter.js +14 -1
  57. package/dist/providers/amazonSagemaker.js +118 -80
  58. package/dist/providers/anthropic/client.js +3 -52
  59. package/dist/providers/anthropic/loopAdapter.js +24 -1
  60. package/dist/providers/anthropic/rateLimitCapture.js +9 -12
  61. package/dist/providers/googleAiStudio/client.d.ts +4 -5
  62. package/dist/providers/googleAiStudio/client.js +38 -40
  63. package/dist/providers/googleVertex/client.d.ts +2 -7
  64. package/dist/providers/googleVertex/client.js +282 -378
  65. package/dist/providers/googleVertex/constants.js +2 -2
  66. package/dist/providers/litellm/client.d.ts +2 -9
  67. package/dist/providers/litellm/client.js +1 -63
  68. package/dist/providers/openAI/client.d.ts +1 -8
  69. package/dist/providers/openAI/client.js +0 -65
  70. package/dist/providers/openaiChatCompletionsBase.d.ts +1 -0
  71. package/dist/providers/openaiChatCompletionsBase.js +61 -4
  72. package/dist/providers/replicate.js +22 -4
  73. package/dist/proxy/codexUsage.js +7 -0
  74. package/dist/proxy/proxyFetch.js +19 -25
  75. package/dist/proxy/proxyTracer.d.ts +14 -7
  76. package/dist/proxy/proxyTracer.js +129 -83
  77. package/dist/proxy/proxyTranslationEngine.js +0 -1
  78. package/dist/proxy/requestLogger.d.ts +1 -1
  79. package/dist/proxy/requestLogger.js +39 -45
  80. package/dist/server/middleware/common.js +1 -1
  81. package/dist/server/routes/agentRoutes.js +4 -4
  82. package/dist/server/routes/claudeProxyRoutes.js +99 -196
  83. package/dist/server/routes/codexProxyRoutes.js +621 -591
  84. package/dist/server/routes/healthRoutes.js +6 -6
  85. package/dist/server/routes/mcpRoutes.js +7 -7
  86. package/dist/server/routes/memoryRoutes.js +7 -7
  87. package/dist/server/routes/openApiRoutes.js +3 -3
  88. package/dist/server/routes/toolRoutes.js +5 -5
  89. package/dist/server/routes/transcribeRoutes.js +1 -1
  90. package/dist/server/utils/validation.d.ts +3 -3
  91. package/dist/services/server/ai/observability/instrumentation.d.ts +34 -201
  92. package/dist/services/server/ai/observability/instrumentation.js +220 -1267
  93. package/dist/services/server/ai/observability/langfuseSetup.d.ts +14 -0
  94. package/dist/services/server/ai/observability/langfuseSetup.js +55 -0
  95. package/dist/services/server/ai/observability/otlpSetup.d.ts +16 -0
  96. package/dist/services/server/ai/observability/otlpSetup.js +58 -0
  97. package/dist/session/globalSessionState.js +0 -11
  98. package/dist/telemetry/attributes.d.ts +23 -46
  99. package/dist/telemetry/attributes.js +76 -162
  100. package/dist/telemetry/chatMessages.d.ts +13 -0
  101. package/dist/telemetry/chatMessages.js +59 -0
  102. package/dist/telemetry/index.d.ts +5 -19
  103. package/dist/telemetry/index.js +5 -26
  104. package/dist/telemetry/observation.d.ts +37 -0
  105. package/dist/telemetry/observation.js +112 -0
  106. package/dist/telemetry/observationNames.d.ts +18 -0
  107. package/dist/telemetry/observationNames.js +24 -0
  108. package/dist/telemetry/spanSanitizer.d.ts +39 -0
  109. package/dist/telemetry/spanSanitizer.js +92 -0
  110. package/dist/telemetry/spanSerialization.d.ts +13 -0
  111. package/dist/telemetry/spanSerialization.js +46 -0
  112. package/dist/telemetry/tracers.d.ts +37 -18
  113. package/dist/telemetry/tracers.js +97 -19
  114. package/dist/telemetry/withSpan.d.ts +47 -25
  115. package/dist/telemetry/withSpan.js +158 -71
  116. package/dist/types/cli.d.ts +0 -11
  117. package/dist/types/decision.d.ts +3 -0
  118. package/dist/types/exporter.d.ts +54 -0
  119. package/dist/types/exporter.js +0 -4
  120. package/dist/types/generate.d.ts +4 -1
  121. package/dist/types/isolatedAgent.d.ts +5 -4
  122. package/dist/types/loopEngine.d.ts +18 -28
  123. package/dist/types/observability.d.ts +127 -160
  124. package/dist/types/providers.d.ts +15 -0
  125. package/dist/types/proxy.d.ts +41 -2
  126. package/dist/utils/anthropicTraceSanitizer.d.ts +2 -2
  127. package/dist/utils/anthropicTraceSanitizer.js +3 -1
  128. package/dist/utils/conversationMemory.js +4 -4
  129. package/dist/utils/fileDetector.js +1 -1
  130. package/dist/utils/messageBuilder.js +1 -1
  131. package/dist/utils/noOutputSentinel.d.ts +4 -16
  132. package/dist/utils/noOutputSentinel.js +12 -28
  133. package/dist/utils/providerRetry.js +4 -4
  134. package/dist/voice/livekit/brain.js +1 -1
  135. package/dist/voice/livekit/realtimeMemory.d.ts +1 -1
  136. package/dist/voice/livekit/realtimeMemory.js +66 -1
  137. package/dist/voice/livekit/realtimeVoiceAgent.js +1 -1
  138. package/docs-site/static/search-index.json +41 -63
  139. package/package.json +5 -4
  140. package/dist/telemetry/telemetryService.d.ts +0 -79
  141. package/dist/telemetry/telemetryService.js +0 -572
  142. package/dist/utils/observabilityHelpers.d.ts +0 -27
  143. package/dist/utils/observabilityHelpers.js +0 -47
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Telemetry Handler Module
3
3
  *
4
- * Handles analytics, evaluation, performance metrics, and telemetry configuration.
4
+ * Handles analytics, evaluation and performance metrics.
5
5
  * Extracted from BaseProvider to follow Single Responsibility Principle.
6
6
  *
7
7
  * Responsibilities:
@@ -9,7 +9,6 @@
9
9
  * - Evaluation generation
10
10
  * - Performance metrics recording
11
11
  * - Cost calculation
12
- * - Telemetry configuration
13
12
  *
14
13
  * @module core/modules/TelemetryHandler
15
14
  */
@@ -17,9 +16,7 @@ import { nanoid } from "nanoid";
17
16
  import { extractTokenUsage } from "../../utils/tokenUtils.js";
18
17
  import { logger } from "../../utils/logger.js";
19
18
  import { recordProviderPerformanceFromMetrics } from "../evaluationProviders.js";
20
- import { TelemetryService } from "../../telemetry/telemetryService.js";
21
19
  import { calculateCost, hasPricing } from "../../utils/pricing.js";
22
- import { getLangfuseContext } from "../../services/server/ai/observability/instrumentation.js";
23
20
  /**
24
21
  * TelemetryHandler class - Handles analytics and telemetry for AI providers
25
22
  */
@@ -87,8 +84,6 @@ export class TelemetryHandler {
87
84
  cost: actualCost,
88
85
  success: true,
89
86
  });
90
- // Wire TelemetryService metrics so OTEL counters/histograms are populated
91
- TelemetryService.getInstance().recordAIRequest(this.providerName, this.modelName, totalTokens, responseTime, actualCost !== undefined && actualCost > 0 ? actualCost : undefined);
92
87
  logger.debug(`Performance recorded for ${this.providerName}`, {
93
88
  responseTime: `${responseTime}ms`,
94
89
  tokens: totalTokens,
@@ -99,26 +94,6 @@ export class TelemetryHandler {
99
94
  logger.warn("⚠️ Performance recording failed:", perfError);
100
95
  }
101
96
  }
102
- /**
103
- * Record a TTS synthesis failure without affecting the generation result.
104
- */
105
- recordTTSFailure(ttsProvider, error, latency) {
106
- try {
107
- const labels = {
108
- provider: ttsProvider,
109
- error_code: error.code,
110
- ...(error.retriable !== undefined
111
- ? { retriable: error.retriable.toString() }
112
- : {}),
113
- };
114
- const telemetry = TelemetryService.getInstance();
115
- telemetry.recordCustomMetric("tts_failures", 1, labels);
116
- telemetry.recordCustomHistogram("tts_failure_latency_ms", latency, labels);
117
- }
118
- catch (telemetryError) {
119
- logger.warn("TTS failure telemetry recording failed:", telemetryError);
120
- }
121
- }
122
97
  /**
123
98
  * Estimate cost only from known per-model rates. Unknown pricing remains
124
99
  * absent from cost metrics instead of using another model's default rate.
@@ -137,46 +112,6 @@ export class TelemetryHandler {
137
112
  return undefined;
138
113
  }
139
114
  }
140
- /**
141
- * Create telemetry configuration for Vercel AI SDK experimental_telemetry
142
- * This enables automatic OpenTelemetry tracing when telemetry is enabled
143
- */
144
- getTelemetryConfig(options, operationType = "stream") {
145
- // Check if telemetry is enabled via NeuroLink observability config
146
- if (!this.neurolink?.isTelemetryEnabled()) {
147
- return undefined;
148
- }
149
- const context = options.context;
150
- const langfuseContext = getLangfuseContext();
151
- const traceName = context?.traceName ?? langfuseContext?.traceName;
152
- const userId = context?.userId ?? langfuseContext?.userId;
153
- const functionId = traceName ? traceName : userId ? userId : "guest";
154
- const metadata = {
155
- ...(context?.metadata || {}),
156
- provider: this.providerName,
157
- model: this.modelName,
158
- toolsEnabled: !options.disableTools,
159
- neurolink: true,
160
- operationType,
161
- originalProvider: this.providerName,
162
- };
163
- // Add sessionId if available
164
- if ("sessionId" in options && options.sessionId) {
165
- const sessionId = options.sessionId;
166
- if (typeof sessionId === "string" ||
167
- typeof sessionId === "number" ||
168
- typeof sessionId === "boolean") {
169
- metadata.sessionId = sessionId;
170
- }
171
- }
172
- return {
173
- isEnabled: true,
174
- functionId,
175
- metadata,
176
- recordInputs: process.env.NEUROLINK_RECORD_INPUTS?.toLowerCase() !== "false",
177
- recordOutputs: true,
178
- };
179
- }
180
115
  /**
181
116
  * Handle tool execution storage if available
182
117
  */
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { createToolEventPayload } from "../toolEvents.js";
3
- import { tracers, ATTR, withSpan } from "../../telemetry/index.js";
3
+ import { tracers, ATTR, observe, toolSpanName, withSpan, } from "../../telemetry/index.js";
4
4
  import { SpanStatusCode } from "@opentelemetry/api";
5
5
  import { logger } from "../../utils/logger.js";
6
6
  import { TOOL_EVENTS_WRAPPED } from "../../types/index.js";
@@ -504,7 +504,6 @@ export class ToolsManager {
504
504
  return execute;
505
505
  }
506
506
  const wrapped = async (params, execOptions) => {
507
- const startTime = Date.now();
508
507
  // The loop's execute options carry the model's tool_call id, which is
509
508
  // what lets a listener pair concurrent same-name start/end events.
510
509
  const toolCallId = execOptions && typeof execOptions === "object"
@@ -513,33 +512,47 @@ export class ToolsManager {
513
512
  const idField = typeof toolCallId === "string" && toolCallId.length > 0
514
513
  ? { toolCallId }
515
514
  : {};
516
- this.emitToolEvent("tool:start", toolName, {
517
- input: params,
518
- ...idField,
519
- });
520
- let result;
521
- try {
522
- result = await execute(params, execOptions);
523
- }
524
- catch (error) {
525
- const errorMsg = error instanceof Error ? error.message : String(error);
515
+ return withSpan({
516
+ name: toolSpanName(toolName),
517
+ tracer: tracers.mcp,
518
+ attributes: { "tool.name": toolName, ...idField },
519
+ }, async (span) => {
520
+ observe(span, "tool", { input: params });
521
+ const startTime = Date.now();
522
+ this.emitToolEvent("tool:start", toolName, {
523
+ input: params,
524
+ ...idField,
525
+ });
526
+ let result;
527
+ try {
528
+ result = await execute(params, execOptions);
529
+ }
530
+ catch (error) {
531
+ const errorMsg = error instanceof Error ? error.message : String(error);
532
+ this.emitToolEvent("tool:end", toolName, {
533
+ error: errorMsg,
534
+ success: false,
535
+ responseTime: Date.now() - startTime,
536
+ ...idField,
537
+ });
538
+ throw error;
539
+ }
540
+ const isErrorText = extractIsErrorText(result);
541
+ observe(span, "tool", {
542
+ output: result,
543
+ ...(isErrorText !== undefined
544
+ ? { level: "ERROR", statusMessage: isErrorText }
545
+ : {}),
546
+ });
526
547
  this.emitToolEvent("tool:end", toolName, {
527
- error: errorMsg,
528
- success: false,
548
+ result,
549
+ success: isErrorText === undefined,
550
+ ...(isErrorText !== undefined ? { error: isErrorText } : {}),
529
551
  responseTime: Date.now() - startTime,
530
552
  ...idField,
531
553
  });
532
- throw error;
533
- }
534
- const isErrorText = extractIsErrorText(result);
535
- this.emitToolEvent("tool:end", toolName, {
536
- result,
537
- success: isErrorText === undefined,
538
- ...(isErrorText !== undefined ? { error: isErrorText } : {}),
539
- responseTime: Date.now() - startTime,
540
- ...idField,
554
+ return result;
541
555
  });
542
- return result;
543
556
  };
544
557
  this.eventWrappedExecutes.add(wrapped);
545
558
  return wrapped;
@@ -592,7 +605,7 @@ export class ToolsManager {
592
605
  * @param functionTag - Function name for logging
593
606
  */
594
607
  setupToolExecutor(sdk, functionTag) {
595
- const span = tracers.sdk.startSpan("neurolink.tools.register", {
608
+ const span = tracers.sdk.startSpan("tools.register", {
596
609
  attributes: {
597
610
  [ATTR.NL_PROVIDER]: this.providerName,
598
611
  "tools.custom_count": sdk.customTools.size,
@@ -632,7 +645,7 @@ export class ToolsManager {
632
645
  */
633
646
  async getAllTools() {
634
647
  return withSpan({
635
- name: "neurolink.tools.getAll",
648
+ name: "tools.getAll",
636
649
  tracer: tracers.sdk,
637
650
  attributes: {
638
651
  [ATTR.NL_PROVIDER]: this.providerName,
@@ -843,14 +856,10 @@ export class ToolsManager {
843
856
  description: toolInfo.description || `Tool ${toolName}`,
844
857
  inputSchema: finalSchema, // AI SDK v6 uses inputSchema (not parameters)
845
858
  execute: async (params) => {
846
- const customToolSpan = tracers.sdk.startSpan("neurolink.tools.execute_custom", {
859
+ const customToolSpan = tracers.sdk.startSpan("tools.execute_custom", {
847
860
  attributes: {
848
861
  "tool.name": toolName,
849
862
  "tool.type": "custom",
850
- // Curator P1-3: pure wrapper — duplicates the AI SDK's
851
- // ai.toolCall observation in Langfuse. Keep the OTel span
852
- // for internal metrics; filter from Langfuse export.
853
- "langfuse.internal": true,
854
863
  },
855
864
  });
856
865
  const startTime = Date.now();
@@ -13,6 +13,8 @@
13
13
  * share this: the v3 result shape (`content` parts, `finishReason`, `usage`) is
14
14
  * the same across Anthropic, the OpenAI-compatible family and SageMaker.
15
15
  */
16
+ import { observeGeneration } from "../telemetry/observation.js";
17
+ import { StepSpans, withGeneration } from "../telemetry/withSpan.js";
16
18
  import { logger } from "../utils/logger.js";
17
19
  import { guardToolExecutor } from "./toolExecutionGuards.js";
18
20
  const DEFAULT_TOOL_CHOICE_STEPS = 1;
@@ -384,7 +386,76 @@ export const appendJsonSchemaInstruction = (conversation, schema) => {
384
386
  : instruction;
385
387
  return conversation.map((message, index) => index === lastSystemIndex ? { ...message, content } : message);
386
388
  };
389
+ function describeV3Response(res) {
390
+ const parts = asParts(res.content);
391
+ const usage = res.usage;
392
+ const finish = res.finishReason;
393
+ const response = res.response;
394
+ const toolCalls = parts
395
+ .filter((p) => p.type === "tool-call")
396
+ .map((p) => ({
397
+ id: p.toolCallId,
398
+ name: p.toolName,
399
+ args: p.input,
400
+ }));
401
+ const reasoning = parts
402
+ .filter((p) => p.type === "reasoning" && typeof p.text === "string")
403
+ .map((p) => p.text)
404
+ .join("");
405
+ return {
406
+ responseModel: typeof response?.modelId === "string" ? response.modelId : undefined,
407
+ output: {
408
+ text: parts
409
+ .filter((p) => p.type === "text" && typeof p.text === "string")
410
+ .map((p) => p.text)
411
+ .join(""),
412
+ ...(reasoning ? { reasoning } : {}),
413
+ ...(toolCalls.length > 0 ? { toolCalls } : {}),
414
+ },
415
+ usage: usage
416
+ ? {
417
+ inputTokens: usage.inputTokens?.noCache ?? usage.inputTokens?.total,
418
+ outputTokens: usage.outputTokens?.total,
419
+ cacheReadTokens: usage.inputTokens?.cacheRead,
420
+ cacheCreationTokens: usage.inputTokens?.cacheWrite,
421
+ reasoningTokens: usage.outputTokens?.reasoning,
422
+ }
423
+ : undefined,
424
+ finishReason: typeof finish === "string"
425
+ ? finish
426
+ : finish?.unified,
427
+ };
428
+ }
429
+ function generateObservedStep(args, request) {
430
+ return withGeneration({
431
+ provider: args.provider,
432
+ model: args.modelId,
433
+ modelParameters: {
434
+ temperature: args.temperature,
435
+ maxTokens: args.maxOutputTokens,
436
+ },
437
+ input: { messages: args.conversation },
438
+ }, async (span) => {
439
+ const res = await args.runStep(() => args.doGenerate(request));
440
+ observeGeneration(span, {
441
+ provider: args.provider,
442
+ model: args.modelId,
443
+ ...describeV3Response(res),
444
+ aborted: args.abortSignal?.aborted,
445
+ });
446
+ return res;
447
+ });
448
+ }
387
449
  export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
450
+ const stepSpans = new StepSpans();
451
+ try {
452
+ return await runGenerateSteps(args, toolExecutionSummaries, stepSpans);
453
+ }
454
+ finally {
455
+ stepSpans.end();
456
+ }
457
+ }
458
+ async function runGenerateSteps(args, toolExecutionSummaries, stepSpans) {
388
459
  const toolsUsed = [];
389
460
  let text = "";
390
461
  let reasoning = "";
@@ -411,6 +482,7 @@ export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
411
482
  const stepRecords = [];
412
483
  for (let step = 0; step < args.maxSteps; step++) {
413
484
  steps = step + 1;
485
+ stepSpans.begin(steps);
414
486
  // The forced-choice window and the caller's hook are resolved BEFORE the
415
487
  // re-ask override below: the re-ask has to win, since its whole point is
416
488
  // a request with tools disabled.
@@ -445,7 +517,7 @@ export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
445
517
  if (guarded) {
446
518
  args.conversation.splice(0, args.conversation.length, ...guarded);
447
519
  }
448
- const runThisStep = () => args.runStep(() => args.doGenerate({
520
+ const runThisStep = () => generateObservedStep(args, {
449
521
  prompt: args.conversation,
450
522
  ...(args.tools && args.tools.length > 0 ? { tools: args.tools } : {}),
451
523
  // The v3 call option is an OBJECT — `{ type: "none" }`. Passing the
@@ -459,9 +531,7 @@ export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
459
531
  : wireToolChoice !== undefined
460
532
  ? { toolChoice: wireToolChoice }
461
533
  : {}),
462
- ...(args.responseFormat
463
- ? { responseFormat: args.responseFormat }
464
- : {}),
534
+ ...(args.responseFormat ? { responseFormat: args.responseFormat } : {}),
465
535
  ...(args.providerOptions
466
536
  ? { providerOptions: args.providerOptions }
467
537
  : {}),
@@ -472,10 +542,10 @@ export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
472
542
  ? { temperature: args.temperature }
473
543
  : {}),
474
544
  ...(args.abortSignal ? { abortSignal: args.abortSignal } : {}),
475
- }));
545
+ });
476
546
  let res;
477
547
  try {
478
- res = await runThisStep();
548
+ res = await stepSpans.run(runThisStep);
479
549
  }
480
550
  catch (stepError) {
481
551
  // Ported from GenerationHandler.recoverEmptyToolCallsFinish's catch: a
@@ -650,11 +720,11 @@ export async function runNativeGenerateLoop(args, toolExecutionSummaries) {
650
720
  ? { toolTimeoutMs: args.toolTimeoutMs }
651
721
  : {}),
652
722
  });
653
- output = await guarded(input, {
723
+ output = await stepSpans.run(() => guarded(input, {
654
724
  toolCallId: id,
655
725
  messages: [],
656
726
  ...(args.abortSignal ? { abortSignal: args.abortSignal } : {}),
657
- });
727
+ }));
658
728
  toolsUsed.push(name);
659
729
  }
660
730
  catch (err) {
@@ -2,7 +2,7 @@
2
2
  * Redis Conversation Memory Manager for NeuroLink
3
3
  * Redis-based implementation of conversation storage with same interface as ConversationMemoryManager
4
4
  */
5
- import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
5
+ import { context, SpanKind, SpanStatusCode } from "@opentelemetry/api";
6
6
  import { tracers } from "../telemetry/tracers.js";
7
7
  import { randomUUID } from "crypto";
8
8
  import { MESSAGES_PER_TURN } from "../config/conversationMemory.js";
@@ -13,7 +13,6 @@ import { NeuroLink } from "../neurolink.js";
13
13
  import { ConversationMemoryError } from "../types/index.js";
14
14
  import { withTimeout } from "../utils/errorHandling.js";
15
15
  import { buildContextFromPointer, getEffectiveTokenThreshold, } from "../utils/conversationMemory.js";
16
- import { runWithCurrentLangfuseContext } from "../services/server/ai/observability/instrumentation.js";
17
16
  import { logger } from "../utils/logger.js";
18
17
  import { assistantTurnIdentity, isTurnAlreadyStored, turnMessageFields, userTurnIdentity, } from "../utils/conversationTurnIdentity.js";
19
18
  import { extractIsErrorText } from "../utils/toolResultStatus.js";
@@ -57,7 +56,7 @@ export class RedisConversationMemoryManager {
57
56
  logger.debug("[RedisConversationMemoryManager] Already initialized, skipping");
58
57
  return;
59
58
  }
60
- await redisTracer.startActiveSpan("neurolink.memory.initialize", {
59
+ await redisTracer.startActiveSpan("memory.initialize", {
61
60
  kind: SpanKind.CLIENT,
62
61
  attributes: {
63
62
  "redis.host": this.redisConfig.host,
@@ -135,7 +134,7 @@ export class RedisConversationMemoryManager {
135
134
  return undefined;
136
135
  }
137
136
  const redisClient = this.redisClient;
138
- return redisTracer.startActiveSpan("neurolink.memory.getSession", { kind: SpanKind.CLIENT, attributes: { "session.id": sessionId } }, async (span) => {
137
+ return redisTracer.startActiveSpan("memory.getSession", { kind: SpanKind.CLIENT, attributes: { "session.id": sessionId } }, async (span) => {
139
138
  if (userId) {
140
139
  span.setAttribute("user.id", userId);
141
140
  }
@@ -380,7 +379,7 @@ export class RedisConversationMemoryManager {
380
379
  });
381
380
  await this.ensureInitialized();
382
381
  // NLK-GAP-012: Add span for storeTurn CRUD operation
383
- return redisTracer.startActiveSpan("neurolink.memory.storeTurn", {
382
+ return redisTracer.startActiveSpan("memory.storeTurn", {
384
383
  kind: SpanKind.CLIENT,
385
384
  attributes: {
386
385
  "session.id": options.sessionId,
@@ -407,10 +406,7 @@ export class RedisConversationMemoryManager {
407
406
  const normalizedUserId = options.userId || "randomUser";
408
407
  if (!conversation) {
409
408
  const titleGenerationKey = `${options.sessionId}:${normalizedUserId}`;
410
- // Capture the current Langfuse ALS context before setImmediate,
411
- // which breaks automatic AsyncLocalStorage propagation and would
412
- // otherwise cause orphaned traces in Langfuse.
413
- const generateTitleWithContext = runWithCurrentLangfuseContext(async () => {
409
+ const generateTitleWithContext = context.bind(context.active(), async () => {
414
410
  if (this.titleGenerationInProgress.has(titleGenerationKey)) {
415
411
  return;
416
412
  }
@@ -509,10 +505,7 @@ export class RedisConversationMemoryManager {
509
505
  const summarizationKey = `${options.sessionId}:${normalizedUserId}`;
510
506
  // Only trigger summarization if not already in progress for this session
511
507
  if (!this.summarizationInProgress.has(summarizationKey)) {
512
- // Capture the current Langfuse ALS context before setImmediate,
513
- // which breaks automatic AsyncLocalStorage propagation and would
514
- // otherwise cause orphaned traces in Langfuse.
515
- const summarizeWithContext = runWithCurrentLangfuseContext(async () => {
508
+ const summarizeWithContext = context.bind(context.active(), async () => {
516
509
  try {
517
510
  await this.checkAndSummarize(conversation, tokenThreshold, options.sessionId, options.userId, options.requestId);
518
511
  }
@@ -761,7 +754,7 @@ export class RedisConversationMemoryManager {
761
754
  }
762
755
  const redisClient = this.redisClient;
763
756
  // NLK-GAP-012: Add span for buildContext CRUD operation
764
- return redisTracer.startActiveSpan("neurolink.memory.buildContext", {
757
+ return redisTracer.startActiveSpan("memory.buildContext", {
765
758
  kind: SpanKind.CLIENT,
766
759
  attributes: {
767
760
  "session.id": sessionId,
@@ -1238,7 +1231,7 @@ User message: "${userMessage}"`;
1238
1231
  }
1239
1232
  const redisClient = this.redisClient;
1240
1233
  // NLK-GAP-012: Add span for clearSession CRUD operation
1241
- return redisTracer.startActiveSpan("neurolink.memory.clear", {
1234
+ return redisTracer.startActiveSpan("memory.clear", {
1242
1235
  kind: SpanKind.CLIENT,
1243
1236
  attributes: {
1244
1237
  "session.id": sessionId,
@@ -29,19 +29,7 @@ import { raceWithAbort, withTimeout } from "../utils/async/index.js";
29
29
  */
30
30
  export function guardToolExecutor(name, execute, guards) {
31
31
  return async (args, opts) => {
32
- const invoke = () => Promise.resolve(execute(args, opts));
33
- // The span wraps the CALL, not the guard: a timeout or an abort is a fact
34
- // about this tool invocation and belongs inside its observation.
35
- const wrapInSpan = guards.withToolSpan;
36
- const toolCallId = typeof opts === "object" &&
37
- opts !== null &&
38
- "toolCallId" in opts &&
39
- typeof opts.toolCallId === "string"
40
- ? opts.toolCallId
41
- : undefined;
42
- const call = wrapInSpan
43
- ? () => wrapInSpan(name, invoke, { args, toolCallId })
44
- : invoke;
32
+ const call = () => Promise.resolve(execute(args, opts));
45
33
  guards.onProgress?.();
46
34
  try {
47
35
  const raced = guards.abortSignal
package/dist/index.d.ts CHANGED
@@ -84,15 +84,16 @@ export { HITLManager } from "./hitl/hitlManager.js";
84
84
  export { ProviderRegistry } from "./factories/providerRegistry.js";
85
85
  import { NeuroLink } from "./neurolink.js";
86
86
  export { NeuroLink };
87
- export { buildObservabilityConfigFromEnv } from "./utils/observabilityHelpers.js";
88
- import { createContextEnricher, flushOpenTelemetry, getLangfuseContext, getLangfuseHealthStatus, getLangfuseSpanProcessor, getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isLangfuseInternalSpan, isOpenTelemetryInitialized, isUsingExternalTracerProvider, langfuseShouldExportSpan, runWithCurrentLangfuseContext, setLangfuseContext, shutdownOpenTelemetry } from "./services/server/ai/observability/instrumentation.js";
89
- export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized, getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider, isLangfuseInternalSpan, langfuseShouldExportSpan, getLangfuseContext, getTracer, runWithCurrentLangfuseContext, };
87
+ import { flushOpenTelemetry, getLangfuseHealthStatus, isOpenTelemetryInitialized, setLangfuseContext, shutdownOpenTelemetry } from "./services/server/ai/observability/instrumentation.js";
88
+ export { shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, isOpenTelemetryInitialized, };
90
89
  export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
91
90
  export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
92
91
  export { MiddlewareFactory } from "./middleware/factory.js";
93
92
  export { tool, jsonSchema, stepCountIs } from "./utils/tool.js";
94
93
  export { ExporterRegistry } from "./observability/exporterRegistry.js";
95
94
  export { NoOpExporter } from "./observability/exporters/baseExporter.js";
95
+ export { createExporterSpanProcessor, ExporterSpanProcessor, } from "./observability/exporterSpanProcessor.js";
96
+ export { LangfuseExporter } from "./observability/exporters/langfuseExporter.js";
96
97
  export { getMetricsAggregator, MetricsAggregator, resetMetricsAggregator, } from "./observability/metricsAggregator.js";
97
98
  export { AlwaysSampler, NeverSampler, } from "./observability/sampling/samplers.js";
98
99
  export { TokenTracker } from "./observability/tokenTracker.js";
@@ -241,14 +242,6 @@ export { getPoolStats } from "./utils/redis.js";
241
242
  export { ToolRoutingCache } from "./core/toolRoutingCache.js";
242
243
  export { resolveToolRoutingExclusions, buildToolRoutingCatalog, buildRoutingQueryFromHistory, DEFAULT_ROUTER_PROMPT_PREFIX, } from "./core/toolRouting.js";
243
244
  export { cosineSimilarity, ToolEmbeddingIndex, selectRelevantToolNames, } from "./core/toolRoutingEmbedding.js";
244
- export declare function initializeTelemetry(): Promise<boolean>;
245
- export declare function getTelemetryStatus(): Promise<{
246
- enabled: boolean;
247
- initialized: boolean;
248
- endpoint?: string;
249
- service?: string;
250
- version?: string;
251
- }>;
252
245
  /**
253
246
  * Evaluation System Exports
254
247
  *
@@ -281,7 +274,7 @@ export declare function getTelemetryStatus(): Promise<{
281
274
  * });
282
275
  * ```
283
276
  */
284
- export { Evaluator, EvaluationAggregator, EvaluatorFactory, getEvaluatorFactory, getEvaluatorRegistry, evaluationErrors, isRetryableEvaluationError, isEvaluationError, createEvaluationFailedError, createParseError, createStrategyNotFoundError, createProviderError, createMaxRetriesExceededError, createBatchEvaluationError, createConfigurationError, contextToErrorContext, createLangfuseAdapter, createMockLangfuseClient, startLangfuseAdapter, createConsoleLoggerHook, createMetricsCollectorHook, ObservabilityHooks, observabilityHooks, pipelineToSpanAttributes, scorerToSpanAttributes, createAndInitializePipeline, createPipeline, EvaluationPipeline, PipelineBuilder, Pipelines, CODE_GENERATION_PIPELINE, COMPREHENSIVE_PIPELINE, CUSTOMER_SUPPORT_PIPELINE, getPreset, getPresetNames, MINIMAL_PIPELINE, QUALITY_PIPELINE, RAG_PIPELINE, SAFETY_PIPELINE, SUMMARIZATION_PIPELINE, BatchStrategy, createBatchStrategy, evaluateBatch, streamBatchEvaluation, createSamplingStrategy, DEFAULT_SAMPLING_CONFIG, SamplingStrategies, SamplingStrategy, createMetricsCollector, globalMetricsCollector, MetricsCollector, createReportGenerator, ReportGenerator, Reports, BaseScorer, DEFAULT_SCORE_SCALE as EVAL_DEFAULT_SCORE_SCALE, composeScorers, createConditionalScorer, createFunctionScorer, createInvertedScorer, createKeywordScorer, createRegexScorer, createScorerMetadata, createSimpleLengthScorer, AnswerRelevancyScorer, createAnswerRelevancyScorer, BaseLLMScorer, DEFAULT_LLM_SCORER_CONFIG, BiasDetectionScorer, createBiasDetectionScorer, ContextPrecisionScorer, createContextPrecisionScorer, ContextRelevancyScorer, createContextRelevancyScorer, createFaithfulnessScorer, FaithfulnessScorer, createHallucinationScorer, HallucinationScorer, createPromptAlignmentScorer, PromptAlignmentScorer, createSummarizationScorer, SummarizationScorer, createToneConsistencyScorer, ToneConsistencyScorer, createToxicityScorer, ToxicityScorer, BaseRuleScorer, DEFAULT_RULE_SCORER_CONFIG, createContentSimilarityScorer, createFormatScorer, FormatScorerPresets, createKeywordCoverageScorer, createLengthScorer, LengthScorerPresets, ScorerBuilder, Scorers, ScorerRegistry, RAGASEvaluator, RetryManager, } from "./evaluation/index.js";
277
+ export { Evaluator, EvaluationAggregator, EvaluatorFactory, getEvaluatorFactory, getEvaluatorRegistry, evaluationErrors, isRetryableEvaluationError, isEvaluationError, createEvaluationFailedError, createParseError, createStrategyNotFoundError, createProviderError, createMaxRetriesExceededError, createBatchEvaluationError, createConfigurationError, contextToErrorContext, createLangfuseAdapter, startLangfuseAdapter, createConsoleLoggerHook, createMetricsCollectorHook, ObservabilityHooks, observabilityHooks, pipelineToSpanAttributes, scorerToSpanAttributes, createAndInitializePipeline, createPipeline, EvaluationPipeline, PipelineBuilder, Pipelines, CODE_GENERATION_PIPELINE, COMPREHENSIVE_PIPELINE, CUSTOMER_SUPPORT_PIPELINE, getPreset, getPresetNames, MINIMAL_PIPELINE, QUALITY_PIPELINE, RAG_PIPELINE, SAFETY_PIPELINE, SUMMARIZATION_PIPELINE, BatchStrategy, createBatchStrategy, evaluateBatch, streamBatchEvaluation, createSamplingStrategy, DEFAULT_SAMPLING_CONFIG, SamplingStrategies, SamplingStrategy, createMetricsCollector, globalMetricsCollector, MetricsCollector, createReportGenerator, ReportGenerator, Reports, BaseScorer, DEFAULT_SCORE_SCALE as EVAL_DEFAULT_SCORE_SCALE, composeScorers, createConditionalScorer, createFunctionScorer, createInvertedScorer, createKeywordScorer, createRegexScorer, createScorerMetadata, createSimpleLengthScorer, AnswerRelevancyScorer, createAnswerRelevancyScorer, BaseLLMScorer, DEFAULT_LLM_SCORER_CONFIG, BiasDetectionScorer, createBiasDetectionScorer, ContextPrecisionScorer, createContextPrecisionScorer, ContextRelevancyScorer, createContextRelevancyScorer, createFaithfulnessScorer, FaithfulnessScorer, createHallucinationScorer, HallucinationScorer, createPromptAlignmentScorer, PromptAlignmentScorer, createSummarizationScorer, SummarizationScorer, createToneConsistencyScorer, ToneConsistencyScorer, createToxicityScorer, ToxicityScorer, BaseRuleScorer, DEFAULT_RULE_SCORER_CONFIG, createContentSimilarityScorer, createFormatScorer, FormatScorerPresets, createKeywordCoverageScorer, createLengthScorer, LengthScorerPresets, ScorerBuilder, Scorers, ScorerRegistry, RAGASEvaluator, RetryManager, } from "./evaluation/index.js";
285
278
  /**
286
279
  * Legacy generateText function for backward compatibility.
287
280
  *
package/dist/index.js CHANGED
@@ -172,23 +172,8 @@ export { ProviderRegistry } from "./factories/providerRegistry.js";
172
172
  import { NeuroLink } from "./neurolink.js";
173
173
  export { NeuroLink };
174
174
  // Observability configuration types
175
- export { buildObservabilityConfigFromEnv } from "./utils/observabilityHelpers.js";
176
- import { createContextEnricher, flushOpenTelemetry,
177
- // Enhanced context and tracing
178
- getLangfuseContext, getLangfuseHealthStatus, getLangfuseSpanProcessor,
179
- // NEW EXPORTS - External TracerProvider Support
180
- getSpanProcessors, getTracer, getTracerProvider, initializeOpenTelemetry, isLangfuseInternalSpan, isOpenTelemetryInitialized, isUsingExternalTracerProvider, langfuseShouldExportSpan, runWithCurrentLangfuseContext, setLangfuseContext, shutdownOpenTelemetry, } from "./services/server/ai/observability/instrumentation.js";
181
- import { getTelemetryStatus as getStatus, initializeTelemetry as init, } from "./telemetry/index.js";
182
- export { initializeOpenTelemetry, shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, getLangfuseSpanProcessor, getTracerProvider, isOpenTelemetryInitialized,
183
- // NEW EXPORTS - External TracerProvider Support
184
- getSpanProcessors, createContextEnricher, isUsingExternalTracerProvider,
185
- // Host-processor filter helpers — reuse NeuroLink's internal-span filtering
186
- // when the host app registers its own LangfuseSpanProcessor.
187
- isLangfuseInternalSpan, langfuseShouldExportSpan,
188
- // Enhanced context and tracing
189
- getLangfuseContext, getTracer,
190
- // ALS context propagation helper
191
- runWithCurrentLangfuseContext, };
175
+ import { flushOpenTelemetry, getLangfuseHealthStatus, isOpenTelemetryInitialized, setLangfuseContext, shutdownOpenTelemetry, } from "./services/server/ai/observability/instrumentation.js";
176
+ export { shutdownOpenTelemetry, flushOpenTelemetry, getLangfuseHealthStatus, setLangfuseContext, isOpenTelemetryInitialized, };
192
177
  // Analytics Middleware exports
193
178
  export { clearAnalyticsMetrics, createAnalyticsMiddleware, getAnalyticsMetrics, } from "./middleware/builtin/analytics.js";
194
179
  export { createLifecycleMiddleware } from "./middleware/builtin/lifecycle.js";
@@ -199,6 +184,8 @@ export { MiddlewareFactory } from "./middleware/factory.js";
199
184
  export { tool, jsonSchema, stepCountIs } from "./utils/tool.js";
200
185
  export { ExporterRegistry } from "./observability/exporterRegistry.js";
201
186
  export { NoOpExporter } from "./observability/exporters/baseExporter.js";
187
+ export { createExporterSpanProcessor, ExporterSpanProcessor, } from "./observability/exporterSpanProcessor.js";
188
+ export { LangfuseExporter } from "./observability/exporters/langfuseExporter.js";
202
189
  // Observability modules and types
203
190
  export { getMetricsAggregator, MetricsAggregator, resetMetricsAggregator, } from "./observability/metricsAggregator.js";
204
191
  export { AlwaysSampler, NeverSampler, } from "./observability/sampling/samplers.js";
@@ -418,25 +405,6 @@ export { resolveToolRoutingExclusions, buildToolRoutingCatalog, buildRoutingQuer
418
405
  // Pre-call tool routing — embedding fast-path (ITEM B: L2 semantic retrieval)
419
406
  export { cosineSimilarity, ToolEmbeddingIndex, selectRelevantToolNames, } from "./core/toolRoutingEmbedding.js";
420
407
  // ============================================================================
421
- // REAL-TIME SERVICES & TELEMETRY - Enterprise Platform Features
422
- // ============================================================================
423
- // Real-time Services (Phase 1) - Basic SSE functionality only
424
- // export { createEnhancedChatService } from './chat/index.js';
425
- // export type * from './services/types.js';
426
- // Optional Telemetry (Phase 2) - Telemetry service initialization
427
- export async function initializeTelemetry() {
428
- try {
429
- const result = await init();
430
- return !!result;
431
- }
432
- catch {
433
- return false;
434
- }
435
- }
436
- export async function getTelemetryStatus() {
437
- return getStatus();
438
- }
439
- // ============================================================================
440
408
  // EVALUATION SYSTEM - Comprehensive Response Evaluation & Scoring
441
409
  // ============================================================================
442
410
  /**
@@ -479,7 +447,7 @@ EvaluationAggregator, EvaluatorFactory, getEvaluatorFactory, getEvaluatorRegistr
479
447
  // Error utilities
480
448
  evaluationErrors, isRetryableEvaluationError, isEvaluationError, createEvaluationFailedError, createParseError, createStrategyNotFoundError, createProviderError, createMaxRetriesExceededError, createBatchEvaluationError, createConfigurationError, contextToErrorContext,
481
449
  // Hooks
482
- createLangfuseAdapter, createMockLangfuseClient, startLangfuseAdapter, createConsoleLoggerHook, createMetricsCollectorHook, ObservabilityHooks, observabilityHooks, pipelineToSpanAttributes, scorerToSpanAttributes,
450
+ createLangfuseAdapter, startLangfuseAdapter, createConsoleLoggerHook, createMetricsCollectorHook, ObservabilityHooks, observabilityHooks, pipelineToSpanAttributes, scorerToSpanAttributes,
483
451
  // Pipeline
484
452
  createAndInitializePipeline, createPipeline, EvaluationPipeline, PipelineBuilder, Pipelines, CODE_GENERATION_PIPELINE, COMPREHENSIVE_PIPELINE, CUSTOMER_SUPPORT_PIPELINE, getPreset, getPresetNames, MINIMAL_PIPELINE, QUALITY_PIPELINE, RAG_PIPELINE, SAFETY_PIPELINE, SUMMARIZATION_PIPELINE,
485
453
  // Strategies
@@ -220,7 +220,7 @@ export class RequestBatcher extends EventEmitter {
220
220
  this.activeBatches++;
221
221
  this.emit("batchStarted", { batchId, size: batchRequests.length });
222
222
  await withSpan({
223
- name: "neurolink.mcp.batch.execute",
223
+ name: "mcp.batch.execute",
224
224
  tracer: tracers.mcp,
225
225
  attributes: {
226
226
  "mcp.batch.id": batchId,