@juspay/neurolink 9.15.0 → 9.16.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 (193) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/adapters/video/videoAnalyzer.d.ts +1 -1
  3. package/dist/adapters/video/videoAnalyzer.js +10 -8
  4. package/dist/cli/commands/setup-anthropic.js +1 -14
  5. package/dist/cli/commands/setup-azure.js +1 -12
  6. package/dist/cli/commands/setup-bedrock.js +1 -9
  7. package/dist/cli/commands/setup-google-ai.js +1 -12
  8. package/dist/cli/commands/setup-openai.js +1 -14
  9. package/dist/cli/commands/workflow.d.ts +27 -0
  10. package/dist/cli/commands/workflow.js +216 -0
  11. package/dist/cli/factories/commandFactory.js +79 -20
  12. package/dist/cli/index.js +0 -1
  13. package/dist/cli/parser.js +4 -1
  14. package/dist/cli/utils/maskCredential.d.ts +11 -0
  15. package/dist/cli/utils/maskCredential.js +23 -0
  16. package/dist/constants/contextWindows.js +107 -16
  17. package/dist/constants/enums.d.ts +99 -15
  18. package/dist/constants/enums.js +152 -22
  19. package/dist/context/budgetChecker.js +1 -1
  20. package/dist/context/contextCompactor.js +31 -4
  21. package/dist/context/emergencyTruncation.d.ts +21 -0
  22. package/dist/context/emergencyTruncation.js +88 -0
  23. package/dist/context/errorDetection.d.ts +16 -0
  24. package/dist/context/errorDetection.js +48 -1
  25. package/dist/context/errors.d.ts +19 -0
  26. package/dist/context/errors.js +21 -0
  27. package/dist/context/stages/slidingWindowTruncator.d.ts +6 -0
  28. package/dist/context/stages/slidingWindowTruncator.js +159 -24
  29. package/dist/core/baseProvider.js +306 -200
  30. package/dist/core/conversationMemoryManager.js +104 -61
  31. package/dist/core/evaluationProviders.js +16 -33
  32. package/dist/core/factory.js +237 -164
  33. package/dist/core/modules/GenerationHandler.js +175 -116
  34. package/dist/core/modules/MessageBuilder.js +222 -170
  35. package/dist/core/modules/StreamHandler.d.ts +1 -0
  36. package/dist/core/modules/StreamHandler.js +95 -27
  37. package/dist/core/modules/TelemetryHandler.d.ts +10 -1
  38. package/dist/core/modules/TelemetryHandler.js +25 -7
  39. package/dist/core/modules/ToolsManager.js +115 -191
  40. package/dist/core/redisConversationMemoryManager.js +418 -282
  41. package/dist/factories/providerRegistry.d.ts +5 -0
  42. package/dist/factories/providerRegistry.js +20 -2
  43. package/dist/index.d.ts +2 -2
  44. package/dist/index.js +4 -2
  45. package/dist/lib/adapters/video/videoAnalyzer.d.ts +1 -1
  46. package/dist/lib/adapters/video/videoAnalyzer.js +10 -8
  47. package/dist/lib/constants/contextWindows.js +107 -16
  48. package/dist/lib/constants/enums.d.ts +99 -15
  49. package/dist/lib/constants/enums.js +152 -22
  50. package/dist/lib/context/budgetChecker.js +1 -1
  51. package/dist/lib/context/contextCompactor.js +31 -4
  52. package/dist/lib/context/emergencyTruncation.d.ts +21 -0
  53. package/dist/lib/context/emergencyTruncation.js +89 -0
  54. package/dist/lib/context/errorDetection.d.ts +16 -0
  55. package/dist/lib/context/errorDetection.js +48 -1
  56. package/dist/lib/context/errors.d.ts +19 -0
  57. package/dist/lib/context/errors.js +22 -0
  58. package/dist/lib/context/stages/slidingWindowTruncator.d.ts +6 -0
  59. package/dist/lib/context/stages/slidingWindowTruncator.js +159 -24
  60. package/dist/lib/core/baseProvider.js +306 -200
  61. package/dist/lib/core/conversationMemoryManager.js +104 -61
  62. package/dist/lib/core/evaluationProviders.js +16 -33
  63. package/dist/lib/core/factory.js +237 -164
  64. package/dist/lib/core/modules/GenerationHandler.js +175 -116
  65. package/dist/lib/core/modules/MessageBuilder.js +222 -170
  66. package/dist/lib/core/modules/StreamHandler.d.ts +1 -0
  67. package/dist/lib/core/modules/StreamHandler.js +95 -27
  68. package/dist/lib/core/modules/TelemetryHandler.d.ts +10 -1
  69. package/dist/lib/core/modules/TelemetryHandler.js +25 -7
  70. package/dist/lib/core/modules/ToolsManager.js +115 -191
  71. package/dist/lib/core/redisConversationMemoryManager.js +418 -282
  72. package/dist/lib/factories/providerRegistry.d.ts +5 -0
  73. package/dist/lib/factories/providerRegistry.js +20 -2
  74. package/dist/lib/index.d.ts +2 -2
  75. package/dist/lib/index.js +4 -2
  76. package/dist/lib/mcp/externalServerManager.js +66 -0
  77. package/dist/lib/mcp/mcpCircuitBreaker.js +24 -0
  78. package/dist/lib/mcp/mcpClientFactory.js +16 -0
  79. package/dist/lib/mcp/toolDiscoveryService.js +32 -6
  80. package/dist/lib/mcp/toolRegistry.js +193 -123
  81. package/dist/lib/neurolink.d.ts +6 -0
  82. package/dist/lib/neurolink.js +1162 -646
  83. package/dist/lib/providers/amazonBedrock.d.ts +1 -1
  84. package/dist/lib/providers/amazonBedrock.js +521 -319
  85. package/dist/lib/providers/anthropic.js +73 -17
  86. package/dist/lib/providers/anthropicBaseProvider.js +77 -17
  87. package/dist/lib/providers/googleAiStudio.d.ts +1 -1
  88. package/dist/lib/providers/googleAiStudio.js +292 -227
  89. package/dist/lib/providers/googleVertex.d.ts +36 -1
  90. package/dist/lib/providers/googleVertex.js +553 -260
  91. package/dist/lib/providers/ollama.js +329 -278
  92. package/dist/lib/providers/openAI.js +77 -19
  93. package/dist/lib/providers/sagemaker/parsers.js +3 -3
  94. package/dist/lib/providers/sagemaker/streaming.js +3 -3
  95. package/dist/lib/proxy/proxyFetch.js +81 -48
  96. package/dist/lib/rag/ChunkerFactory.js +1 -1
  97. package/dist/lib/rag/chunkers/MarkdownChunker.d.ts +22 -0
  98. package/dist/lib/rag/chunkers/MarkdownChunker.js +213 -9
  99. package/dist/lib/rag/chunking/markdownChunker.d.ts +16 -0
  100. package/dist/lib/rag/chunking/markdownChunker.js +174 -2
  101. package/dist/lib/rag/pipeline/contextAssembly.js +2 -1
  102. package/dist/lib/rag/ragIntegration.d.ts +18 -1
  103. package/dist/lib/rag/ragIntegration.js +94 -14
  104. package/dist/lib/rag/retrieval/vectorQueryTool.js +21 -4
  105. package/dist/lib/server/abstract/baseServerAdapter.js +4 -1
  106. package/dist/lib/server/adapters/fastifyAdapter.js +35 -30
  107. package/dist/lib/services/server/ai/observability/instrumentation.d.ts +32 -0
  108. package/dist/lib/services/server/ai/observability/instrumentation.js +39 -0
  109. package/dist/lib/telemetry/attributes.d.ts +52 -0
  110. package/dist/lib/telemetry/attributes.js +61 -0
  111. package/dist/lib/telemetry/index.d.ts +3 -0
  112. package/dist/lib/telemetry/index.js +3 -0
  113. package/dist/lib/telemetry/telemetryService.d.ts +6 -0
  114. package/dist/lib/telemetry/telemetryService.js +6 -0
  115. package/dist/lib/telemetry/tracers.d.ts +15 -0
  116. package/dist/lib/telemetry/tracers.js +17 -0
  117. package/dist/lib/telemetry/withSpan.d.ts +9 -0
  118. package/dist/lib/telemetry/withSpan.js +35 -0
  119. package/dist/lib/types/contextTypes.d.ts +10 -0
  120. package/dist/lib/types/streamTypes.d.ts +14 -0
  121. package/dist/lib/utils/conversationMemory.js +121 -82
  122. package/dist/lib/utils/logger.d.ts +5 -0
  123. package/dist/lib/utils/logger.js +50 -2
  124. package/dist/lib/utils/messageBuilder.js +22 -42
  125. package/dist/lib/utils/modelDetection.js +3 -3
  126. package/dist/lib/utils/providerRetry.d.ts +41 -0
  127. package/dist/lib/utils/providerRetry.js +114 -0
  128. package/dist/lib/utils/retryability.d.ts +14 -0
  129. package/dist/lib/utils/retryability.js +23 -0
  130. package/dist/lib/utils/sanitizers/svg.js +4 -5
  131. package/dist/lib/utils/tokenEstimation.d.ts +11 -1
  132. package/dist/lib/utils/tokenEstimation.js +19 -4
  133. package/dist/lib/utils/videoAnalysisProcessor.js +7 -3
  134. package/dist/mcp/externalServerManager.js +66 -0
  135. package/dist/mcp/mcpCircuitBreaker.js +24 -0
  136. package/dist/mcp/mcpClientFactory.js +16 -0
  137. package/dist/mcp/toolDiscoveryService.js +32 -6
  138. package/dist/mcp/toolRegistry.js +193 -123
  139. package/dist/neurolink.d.ts +6 -0
  140. package/dist/neurolink.js +1162 -646
  141. package/dist/providers/amazonBedrock.d.ts +1 -1
  142. package/dist/providers/amazonBedrock.js +521 -319
  143. package/dist/providers/anthropic.js +73 -17
  144. package/dist/providers/anthropicBaseProvider.js +77 -17
  145. package/dist/providers/googleAiStudio.d.ts +1 -1
  146. package/dist/providers/googleAiStudio.js +292 -227
  147. package/dist/providers/googleVertex.d.ts +36 -1
  148. package/dist/providers/googleVertex.js +553 -260
  149. package/dist/providers/ollama.js +329 -278
  150. package/dist/providers/openAI.js +77 -19
  151. package/dist/providers/sagemaker/parsers.js +3 -3
  152. package/dist/providers/sagemaker/streaming.js +3 -3
  153. package/dist/proxy/proxyFetch.js +81 -48
  154. package/dist/rag/ChunkerFactory.js +1 -1
  155. package/dist/rag/chunkers/MarkdownChunker.d.ts +22 -0
  156. package/dist/rag/chunkers/MarkdownChunker.js +213 -9
  157. package/dist/rag/chunking/markdownChunker.d.ts +16 -0
  158. package/dist/rag/chunking/markdownChunker.js +174 -2
  159. package/dist/rag/pipeline/contextAssembly.js +2 -1
  160. package/dist/rag/ragIntegration.d.ts +18 -1
  161. package/dist/rag/ragIntegration.js +94 -14
  162. package/dist/rag/retrieval/vectorQueryTool.js +21 -4
  163. package/dist/server/abstract/baseServerAdapter.js +4 -1
  164. package/dist/server/adapters/fastifyAdapter.js +35 -30
  165. package/dist/services/server/ai/observability/instrumentation.d.ts +32 -0
  166. package/dist/services/server/ai/observability/instrumentation.js +39 -0
  167. package/dist/telemetry/attributes.d.ts +52 -0
  168. package/dist/telemetry/attributes.js +60 -0
  169. package/dist/telemetry/index.d.ts +3 -0
  170. package/dist/telemetry/index.js +3 -0
  171. package/dist/telemetry/telemetryService.d.ts +6 -0
  172. package/dist/telemetry/telemetryService.js +6 -0
  173. package/dist/telemetry/tracers.d.ts +15 -0
  174. package/dist/telemetry/tracers.js +16 -0
  175. package/dist/telemetry/withSpan.d.ts +9 -0
  176. package/dist/telemetry/withSpan.js +34 -0
  177. package/dist/types/contextTypes.d.ts +10 -0
  178. package/dist/types/streamTypes.d.ts +14 -0
  179. package/dist/utils/conversationMemory.js +121 -82
  180. package/dist/utils/logger.d.ts +5 -0
  181. package/dist/utils/logger.js +50 -2
  182. package/dist/utils/messageBuilder.js +22 -42
  183. package/dist/utils/modelDetection.js +3 -3
  184. package/dist/utils/providerRetry.d.ts +41 -0
  185. package/dist/utils/providerRetry.js +113 -0
  186. package/dist/utils/retryability.d.ts +14 -0
  187. package/dist/utils/retryability.js +22 -0
  188. package/dist/utils/sanitizers/svg.js +4 -5
  189. package/dist/utils/tokenEstimation.d.ts +11 -1
  190. package/dist/utils/tokenEstimation.js +19 -4
  191. package/dist/utils/videoAnalysisProcessor.js +7 -3
  192. package/dist/workflow/config.d.ts +26 -26
  193. package/package.json +1 -1
