@eka-care/medassist-core 1.0.66 → 1.0.67
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/dist/Synapse.d.ts +0 -1
- package/dist/connection/ConnectionFactory.d.ts +0 -1
- package/dist/connection/SSE.d.ts +0 -1
- package/dist/connection/Websocket.d.ts +0 -1
- package/dist/constants/index.d.ts +0 -1
- package/dist/constants/types.d.ts +0 -1
- package/dist/conversation.d.ts +0 -1
- package/dist/esm/Synapse.js +612 -0
- package/dist/esm/connection/ConnectionFactory.js +27 -0
- package/dist/esm/connection/SSE.js +212 -0
- package/dist/esm/connection/Websocket.js +178 -0
- package/dist/esm/constants/index.js +25 -0
- package/dist/esm/constants/types.js +1 -0
- package/dist/esm/conversation.js +7 -0
- package/dist/esm/events/Events.js +41 -0
- package/dist/esm/events/Incoming.js +1 -0
- package/dist/esm/events/Outgoing.js +1 -0
- package/dist/esm/events/index.js +2 -0
- package/dist/esm/events/types.js +5 -0
- package/dist/esm/index.js +34 -0
- package/dist/esm/internal/Api/BaseResource.js +50 -0
- package/dist/esm/internal/Api/HttpClient.js +131 -0
- package/dist/esm/internal/Api/types.js +1 -0
- package/dist/esm/internal/Error/Error.js +229 -0
- package/dist/esm/internal/Error/types.js +9 -0
- package/dist/esm/internal/connection/BaseConnection.js +134 -0
- package/dist/esm/internal/connection/types.js +17 -0
- package/dist/esm/internal/events/EventEmitter.js +26 -0
- package/dist/esm/internal/store/index.js +5 -0
- package/dist/esm/media/audio/Audio.copy.js +363 -0
- package/dist/esm/media/audio/Audio.js +310 -0
- package/dist/esm/media/audio/types.js +13 -0
- package/dist/esm/media/file/File.js +159 -0
- package/dist/esm/messages/MessageManager.js +476 -0
- package/dist/esm/messages/types.js +35 -0
- package/dist/esm/resources/config/Config.js +11 -0
- package/dist/esm/resources/feedback/Feedback.js +9 -0
- package/dist/esm/resources/feedback/types.js +7 -0
- package/dist/esm/resources/index.js +152 -0
- package/dist/esm/resources/session/Session.js +44 -0
- package/dist/esm/resources/session/types.js +5 -0
- package/dist/esm/resources/toolCall/ToolCall.js +12 -0
- package/dist/esm/resources/toolCall/types.js +34 -0
- package/dist/esm/resources/types.js +4 -0
- package/dist/esm/resources/voice/VoiceResource.js +14 -0
- package/dist/esm/resources/voice/types.js +1 -0
- package/dist/esm/types/index.js +8 -0
- package/dist/esm/utils/Error.js +110 -0
- package/dist/esm/voice/VoiceAgent.js +305 -0
- package/dist/esm/voice/VoiceAudioAnalyser.js +32 -0
- package/dist/esm/voice/index.js +1 -0
- package/dist/esm/voice/types.js +15 -0
- package/dist/events/Events.d.ts +0 -1
- package/dist/events/Incoming.d.ts +0 -1
- package/dist/events/Outgoing.d.ts +0 -1
- package/dist/events/index.d.ts +0 -1
- package/dist/events/types.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/internal/Api/BaseResource.d.ts +0 -1
- package/dist/internal/Api/HttpClient.d.ts +0 -1
- package/dist/internal/Api/types.d.ts +0 -1
- package/dist/internal/Error/Error.d.ts +0 -1
- package/dist/internal/Error/types.d.ts +0 -1
- package/dist/internal/connection/BaseConnection.d.ts +0 -1
- package/dist/internal/connection/types.d.ts +0 -1
- package/dist/internal/events/EventEmitter.d.ts +0 -1
- package/dist/internal/store/index.d.ts +0 -1
- package/dist/media/audio/Audio.copy.d.ts +0 -1
- package/dist/media/audio/Audio.d.ts +0 -1
- package/dist/media/audio/types.d.ts +0 -1
- package/dist/media/file/File.d.ts +0 -1
- package/dist/messages/MessageManager.d.ts +0 -1
- package/dist/messages/types.d.ts +0 -1
- package/dist/resources/config/Config.d.ts +0 -1
- package/dist/resources/feedback/Feedback.d.ts +0 -1
- package/dist/resources/feedback/types.d.ts +0 -1
- package/dist/resources/index.d.ts +0 -1
- package/dist/resources/session/Session.d.ts +0 -1
- package/dist/resources/session/types.d.ts +0 -1
- package/dist/resources/toolCall/ToolCall.d.ts +0 -1
- package/dist/resources/toolCall/types.d.ts +0 -1
- package/dist/resources/types.d.ts +0 -1
- package/dist/resources/voice/VoiceResource.d.ts +0 -1
- package/dist/resources/voice/types.d.ts +0 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/utils/Error.d.ts +0 -1
- package/dist/voice/VoiceAgent.d.ts +0 -1
- package/dist/voice/VoiceAudioAnalyser.d.ts +0 -1
- package/dist/voice/index.d.ts +0 -1
- package/dist/voice/types.d.ts +0 -1
- package/package.json +4 -2
- package/dist/Synapse.d.ts.map +0 -1
- package/dist/auth/constants.d.ts +0 -12
- package/dist/auth/constants.d.ts.map +0 -1
- package/dist/auth/constants.js +0 -10
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -18
- package/dist/auth/session.d.ts +0 -5
- package/dist/auth/session.d.ts.map +0 -1
- package/dist/auth/session.js +0 -36
- package/dist/connection/ConnectionFactory.d.ts.map +0 -1
- package/dist/connection/SSE.d.ts.map +0 -1
- package/dist/connection/Websocket.d.ts.map +0 -1
- package/dist/constants/index.d.ts.map +0 -1
- package/dist/constants/types.d.ts.map +0 -1
- package/dist/conversation.d.ts.map +0 -1
- package/dist/events/Events.d.ts.map +0 -1
- package/dist/events/Incoming.d.ts.map +0 -1
- package/dist/events/Outgoing.d.ts.map +0 -1
- package/dist/events/index.d.ts.map +0 -1
- package/dist/events/types.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/internal/Api/BaseResource.d.ts.map +0 -1
- package/dist/internal/Api/HttpClient.d.ts.map +0 -1
- package/dist/internal/Api/types.d.ts.map +0 -1
- package/dist/internal/Error/Error.d.ts.map +0 -1
- package/dist/internal/Error/types.d.ts.map +0 -1
- package/dist/internal/connection/BaseConnection.d.ts.map +0 -1
- package/dist/internal/connection/types.d.ts.map +0 -1
- package/dist/internal/events/EventEmitter.d.ts.map +0 -1
- package/dist/internal/store/index.d.ts.map +0 -1
- package/dist/media/audio/Audio.copy.d.ts.map +0 -1
- package/dist/media/audio/Audio.d.ts.map +0 -1
- package/dist/media/audio/types.d.ts.map +0 -1
- package/dist/media/file/File.d.ts.map +0 -1
- package/dist/messages/MessageManager.d.ts.map +0 -1
- package/dist/messages/types.d.ts.map +0 -1
- package/dist/resources/config/Config.d.ts.map +0 -1
- package/dist/resources/feedback/Feedback.d.ts.map +0 -1
- package/dist/resources/feedback/types.d.ts.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/session/Session.d.ts.map +0 -1
- package/dist/resources/session/types.d.ts.map +0 -1
- package/dist/resources/toolCall/ToolCall.d.ts.map +0 -1
- package/dist/resources/toolCall/types.d.ts.map +0 -1
- package/dist/resources/types.d.ts.map +0 -1
- package/dist/resources/voice/VoiceResource.d.ts.map +0 -1
- package/dist/resources/voice/types.d.ts.map +0 -1
- package/dist/types/index.d.ts.map +0 -1
- package/dist/utils/Error.d.ts.map +0 -1
- package/dist/voice/VoiceAgent.d.ts.map +0 -1
- package/dist/voice/VoiceAudioAnalyser.d.ts.map +0 -1
- package/dist/voice/index.d.ts.map +0 -1
- package/dist/voice/types.d.ts.map +0 -1
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-Sent Events implementation of BaseConnection.
|
|
3
|
+
*
|
|
4
|
+
* Unlike the classic GET + EventSource pattern, this server streams events
|
|
5
|
+
* back as the response body of a POST to /chat. The request body is the
|
|
6
|
+
* same OutgoingSocketMessage envelope used over the WebSocket, so
|
|
7
|
+
* MessageManager parsing is shared.
|
|
8
|
+
*
|
|
9
|
+
* Wire shape:
|
|
10
|
+
* POST {serverUrl}/med-assist/session/{sessionId}/chat
|
|
11
|
+
* Headers: Content-Type: application/json
|
|
12
|
+
* Accept: text/event-stream
|
|
13
|
+
* sess-token: <session token>
|
|
14
|
+
* x-agent-id: <agent id>
|
|
15
|
+
* Body: JSON-encoded OutgoingSocketMessage
|
|
16
|
+
* Response: text/event-stream with `data: <json>\n\n` frames whose payloads
|
|
17
|
+
* match the IncomingSocketMessage shape.
|
|
18
|
+
*/
|
|
19
|
+
import { BaseConnection } from "../internal/connection/BaseConnection";
|
|
20
|
+
import { ConnectionStatus, } from "../internal/connection/types";
|
|
21
|
+
import { ConnectionError, MessageError, normalizeError, } from "../internal/Error/Error";
|
|
22
|
+
export class SSEConnection extends BaseConnection {
|
|
23
|
+
conversationId;
|
|
24
|
+
config;
|
|
25
|
+
connected = false;
|
|
26
|
+
activeStreamController = null;
|
|
27
|
+
constructor(config) {
|
|
28
|
+
super();
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.conversationId = config.auth.sessionId;
|
|
31
|
+
this.connect();
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* "Open" the SSE channel. There is no persistent socket — the connection
|
|
35
|
+
* is considered ready as soon as the session/agent identifiers are set.
|
|
36
|
+
* Each outgoing message opens its own short-lived streaming response.
|
|
37
|
+
*
|
|
38
|
+
* The open callback is deferred to a macrotask so it fires AFTER
|
|
39
|
+
* `Synapse.startSession()` resolves and the consumer (e.g. widget's
|
|
40
|
+
* `setUpEventListeners`) has had a chance to register the CONNECTED
|
|
41
|
+
* listener. With WebSocket the real socket open is naturally async; we
|
|
42
|
+
* have to simulate that here, otherwise the CONNECTED event is emitted
|
|
43
|
+
* before anyone is listening for it.
|
|
44
|
+
*/
|
|
45
|
+
connect() {
|
|
46
|
+
this.updateStatus(ConnectionStatus.CONNECTING);
|
|
47
|
+
this.connected = true;
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
this.onOpenCallback?.();
|
|
50
|
+
}, 0);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fire the open callback if the connection is already up by the time
|
|
54
|
+
* the listener registers. Mirrors the queued-disconnect pattern in
|
|
55
|
+
* BaseConnection so late-registering consumers don't miss the open
|
|
56
|
+
* event. Deferred via setTimeout for the same reason as connect().
|
|
57
|
+
*/
|
|
58
|
+
onOpen(callback) {
|
|
59
|
+
this.onOpenCallback = callback;
|
|
60
|
+
if (this.connected) {
|
|
61
|
+
setTimeout(() => callback(), 0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
buildChatUrl() {
|
|
65
|
+
const url = new URL(this.config.serverUrl);
|
|
66
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}/med-assist/session/${this.config.auth.sessionId}/chat`;
|
|
67
|
+
return url.toString();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Send an outgoing message. POSTs the same envelope used over the
|
|
71
|
+
* WebSocket and streams the response back as SSE-encoded events.
|
|
72
|
+
*/
|
|
73
|
+
sendMessage(message) {
|
|
74
|
+
if (!this.connected) {
|
|
75
|
+
const error = new ConnectionError("SSE connection is not open", {
|
|
76
|
+
context: { stage: "sendMessage" },
|
|
77
|
+
});
|
|
78
|
+
this.onErrorCallback?.(error);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.activeStreamController?.abort();
|
|
82
|
+
const controller = new AbortController();
|
|
83
|
+
this.activeStreamController = controller;
|
|
84
|
+
void this.streamChat(message, controller);
|
|
85
|
+
}
|
|
86
|
+
async streamChat(message, controller) {
|
|
87
|
+
const url = this.buildChatUrl();
|
|
88
|
+
try {
|
|
89
|
+
const response = await fetch(url, {
|
|
90
|
+
method: "POST",
|
|
91
|
+
headers: {
|
|
92
|
+
"Content-Type": "application/json",
|
|
93
|
+
Accept: "text/event-stream",
|
|
94
|
+
"sess-token": this.config.auth.sessionToken,
|
|
95
|
+
"x-agent-id": this.config.agentId,
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify(message),
|
|
98
|
+
signal: controller.signal,
|
|
99
|
+
});
|
|
100
|
+
if (!response.ok || !response.body) {
|
|
101
|
+
throw new ConnectionError(`SSE chat request failed with status ${response.status}`, { context: { stage: "streamChat", status: response.status } });
|
|
102
|
+
}
|
|
103
|
+
const reader = response.body.getReader();
|
|
104
|
+
const decoder = new TextDecoder();
|
|
105
|
+
let buffer = "";
|
|
106
|
+
while (true) {
|
|
107
|
+
const { done, value } = await reader.read();
|
|
108
|
+
if (done)
|
|
109
|
+
break;
|
|
110
|
+
buffer += decoder.decode(value, { stream: true });
|
|
111
|
+
buffer = this.extractJsonObjects(buffer);
|
|
112
|
+
}
|
|
113
|
+
// Final flush in case the stream ends mid-object.
|
|
114
|
+
if (buffer.trim().length > 0) {
|
|
115
|
+
this.extractJsonObjects(buffer);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
if (controller.signal.aborted)
|
|
120
|
+
return;
|
|
121
|
+
const connectionError = normalizeError(error, ConnectionError, "SSE chat stream failed", { context: { stage: "streamChat" } });
|
|
122
|
+
this.onErrorCallback?.(connectionError);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
if (this.activeStreamController === controller) {
|
|
126
|
+
this.activeStreamController = null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Walk the buffer and dispatch every complete top-level JSON object it
|
|
132
|
+
* contains, returning any trailing partial-object bytes that still need
|
|
133
|
+
* more data. The server streams back-to-back objects (`{…}{…}{…}`) with
|
|
134
|
+
* no separator, so we track brace depth (respecting strings/escapes)
|
|
135
|
+
* to find each object boundary. Any whitespace or stray SSE prefixes
|
|
136
|
+
* between objects is skipped automatically since we anchor on `{`.
|
|
137
|
+
*/
|
|
138
|
+
extractJsonObjects(buffer) {
|
|
139
|
+
let i = 0;
|
|
140
|
+
while (i < buffer.length) {
|
|
141
|
+
const start = buffer.indexOf("{", i);
|
|
142
|
+
if (start === -1)
|
|
143
|
+
return "";
|
|
144
|
+
const end = this.findObjectEnd(buffer, start);
|
|
145
|
+
if (end === -1) {
|
|
146
|
+
// Incomplete object — keep from `start` onward for the next chunk.
|
|
147
|
+
return buffer.slice(start);
|
|
148
|
+
}
|
|
149
|
+
const objStr = buffer.slice(start, end + 1);
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(objStr);
|
|
152
|
+
this.handleMessage(parsed);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
const parseError = normalizeError(error, MessageError, "Failed to parse incoming SSE object", { context: { stage: "extractJsonObjects", objStr } });
|
|
156
|
+
this.onErrorCallback?.(parseError);
|
|
157
|
+
}
|
|
158
|
+
i = end + 1;
|
|
159
|
+
}
|
|
160
|
+
return "";
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Return the index of the matching closing brace for the `{` at `start`,
|
|
164
|
+
* or -1 if the buffer ends before the object closes. String contents
|
|
165
|
+
* and escape sequences are skipped so braces inside strings don't
|
|
166
|
+
* affect the depth count.
|
|
167
|
+
*/
|
|
168
|
+
findObjectEnd(buffer, start) {
|
|
169
|
+
let depth = 0;
|
|
170
|
+
let inString = false;
|
|
171
|
+
let escape = false;
|
|
172
|
+
for (let i = start; i < buffer.length; i++) {
|
|
173
|
+
const ch = buffer[i];
|
|
174
|
+
if (inString) {
|
|
175
|
+
if (escape) {
|
|
176
|
+
escape = false;
|
|
177
|
+
}
|
|
178
|
+
else if (ch === "\\") {
|
|
179
|
+
escape = true;
|
|
180
|
+
}
|
|
181
|
+
else if (ch === '"') {
|
|
182
|
+
inString = false;
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (ch === '"') {
|
|
187
|
+
inString = true;
|
|
188
|
+
}
|
|
189
|
+
else if (ch === "{") {
|
|
190
|
+
depth++;
|
|
191
|
+
}
|
|
192
|
+
else if (ch === "}") {
|
|
193
|
+
depth--;
|
|
194
|
+
if (depth === 0)
|
|
195
|
+
return i;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return -1;
|
|
199
|
+
}
|
|
200
|
+
close() {
|
|
201
|
+
this.connected = false;
|
|
202
|
+
this.activeStreamController?.abort();
|
|
203
|
+
this.activeStreamController = null;
|
|
204
|
+
this.updateStatus(ConnectionStatus.NOT_CONNECTED);
|
|
205
|
+
}
|
|
206
|
+
isConnected() {
|
|
207
|
+
return this.connected;
|
|
208
|
+
}
|
|
209
|
+
handleConnected() {
|
|
210
|
+
this.updateStatus(ConnectionStatus.CONNECTED);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket implementation of BaseConnection
|
|
3
|
+
*/
|
|
4
|
+
import { BaseConnection } from "../internal/connection/BaseConnection";
|
|
5
|
+
import { ConnectionStatus, DisconnectionReason, } from "../internal/connection/types";
|
|
6
|
+
import { ConnectionError, MessageError, normalizeError, } from "../internal/Error/Error";
|
|
7
|
+
export class WebSocketConnection extends BaseConnection {
|
|
8
|
+
conversationId;
|
|
9
|
+
socket = null;
|
|
10
|
+
config;
|
|
11
|
+
authenticated = false;
|
|
12
|
+
constructor(config) {
|
|
13
|
+
super();
|
|
14
|
+
this.config = {
|
|
15
|
+
...config,
|
|
16
|
+
reconnect: config.reconnect || true,
|
|
17
|
+
reconnectAttempts: config.reconnectAttempts || 3,
|
|
18
|
+
reconnectDelay: config.reconnectDelay || 1000,
|
|
19
|
+
};
|
|
20
|
+
this.conversationId = config.auth.sessionId;
|
|
21
|
+
this.connect();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Establish WebSocket connection
|
|
25
|
+
*/
|
|
26
|
+
connect() {
|
|
27
|
+
try {
|
|
28
|
+
if ((this.socket && this.socket.readyState === WebSocket.OPEN) || this.authenticated)
|
|
29
|
+
return;
|
|
30
|
+
this.updateStatus(ConnectionStatus.CONNECTING);
|
|
31
|
+
// Build WebSocket URL with query parameters
|
|
32
|
+
const url = this.buildConnectionUrl();
|
|
33
|
+
this.socket = new WebSocket(url);
|
|
34
|
+
//bind creates a new function permanently set to the object we pass, no matter how or where you call the function
|
|
35
|
+
// Now when WebSocket calls the function, 'this' refers to your WebSocketConnection instance
|
|
36
|
+
this.socket.onopen = this.handleOpen.bind(this);
|
|
37
|
+
this.socket.onmessage = this.handleSocketMessage.bind(this);
|
|
38
|
+
this.socket.onerror = this.handleError.bind(this);
|
|
39
|
+
this.socket.onclose = this.handleClose.bind(this);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.log("error from connect", error);
|
|
43
|
+
const connectionError = normalizeError(error, ConnectionError, "Failed to establish WebSocket connection", { context: { stage: "connect" } });
|
|
44
|
+
this.onErrorCallback?.(connectionError);
|
|
45
|
+
this.disconnect({
|
|
46
|
+
code: 1001,
|
|
47
|
+
reason: DisconnectionReason.CONNECTION_ERROR,
|
|
48
|
+
message: connectionError.message,
|
|
49
|
+
timestamp: new Date(),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build WebSocket connection URL with session config
|
|
55
|
+
*/
|
|
56
|
+
buildConnectionUrl() {
|
|
57
|
+
const url = new URL(this.config.serverUrl);
|
|
58
|
+
url.pathname = `/reloaded/ws/med-assist/session/${this.config.auth.sessionId}/`;
|
|
59
|
+
return url.toString();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Handle WebSocket open event
|
|
63
|
+
*/
|
|
64
|
+
handleOpen() {
|
|
65
|
+
if (this.authenticated || this.socket?.readyState !== WebSocket.OPEN)
|
|
66
|
+
return;
|
|
67
|
+
this.onOpenCallback?.();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Handle incoming WebSocket messages
|
|
71
|
+
*/
|
|
72
|
+
handleSocketMessage(event) {
|
|
73
|
+
try {
|
|
74
|
+
const data = JSON.parse(event.data);
|
|
75
|
+
this.handleMessage(data);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const parseError = normalizeError(error, MessageError, "Failed to parse incoming WebSocket message", { context: { stage: "handleSocketMessage" } });
|
|
79
|
+
this.onErrorCallback?.(parseError);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Handle WebSocket errors
|
|
84
|
+
*/
|
|
85
|
+
handleError(event) {
|
|
86
|
+
console.log("handleError", event);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Handle WebSocket close event
|
|
90
|
+
*/
|
|
91
|
+
handleClose(event) {
|
|
92
|
+
const wasClean = event.wasClean;
|
|
93
|
+
const code = event.code;
|
|
94
|
+
const reason = event.reason;
|
|
95
|
+
let disconnectReason;
|
|
96
|
+
let message;
|
|
97
|
+
this.authenticated = false;
|
|
98
|
+
if (this.isConnected()) {
|
|
99
|
+
console.log("WebSocket is already connected, skipping reconnection");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (code === 1000) {
|
|
103
|
+
disconnectReason = DisconnectionReason.CLIENT_CLOSED;
|
|
104
|
+
message = "Connection closed normally";
|
|
105
|
+
}
|
|
106
|
+
else if (code === 1001 || code === 1006) {
|
|
107
|
+
disconnectReason = DisconnectionReason.NETWORK_ERROR;
|
|
108
|
+
message = event.reason || "Network connection lost";
|
|
109
|
+
}
|
|
110
|
+
else if (code === 4001 || code === 1008) {
|
|
111
|
+
if (event.reason === "timeout" || event.reason === "Authentication timeout") {
|
|
112
|
+
disconnectReason = DisconnectionReason.TIMEOUT;
|
|
113
|
+
message = "Connection timed out";
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
disconnectReason = DisconnectionReason.AUTHENTICATION_ERROR;
|
|
117
|
+
message = event.reason || "Authentication failed";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else if (wasClean) {
|
|
121
|
+
disconnectReason = DisconnectionReason.SERVER_CLOSED;
|
|
122
|
+
message = reason || "Server closed connection";
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
disconnectReason = DisconnectionReason.CONNECTION_ERROR;
|
|
126
|
+
message = reason || "Connection closed unexpectedly";
|
|
127
|
+
}
|
|
128
|
+
this.disconnect({
|
|
129
|
+
reason: disconnectReason,
|
|
130
|
+
message,
|
|
131
|
+
code,
|
|
132
|
+
timestamp: new Date(),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Send a message through the WebSocket
|
|
137
|
+
*/
|
|
138
|
+
sendMessage(message) {
|
|
139
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
140
|
+
throw new ConnectionError("Cannot send message: connection not open", {
|
|
141
|
+
context: {
|
|
142
|
+
stage: "sendMessage",
|
|
143
|
+
readyState: this.socket?.readyState,
|
|
144
|
+
},
|
|
145
|
+
hint: "Ensure the WebSocket is open before sending messages.",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
this.socket.send(JSON.stringify(message));
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const connectionError = normalizeError(error, ConnectionError, "Failed to send WebSocket message", { context: { stage: "sendMessage" } });
|
|
153
|
+
this.onErrorCallback?.(connectionError);
|
|
154
|
+
throw connectionError;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Close the WebSocket connection
|
|
159
|
+
*/
|
|
160
|
+
close() {
|
|
161
|
+
this.authenticated = false;
|
|
162
|
+
if (this.socket) {
|
|
163
|
+
this.updateStatus(ConnectionStatus.NOT_CONNECTED);
|
|
164
|
+
this.socket.close(1000, "Client closed connection");
|
|
165
|
+
this.socket = null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Check if the WebSocket is connected
|
|
170
|
+
*/
|
|
171
|
+
isConnected() {
|
|
172
|
+
return this.socket?.readyState === WebSocket.OPEN;
|
|
173
|
+
}
|
|
174
|
+
handleConnected() {
|
|
175
|
+
this.updateStatus(ConnectionStatus.CONNECTED);
|
|
176
|
+
this.authenticated = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const PRODUCTION_CONFIG = {
|
|
2
|
+
WEBSOCKET_URL: "wss://matrix-ws.eka.care/reloaded",
|
|
3
|
+
BASE_API_URL: "https://matrix.eka.care/reloaded",
|
|
4
|
+
};
|
|
5
|
+
const DEVELOPMENT_CONFIG = {
|
|
6
|
+
// can be used for local development
|
|
7
|
+
WEBSOCKET_URL: "wss://matrix-ws.dev.eka.care/reloaded",
|
|
8
|
+
BASE_API_URL: "https://matrix.dev.eka.care/reloaded",
|
|
9
|
+
};
|
|
10
|
+
const STAGING_CONFIG = {
|
|
11
|
+
WEBSOCKET_URL: "wss://matrix-ws.dev.eka.care",
|
|
12
|
+
BASE_API_URL: "https://matrix.dev.eka.care",
|
|
13
|
+
};
|
|
14
|
+
export const getConfig = (environment = "production") => {
|
|
15
|
+
if (environment === "development") {
|
|
16
|
+
return DEVELOPMENT_CONFIG;
|
|
17
|
+
}
|
|
18
|
+
else if (environment === "production") {
|
|
19
|
+
return PRODUCTION_CONFIG;
|
|
20
|
+
}
|
|
21
|
+
else if (environment === "staging") {
|
|
22
|
+
return STAGING_CONFIG;
|
|
23
|
+
}
|
|
24
|
+
return PRODUCTION_CONFIG;
|
|
25
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export var SYNAPSE_CONVERSATION;
|
|
2
|
+
(function (SYNAPSE_CONVERSATION) {
|
|
3
|
+
SYNAPSE_CONVERSATION["TEXT"] = "text";
|
|
4
|
+
SYNAPSE_CONVERSATION["FILE"] = "file";
|
|
5
|
+
SYNAPSE_CONVERSATION["AUDIO"] = "audio";
|
|
6
|
+
SYNAPSE_CONVERSATION["VOICE"] = "voice";
|
|
7
|
+
})(SYNAPSE_CONVERSATION || (SYNAPSE_CONVERSATION = {}));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Content types
|
|
2
|
+
export const SOCKET_CONTENT_TYPES = {
|
|
3
|
+
TEXT: "text",
|
|
4
|
+
AUDIO: "audio",
|
|
5
|
+
FILE: "file",
|
|
6
|
+
TOOL_CALL: "tool",
|
|
7
|
+
TIPS: "tips",
|
|
8
|
+
AUDIO_TRANSCRIPT: "audio_transcript",
|
|
9
|
+
TOOL_START: "tool_start",
|
|
10
|
+
TOOL_END: "tool_end",
|
|
11
|
+
};
|
|
12
|
+
export const SOCKET_EVENTS = {
|
|
13
|
+
// Client to Server events
|
|
14
|
+
PING: "ping",
|
|
15
|
+
PONG: "pong",
|
|
16
|
+
CHAT: "chat",
|
|
17
|
+
STREAM: "stream",
|
|
18
|
+
AUTH: "auth",
|
|
19
|
+
END_OF_STREAM: "eos",
|
|
20
|
+
SYNC: "sync",
|
|
21
|
+
ERROR: "err",
|
|
22
|
+
// Server to Client events
|
|
23
|
+
CONNECTION_ESTABLISHED: "conn",
|
|
24
|
+
};
|
|
25
|
+
export const MULTI_SELECT_ADDITIONAL_OPTION = {
|
|
26
|
+
NOTA: "none_of_the_above",
|
|
27
|
+
AOTA: "all_of_the_above",
|
|
28
|
+
};
|
|
29
|
+
export function IsValidSocketMesssage(message) {
|
|
30
|
+
const validEvents = [
|
|
31
|
+
SOCKET_EVENTS.PING,
|
|
32
|
+
SOCKET_EVENTS.PONG,
|
|
33
|
+
SOCKET_EVENTS.ERROR,
|
|
34
|
+
SOCKET_EVENTS.SYNC,
|
|
35
|
+
SOCKET_EVENTS.CONNECTION_ESTABLISHED,
|
|
36
|
+
SOCKET_EVENTS.CHAT,
|
|
37
|
+
SOCKET_EVENTS.STREAM,
|
|
38
|
+
SOCKET_EVENTS.END_OF_STREAM,
|
|
39
|
+
];
|
|
40
|
+
return validEvents.includes(message.ev);
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Synapse SDK - Main exports
|
|
3
|
+
* Medical Chatbot TypeScript SDK
|
|
4
|
+
*/
|
|
5
|
+
// High-level API (Primary interface for most users)
|
|
6
|
+
export { SynapseSDK } from "./Synapse";
|
|
7
|
+
// Connection layer (for advanced usage)
|
|
8
|
+
export { BaseConnection } from "./internal/connection/BaseConnection";
|
|
9
|
+
export { WebSocketConnection } from "./connection/Websocket";
|
|
10
|
+
export { SSEConnection } from "./connection/SSE";
|
|
11
|
+
export { ConnectionFactory, ConnectionType, } from "./connection/ConnectionFactory";
|
|
12
|
+
export * from "./internal/connection/types";
|
|
13
|
+
// Resources layer (for advanced usage)
|
|
14
|
+
export { ResourceManager } from "./resources";
|
|
15
|
+
export * from "./resources/types";
|
|
16
|
+
export { Session } from "./resources/session/Session";
|
|
17
|
+
export * from "./resources/session/types";
|
|
18
|
+
export { ToolCall } from "./resources/toolCall/ToolCall";
|
|
19
|
+
export * from "./resources/toolCall/types";
|
|
20
|
+
// Message layer (for advanced usage)
|
|
21
|
+
export { MessageManager } from "./messages/MessageManager";
|
|
22
|
+
export * from "./messages/types";
|
|
23
|
+
export * from "./events/types";
|
|
24
|
+
// Core HTTP client (for advanced usage)
|
|
25
|
+
export { HttpClient } from "./internal/Api/HttpClient";
|
|
26
|
+
// Error handling
|
|
27
|
+
export { SynapseError, APIError, APIUserAbortError, APIConnectionTimeoutError, BadRequestError, UnauthorizedError, PermissionDeniedError, NotFoundError, MethodNotAllowedError, RateLimitError, InternalServerError, SynapseErrorCode, } from "./internal/Error/Error";
|
|
28
|
+
export { ErrorType } from "./internal/Error/types";
|
|
29
|
+
// Utilities
|
|
30
|
+
export { ErrorUtils } from "./utils/Error";
|
|
31
|
+
export * from "./media/audio/types";
|
|
32
|
+
export { SYNAPSE_MESSAGE_TYPES } from "./messages/types";
|
|
33
|
+
export * from "./conversation";
|
|
34
|
+
export * from "./voice";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for all resources
|
|
3
|
+
*/
|
|
4
|
+
export class BaseResource {
|
|
5
|
+
client;
|
|
6
|
+
constructor(httpClient) {
|
|
7
|
+
this.client = httpClient;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Helper method for making GET requests
|
|
11
|
+
*/
|
|
12
|
+
async get(path, params, options) {
|
|
13
|
+
return this.client.get(path, params, options);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Helper method for making POST requests
|
|
17
|
+
*/
|
|
18
|
+
async post(path, body, options) {
|
|
19
|
+
return this.client.post(path, body, options);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper method for making PUT requests
|
|
23
|
+
*/
|
|
24
|
+
async put(path, body, options) {
|
|
25
|
+
return this.client.put(path, body, options);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Helper method for making DELETE requests
|
|
29
|
+
*/
|
|
30
|
+
async delete(path, options) {
|
|
31
|
+
return this.client.delete(path, options);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Helper method for making PATCH requests
|
|
35
|
+
*/
|
|
36
|
+
async patch(path, body, options) {
|
|
37
|
+
return this.client.patch(path, body, options);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Build a path with parameters
|
|
41
|
+
* Example: buildPath('/sessions/:id', { id: '123' }) => '/sessions/123'
|
|
42
|
+
*/
|
|
43
|
+
buildPath(template, params) {
|
|
44
|
+
let path = template;
|
|
45
|
+
for (const [key, value] of Object.entries(params)) {
|
|
46
|
+
path = path.replace(`:${key}`, encodeURIComponent(value));
|
|
47
|
+
}
|
|
48
|
+
return path;
|
|
49
|
+
}
|
|
50
|
+
}
|