@juspay/neurolink 3.0.1 → 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.
- package/CHANGELOG.md +57 -6
- package/README.md +235 -2
- package/dist/agent/direct-tools.d.ts +6 -6
- package/dist/chat/client-utils.d.ts +92 -0
- package/dist/chat/client-utils.js +298 -0
- package/dist/chat/index.d.ts +27 -0
- package/dist/chat/index.js +41 -0
- package/dist/chat/session-storage.d.ts +77 -0
- package/dist/chat/session-storage.js +233 -0
- package/dist/chat/session.d.ts +95 -0
- package/dist/chat/session.js +257 -0
- package/dist/chat/sse-handler.d.ts +49 -0
- package/dist/chat/sse-handler.js +266 -0
- package/dist/chat/types.d.ts +73 -0
- package/dist/chat/types.js +5 -0
- package/dist/chat/websocket-chat-handler.d.ts +36 -0
- package/dist/chat/websocket-chat-handler.js +262 -0
- package/dist/cli/commands/config.js +12 -12
- package/dist/cli/commands/mcp.js +3 -4
- package/dist/cli/index.d.ts +0 -7
- package/dist/cli/index.js +247 -28
- package/dist/config/configManager.d.ts +60 -0
- package/dist/config/configManager.js +300 -0
- package/dist/config/types.d.ts +136 -0
- package/dist/config/types.js +43 -0
- package/dist/core/analytics.d.ts +23 -0
- package/dist/core/analytics.js +131 -0
- package/dist/core/constants.d.ts +41 -0
- package/dist/core/constants.js +50 -0
- package/dist/core/defaults.d.ts +18 -0
- package/dist/core/defaults.js +29 -0
- package/dist/core/evaluation-config.d.ts +29 -0
- package/dist/core/evaluation-config.js +144 -0
- package/dist/core/evaluation-providers.d.ts +30 -0
- package/dist/core/evaluation-providers.js +187 -0
- package/dist/core/evaluation.d.ts +117 -0
- package/dist/core/evaluation.js +528 -0
- package/dist/core/factory.js +33 -25
- package/dist/core/types.d.ts +165 -6
- package/dist/core/types.js +3 -4
- package/dist/index.d.ts +9 -4
- package/dist/index.js +25 -4
- package/dist/lib/agent/direct-tools.d.ts +6 -6
- package/dist/lib/chat/client-utils.d.ts +92 -0
- package/dist/lib/chat/client-utils.js +298 -0
- package/dist/lib/chat/index.d.ts +27 -0
- package/dist/lib/chat/index.js +41 -0
- package/dist/lib/chat/session-storage.d.ts +77 -0
- package/dist/lib/chat/session-storage.js +233 -0
- package/dist/lib/chat/session.d.ts +95 -0
- package/dist/lib/chat/session.js +257 -0
- package/dist/lib/chat/sse-handler.d.ts +49 -0
- package/dist/lib/chat/sse-handler.js +266 -0
- package/dist/lib/chat/types.d.ts +73 -0
- package/dist/lib/chat/types.js +5 -0
- package/dist/lib/chat/websocket-chat-handler.d.ts +36 -0
- package/dist/lib/chat/websocket-chat-handler.js +262 -0
- package/dist/lib/config/configManager.d.ts +60 -0
- package/dist/lib/config/configManager.js +300 -0
- package/dist/lib/config/types.d.ts +136 -0
- package/dist/lib/config/types.js +43 -0
- package/dist/lib/core/analytics.d.ts +23 -0
- package/dist/lib/core/analytics.js +131 -0
- package/dist/lib/core/constants.d.ts +41 -0
- package/dist/lib/core/constants.js +50 -0
- package/dist/lib/core/defaults.d.ts +18 -0
- package/dist/lib/core/defaults.js +29 -0
- package/dist/lib/core/evaluation-config.d.ts +29 -0
- package/dist/lib/core/evaluation-config.js +144 -0
- package/dist/lib/core/evaluation-providers.d.ts +30 -0
- package/dist/lib/core/evaluation-providers.js +187 -0
- package/dist/lib/core/evaluation.d.ts +117 -0
- package/dist/lib/core/evaluation.js +528 -0
- package/dist/lib/core/factory.js +33 -26
- package/dist/lib/core/types.d.ts +165 -6
- package/dist/lib/core/types.js +3 -4
- package/dist/lib/index.d.ts +9 -4
- package/dist/lib/index.js +25 -4
- package/dist/lib/mcp/contracts/mcpContract.d.ts +118 -0
- package/dist/lib/mcp/contracts/mcpContract.js +5 -0
- package/dist/lib/mcp/function-calling.js +11 -3
- package/dist/lib/mcp/logging.js +5 -0
- package/dist/lib/mcp/neurolink-mcp-client.js +2 -1
- package/dist/lib/mcp/orchestrator.js +18 -9
- package/dist/lib/mcp/registry.d.ts +49 -16
- package/dist/lib/mcp/registry.js +80 -6
- package/dist/lib/mcp/servers/ai-providers/ai-workflow-tools.js +5 -4
- package/dist/lib/mcp/tool-integration.js +1 -1
- package/dist/lib/mcp/tool-registry.d.ts +55 -34
- package/dist/lib/mcp/tool-registry.js +111 -97
- package/dist/lib/mcp/unified-mcp.js +6 -1
- package/dist/lib/mcp/unified-registry.d.ts +12 -4
- package/dist/lib/mcp/unified-registry.js +17 -4
- package/dist/lib/neurolink.d.ts +26 -0
- package/dist/lib/neurolink.js +43 -1
- package/dist/lib/providers/agent-enhanced-provider.d.ts +11 -2
- package/dist/lib/providers/agent-enhanced-provider.js +86 -15
- package/dist/lib/providers/amazonBedrock.d.ts +9 -1
- package/dist/lib/providers/amazonBedrock.js +26 -2
- package/dist/lib/providers/analytics-helper.d.ts +53 -0
- package/dist/lib/providers/analytics-helper.js +151 -0
- package/dist/lib/providers/anthropic.d.ts +11 -1
- package/dist/lib/providers/anthropic.js +29 -4
- package/dist/lib/providers/azureOpenAI.d.ts +3 -1
- package/dist/lib/providers/azureOpenAI.js +28 -4
- package/dist/lib/providers/function-calling-provider.d.ts +9 -1
- package/dist/lib/providers/function-calling-provider.js +14 -1
- package/dist/lib/providers/googleAIStudio.d.ts +15 -1
- package/dist/lib/providers/googleAIStudio.js +32 -2
- package/dist/lib/providers/googleVertexAI.d.ts +9 -1
- package/dist/lib/providers/googleVertexAI.js +31 -2
- package/dist/lib/providers/huggingFace.d.ts +3 -1
- package/dist/lib/providers/huggingFace.js +26 -3
- package/dist/lib/providers/mcp-provider.d.ts +9 -1
- package/dist/lib/providers/mcp-provider.js +12 -0
- package/dist/lib/providers/mistralAI.d.ts +3 -1
- package/dist/lib/providers/mistralAI.js +25 -2
- package/dist/lib/providers/ollama.d.ts +3 -1
- package/dist/lib/providers/ollama.js +27 -4
- package/dist/lib/providers/openAI.d.ts +15 -1
- package/dist/lib/providers/openAI.js +32 -2
- package/dist/lib/proxy/proxy-fetch.js +8 -7
- package/dist/lib/services/streaming/streaming-manager.d.ts +29 -0
- package/dist/lib/services/streaming/streaming-manager.js +244 -0
- package/dist/lib/services/types.d.ts +155 -0
- package/dist/lib/services/types.js +2 -0
- package/dist/lib/services/websocket/websocket-server.d.ts +34 -0
- package/dist/lib/services/websocket/websocket-server.js +304 -0
- package/dist/lib/telemetry/index.d.ts +15 -0
- package/dist/lib/telemetry/index.js +22 -0
- package/dist/lib/telemetry/telemetry-service.d.ts +47 -0
- package/dist/lib/telemetry/telemetry-service.js +259 -0
- package/dist/lib/utils/streaming-utils.d.ts +67 -0
- package/dist/lib/utils/streaming-utils.js +201 -0
- package/dist/mcp/contracts/mcpContract.d.ts +118 -0
- package/dist/mcp/contracts/mcpContract.js +5 -0
- package/dist/mcp/function-calling.js +11 -3
- package/dist/mcp/logging.js +5 -0
- package/dist/mcp/neurolink-mcp-client.js +2 -1
- package/dist/mcp/orchestrator.js +18 -9
- package/dist/mcp/registry.d.ts +49 -16
- package/dist/mcp/registry.js +80 -6
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.d.ts +2 -2
- package/dist/mcp/servers/ai-providers/ai-workflow-tools.js +5 -4
- package/dist/mcp/tool-integration.js +1 -1
- package/dist/mcp/tool-registry.d.ts +55 -34
- package/dist/mcp/tool-registry.js +111 -97
- package/dist/mcp/unified-mcp.js +6 -1
- package/dist/mcp/unified-registry.d.ts +12 -4
- package/dist/mcp/unified-registry.js +17 -4
- package/dist/neurolink.d.ts +26 -0
- package/dist/neurolink.js +43 -1
- package/dist/providers/agent-enhanced-provider.d.ts +11 -2
- package/dist/providers/agent-enhanced-provider.js +86 -15
- package/dist/providers/amazonBedrock.d.ts +9 -1
- package/dist/providers/amazonBedrock.js +26 -2
- package/dist/providers/analytics-helper.d.ts +53 -0
- package/dist/providers/analytics-helper.js +151 -0
- package/dist/providers/anthropic.d.ts +11 -1
- package/dist/providers/anthropic.js +29 -4
- package/dist/providers/azureOpenAI.d.ts +3 -1
- package/dist/providers/azureOpenAI.js +29 -4
- package/dist/providers/function-calling-provider.d.ts +9 -1
- package/dist/providers/function-calling-provider.js +14 -1
- package/dist/providers/googleAIStudio.d.ts +15 -1
- package/dist/providers/googleAIStudio.js +32 -2
- package/dist/providers/googleVertexAI.d.ts +9 -1
- package/dist/providers/googleVertexAI.js +31 -2
- package/dist/providers/huggingFace.d.ts +3 -1
- package/dist/providers/huggingFace.js +26 -3
- package/dist/providers/mcp-provider.d.ts +9 -1
- package/dist/providers/mcp-provider.js +12 -0
- package/dist/providers/mistralAI.d.ts +3 -1
- package/dist/providers/mistralAI.js +25 -2
- package/dist/providers/ollama.d.ts +3 -1
- package/dist/providers/ollama.js +27 -4
- package/dist/providers/openAI.d.ts +15 -1
- package/dist/providers/openAI.js +33 -2
- package/dist/proxy/proxy-fetch.js +8 -7
- package/dist/services/streaming/streaming-manager.d.ts +29 -0
- package/dist/services/streaming/streaming-manager.js +244 -0
- package/dist/services/types.d.ts +155 -0
- package/dist/services/types.js +2 -0
- package/dist/services/websocket/websocket-server.d.ts +34 -0
- package/dist/services/websocket/websocket-server.js +304 -0
- package/dist/telemetry/index.d.ts +15 -0
- package/dist/telemetry/index.js +22 -0
- package/dist/telemetry/telemetry-service.d.ts +47 -0
- package/dist/telemetry/telemetry-service.js +261 -0
- package/dist/utils/streaming-utils.d.ts +67 -0
- package/dist/utils/streaming-utils.js +201 -0
- package/package.json +18 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
export interface StreamingSession {
|
|
2
|
+
id: string;
|
|
3
|
+
connectionId: string;
|
|
4
|
+
provider: string;
|
|
5
|
+
status: "active" | "paused" | "terminated";
|
|
6
|
+
startTime: number;
|
|
7
|
+
lastActivity: number;
|
|
8
|
+
config: StreamingConfig;
|
|
9
|
+
metrics: {
|
|
10
|
+
bytesTransferred: number;
|
|
11
|
+
messagesCount: number;
|
|
12
|
+
averageLatency: number;
|
|
13
|
+
errorCount: number;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface WebSocketOptions {
|
|
17
|
+
port?: number;
|
|
18
|
+
maxConnections?: number;
|
|
19
|
+
heartbeatInterval?: number;
|
|
20
|
+
enableCompression?: boolean;
|
|
21
|
+
enableBackpressure?: boolean;
|
|
22
|
+
bufferSize?: number;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface StreamingConfig {
|
|
26
|
+
provider: string;
|
|
27
|
+
model: string;
|
|
28
|
+
streamingMode: "real-time" | "buffered" | "adaptive";
|
|
29
|
+
compressionEnabled: boolean;
|
|
30
|
+
maxChunkSize: number;
|
|
31
|
+
bufferSize: number;
|
|
32
|
+
latencyTarget: number;
|
|
33
|
+
}
|
|
34
|
+
export interface StreamingPool {
|
|
35
|
+
id: string;
|
|
36
|
+
maxSessions: number;
|
|
37
|
+
activeSessions: Set<string>;
|
|
38
|
+
config: StreamingPoolConfig;
|
|
39
|
+
loadBalancer: LoadBalancingStrategy;
|
|
40
|
+
}
|
|
41
|
+
export interface StreamingPoolConfig {
|
|
42
|
+
maxConcurrentSessions: number;
|
|
43
|
+
sessionTimeout: number;
|
|
44
|
+
loadBalancing: LoadBalancingStrategy;
|
|
45
|
+
autoScaling: {
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
minSessions: number;
|
|
48
|
+
maxSessions: number;
|
|
49
|
+
scaleUpThreshold: number;
|
|
50
|
+
scaleDownThreshold: number;
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export type LoadBalancingStrategy = "round-robin" | "least-connections" | "weighted" | "adaptive";
|
|
54
|
+
export interface StreamingChannel {
|
|
55
|
+
id: string;
|
|
56
|
+
connectionId: string;
|
|
57
|
+
type: "ai-response" | "mcp-tool" | "chat" | "notification";
|
|
58
|
+
status: "open" | "closed" | "error";
|
|
59
|
+
buffer: StreamingBuffer;
|
|
60
|
+
onData: (data: any) => void;
|
|
61
|
+
onError: (error: Error) => void;
|
|
62
|
+
onClose: () => void;
|
|
63
|
+
}
|
|
64
|
+
export interface StreamingBuffer {
|
|
65
|
+
data: any[];
|
|
66
|
+
maxSize: number;
|
|
67
|
+
currentSize: number;
|
|
68
|
+
flushThreshold: number;
|
|
69
|
+
lastFlush: number;
|
|
70
|
+
}
|
|
71
|
+
export interface StreamingMetrics {
|
|
72
|
+
sessionId?: string;
|
|
73
|
+
activeSessions: number;
|
|
74
|
+
totalBytesTransferred: number;
|
|
75
|
+
averageLatency: number;
|
|
76
|
+
throughputBps: number;
|
|
77
|
+
errorRate: number;
|
|
78
|
+
connectionCount: number;
|
|
79
|
+
uptime: number;
|
|
80
|
+
}
|
|
81
|
+
export interface StreamingHealthStatus {
|
|
82
|
+
status: "healthy" | "degraded" | "unhealthy";
|
|
83
|
+
activeSessions: number;
|
|
84
|
+
errorRate: number;
|
|
85
|
+
averageLatency: number;
|
|
86
|
+
lastHealthCheck: number;
|
|
87
|
+
issues: string[];
|
|
88
|
+
}
|
|
89
|
+
export interface WebSocketMessage {
|
|
90
|
+
id: string;
|
|
91
|
+
type: "chat" | "ai-response" | "tool-result" | "system" | "heartbeat" | "error";
|
|
92
|
+
connectionId: string;
|
|
93
|
+
roomId?: string;
|
|
94
|
+
timestamp: number;
|
|
95
|
+
data: any;
|
|
96
|
+
metadata?: {
|
|
97
|
+
provider?: string;
|
|
98
|
+
model?: string;
|
|
99
|
+
tokens?: number;
|
|
100
|
+
latency?: number;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export interface ChatRequest {
|
|
104
|
+
prompt: string;
|
|
105
|
+
sessionId?: string;
|
|
106
|
+
options?: {
|
|
107
|
+
temperature?: number;
|
|
108
|
+
maxTokens?: number;
|
|
109
|
+
streaming?: boolean;
|
|
110
|
+
enableTools?: boolean;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export interface GroupChatRequest extends ChatRequest {
|
|
114
|
+
roomId: string;
|
|
115
|
+
userId?: string;
|
|
116
|
+
broadcastToRoom?: boolean;
|
|
117
|
+
}
|
|
118
|
+
export interface StreamingChatRequest extends ChatRequest {
|
|
119
|
+
streamingOptions?: {
|
|
120
|
+
chunkSize?: number;
|
|
121
|
+
flushInterval?: number;
|
|
122
|
+
enableCompression?: boolean;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export interface MultiModalContent {
|
|
126
|
+
type: "text" | "image" | "audio" | "video" | "file";
|
|
127
|
+
content: string | Buffer;
|
|
128
|
+
metadata?: {
|
|
129
|
+
mimeType?: string;
|
|
130
|
+
size?: number;
|
|
131
|
+
duration?: number;
|
|
132
|
+
dimensions?: {
|
|
133
|
+
width: number;
|
|
134
|
+
height: number;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
export interface BufferConfig {
|
|
139
|
+
maxSize: number;
|
|
140
|
+
flushThreshold: number;
|
|
141
|
+
flushInterval: number;
|
|
142
|
+
compressionEnabled: boolean;
|
|
143
|
+
persistToDisk: boolean;
|
|
144
|
+
}
|
|
145
|
+
export interface ConnectionInfo {
|
|
146
|
+
id: string;
|
|
147
|
+
userId?: string;
|
|
148
|
+
userAgent?: string;
|
|
149
|
+
ipAddress?: string;
|
|
150
|
+
connectedAt: number;
|
|
151
|
+
lastActivity: number;
|
|
152
|
+
rooms: Set<string>;
|
|
153
|
+
subscriptions: Set<string>;
|
|
154
|
+
metadata: Record<string, any>;
|
|
155
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import type { WebSocketOptions, WebSocketMessage, StreamingChannel } from "../types.js";
|
|
3
|
+
export declare class NeuroLinkWebSocketServer extends EventEmitter {
|
|
4
|
+
private wss;
|
|
5
|
+
private connections;
|
|
6
|
+
private connectionInfo;
|
|
7
|
+
private rooms;
|
|
8
|
+
private streamingChannels;
|
|
9
|
+
private heartbeatInterval?;
|
|
10
|
+
private options;
|
|
11
|
+
constructor(options?: WebSocketOptions);
|
|
12
|
+
private setupEventHandlers;
|
|
13
|
+
private handleConnection;
|
|
14
|
+
private handleMessage;
|
|
15
|
+
private handleDisconnection;
|
|
16
|
+
joinRoom(connectionId: string, roomId: string): boolean;
|
|
17
|
+
leaveRoom(connectionId: string, roomId: string): boolean;
|
|
18
|
+
broadcastToRoom(roomId: string, message: WebSocketMessage): void;
|
|
19
|
+
createStreamingChannel(connectionId: string, channelId: string): StreamingChannel;
|
|
20
|
+
destroyStreamingChannel(channelId: string): void;
|
|
21
|
+
private sendMessage;
|
|
22
|
+
private sendError;
|
|
23
|
+
private updateLastActivity;
|
|
24
|
+
private handleHeartbeat;
|
|
25
|
+
private handleChatMessage;
|
|
26
|
+
private startHeartbeat;
|
|
27
|
+
getConnectionCount(): number;
|
|
28
|
+
getRoomCount(): number;
|
|
29
|
+
getActiveChannels(): number;
|
|
30
|
+
close(): void;
|
|
31
|
+
private handleChannelData;
|
|
32
|
+
private handleChannelError;
|
|
33
|
+
private handleChannelClose;
|
|
34
|
+
}
|
|
@@ -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
|
+
}
|