@juspay/neurolink 3.0.0 → 4.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 (192) hide show
  1. package/CHANGELOG.md +62 -4
  2. package/README.md +235 -2
  3. package/dist/agent/direct-tools.d.ts +6 -6
  4. package/dist/chat/client-utils.d.ts +92 -0
  5. package/dist/chat/client-utils.js +298 -0
  6. package/dist/chat/index.d.ts +27 -0
  7. package/dist/chat/index.js +41 -0
  8. package/dist/chat/session-storage.d.ts +77 -0
  9. package/dist/chat/session-storage.js +233 -0
  10. package/dist/chat/session.d.ts +95 -0
  11. package/dist/chat/session.js +257 -0
  12. package/dist/chat/sse-handler.d.ts +49 -0
  13. package/dist/chat/sse-handler.js +266 -0
  14. package/dist/chat/types.d.ts +73 -0
  15. package/dist/chat/types.js +5 -0
  16. package/dist/chat/websocket-chat-handler.d.ts +36 -0
  17. package/dist/chat/websocket-chat-handler.js +262 -0
  18. package/dist/cli/commands/config.js +12 -12
  19. package/dist/cli/commands/mcp.js +3 -4
  20. package/dist/cli/index.d.ts +0 -7
  21. package/dist/cli/index.js +256 -27
  22. package/dist/config/configManager.d.ts +60 -0
  23. package/dist/config/configManager.js +300 -0
  24. package/dist/config/types.d.ts +136 -0
  25. package/dist/config/types.js +43 -0
  26. package/dist/core/analytics.d.ts +23 -0
  27. package/dist/core/analytics.js +131 -0
  28. package/dist/core/constants.d.ts +41 -0
  29. package/dist/core/constants.js +50 -0
  30. package/dist/core/defaults.d.ts +18 -0
  31. package/dist/core/defaults.js +29 -0
  32. package/dist/core/evaluation-config.d.ts +29 -0
  33. package/dist/core/evaluation-config.js +144 -0
  34. package/dist/core/evaluation-providers.d.ts +30 -0
  35. package/dist/core/evaluation-providers.js +187 -0
  36. package/dist/core/evaluation.d.ts +117 -0
  37. package/dist/core/evaluation.js +528 -0
  38. package/dist/core/factory.js +33 -25
  39. package/dist/core/types.d.ts +165 -6
  40. package/dist/core/types.js +3 -4
  41. package/dist/index.d.ts +9 -4
  42. package/dist/index.js +25 -4
  43. package/dist/lib/agent/direct-tools.d.ts +6 -6
  44. package/dist/lib/chat/client-utils.d.ts +92 -0
  45. package/dist/lib/chat/client-utils.js +298 -0
  46. package/dist/lib/chat/index.d.ts +27 -0
  47. package/dist/lib/chat/index.js +41 -0
  48. package/dist/lib/chat/session-storage.d.ts +77 -0
  49. package/dist/lib/chat/session-storage.js +233 -0
  50. package/dist/lib/chat/session.d.ts +95 -0
  51. package/dist/lib/chat/session.js +257 -0
  52. package/dist/lib/chat/sse-handler.d.ts +49 -0
  53. package/dist/lib/chat/sse-handler.js +266 -0
  54. package/dist/lib/chat/types.d.ts +73 -0
  55. package/dist/lib/chat/types.js +5 -0
  56. package/dist/lib/chat/websocket-chat-handler.d.ts +36 -0
  57. package/dist/lib/chat/websocket-chat-handler.js +262 -0
  58. package/dist/lib/config/configManager.d.ts +60 -0
  59. package/dist/lib/config/configManager.js +300 -0
  60. package/dist/lib/config/types.d.ts +136 -0
  61. package/dist/lib/config/types.js +43 -0
  62. package/dist/lib/core/analytics.d.ts +23 -0
  63. package/dist/lib/core/analytics.js +131 -0
  64. package/dist/lib/core/constants.d.ts +41 -0
  65. package/dist/lib/core/constants.js +50 -0
  66. package/dist/lib/core/defaults.d.ts +18 -0
  67. package/dist/lib/core/defaults.js +29 -0
  68. package/dist/lib/core/evaluation-config.d.ts +29 -0
  69. package/dist/lib/core/evaluation-config.js +144 -0
  70. package/dist/lib/core/evaluation-providers.d.ts +30 -0
  71. package/dist/lib/core/evaluation-providers.js +187 -0
  72. package/dist/lib/core/evaluation.d.ts +117 -0
  73. package/dist/lib/core/evaluation.js +528 -0
  74. package/dist/lib/core/factory.js +33 -26
  75. package/dist/lib/core/types.d.ts +165 -6
  76. package/dist/lib/core/types.js +3 -4
  77. package/dist/lib/index.d.ts +9 -4
  78. package/dist/lib/index.js +25 -4
  79. package/dist/lib/mcp/contracts/mcpContract.d.ts +118 -0
  80. package/dist/lib/mcp/contracts/mcpContract.js +5 -0
  81. package/dist/lib/mcp/function-calling.js +11 -3
  82. package/dist/lib/mcp/logging.js +5 -0
  83. package/dist/lib/mcp/neurolink-mcp-client.js +2 -1
  84. package/dist/lib/mcp/orchestrator.js +18 -9
  85. package/dist/lib/mcp/registry.d.ts +49 -16
  86. package/dist/lib/mcp/registry.js +80 -6
  87. package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +5 -4
  88. package/dist/lib/mcp/tool-integration.js +1 -1
  89. package/dist/lib/mcp/tool-registry.d.ts +55 -34
  90. package/dist/lib/mcp/tool-registry.js +111 -97
  91. package/dist/lib/mcp/unified-mcp.js +6 -1
  92. package/dist/lib/mcp/unified-registry.d.ts +12 -4
  93. package/dist/lib/mcp/unified-registry.js +17 -4
  94. package/dist/lib/neurolink.d.ts +28 -0
  95. package/dist/lib/neurolink.js +48 -4
  96. package/dist/lib/providers/agent-enhanced-provider.d.ts +11 -2
  97. package/dist/lib/providers/agent-enhanced-provider.js +86 -15
  98. package/dist/lib/providers/amazonBedrock.d.ts +9 -1
  99. package/dist/lib/providers/amazonBedrock.js +26 -2
  100. package/dist/lib/providers/analytics-helper.d.ts +53 -0
  101. package/dist/lib/providers/analytics-helper.js +151 -0
  102. package/dist/lib/providers/anthropic.d.ts +11 -1
  103. package/dist/lib/providers/anthropic.js +29 -4
  104. package/dist/lib/providers/azureOpenAI.d.ts +3 -1
  105. package/dist/lib/providers/azureOpenAI.js +28 -4
  106. package/dist/lib/providers/function-calling-provider.d.ts +9 -1
  107. package/dist/lib/providers/function-calling-provider.js +14 -1
  108. package/dist/lib/providers/googleAIStudio.d.ts +15 -1
  109. package/dist/lib/providers/googleAIStudio.js +32 -2
  110. package/dist/lib/providers/googleVertexAI.d.ts +9 -1
  111. package/dist/lib/providers/googleVertexAI.js +31 -2
  112. package/dist/lib/providers/huggingFace.d.ts +3 -1
  113. package/dist/lib/providers/huggingFace.js +26 -3
  114. package/dist/lib/providers/mcp-provider.d.ts +9 -1
  115. package/dist/lib/providers/mcp-provider.js +12 -0
  116. package/dist/lib/providers/mistralAI.d.ts +3 -1
  117. package/dist/lib/providers/mistralAI.js +25 -2
  118. package/dist/lib/providers/ollama.d.ts +3 -1
  119. package/dist/lib/providers/ollama.js +27 -4
  120. package/dist/lib/providers/openAI.d.ts +15 -1
  121. package/dist/lib/providers/openAI.js +32 -2
  122. package/dist/lib/proxy/proxy-fetch.js +8 -7
  123. package/dist/lib/services/streaming/streaming-manager.d.ts +29 -0
  124. package/dist/lib/services/streaming/streaming-manager.js +244 -0
  125. package/dist/lib/services/types.d.ts +155 -0
  126. package/dist/lib/services/types.js +2 -0
  127. package/dist/lib/services/websocket/websocket-server.d.ts +34 -0
  128. package/dist/lib/services/websocket/websocket-server.js +304 -0
  129. package/dist/lib/telemetry/index.d.ts +15 -0
  130. package/dist/lib/telemetry/index.js +22 -0
  131. package/dist/lib/telemetry/telemetry-service.d.ts +47 -0
  132. package/dist/lib/telemetry/telemetry-service.js +259 -0
  133. package/dist/lib/utils/streaming-utils.d.ts +67 -0
  134. package/dist/lib/utils/streaming-utils.js +201 -0
  135. package/dist/mcp/contracts/mcpContract.d.ts +118 -0
  136. package/dist/mcp/contracts/mcpContract.js +5 -0
  137. package/dist/mcp/function-calling.js +11 -3
  138. package/dist/mcp/logging.js +5 -0
  139. package/dist/mcp/neurolink-mcp-client.js +2 -1
  140. package/dist/mcp/orchestrator.js +18 -9
  141. package/dist/mcp/registry.d.ts +49 -16
  142. package/dist/mcp/registry.js +80 -6
  143. package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
  144. package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +5 -4
  145. package/dist/mcp/tool-integration.js +1 -1
  146. package/dist/mcp/tool-registry.d.ts +55 -34
  147. package/dist/mcp/tool-registry.js +111 -97
  148. package/dist/mcp/unified-mcp.js +6 -1
  149. package/dist/mcp/unified-registry.d.ts +12 -4
  150. package/dist/mcp/unified-registry.js +17 -4
  151. package/dist/neurolink.d.ts +28 -0
  152. package/dist/neurolink.js +48 -4
  153. package/dist/providers/agent-enhanced-provider.d.ts +11 -2
  154. package/dist/providers/agent-enhanced-provider.js +86 -15
  155. package/dist/providers/amazonBedrock.d.ts +9 -1
  156. package/dist/providers/amazonBedrock.js +26 -2
  157. package/dist/providers/analytics-helper.d.ts +53 -0
  158. package/dist/providers/analytics-helper.js +151 -0
  159. package/dist/providers/anthropic.d.ts +11 -1
  160. package/dist/providers/anthropic.js +29 -4
  161. package/dist/providers/azureOpenAI.d.ts +3 -1
  162. package/dist/providers/azureOpenAI.js +29 -4
  163. package/dist/providers/function-calling-provider.d.ts +9 -1
  164. package/dist/providers/function-calling-provider.js +14 -1
  165. package/dist/providers/googleAIStudio.d.ts +15 -1
  166. package/dist/providers/googleAIStudio.js +32 -2
  167. package/dist/providers/googleVertexAI.d.ts +9 -1
  168. package/dist/providers/googleVertexAI.js +31 -2
  169. package/dist/providers/huggingFace.d.ts +3 -1
  170. package/dist/providers/huggingFace.js +26 -3
  171. package/dist/providers/mcp-provider.d.ts +9 -1
  172. package/dist/providers/mcp-provider.js +12 -0
  173. package/dist/providers/mistralAI.d.ts +3 -1
  174. package/dist/providers/mistralAI.js +25 -2
  175. package/dist/providers/ollama.d.ts +3 -1
  176. package/dist/providers/ollama.js +27 -4
  177. package/dist/providers/openAI.d.ts +15 -1
  178. package/dist/providers/openAI.js +33 -2
  179. package/dist/proxy/proxy-fetch.js +8 -7
  180. package/dist/services/streaming/streaming-manager.d.ts +29 -0
  181. package/dist/services/streaming/streaming-manager.js +244 -0
  182. package/dist/services/types.d.ts +155 -0
  183. package/dist/services/types.js +2 -0
  184. package/dist/services/websocket/websocket-server.d.ts +34 -0
  185. package/dist/services/websocket/websocket-server.js +304 -0
  186. package/dist/telemetry/index.d.ts +15 -0
  187. package/dist/telemetry/index.js +22 -0
  188. package/dist/telemetry/telemetry-service.d.ts +47 -0
  189. package/dist/telemetry/telemetry-service.js +261 -0
  190. package/dist/utils/streaming-utils.d.ts +67 -0
  191. package/dist/utils/streaming-utils.js +201 -0
  192. package/package.json +18 -2
