@juspay/neurolink 7.54.0 → 8.0.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.
@@ -55,3 +55,20 @@ export declare function getLangfuseHealthStatus(): {
55
55
  release: string;
56
56
  } | undefined;
57
57
  };
58
+ /**
59
+ * Set user and session context for Langfuse spans in the current async context
60
+ *
61
+ * Merges the provided context with existing AsyncLocalStorage context. If a callback is provided,
62
+ * the context is scoped to that callback execution. Without a callback, the context applies to
63
+ * the current execution context and its children.
64
+ *
65
+ * Uses AsyncLocalStorage to properly scope context per request, avoiding race conditions
66
+ * in concurrent scenarios.
67
+ *
68
+ * @param context - Object containing optional userId and/or sessionId to merge with existing context
69
+ * @param callback - Optional callback to run within the context scope. If omitted, context applies to current execution
70
+ */
71
+ export declare function setLangfuseContext(context: {
72
+ userId?: string | null;
73
+ sessionId?: string | null;
74
+ }, callback?: () => void | Promise<void>): Promise<void>;
@@ -10,13 +10,38 @@ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
10
10
  import { LangfuseSpanProcessor } from "@langfuse/otel";
11
11
  import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions";
12
12
  import { resourceFromAttributes } from "@opentelemetry/resources";
13
+ import { AsyncLocalStorage } from "async_hooks";
13
14
  import { logger } from "../../../../utils/logger.js";
14
15
  const LOG_PREFIX = "[OpenTelemetry]";
16
+ const contextStorage = new AsyncLocalStorage();
15
17
  let tracerProvider = null;
16
18
  let langfuseProcessor = null;
17
19
  let isInitialized = false;
18
20
  let isCredentialsValid = false;
19
21
  let currentConfig = null;
22
+ /**
23
+ * Span processor that enriches spans with user and session context from AsyncLocalStorage
24
+ */
25
+ class ContextEnricher {
26
+ onStart(span) {
27
+ const context = contextStorage.getStore();
28
+ const userId = context?.userId ?? currentConfig?.userId;
29
+ const sessionId = context?.sessionId ?? currentConfig?.sessionId;
30
+ if (userId) {
31
+ span.setAttribute("user.id", userId);
32
+ }
33
+ if (sessionId) {
34
+ span.setAttribute("session.id", sessionId);
35
+ }
36
+ }
37
+ onEnd() { }
38
+ shutdown() {
39
+ return Promise.resolve();
40
+ }
41
+ forceFlush() {
42
+ return Promise.resolve();
43
+ }
44
+ }
20
45
  /**
21
46
  * Initialize OpenTelemetry with Langfuse span processor
22
47
  *
@@ -46,7 +71,6 @@ export function initializeOpenTelemetry(config) {
46
71
  try {
47
72
  currentConfig = config;
48
73
  isCredentialsValid = true;
49
- // Create Langfuse span processor with configuration
50
74
  langfuseProcessor = new LangfuseSpanProcessor({
51
75
  publicKey: config.publicKey,
52
76
  secretKey: config.secretKey,
@@ -54,18 +78,15 @@ export function initializeOpenTelemetry(config) {
54
78
  environment: config.environment || "dev",
55
79
  release: config.release || "v1.0.0",
56
80
  });
57
- // Create resource with service metadata (v2.x API)
58
81
  const resource = resourceFromAttributes({
59
82
  [ATTR_SERVICE_NAME]: "neurolink",
60
83
  [ATTR_SERVICE_VERSION]: config.release || "v1.0.0",
61
84
  "deployment.environment": config.environment || "dev",
62
85
  });
63
- // Initialize tracer provider with span processor and resource
64
86
  tracerProvider = new NodeTracerProvider({
65
87
  resource,
66
- spanProcessors: [langfuseProcessor],
88
+ spanProcessors: [new ContextEnricher(), langfuseProcessor],
67
89
  });
68
- // Register provider globally so Vercel AI SDK can use it
69
90
  tracerProvider.register();
70
91
  isInitialized = true;
71
92
  logger.info(`${LOG_PREFIX} Initialized with Langfuse span processor`, {
@@ -168,3 +189,31 @@ export function getLangfuseHealthStatus() {
168
189
  : undefined,
169
190
  };
170
191
  }
192
+ /**
193
+ * Set user and session context for Langfuse spans in the current async context
194
+ *
195
+ * Merges the provided context with existing AsyncLocalStorage context. If a callback is provided,
196
+ * the context is scoped to that callback execution. Without a callback, the context applies to
197
+ * the current execution context and its children.
198
+ *
199
+ * Uses AsyncLocalStorage to properly scope context per request, avoiding race conditions
200
+ * in concurrent scenarios.
201
+ *
202
+ * @param context - Object containing optional userId and/or sessionId to merge with existing context
203
+ * @param callback - Optional callback to run within the context scope. If omitted, context applies to current execution
204
+ */
205
+ export async function setLangfuseContext(context, callback) {
206
+ const currentContext = contextStorage.getStore() || {};
207
+ const newContext = {
208
+ userId: context.userId !== undefined ? context.userId : currentContext.userId,
209
+ sessionId: context.sessionId !== undefined
210
+ ? context.sessionId
211
+ : currentContext.sessionId,
212
+ };
213
+ if (callback) {
214
+ return await contextStorage.run(newContext, callback);
215
+ }
216
+ else {
217
+ contextStorage.enterWith(newContext);
218
+ }
219
+ }
@@ -2,10 +2,7 @@
2
2
  * Conversation Memory Types for NeuroLink