@@ -700,6 +700,45 @@ export async function setLangfuseContext(context, callback) {
700
700
  export function getLangfuseContext() {
701
701
  return contextStorage.getStore();
702
702
  }
703
+ /**
704
+ * Capture the current Langfuse AsyncLocalStorage context and return a wrapper
705
+ * that re-enters that context when executing the provided callback.
706
+ *
707
+ * This is essential for preserving trace context across async boundaries that
708
+ * break the automatic ALS propagation chain, such as `setImmediate()`,
709
+ * `setTimeout()`, or event-emitter callbacks. Without this, spans created
710
+ * inside those callbacks become orphaned traces in Langfuse.
711
+ *
712
+ * **How it works:**
713
+ * 1. Captures the current ALS store at call time (synchronously).
714
+ * 2. Returns an async function that, when invoked, re-enters the captured
715
+ * context via `contextStorage.run()` before executing the callback.
716
+ * 3. If no context exists at capture time, the callback runs without
717
+ * ALS wrapping (no-op passthrough).
718
+ *
719
+ * @param fn - The async function to execute within the captured context
720
+ * @returns A new async function that preserves the Langfuse ALS context
721
+ *
722
+ * @example
723
+ * // Before (broken — setImmediate loses ALS context):
724
+ * setImmediate(async () => {
725
+ * await this.checkAndSummarize(session, threshold);
726
+ * });
727
+ *
728
+ * // After (fixed — context is captured and re-entered):
729
+ * const wrappedFn = runWithCurrentLangfuseContext(async () => {
730
+ * await this.checkAndSummarize(session, threshold);
731
+ * });
732
+ * setImmediate(wrappedFn);
733
+ */
734
+ export function runWithCurrentLangfuseContext(fn) {
735
+ const capturedContext = contextStorage.getStore();
736
+ if (capturedContext) {
737
+ return () => contextStorage.run(capturedContext, fn);
738
+ }
739
+ // No context to preserve — return the function as-is
740
+ return fn;
741
+ }
703
742
  /**
704
743
  * Get an OpenTelemetry Tracer for creating custom spans
705
744
  *
@@ -0,0 +1,52 @@
1
+ export declare const ATTR: {
2
+ readonly GEN_AI_SYSTEM: "gen_ai.system";
3
+ readonly GEN_AI_MODEL: "gen_ai.request.model";
4
+ readonly GEN_AI_OPERATION: "gen_ai.operation.name";
5
+ readonly GEN_AI_INPUT_TOKENS: "gen_ai.usage.input_tokens";
6
+ readonly GEN_AI_OUTPUT_TOKENS: "gen_ai.usage.output_tokens";
7
+ readonly GEN_AI_FINISH_REASON: "gen_ai.response.finish_reason";
8
+ readonly GEN_AI_COST_USD: "gen_ai.cost_usd";
9
+ readonly GEN_AI_TOOL_NAME: "gen_ai.tool.name";
10
+ readonly GEN_AI_TEMPERATURE: "gen_ai.request.temperature";
11
+ readonly GEN_AI_MAX_TOKENS: "gen_ai.request.max_tokens";
12
+ readonly NL_PROVIDER: "neurolink.provider";
13
+ readonly NL_MODEL: "neurolink.model";
14
+ readonly NL_STREAM_MODE: "neurolink.stream_mode";
15
+ readonly NL_TOOL_COUNT: "neurolink.tool_count";
16
+ readonly NL_MESSAGE_COUNT: "neurolink.message_count";
17
+ readonly NL_HAS_TOOLS: "neurolink.has_tools";
18
+ readonly NL_INPUT_LENGTH: "neurolink.input_length";
19
+ readonly NL_OUTPUT_LENGTH: "neurolink.output_length";
20
+ readonly NL_REQUEST_ID: "neurolink.request_id";
21
+ readonly NL_PATH: "neurolink.path";
22
+ readonly NL_HAS_MEMORY: "neurolink.has_conversation_memory";
23
+ readonly NL_COST: "neurolink.cost";
24
+ readonly NL_STRUCTURED_OUTPUT: "neurolink.structured_output";
25
+ readonly NL_HAS_FALLBACK: "neurolink.has_fallback";
26
+ readonly MCP_SERVER_ID: "mcp.server_id";
27
+ readonly MCP_TOOL_NAME: "mcp.tool_name";
28
+ readonly MCP_TIMEOUT_MS: "mcp.timeout_ms";
29
+ readonly MCP_TRANSPORT: "mcp.transport";
30
+ readonly MCP_CIRCUIT_STATE: "mcp.circuit_state";
31
+ readonly SESSION_ID: "session.id";
32
+ readonly USER_ID: "user.id";
33
+ readonly MEMORY_TYPE: "memory.type";
34
+ readonly MESSAGE_COUNT: "message.count";
35
+ readonly CONTENT_LENGTH: "content.length";
36
+ readonly RAG_FILE_COUNT: "rag.file_count";
37
+ readonly RAG_STRATEGY: "rag.strategy";
38
+ readonly RAG_CHUNK_SIZE: "rag.chunk_size";
39
+ readonly RAG_TOP_K: "rag.top_k";
40
+ readonly RAG_RESULT_COUNT: "rag.result_count";
41
+ readonly MSG_COUNT: "message.build.count";
42
+ readonly MSG_HAS_IMAGES: "message.has_images";
43
+ readonly MSG_HAS_FILES: "message.has_files";
44
+ readonly MSG_HAS_SYSTEM_PROMPT: "message.has_system_prompt";
45
+ readonly MSG_TOTAL_CONTENT_LENGTH: "message.total_content_length";
46
+ readonly MSG_IS_MULTIMODAL: "message.is_multimodal";
47
+ readonly CONTEXT_STAGE: "context.compaction_stage";
48
+ readonly CONTEXT_TOKENS_BEFORE: "context.tokens_before";
49
+ readonly CONTEXT_TOKENS_AFTER: "context.tokens_after";
50
+ readonly MW_COUNT: "middleware.count";
51
+ readonly MW_NAMES: "middleware.names";
52
+ };
@@ -0,0 +1,61 @@
1
+ export const ATTR = {
2
+ // GenAI standard (OpenTelemetry semantic conventions)
3
+ GEN_AI_SYSTEM: "gen_ai.system",
4
+ GEN_AI_MODEL: "gen_ai.request.model",
5
+ GEN_AI_OPERATION: "gen_ai.operation.name",
6
+ GEN_AI_INPUT_TOKENS: "gen_ai.usage.input_tokens",
7
+ GEN_AI_OUTPUT_TOKENS: "gen_ai.usage.output_tokens",
8
+ GEN_AI_FINISH_REASON: "gen_ai.response.finish_reason",
9
+ GEN_AI_COST_USD: "gen_ai.cost_usd",
10
+ GEN_AI_TOOL_NAME: "gen_ai.tool.name",
11
+ GEN_AI_TEMPERATURE: "gen_ai.request.temperature",
12
+ GEN_AI_MAX_TOKENS: "gen_ai.request.max_tokens",
13
+ // NeuroLink custom
14
+ NL_PROVIDER: "neurolink.provider",
15
+ NL_MODEL: "neurolink.model",
16
+ NL_STREAM_MODE: "neurolink.stream_mode",
17
+ NL_TOOL_COUNT: "neurolink.tool_count",
18
+ NL_MESSAGE_COUNT: "neurolink.message_count",
19
+ NL_HAS_TOOLS: "neurolink.has_tools",
20
+ NL_INPUT_LENGTH: "neurolink.input_length",
21
+ NL_OUTPUT_LENGTH: "neurolink.output_length",
22
+ NL_REQUEST_ID: "neurolink.request_id",
23
+ NL_PATH: "neurolink.path",
24
+ NL_HAS_MEMORY: "neurolink.has_conversation_memory",
25
+ NL_COST: "neurolink.cost",
26
+ NL_STRUCTURED_OUTPUT: "neurolink.structured_output",
27
+ NL_HAS_FALLBACK: "neurolink.has_fallback",
28
+ // MCP
29
+ MCP_SERVER_ID: "mcp.server_id",
30
+ MCP_TOOL_NAME: "mcp.tool_name",
31
+ MCP_TIMEOUT_MS: "mcp.timeout_ms",
32
+ MCP_TRANSPORT: "mcp.transport",
33
+ MCP_CIRCUIT_STATE: "mcp.circuit_state",
34
+ // Session/Memory
35
+ SESSION_ID: "session.id",
36
+ USER_ID: "user.id",
37
+ MEMORY_TYPE: "memory.type",
38
+ MESSAGE_COUNT: "message.count",
39
+ CONTENT_LENGTH: "content.length",
40
+ // RAG
41
+ RAG_FILE_COUNT: "rag.file_count",
42
+ RAG_STRATEGY: "rag.strategy",
43
+ RAG_CHUNK_SIZE: "rag.chunk_size",
44
+ RAG_TOP_K: "rag.top_k",
45
+ RAG_RESULT_COUNT: "rag.result_count",
46
+ // Message building
47
+ MSG_COUNT: "message.build.count",
48
+ MSG_HAS_IMAGES: "message.has_images",
49
+ MSG_HAS_FILES: "message.has_files",
50
+ MSG_HAS_SYSTEM_PROMPT: "message.has_system_prompt",
51
+ MSG_TOTAL_CONTENT_LENGTH: "message.total_content_length",
52
+ MSG_IS_MULTIMODAL: "message.is_multimodal",
53
+ // Context
54
+ CONTEXT_STAGE: "context.compaction_stage",
55
+ CONTEXT_TOKENS_BEFORE: "context.tokens_before",
56
+ CONTEXT_TOKENS_AFTER: "context.tokens_after",
57
+ // Middleware
58
+ MW_COUNT: "middleware.count",
59
+ MW_NAMES: "middleware.names",
60
+ };
61
+ //# sourceMappingURL=attributes.js.map
@@ -1,4 +1,7 @@
1
1
  export { TelemetryService, type HealthMetrics } from "./telemetryService.js";
2
+ export { tracers } from "./tracers.js";
3
+ export { withSpan, withClientSpan, type SpanOptions } from "./withSpan.js";
4
+ export { ATTR } from "./attributes.js";
2
5
  /**
3
6
  * Initialize telemetry for NeuroLink
4
7
  * OPTIONAL - Only works when NEUROLINK_TELEMETRY_ENABLED=true
@@ -1,5 +1,8 @@
1
1
  // Optional Telemetry Infrastructure (Phase 2)
2
2
  export { TelemetryService } from "./telemetryService.js";
3
+ export { tracers } from "./tracers.js";
4
+ export { withSpan, withClientSpan } from "./withSpan.js";
5
+ export { ATTR } from "./attributes.js";
3
6
  import { logger } from "../utils/logger.js";
4
7
  /**
5
8
  * Initialize telemetry for NeuroLink
@@ -32,6 +32,12 @@ export declare class TelemetryService {
32
32
  private initializeTelemetry;
33
33
  private initializeMetrics;
34
34
  initialize(): Promise<void>;
35
+ /**
36
+ * @deprecated Vercel AI SDK's experimental_telemetry creates ai.generateText/ai.streamText
37
+ * spans automatically via OpenTelemetry. Using this method would create duplicate spans.
38
+ * Kept for potential future use with non-Vercel providers (e.g., Amazon Bedrock).
39
+ * See: TelemetryHandler.getTelemetryConfig() for the active telemetry path.
40
+ */
35
41
  traceAIRequest<T>(provider: string, operation: () => Promise<T>, operationType?: string): Promise<T>;
36
42
  recordAIRequest(provider: string, model: string, tokens: number, duration: number, cost?: number): void;
37
43
  recordAIError(provider: string, error: Error): void;
@@ -112,6 +112,12 @@ export class TelemetryService {
112
112
  }
113
113
  }