@@ -0,0 +1,304 @@
1
+ import { WebSocketServer, WebSocket } from "ws";
2
+ import { EventEmitter } from "events";
3
+ import { randomUUID } from "crypto";
4
+ export class NeuroLinkWebSocketServer extends EventEmitter {
5
+ wss;
6
+ connections = new Map();
7
+ connectionInfo = new Map();
8
+ rooms = new Map();
9
+ streamingChannels = new Map();
10
+ heartbeatInterval;
11
+ options;
12
+ constructor(options = {}) {
13
+ super();
14
+ this.options = {
15
+ port: options.port || 8080,
16
+ maxConnections: options.maxConnections || 1000,
17
+ heartbeatInterval: options.heartbeatInterval || 30000,
18
+ enableCompression: options.enableCompression ?? true,
19
+ enableBackpressure: options.enableBackpressure ?? true,
20
+ bufferSize: options.bufferSize || 8192,
21
+ timeoutMs: options.timeoutMs || 30000,
22
+ };
23
+ this.wss = new WebSocketServer({
24
+ port: this.options.port,
25
+ perMessageDeflate: this.options.enableCompression,
26
+ });
27
+ this.setupEventHandlers();
28
+ this.startHeartbeat();
29
+ }
30
+ setupEventHandlers() {
31
+ this.wss.on("connection", (ws, request) => {
32
+ this.handleConnection(ws, request);
33
+ });
34
+ this.wss.on("error", (error) => {
35
+ console.error("[WebSocket Server] Error:", error);
36
+ this.emit("error", error);
37
+ });
38
+ }
39
+ handleConnection(ws, request) {
40
+ if (this.connections.size >= this.options.maxConnections) {
41
+ ws.close(1013, "Server at capacity");
42
+ return;
43
+ }
44
+ const connectionId = randomUUID();
45
+ const userAgent = request.headers["user-agent"];
46
+ const ipAddress = request.socket.remoteAddress;
47
+ // Store connection
48
+ this.connections.set(connectionId, ws);
49
+ this.connectionInfo.set(connectionId, {
50
+ id: connectionId,
51
+ userAgent,
52
+ ipAddress,
53
+ connectedAt: Date.now(),
54
+ lastActivity: Date.now(),
55
+ rooms: new Set(),
56
+ subscriptions: new Set(),
57
+ metadata: {},
58
+ });
59
+ // Setup WebSocket event handlers
60
+ ws.on("message", (data) => {
61
+ this.handleMessage(connectionId, data);
62
+ });
63
+ ws.on("close", () => {
64
+ this.handleDisconnection(connectionId);
65
+ });
66
+ ws.on("error", (error) => {
67
+ console.error(`[WebSocket] Connection ${connectionId} error:`, error);
68
+ this.handleDisconnection(connectionId);
69
+ });
70
+ // Send connection confirmation
71
+ this.sendMessage(connectionId, {
72
+ id: randomUUID(),
73
+ type: "system",
74
+ connectionId,
75
+ timestamp: Date.now(),
76
+ data: {
77
+ event: "connected",
78
+ connectionId,
79
+ serverInfo: {
80
+ version: "3.0.1",
81
+ features: ["streaming", "rooms", "tools"],
82
+ },
83
+ },
84
+ });
85
+ console.log(`[WebSocket] New connection: ${connectionId} (${this.connections.size}/${this.options.maxConnections})`);
86
+ this.emit("connection", { connectionId, userAgent, ipAddress });
87
+ }
88
+ handleMessage(connectionId, data) {
89
+ try {
90
+ const message = JSON.parse(data.toString());
91
+ this.updateLastActivity(connectionId);
92
+ switch (message.type) {
93
+ case "heartbeat":
94
+ this.handleHeartbeat(connectionId);
95
+ break;
96
+ case "chat":
97
+ this.handleChatMessage(connectionId, message);
98
+ break;
99
+ default:
100
+ this.emit("message", { connectionId, message });
101
+ }
102
+ }
103
+ catch (error) {
104
+ console.error(`[WebSocket] Invalid message from ${connectionId}:`, error);
105
+ this.sendError(connectionId, "Invalid message format");
106
+ }
107
+ }
108
+ handleDisconnection(connectionId) {
109
+ const connection = this.connectionInfo.get(connectionId);
110
+ if (!connection) {
111
+ return;
112
+ }
113
+ // Leave all rooms
114
+ connection.rooms.forEach((roomId) => {
115
+ this.leaveRoom(connectionId, roomId);
116
+ });
117
+ // Close streaming channels
118
+ this.streamingChannels.forEach((channel, channelId) => {
119
+ if (channel.connectionId === connectionId) {
120
+ channel.onClose();
121
+ this.streamingChannels.delete(channelId);
122
+ }
123
+ });
124
+ // Clean up
125
+ this.connections.delete(connectionId);
126
+ this.connectionInfo.delete(connectionId);
127
+ console.log(`[WebSocket] Disconnected: ${connectionId} (${this.connections.size}/${this.options.maxConnections})`);
128
+ this.emit("disconnection", { connectionId });
129
+ }
130
+ // Room Management
131
+ joinRoom(connectionId, roomId) {
132
+ const connection = this.connectionInfo.get(connectionId);
133
+ if (!connection) {
134
+ return false;
135
+ }
136
+ if (!this.rooms.has(roomId)) {
137
+ this.rooms.set(roomId, new Set());
138
+ }
139
+ this.rooms.get(roomId).add(connectionId);
140
+ connection.rooms.add(roomId);
141
+ this.sendMessage(connectionId, {
142
+ id: randomUUID(),
143
+ type: "system",
144
+ connectionId,
145
+ roomId,
146
+ timestamp: Date.now(),
147
+ data: {
148
+ event: "room_joined",
149
+ roomId,
150
+ memberCount: this.rooms.get(roomId).size,
151
+ },
152
+ });
153
+ console.log(`[WebSocket] ${connectionId} joined room ${roomId}`);
154
+ return true;
155
+ }
156
+ leaveRoom(connectionId, roomId) {
157
+ const connection = this.connectionInfo.get(connectionId);
158
+ if (!connection) {
159
+ return false;
160
+ }
161
+ const room = this.rooms.get(roomId);
162
+ if (!room) {
163
+ return false;
164
+ }
165
+ room.delete(connectionId);
166
+ connection.rooms.delete(roomId);
167
+ if (room.size === 0) {
168
+ this.rooms.delete(roomId);
169
+ }
170
+ this.sendMessage(connectionId, {
171
+ id: randomUUID(),
172
+ type: "system",
173
+ connectionId,
174
+ roomId,
175
+ timestamp: Date.now(),
176
+ data: {
177
+ event: "room_left",
178
+ roomId,
179
+ memberCount: room.size,
180
+ },
181
+ });
182
+ console.log(`[WebSocket] ${connectionId} left room ${roomId}`);
183
+ return true;
184
+ }
185
+ broadcastToRoom(roomId, message) {
186
+ const room = this.rooms.get(roomId);
187
+ if (!room) {
188
+ return;
189
+ }
190
+ room.forEach((connectionId) => {
191
+ this.sendMessage(connectionId, { ...message, roomId });
192
+ });
193
+ }
194
+ // Streaming Support
195
+ createStreamingChannel(connectionId, channelId) {
196
+ const channel = {
197
+ id: channelId,
198
+ connectionId,
199
+ type: "ai-response",
200
+ status: "open",
201
+ buffer: {
202
+ data: [],
203
+ maxSize: this.options.bufferSize,
204
+ currentSize: 0,
205
+ flushThreshold: Math.floor(this.options.bufferSize * 0.8),
206
+ lastFlush: Date.now(),
207
+ },
208
+ onData: (data) => this.handleChannelData(channelId, data),
209
+ onError: (error) => this.handleChannelError(channelId, error),
210
+ onClose: () => this.handleChannelClose(channelId),
211
+ };
212
+ this.streamingChannels.set(channelId, channel);
213
+ return channel;
214
+ }
215
+ destroyStreamingChannel(channelId) {
216
+ const channel = this.streamingChannels.get(channelId);
217
+ if (channel) {
218
+ channel.status = "closed";
219
+ channel.onClose();
220
+ this.streamingChannels.delete(channelId);
221
+ }
222
+ }
223
+ // Helper methods
224
+ sendMessage(connectionId, message) {
225
+ const ws = this.connections.get(connectionId);
226
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
227
+ return false;
228
+ }
229
+ try {
230
+ ws.send(JSON.stringify(message));
231
+ return true;
232
+ }
233
+ catch (error) {
234
+ console.error(`[WebSocket] Failed to send message to ${connectionId}:`, error);
235
+ return false;
236
+ }
237
+ }
238
+ sendError(connectionId, errorMessage) {
239
+ this.sendMessage(connectionId, {
240
+ id: randomUUID(),
241
+ type: "error",
242
+ connectionId,
243
+ timestamp: Date.now(),
244
+ data: { error: errorMessage },
245
+ });
246
+ }
247
+ updateLastActivity(connectionId) {
248
+ const connection = this.connectionInfo.get(connectionId);
249
+ if (connection) {
250
+ connection.lastActivity = Date.now();
251
+ }
252
+ }
253
+ handleHeartbeat(connectionId) {
254
+ this.sendMessage(connectionId, {
255
+ id: randomUUID(),
256
+ type: "heartbeat",
257
+ connectionId,
258
+ timestamp: Date.now(),
259
+ data: { pong: true },
260
+ });
261
+ }
262
+ handleChatMessage(connectionId, message) {
263
+ this.emit("chat-message", { connectionId, message });
264
+ }
265
+ startHeartbeat() {
266
+ this.heartbeatInterval = setInterval(() => {
267
+ this.connections.forEach((ws, connectionId) => {
268
+ if (ws.readyState === WebSocket.OPEN) {
269
+ const connection = this.connectionInfo.get(connectionId);
270
+ if (connection &&
271
+ Date.now() - connection.lastActivity > this.options.timeoutMs) {
272
+ console.log(`[WebSocket] Timeout for connection ${connectionId}`);
273
+ ws.terminate();
274
+ }
275
+ }
276
+ });
277
+ }, this.options.heartbeatInterval);
278
+ }
279
+ // Public getters
280
+ getConnectionCount() {
281
+ return this.connections.size;
282
+ }
283
+ getRoomCount() {
284
+ return this.rooms.size;
285
+ }
286
+ getActiveChannels() {
287
+ return this.streamingChannels.size;
288
+ }
289
+ close() {
290
+ if (this.heartbeatInterval) {
291
+ clearInterval(this.heartbeatInterval);
292
+ }
293
+ this.wss.close();
294
+ }
295
+ handleChannelData(channelId, data) {
296
+ // Implementation for channel data handling
297
+ }
298
+ handleChannelError(channelId, error) {
299
+ console.error(`[Streaming Channel] ${channelId} error:`, error);
300
+ }
301
+ handleChannelClose(channelId) {
302
+ console.log(`[Streaming Channel] ${channelId} closed`);
303
+ }
304
+ }
@@ -0,0 +1,15 @@
1
+ export { TelemetryService, type HealthMetrics } from "./telemetry-service.js";
2
+ /**
3
+ * Initialize telemetry for NeuroLink
4
+ * OPTIONAL - Only works when NEUROLINK_TELEMETRY_ENABLED=true
5
+ */
6
+ export declare function initializeTelemetry(): Promise<import("./telemetry-service.js").TelemetryService>;
7
+ /**
8
+ * Get telemetry status
9
+ */
10
+ export declare function getTelemetryStatus(): Promise<{
11
+ enabled: boolean;
12
+ endpoint?: string;
13
+ service?: string;
14
+ version?: string;
15
+ }>;
@@ -0,0 +1,22 @@
1
+ // Optional Telemetry Infrastructure (Phase 2)
2
+ export { TelemetryService } from "./telemetry-service.js";
3
+ /**
4
+ * Initialize telemetry for NeuroLink
5
+ * OPTIONAL - Only works when NEUROLINK_TELEMETRY_ENABLED=true
6
+ */
7
+ export async function initializeTelemetry() {
8
+ const { TelemetryService } = await import("./telemetry-service.js");
9
+ const telemetry = TelemetryService.getInstance();
10
+ if (telemetry.isEnabled()) {
11
+ await telemetry.initialize();
12
+ console.log("[NeuroLink] Telemetry initialized");
13
+ }
14
+ return telemetry;
15
+ }
16
+ /**
17
+ * Get telemetry status
18
+ */
19
+ export async function getTelemetryStatus() {
20
+ const { TelemetryService } = await import("./telemetry-service.js");
21
+ return TelemetryService.getInstance().getStatus();
22
+ }
@@ -0,0 +1,47 @@
1
+ export interface HealthMetrics {
2
+ timestamp: number;
3
+ memoryUsage: NodeJS.MemoryUsage;
4
+ uptime: number;
5
+ activeConnections: number;
6
+ errorRate: number;
7
+ averageResponseTime: number;
8
+ }
9
+ export declare class TelemetryService {
10
+ private static instance;
11
+ private sdk?;
12
+ private enabled;
13
+ private meter?;
14
+ private tracer?;
15
+ private aiRequestCounter?;
16
+ private aiRequestDuration?;
17
+ private aiTokensUsed?;
18
+ private aiProviderErrors?;
19
+ private mcpToolCalls?;
20
+ private connectionCounter?;
21
+ private responseTimeHistogram?;
22
+ private constructor();
23
+ static getInstance(): TelemetryService;
24
+ private isTelemetryEnabled;
25
+ private initializeTelemetry;
26
+ private initializeMetrics;
27
+ initialize(): Promise<void>;
28
+ traceAIRequest<T>(provider: string, operation: () => Promise<T>): Promise<T>;
29
+ recordAIRequest(provider: string, model: string, tokens: number, duration: number): void;
30
+ recordAIError(provider: string, error: Error): void;
31
+ recordMCPToolCall(toolName: string, duration: number, success: boolean): void;
32
+ recordConnection(type: "websocket" | "sse" | "http"): void;
33
+ recordResponseTime(endpoint: string, method: string, duration: number): void;
34
+ recordCustomMetric(name: string, value: number, labels?: Record<string, string>): void;
35
+ recordCustomHistogram(name: string, value: number, labels?: Record<string, string>): void;
36
+ getHealthMetrics(): Promise<HealthMetrics>;
37
+ isEnabled(): boolean;
38
+ getStatus(): {
39
+ enabled: boolean;
40
+ endpoint?: string;
41
+ service?: string;
42
+ version?: string;
43
+ };
44
+ private getDurationBucket;
45
+ private getStatusBucket;
46
+ shutdown(): Promise<void>;
47
+ }
@@ -0,0 +1,259 @@
1
+ import { NodeSDK } from "@opentelemetry/sdk-node";
2
+ import { metrics, trace } from "@opentelemetry/api";
3
+ import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
4
+ import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
5
+ import { Resource } from "@opentelemetry/resources";
6
+ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION, } from "@opentelemetry/semantic-conventions";
7
+ export class TelemetryService {
8
+ static instance;
9
+ sdk;
10
+ enabled = false;
11
+ meter;
12
+ tracer;
13
+ // Optional Metrics (only created when enabled)
14
+ aiRequestCounter;
15
+ aiRequestDuration;
16
+ aiTokensUsed;
17
+ aiProviderErrors;
18
+ mcpToolCalls;
19
+ connectionCounter;
20
+ responseTimeHistogram;
21
+ constructor() {
22
+ // Check if telemetry is enabled
23
+ this.enabled = this.isTelemetryEnabled();
24
+ if (this.enabled) {
25
+ this.initializeTelemetry();
26
+ }
27
+ else {
28
+ console.log("[Telemetry] Disabled - set NEUROLINK_TELEMETRY_ENABLED=true or configure OTEL_EXPORTER_OTLP_ENDPOINT to enable");
29
+ }
30
+ }
31
+ static getInstance() {
32
+ if (!TelemetryService.instance) {
33
+ TelemetryService.instance = new TelemetryService();
34
+ }
35
+ return TelemetryService.instance;
36
+ }
37
+ isTelemetryEnabled() {
38
+ return (process.env.NEUROLINK_TELEMETRY_ENABLED === "true" ||
39
+ process.env.OTEL_EXPORTER_OTLP_ENDPOINT !== undefined);
40
+ }
41
+ initializeTelemetry() {
42
+ try {
43
+ const resource = new Resource({
44
+ [ATTR_SERVICE_NAME]: process.env.OTEL_SERVICE_NAME || "neurolink-ai",
45
+ [ATTR_SERVICE_VERSION]: process.env.OTEL_SERVICE_VERSION || "3.0.1",
46
+ });
47
+ this.sdk = new NodeSDK({
48
+ resource,
49
+ traceExporter: new OTLPTraceExporter({
50
+ url: process.env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT ||
51
+ `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT}/v1/traces`,
52
+ }),
53
+ // Note: Metric reader configured separately
54
+ instrumentations: [getNodeAutoInstrumentations()],
55
+ });
56
+ this.meter = metrics.getMeter("neurolink-ai");
57
+ this.tracer = trace.getTracer("neurolink-ai");
58
+ this.initializeMetrics();
59
+ console.log("[Telemetry] Initialized with endpoint:", process.env.OTEL_EXPORTER_OTLP_ENDPOINT);
60
+ }
61
+ catch (error) {
62
+ console.error("[Telemetry] Failed to initialize:", error);
63
+ this.enabled = false;
64
+ }
65
+ }
66
+ initializeMetrics() {
67
+ if (!this.enabled || !this.meter) {
68
+ return;
69
+ }
70
+ this.aiRequestCounter = this.meter.createCounter("ai_requests_total", {
71
+ description: "Total number of AI requests",
72
+ });
73
+ this.aiRequestDuration = this.meter.createHistogram("ai_request_duration_ms", {
74
+ description: "AI request duration in milliseconds",
75
+ });
76
+ this.aiTokensUsed = this.meter.createCounter("ai_tokens_used_total", {
77
+ description: "Total number of AI tokens used",
78
+ });
79
+ this.aiProviderErrors = this.meter.createCounter("ai_provider_errors_total", {
80
+ description: "Total number of AI provider errors",
81
+ });
82
+ this.mcpToolCalls = this.meter.createCounter("mcp_tool_calls_total", {
83
+ description: "Total number of MCP tool calls",
84
+ });
85
+ this.connectionCounter = this.meter.createCounter("connections_total", {
86
+ description: "Total number of connections",
87
+ });
88
+ this.responseTimeHistogram = this.meter.createHistogram("response_time_ms", {
89
+ description: "Response time in milliseconds",
90
+ });
91
+ }
92
+ async initialize() {
93
+ if (!this.enabled) {
94
+ return;
95
+ }
96
+ try {
97
+ await this.sdk?.start();
98
+ console.log("[Telemetry] SDK started successfully");
99
+ }
100
+ catch (error) {
101
+ console.error("[Telemetry] Failed to start SDK:", error);
102
+ this.enabled = false;
103
+ }
104
+ }
105
+ // AI Operation Tracing (NO-OP when disabled)
106
+ async traceAIRequest(provider, operation) {
107
+ if (!this.enabled || !this.tracer) {
108
+ return await operation(); // Direct execution when disabled
109
+ }
110
+ const span = this.tracer.startSpan(`ai.${provider}.generate_text`, {
111
+ attributes: {
112
+ "ai.provider": provider,
113
+ "ai.operation": "generate_text",
114
+ },
115
+ });
116
+ try {
117
+ const result = await operation();
118
+ span.setStatus({ code: 1 }); // OK
119
+ return result;
120
+ }
121
+ catch (error) {
122
+ span.setStatus({
123
+ code: 2,
124
+ message: error instanceof Error ? error.message : "Unknown error",
125
+ }); // ERROR
126
+ span.recordException(error);
127
+ throw error;
128
+ }
129
+ finally {
130
+ span.end();
131
+ }
132
+ }
133
+ // Metrics Recording (NO-OP when disabled)
134
+ recordAIRequest(provider, model, tokens, duration) {
135
+ if (!this.enabled || !this.aiRequestCounter) {
136
+ return;
137
+ }
138
+ const labels = { provider, model };
139
+ this.aiRequestCounter.add(1, labels);
140
+ this.aiRequestDuration?.record(duration, labels);
141
+ this.aiTokensUsed?.add(tokens, labels);
142
+ }
143
+ recordAIError(provider, error) {
144
+ if (!this.enabled || !this.aiProviderErrors) {
145
+ return;
146
+ }
147
+ this.aiProviderErrors.add(1, {
148
+ provider,
149
+ error: error.name,
150
+ message: error.message.substring(0, 100), // Limit message length
151
+ });
152
+ }
153
+ recordMCPToolCall(toolName, duration, success) {
154
+ if (!this.enabled || !this.mcpToolCalls) {
155
+ return;
156
+ }
157
+ this.mcpToolCalls.add(1, {
158
+ tool: toolName,
159
+ success: success.toString(),
160
+ duration_bucket: this.getDurationBucket(duration),
161
+ });
162
+ }
163
+ recordConnection(type) {
164
+ if (!this.enabled || !this.connectionCounter) {
165
+ return;
166
+ }
167
+ this.connectionCounter.add(1, { connection_type: type });
168
+ }
169
+ recordResponseTime(endpoint, method, duration) {
170
+ if (!this.enabled || !this.responseTimeHistogram) {
171
+ return;
172
+ }
173
+ this.responseTimeHistogram.record(duration, {
174
+ endpoint,
175
+ method,
176
+ status_bucket: this.getStatusBucket(duration),
177
+ });
178
+ }
179
+ // Custom Metrics
180
+ recordCustomMetric(name, value, labels) {
181
+ if (!this.enabled || !this.meter) {
182
+ return;
183
+ }
184
+ const counter = this.meter.createCounter(`custom_${name}`, {
185
+ description: `Custom metric: ${name}`,
186
+ });
187
+ counter.add(value, labels || {});
188
+ }
189
+ recordCustomHistogram(name, value, labels) {
190
+ if (!this.enabled || !this.meter) {
191
+ return;
192
+ }
193
+ const histogram = this.meter.createHistogram(`custom_${name}_histogram`, {
194
+ description: `Custom histogram: ${name}`,
195
+ });
196
+ histogram.record(value, labels || {});
197
+ }
198
+ // Health Checks
199
+ async getHealthMetrics() {
200
+ const memoryUsage = process.memoryUsage();
201
+ return {
202
+ timestamp: Date.now(),
203
+ memoryUsage,
204
+ uptime: process.uptime(),
205
+ activeConnections: 0, // Would need to be provided by calling code
206
+ errorRate: 0, // Would need to be calculated from metrics
207
+ averageResponseTime: 0, // Would need to be calculated from metrics
208
+ };
209
+ }
210
+ // Telemetry Status
211
+ isEnabled() {
212
+ return this.enabled;
213
+ }
214
+ getStatus() {
215
+ return {
216
+ enabled: this.enabled,
217
+ endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
218
+ service: process.env.OTEL_SERVICE_NAME || "neurolink-ai",
219
+ version: process.env.OTEL_SERVICE_VERSION || "3.0.1",
220
+ };
221
+ }
222
+ // Helper methods
223
+ getDurationBucket(duration) {
224
+ if (duration < 100) {
225
+ return "fast";
226
+ }
227
+ if (duration < 500) {
228
+ return "medium";
229
+ }
230
+ if (duration < 1000) {
231
+ return "slow";
232
+ }
233
+ return "very_slow";
234
+ }
235
+ getStatusBucket(duration) {
236
+ if (duration < 200) {
237
+ return "excellent";
238
+ }
239
+ if (duration < 500) {
240
+ return "good";
241
+ }
242
+ if (duration < 1000) {
243
+ return "acceptable";
244
+ }
245
+ return "poor";
246
+ }
247
+ // Cleanup
248
+ async shutdown() {
249
+ if (this.enabled && this.sdk) {
250
+ try {
251
+ await this.sdk.shutdown();
252
+ console.log("[Telemetry] SDK shutdown completed");
253
+ }
254
+ catch (error) {
255
+ console.error("[Telemetry] Error during shutdown:", error);
256
+ }
257
+ }
258
+ }
259
+ }