3
3
  * Provides type-safe conversation storage and context management
4
4
  */
5
- import type { MemoryConfig } from "mem0ai/oss";
6
- /**
7
- * Mem0 configuration type matching mem0ai/oss MemoryConfig structure
8
- */
5
+ import type { Mem0Config } from "../memory/mem0Initializer.js";
9
6
  /**
10
7
  * Configuration for conversation memory feature
11
8
  */
@@ -28,8 +25,8 @@ export type ConversationMemoryConfig = {
28
25
  summarizationModel?: string;
29
26
  /** Enable mem0 integration for conversation memory */
30
27
  mem0Enabled?: boolean;
31
- /** Configuration for mem0 integration */
32
- mem0Config?: MemoryConfig;
28
+ /** Configuration for mem0 cloud API integration */
29
+ mem0Config?: Mem0Config;
33
30
  /** Redis configuration (optional) - overrides environment variables */
34
31
  redisConfig?: RedisStorageConfig;
35
32
  };
@@ -24,6 +24,10 @@ export type LangfuseConfig = {
24
24
  environment?: string;
25
25
  /** Release/version identifier */
26
26
  release?: string;
27
+ /** Optional default user id to attach to spans */
28
+ userId?: string;
29
+ /** Optional default session id to attach to spans */
30
+ sessionId?: string;
27
31
  };
28
32
  /**
29
33
  * OpenTelemetry configuration
@@ -171,38 +171,3 @@ export type EnvVarValidationResult = {
171
171
  invalidVars: string[];
172
172
  warnings: string[];
173
173
  };
174
- /**
175
- * Interface for mem0 Memory instance methods based on actual mem0ai/oss API
176
- */
177
- export type Mem0Memory = {
178
- search(query: string, config: {
179
- userId?: string;
180
- limit?: number;
181
- }): Promise<{
182
- results: Array<{
183
- memory: string;
184
- id: string;
185
- }>;
186
- }>;
187
- add(messages: string, config: {
188
- userId?: string;
189
- metadata?: Record<string, unknown>;
190
- }): Promise<{
191
- results: Array<{
192
- id: string;
193
- memory: string;
194
- }>;
195
- }>;
196
- get(memoryId: string): Promise<{
197
- id: string;
198
- memory: string;
199
- } | null>;
200
- update(memoryId: string, data: string): Promise<{
201
- message: string;
202
- }>;
203
- delete(memoryId: string): Promise<{
204
- message: string;
205
- }>;
206
- history(memoryId: string): Promise<unknown[]>;
207
- reset(): Promise<void>;
208
- };
@@ -3,7 +3,7 @@
3
3
  * Centralized file detection for all multimodal file types
4
4
  * Uses multi-strategy approach for reliable type identification
5
5
  */
6
- import { request } from "undici";
6
+ import { request, getGlobalDispatcher, interceptors } from "undici";
7
7
  import { readFile, stat } from "fs/promises";
8
8
  import { logger } from "./logger.js";
9
9
  import { CSVProcessor } from "./csvProcessor.js";