114
114
  // AI Operation Tracing (NO-OP when disabled)
115
+ /**
116
+ * @deprecated Vercel AI SDK's experimental_telemetry creates ai.generateText/ai.streamText
117
+ * spans automatically via OpenTelemetry. Using this method would create duplicate spans.
118
+ * Kept for potential future use with non-Vercel providers (e.g., Amazon Bedrock).
119
+ * See: TelemetryHandler.getTelemetryConfig() for the active telemetry path.
120
+ */
115
121
  async traceAIRequest(provider, operation, operationType = "generate_text") {
116
122
  if (!this.enabled || !this.tracer) {
117
123
  return await operation();
@@ -0,0 +1,15 @@
1
+ export declare const tracers: {
2
+ readonly sdk: import("@opentelemetry/api").Tracer;
3
+ readonly provider: import("@opentelemetry/api").Tracer;
4
+ readonly generation: import("@opentelemetry/api").Tracer;
5
+ readonly stream: import("@opentelemetry/api").Tracer;
6
+ readonly http: import("@opentelemetry/api").Tracer;
7
+ readonly mcp: import("@opentelemetry/api").Tracer;
8
+ readonly memory: import("@opentelemetry/api").Tracer;
9
+ readonly redis: import("@opentelemetry/api").Tracer;
10
+ readonly factory: import("@opentelemetry/api").Tracer;
11
+ readonly rag: import("@opentelemetry/api").Tracer;
12
+ readonly context: import("@opentelemetry/api").Tracer;
13
+ readonly middleware: import("@opentelemetry/api").Tracer;
14
+ readonly processor: import("@opentelemetry/api").Tracer;
15
+ };
@@ -0,0 +1,17 @@
1
+ import { trace } from "@opentelemetry/api";
2
+ export const tracers = {
3
+ sdk: trace.getTracer("neurolink"),
4
+ provider: trace.getTracer("neurolink.provider"),
5
+ generation: trace.getTracer("neurolink.generation"),
6
+ stream: trace.getTracer("neurolink.stream"),
7
+ http: trace.getTracer("neurolink.http"),
8
+ mcp: trace.getTracer("neurolink.mcp"),
9
+ memory: trace.getTracer("neurolink.memory"),
10
+ redis: trace.getTracer("neurolink.redis"),
11
+ factory: trace.getTracer("neurolink.factory"),
12
+ rag: trace.getTracer("neurolink.rag"),
13
+ context: trace.getTracer("neurolink.context"),
14
+ middleware: trace.getTracer("neurolink.middleware"),
15
+ processor: trace.getTracer("neurolink.processor"),
16
+ };
17
+ //# sourceMappingURL=tracers.js.map
@@ -0,0 +1,9 @@
1
+ import { type Span, type Tracer, SpanKind } from "@opentelemetry/api";
2
+ export type SpanOptions = {
3
+ name: string;
4
+ tracer: Tracer;
5
+ kind?: SpanKind;
6
+ attributes?: Record<string, string | number | boolean | undefined>;
7
+ };
8
+ export declare function withSpan<T>(options: SpanOptions, fn: (span: Span) => Promise<T>): Promise<T>;
9
+ export declare function withClientSpan<T>(options: Omit<SpanOptions, "kind">, fn: (span: Span) => Promise<T>): Promise<T>;
@@ -0,0 +1,35 @@
1
+ import { SpanKind, SpanStatusCode, } from "@opentelemetry/api";
2
+ export async function withSpan(options, fn) {
3
+ const { name, tracer, kind = SpanKind.INTERNAL, attributes } = options;
4
+ return tracer.startActiveSpan(name, { kind }, async (span) => {
5
+ if (attributes) {
6
+ for (const [key, value] of Object.entries(attributes)) {
7
+ if (value !== undefined) {
8
+ span.setAttribute(key, value);
9
+ }
10
+ }
11
+ }
12
+ try {
13
+ const result = await fn(span);
14
+ span.setStatus({ code: SpanStatusCode.OK });
15
+ return result;
16
+ }
17
+ catch (error) {
18
+ span.setStatus({
19
+ code: SpanStatusCode.ERROR,
20
+ message: error instanceof Error ? error.message : String(error),
21
+ });
22
+ if (error instanceof Error) {
23
+ span.recordException(error);
24
+ }
25
+ throw error;
26
+ }
27
+ finally {
28
+ span.end();
29
+ }
30
+ });
31
+ }
32
+ export async function withClientSpan(options, fn) {
33
+ return withSpan({ ...options, kind: SpanKind.CLIENT }, fn);
34
+ }
35
+ //# sourceMappingURL=withSpan.js.map
@@ -424,6 +424,16 @@ export type PruneResult = {
424
424
  /** Configuration for sliding window truncation (Stage 4). */
425
425
  export type TruncationConfig = {
426
426
  fraction?: number;
427
+ /** Current estimated tokens (enables adaptive mode) */
428
+ currentTokens?: number;
429
+ /** Target token budget (enables adaptive mode) */
430
+ targetTokens?: number;
431
+ /** Provider for token estimation (enables adaptive mode) */
432
+ provider?: string;
433
+ /** Buffer above required reduction (default: 0.15 = 15%) */
434
+ adaptiveBuffer?: number;
435
+ /** Maximum iterations for adaptive truncation (default: 3) */
436
+ maxIterations?: number;
427
437
  };
428
438
  /** Result of sliding window truncation (Stage 4). */
429
439
  export type TruncationResult = {
@@ -364,6 +364,20 @@ export type StreamOptions = {
364
364
  workflow?: string;
365
365
  workflowConfig?: import("../workflow/types.js").WorkflowConfig;
366
366
  enableSummarization?: boolean;
367
+ /**
368
+ * Maximum cumulative cost (USD) for this session.
369
+ * Once the session spend reaches this limit, subsequent stream() calls
370
+ * will throw a SESSION_BUDGET_EXCEEDED error instead of making API calls.
371
+ *
372
+ * @example
373
+ * ```typescript
374
+ * const result = await neurolink.stream({
375
+ * input: { text: "Summarize this" },
376
+ * maxBudgetUsd: 1.00
377
+ * });
378
+ * ```
379
+ */
380
+ maxBudgetUsd?: number;
367
381
  /**
368
382
  * RAG (Retrieval-Augmented Generation) configuration.
369
383
  *
@@ -2,11 +2,16 @@
2
2
  * Conversation Memory Utilities
3
3
  * Handles configuration merging and conversation memory operations
4
4
  */
5
+ import { SpanKind, SpanStatusCode } from "@opentelemetry/api";
6
+ import { tracers } from "../telemetry/tracers.js";
7
+ import { withTimeout } from "./errorHandling.js";
5
8
  import { DEFAULT_FALLBACK_THRESHOLD, getConversationMemoryDefaults, MEMORY_THRESHOLD_PERCENTAGE, } from "../config/conversationMemory.js";
6
9
  import { getAvailableInputTokens } from "../constants/contextWindows.js";
7
10
  import { buildSummarizationPrompt } from "../context/prompts/summarizationPrompt.js";
8
- import { NeuroLink } from "../neurolink.js";
9
11
  import { logger } from "./logger.js";
12
+ const memoryTracer = tracers.memory;
13
+ // Cached NeuroLink instance for summarization to avoid creating a new instance per call
14
+ let cachedSummarizer = null;
10
15
  /**
11
16
  * Apply conversation memory defaults to user configuration
12
17
  * Merges user config with environment variables and default values
@@ -34,41 +39,49 @@ export async function getConversationMessages(conversationMemory, options) {
34
39
  });
35
40
  return [];
36
41
  }
37
- try {
38
- // Extract userId from context
39
- const userId = options.context?.userId;
40
- const enableSummarization = options.enableSummarization ?? undefined;
41
- const messages = await conversationMemory.buildContextMessages(sessionId, userId, enableSummarization);
42
- logger.debug("[conversationMemoryUtils] Conversation messages retrieved successfully", {
43
- sessionId,
44
- messageCount: messages.length,
45
- messageTypes: messages.map((m) => m.role),
46
- firstMessage: messages.length > 0
47
- ? {
48
- role: messages[0].role,
49
- contentLength: messages[0].content.length,
50
- contentPreview: messages[0].content.substring(0, 50),
51
- }
52
- : null,
53
- lastMessage: messages.length > 0
54
- ? {
55
- role: messages[messages.length - 1].role,
56
- contentLength: messages[messages.length - 1].content.length,
57
- contentPreview: messages[messages.length - 1].content.substring(0, 50),
58
- }
59
- : null,
60
- });
61
- return messages;
62
- }
63
- catch (error) {
64
- logger.warn("[conversationMemoryUtils] Failed to get conversation messages", {
65
- sessionId,
66
- memoryType: conversationMemory.constructor.name,
67
- error: error instanceof Error ? error.message : String(error),
68
- stack: error instanceof Error ? error.stack : undefined,
69
- });
70
- return [];
71
- }
42
+ return memoryTracer.startActiveSpan("neurolink.conversation.getMessages", {
43
+ kind: SpanKind.INTERNAL,
44
+ attributes: {
45
+ "session.id": sessionId,
46
+ "memory.type": conversationMemory.constructor.name,
47
+ },
48
+ }, async (span) => {
49
+ try {
50
+ // Extract userId from context
51
+ const userId = options.context?.userId;
52
+ if (userId) {
53
+ span.setAttribute("user.id", userId);
54
+ }
55
+ const enableSummarization = options.enableSummarization ?? undefined;
56
+ const messages = await conversationMemory.buildContextMessages(sessionId, userId, enableSummarization);
57
+ span.setAttribute("message.count", messages.length);
58
+ if (logger.shouldLog("debug")) {
59
+ logger.debug("[conversationMemoryUtils] Conversation messages retrieved successfully", {
60
+ sessionId,
61
+ messageCount: messages.length,
62
+ messageTypes: messages.map((m) => m.role),
63
+ });
64
+ }
65
+ return messages;
66
+ }
67
+ catch (error) {
68
+ span.setStatus({
69
+ code: SpanStatusCode.ERROR,
70
+ message: error instanceof Error ? error.message : String(error),
71
+ });
72
+ span.recordException(error instanceof Error ? error : new Error(String(error)));
73
+ logger.warn("[conversationMemoryUtils] Failed to get conversation messages", {
74
+ sessionId,
75
+ memoryType: conversationMemory.constructor.name,
76
+ error: error instanceof Error ? error.message : String(error),
77
+ stack: error instanceof Error ? error.stack : undefined,
78
+ });
79
+ return [];
80
+ }
81
+ finally {
82
+ span.end();
83
+ }
84
+ });
72
85
  }
73
86
  /**
74
87
  * Store conversation turn for future context
@@ -128,47 +141,66 @@ export async function storeConversationTurn(conversationMemory, originalOptions,
128
141
  model: result.model,
129
142
  };
130
143
  }
131
- try {
132
- await conversationMemory.storeConversationTurn({
133
- sessionId,
134
- userId,
135
- userMessage,
136
- aiResponse,
137
- startTimeStamp,
138
- providerDetails,
139
- enableSummarization: originalOptions.enableSummarization,
140
- requestId,
141
- tokenUsage: result.usage
142
- ? {
143
- inputTokens: result.usage.input,
144
- outputTokens: result.usage.output,
145
- totalTokens: result.usage.total,
146
- cacheReadTokens: result.usage.cacheReadTokens,
147
- cacheWriteTokens: result.usage.cacheCreationTokens,
148
- }
149
- : undefined,
150
- });
151
- logger.debug("[conversationMemoryUtils] Conversation turn stored successfully", {
152
- requestId,
153
- sessionId,
154
- userId,
155
- memoryType: conversationMemory.constructor.name,
156
- userMessageLength: userMessage.length,
157
- aiResponseLength: aiResponse.length,
158
- });
159
- }
160
- catch (error) {
161
- const details = error?.details;
162
- logger.warn("[conversationMemoryUtils] Failed to store conversation turn", {
163
- sessionId,
164
- userId,
165
- memoryType: conversationMemory.constructor.name,
166
- error: error instanceof Error ? error.message : String(error),
167
- innerError: details?.error || "none",
168
- errorCode: error?.code || "unknown",
169
- stack: error instanceof Error ? error.stack : undefined,
170
- });
171
- }
144
+ await memoryTracer.startActiveSpan("neurolink.conversation.storeTurn", {
145
+ kind: SpanKind.INTERNAL,
146
+ attributes: {
147
+ "session.id": sessionId,
148
+ "content.length": userMessage.length + aiResponse.length,
149
+ },
150
+ }, async (span) => {
151
+ if (userId) {
152
+ span.setAttribute("user.id", userId);
153
+ }
154
+ try {
155
+ await conversationMemory.storeConversationTurn({
156
+ sessionId,
157
+ userId,
158
+ userMessage,
159
+ aiResponse,
160
+ startTimeStamp,
161
+ providerDetails,
162
+ enableSummarization: originalOptions.enableSummarization,
163
+ requestId,
164
+ tokenUsage: result.usage
165
+ ? {
166
+ inputTokens: result.usage.input,
167
+ outputTokens: result.usage.output,
168
+ totalTokens: result.usage.total,
169
+ cacheReadTokens: result.usage.cacheReadTokens,
170
+ cacheWriteTokens: result.usage.cacheCreationTokens,
171
+ }
172
+ : undefined,
173
+ });
174
+ logger.debug("[conversationMemoryUtils] Conversation turn stored successfully", {
175
+ requestId,
176
+ sessionId,
177
+ userId,
178
+ memoryType: conversationMemory.constructor.name,
179
+ userMessageLength: userMessage.length,
180
+ aiResponseLength: aiResponse.length,
181
+ });
182
+ }
183
+ catch (error) {
184
+ span.setStatus({
185
+ code: SpanStatusCode.ERROR,
186
+ message: error instanceof Error ? error.message : String(error),
187
+ });
188
+ span.recordException(error instanceof Error ? error : new Error(String(error)));
189
+ const details = error?.details;
190
+ logger.warn("[conversationMemoryUtils] Failed to store conversation turn", {
191
+ sessionId,
192
+ userId,
193
+ memoryType: conversationMemory.constructor.name,
194
+ error: error instanceof Error ? error.message : String(error),
195
+ innerError: details?.error || "none",
196
+ errorCode: error?.code || "unknown",
197
+ stack: error instanceof Error ? error.stack : undefined,
198
+ });
199
+ }
200
+ finally {
201
+ span.end();
202
+ }
203
+ });
172
204
  }
173
205
  /**
174
206
  * Build context messages from pointer onwards (token-based memory)
@@ -322,22 +354,29 @@ export function getEffectiveTokenThreshold(provider, model, envOverride, session
322
354
  */
323
355
  export async function generateSummary(messages, config, logPrefix = "[ConversationMemory]", previousSummary, requestId) {
324
356
  const summarizationPrompt = createSummarizationPrompt(messages, previousSummary);
325
- const summarizer = new NeuroLink({
326
- conversationMemory: { enabled: false },
327
- });
357
+ const SUMMARIZER_INIT_TIMEOUT = 15_000;
358
+ const SUMMARIZER_GENERATE_TIMEOUT = 60_000;
328
359
  try {
360
+ if (!cachedSummarizer) {
361
+ cachedSummarizer = await withTimeout((async () => {
362
+ const { NeuroLink: NeuroLinkClass } = await import("../neurolink.js");
363
+ return new NeuroLinkClass({
364
+ conversationMemory: { enabled: false },
365
+ });
366
+ })(), SUMMARIZER_INIT_TIMEOUT, new Error("Summarizer initialization timed out"));
367
+ }
329
368
  if (!config.summarizationProvider || !config.summarizationModel) {
330
369
  logger.error(`${logPrefix} Missing summarization provider`, {
331
370
  requestId,
332
371
  });
333
372
  return null;
334
373
  }
335
- const summaryResult = await summarizer.generate({
374
+ const summaryResult = await withTimeout(cachedSummarizer.generate({
336
375
  input: { text: summarizationPrompt },
337
376
  provider: config.summarizationProvider,
338
377
  model: config.summarizationModel,
339
378
  disableTools: true,
340
- });
379
+ }), SUMMARIZER_GENERATE_TIMEOUT, new Error("Summary generation timed out"));
341
380
  return summaryResult.content || null;
342
381
  }
343
382
  catch (error) {
@@ -63,6 +63,11 @@ declare class NeuroLinkLogger {
63
63
  * @returns Formatted prefix string like "[2025-08-18T13:45:30.123Z] [NEUROLINK:ERROR]"
64
64
  */
65
65
  private getLogPrefix;
66
+ /**
67
+ * Extracts current OTel trace context (trace_id, span_id) if available.
68
+ * Returns empty object if OTel is not initialized or no active span exists.
69
+ */
70
+ private getTraceContext;
66
71
  /**
67
72
  * Safely serialize data to fully expanded JSON string.
68
73
  * Handles circular references and non-serializable values.