@@ -146,10 +146,10 @@ export class FileDetector {
146
146
  const maxSize = options?.maxSize || 10 * 1024 * 1024;
147
147
  const timeout = options?.timeout || 30000;
148
148
  const response = await request(url, {
149
+ dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
149
150
  method: "GET",
150
151
  headersTimeout: timeout,
151
152
  bodyTimeout: timeout,
152
- maxRedirections: 5,
153
153
  });
154
154
  if (response.statusCode !== 200) {
155
155
  throw new Error(`HTTP ${response.statusCode}`);
@@ -271,10 +271,10 @@ class MimeTypeStrategy {
271
271
  }
272
272
  try {
273
273
  const response = await request(input, {
274
+ dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
274
275
  method: "HEAD",
275
276
  headersTimeout: 5000,
276
277
  bodyTimeout: 5000,
277
- maxRedirections: 5,
278
278
  });
279
279
  const contentType = response.headers["content-type"] || "";
280
280
  const type = this.mimeToFileType(contentType);
@@ -8,7 +8,7 @@ import { ProviderImageAdapter, MultimodalLogger, } from "../adapters/providerIma
8
8
  import { logger } from "./logger.js";
9
9
  import { FileDetector } from "./fileDetector.js";
10
10
  import { PDFProcessor } from "./pdfProcessor.js";
11
- import { request } from "undici";
11
+ import { request, getGlobalDispatcher, interceptors } from "undici";
12
12
  import { readFileSync, existsSync } from "fs";
13
13
  /**
14
14
  * Type guard for validating message roles
@@ -562,10 +562,10 @@ function isInternetUrl(input) {
562
562
  async function downloadImageFromUrl(url) {
563
563
  try {
564
564
  const response = await request(url, {
565
+ dispatcher: getGlobalDispatcher().compose(interceptors.redirect({ maxRedirections: 5 })),
565
566
  method: "GET",
566
567
  headersTimeout: 10000, // 10 second timeout for headers
567
- bodyTimeout: 30000, // 30 second timeout for body
568
- maxRedirections: 5,
568
+ bodyTimeout: 30000, // 30 second timeout for body,
569
569
  });
570
570
  if (response.statusCode !== 200) {
571
571
  throw new Error(`HTTP ${response.statusCode}: Failed to download image from ${url}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "7.54.0",
3
+ "version": "8.0.1",
4
4
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, and professional CLI. Built-in tools operational, 58+ external MCP servers discoverable. Connect to filesystem, GitHub, database operations, and more. Build, test, and deploy AI applications with 9 major providers: OpenAI, Anthropic, Google AI, AWS Bedrock, Azure, Hugging Face, Ollama, and Mistral AI.",
5
5
  "author": {
6
6
  "name": "Juspay Technologies",
@@ -21,8 +21,8 @@
21
21
  "url": "https://github.com/sponsors/juspay"
22
22
  },
23
23
  "engines": {
24
- "node": ">=18.0.0",
25
- "npm": ">=8.0.0",
24
+ "node": ">=20.18.1",
25
+ "npm": ">=10.0.0",
26
26
  "pnpm": ">=8.0.0"
27
27
  },
28
28
  "scripts": {
@@ -192,7 +192,7 @@
192
192
  "p-limit": "^6.2.0",
193
193
  "reconnecting-eventsource": "^1.6.4",
194
194
  "redis": "^5.8.2",
195
- "undici": "^6.21.3",
195
+ "undici": "^7.5.0",
196
196
  "uuid": "^11.1.0",
197
197
  "ws": "^8.18.3",
198
198
  "yargs": "^17.7.2",
@@ -240,7 +240,7 @@
240
240
  "svelte-check": "^4.0.0",
241
241
  "tslib": "^2.4.1",
242
242
  "typescript": "^5.0.0",
243
- "vite": "^6.3.6",
243
+ "vite": "^6.4.1",
244
244
  "vitest": "^2.0.0",
245
245
  "why-is-node-running": "^3.2.2"
246
246
  },
@@ -293,7 +293,8 @@
293
293
  "cookie@<0.7.0": ">=0.7.0",
294
294
  "@eslint/plugin-kit@<0.3.4": ">=0.3.4",
295
295
  "tmp@<=0.2.3": ">=0.2.4",
296
- "axios@<1.8.2": ">=1.8.2"
296
+ "axios@<1.8.2": ">=1.8.2",
297
+ "glob@>=10.3.7 <=11.0.3": ">=11.1.0"
297
298
  }
298
299
  },
299
300
  "os": [