@nlxai/core 1.2.3 → 1.2.4-alpha.10
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/README.md +358 -16
- package/docs/README.md +1930 -0
- package/lib/index.cjs +315 -76
- package/lib/index.d.ts +195 -10
- package/lib/index.esm.js +315 -77
- package/lib/index.umd.js +2 -2
- package/package.json +10 -3
package/lib/index.esm.js
CHANGED
|
@@ -4,7 +4,7 @@ import ReconnectingWebSocket from 'reconnecting-websocket';
|
|
|
4
4
|
import { v4 } from 'uuid';
|
|
5
5
|
|
|
6
6
|
var name = "@nlxai/core";
|
|
7
|
-
var version$1 = "1.2.
|
|
7
|
+
var version$1 = "1.2.4-alpha.10";
|
|
8
8
|
var description = "Low-level SDK for building NLX experiences";
|
|
9
9
|
var type = "module";
|
|
10
10
|
var main = "lib/index.cjs";
|
|
@@ -32,14 +32,21 @@ var scripts = {
|
|
|
32
32
|
var author = "Peter Szerzo <peter@nlx.ai>";
|
|
33
33
|
var license = "MIT";
|
|
34
34
|
var devDependencies = {
|
|
35
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
36
|
+
"@rollup/plugin-json": "^6.0.1",
|
|
37
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
38
|
+
"@rollup/plugin-replace": "^5.0.5",
|
|
39
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
40
|
+
"@rollup/plugin-typescript": "^11.1.5",
|
|
35
41
|
"@types/isomorphic-fetch": "^0.0.39",
|
|
36
42
|
"@types/node": "^24.10.1",
|
|
37
43
|
"@types/ramda": "0.31.1",
|
|
38
44
|
"@types/uuid": "^9.0.7",
|
|
39
|
-
"concat-md": "^0.5.1",
|
|
40
45
|
"eslint-config-nlx": "*",
|
|
41
46
|
prettier: "^3.1.0",
|
|
47
|
+
rollup: "^4.3.0",
|
|
42
48
|
"rollup-config-nlx": "*",
|
|
49
|
+
"rollup-plugin-node-polyfills": "^0.2.1",
|
|
43
50
|
typedoc: "^0.28.14",
|
|
44
51
|
"typedoc-plugin-markdown": "^4.9.0",
|
|
45
52
|
typescript: "^5.5.4"
|
|
@@ -53,7 +60,7 @@ var dependencies = {
|
|
|
53
60
|
var publishConfig = {
|
|
54
61
|
access: "public"
|
|
55
62
|
};
|
|
56
|
-
var gitHead = "
|
|
63
|
+
var gitHead = "9a1bfacce5cc21ca2d64e79c86805dab37568de3";
|
|
57
64
|
var packageJson = {
|
|
58
65
|
name: name,
|
|
59
66
|
version: version$1,
|
|
@@ -79,6 +86,24 @@ var packageJson = {
|
|
|
79
86
|
const version = packageJson.version;
|
|
80
87
|
// use a custom Console to indicate we really want to log to the console and it's not incidental. `console.log` causes an eslint error
|
|
81
88
|
const Console = console;
|
|
89
|
+
/**
|
|
90
|
+
* The protocol used to communicate with the application
|
|
91
|
+
*/
|
|
92
|
+
var Protocol;
|
|
93
|
+
(function (Protocol) {
|
|
94
|
+
/**
|
|
95
|
+
* Regular encrypted HTTPS, without support for post-escalation message handling, interim messages and other streaming features.
|
|
96
|
+
*/
|
|
97
|
+
Protocol["Https"] = "https";
|
|
98
|
+
/**
|
|
99
|
+
* Encrypted HTTPS with streaming enabled. This is the default setting and supports interim messages. Does not support post-escalation message handling.
|
|
100
|
+
*/
|
|
101
|
+
Protocol["HttpsWithStreaming"] = "httpsWithStreaming";
|
|
102
|
+
/**
|
|
103
|
+
* Websocket, with support for post-escalation message handling.
|
|
104
|
+
*/
|
|
105
|
+
Protocol["Websocket"] = "websocket";
|
|
106
|
+
})(Protocol || (Protocol = {}));
|
|
82
107
|
/**
|
|
83
108
|
* Response type
|
|
84
109
|
*/
|
|
@@ -125,42 +150,181 @@ const safeJsonParse = (val) => {
|
|
|
125
150
|
return null;
|
|
126
151
|
}
|
|
127
152
|
};
|
|
128
|
-
const
|
|
153
|
+
const getHost = (url) => url.match(/(bots\.dev\.studio\.nlx\.ai|bots\.studio\.nlx\.ai|apps\.nlx\.ai|dev\.apps\.nlx\.ai)/g)?.[0] ?? "apps.nlx.ai";
|
|
129
154
|
/**
|
|
130
|
-
*
|
|
131
|
-
* @param
|
|
132
|
-
* @returns
|
|
155
|
+
* Parse configuration into structured connection information, taking into account `applicationUrl`-based configs.
|
|
156
|
+
* @param config - client configuration.
|
|
157
|
+
* @returns connection - connection information, or `null` if the configuration is invalid.
|
|
133
158
|
*/
|
|
134
|
-
const
|
|
159
|
+
const parseConnection = (config) => {
|
|
160
|
+
const applicationUrl = config.applicationUrl ?? "";
|
|
161
|
+
const apiKey = config.apiKey ?? config.headers?.["nlx-api-key"] ?? "";
|
|
162
|
+
const protocol = config.protocol ??
|
|
163
|
+
/**
|
|
164
|
+
* Backwards-compatibility: if a websocket URL was specified, assume it's websocket. Otherwise, look at the legacy experimental streamsetting
|
|
165
|
+
* and only assume non-streaming if it's explicitly set to false.
|
|
166
|
+
*/
|
|
167
|
+
(isWebsocketUrl(applicationUrl)
|
|
168
|
+
? Protocol.Websocket
|
|
169
|
+
: config.experimental?.streamHttp === false
|
|
170
|
+
? Protocol.Https
|
|
171
|
+
: Protocol.HttpsWithStreaming);
|
|
172
|
+
if (config.host != null &&
|
|
173
|
+
config.channelKey != null &&
|
|
174
|
+
config.deploymentKey != null) {
|
|
175
|
+
return {
|
|
176
|
+
protocol,
|
|
177
|
+
apiKey,
|
|
178
|
+
host: config.host,
|
|
179
|
+
channelKey: config.channelKey,
|
|
180
|
+
deploymentKey: config.deploymentKey,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// `applicationUrl`-based definition: websocket case
|
|
135
184
|
if (isWebsocketUrl(applicationUrl)) {
|
|
136
|
-
|
|
185
|
+
const host = getHost(applicationUrl);
|
|
186
|
+
const url = new URL(applicationUrl);
|
|
187
|
+
const params = new URLSearchParams(url.search);
|
|
188
|
+
const channelKey = params.get("channelKey");
|
|
189
|
+
const deploymentKey = params.get("deploymentKey");
|
|
190
|
+
if (channelKey != null && deploymentKey != null) {
|
|
191
|
+
return { protocol, channelKey, deploymentKey, host, apiKey };
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
137
194
|
}
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return applicationUrl;
|
|
195
|
+
// `applicationUrl`-based definition: http case
|
|
196
|
+
const host = getHost(applicationUrl);
|
|
197
|
+
const parseResult = new URLPattern({
|
|
198
|
+
pathname: "/c/:deploymentKey/:channelKey",
|
|
199
|
+
}).exec(applicationUrl);
|
|
200
|
+
if (parseResult?.pathname.groups.channelKey != null &&
|
|
201
|
+
parseResult?.pathname.groups.deploymentKey != null) {
|
|
202
|
+
return {
|
|
203
|
+
protocol,
|
|
204
|
+
channelKey: parseResult.pathname.groups.channelKey,
|
|
205
|
+
deploymentKey: parseResult.pathname.groups.deploymentKey,
|
|
206
|
+
host,
|
|
207
|
+
apiKey,
|
|
208
|
+
};
|
|
153
209
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
210
|
+
return null;
|
|
211
|
+
};
|
|
212
|
+
const toWebsocketUrl = (connection) => {
|
|
213
|
+
return `wss://us-east-1-ws.${connection.host}?deploymentKey=${connection.deploymentKey}&channelKey=${connection.channelKey}&apiKey=${connection.apiKey}`;
|
|
214
|
+
};
|
|
215
|
+
const toHttpUrl = (connection) => {
|
|
216
|
+
return `https://${connection.host}/c/${connection.deploymentKey}/${connection.channelKey}`;
|
|
160
217
|
};
|
|
161
218
|
const isWebsocketUrl = (url) => {
|
|
162
219
|
return url.indexOf("wss://") === 0;
|
|
163
220
|
};
|
|
221
|
+
const fetchUserMessage = async ({ fullApplicationUrl, apiKey, headers, body, stream, eventListeners, }) => {
|
|
222
|
+
const streamRequest = async (body) => {
|
|
223
|
+
const response = await fetch(fullApplicationUrl, {
|
|
224
|
+
method: "POST",
|
|
225
|
+
headers: {
|
|
226
|
+
...headers,
|
|
227
|
+
"nlx-api-key": apiKey,
|
|
228
|
+
"Content-Type": "application/json",
|
|
229
|
+
// Legacy header
|
|
230
|
+
"nlx-sdk-version": packageJson.version,
|
|
231
|
+
"nlx-core-version": packageJson.version,
|
|
232
|
+
},
|
|
233
|
+
body: JSON.stringify({ ...body, stream: true }),
|
|
234
|
+
});
|
|
235
|
+
if (!response.ok || response.body == null)
|
|
236
|
+
throw new Error(`HTTP Error: ${response.status}`);
|
|
237
|
+
const reader = response.body.getReader();
|
|
238
|
+
const decoder = new TextDecoder();
|
|
239
|
+
let buffer = "";
|
|
240
|
+
const messages = [];
|
|
241
|
+
let finalResponse = {};
|
|
242
|
+
while (true) {
|
|
243
|
+
const { done, value } = await reader.read();
|
|
244
|
+
if (done)
|
|
245
|
+
break;
|
|
246
|
+
buffer += decoder.decode(value, { stream: true });
|
|
247
|
+
while (true) {
|
|
248
|
+
const openBrace = buffer.indexOf("{");
|
|
249
|
+
if (openBrace === -1)
|
|
250
|
+
break;
|
|
251
|
+
let foundObject = false;
|
|
252
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
253
|
+
if (buffer[i] === "}") {
|
|
254
|
+
const candidate = buffer.substring(openBrace, i + 1);
|
|
255
|
+
try {
|
|
256
|
+
const json = JSON.parse(candidate);
|
|
257
|
+
if (json.type === "interim") {
|
|
258
|
+
const text = json.text;
|
|
259
|
+
if (typeof text === "string") {
|
|
260
|
+
eventListeners.interimMessage.forEach((listener) => {
|
|
261
|
+
listener(text);
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else if (json.type === "message") {
|
|
266
|
+
messages.push({
|
|
267
|
+
text: json.text,
|
|
268
|
+
choices: json.choices ?? [],
|
|
269
|
+
messageId: json.messageId,
|
|
270
|
+
metadata: json.metadata,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else if (json.type === "final_response") {
|
|
274
|
+
finalResponse = json.data;
|
|
275
|
+
}
|
|
276
|
+
buffer = buffer.substring(i + 1);
|
|
277
|
+
foundObject = true;
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
/* keep scanning */
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
if (!foundObject)
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
eventListeners.interimMessage.forEach((listener) => {
|
|
290
|
+
listener(undefined);
|
|
291
|
+
});
|
|
292
|
+
return {
|
|
293
|
+
...finalResponse,
|
|
294
|
+
messages: [
|
|
295
|
+
...messages,
|
|
296
|
+
...(finalResponse.messages ?? []).map((json) => ({
|
|
297
|
+
text: json.text,
|
|
298
|
+
choices: json.choices ?? [],
|
|
299
|
+
messageId: json.messageId,
|
|
300
|
+
metadata: json.metadata,
|
|
301
|
+
})),
|
|
302
|
+
],
|
|
303
|
+
};
|
|
304
|
+
};
|
|
305
|
+
if (stream) {
|
|
306
|
+
return await streamRequest(body);
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
const response = await fetch(fullApplicationUrl, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: {
|
|
312
|
+
...(headers ?? {}),
|
|
313
|
+
"nlx-api-key": apiKey,
|
|
314
|
+
Accept: "application/json",
|
|
315
|
+
"Content-Type": "application/json",
|
|
316
|
+
// Legacy header
|
|
317
|
+
"nlx-sdk-version": packageJson.version,
|
|
318
|
+
"nlx-core-version": packageJson.version,
|
|
319
|
+
},
|
|
320
|
+
body: JSON.stringify(body),
|
|
321
|
+
});
|
|
322
|
+
if (!response.ok || response.body == null)
|
|
323
|
+
throw new Error(`HTTP Error: ${response.status}`);
|
|
324
|
+
const json = await response.json();
|
|
325
|
+
return json;
|
|
326
|
+
}
|
|
327
|
+
};
|
|
164
328
|
/**
|
|
165
329
|
* Call this to create a conversation handler.
|
|
166
330
|
* @param configuration - The necessary configuration to create the conversation.
|
|
@@ -186,12 +350,21 @@ function createConversation(configuration) {
|
|
|
186
350
|
let voicePlusSocket;
|
|
187
351
|
let voicePlusSocketMessageQueue = [];
|
|
188
352
|
let voicePlusSocketMessageQueueCheckInterval = null;
|
|
189
|
-
const
|
|
353
|
+
const connection = parseConnection(configuration);
|
|
354
|
+
const websocketApplicationUrl = connection != null
|
|
355
|
+
? toWebsocketUrl(connection)
|
|
356
|
+
: configuration.applicationUrl ?? "";
|
|
357
|
+
const httpApplicationUrl = connection != null
|
|
358
|
+
? toHttpUrl(connection)
|
|
359
|
+
: configuration.applicationUrl ?? "";
|
|
190
360
|
// Check if the application URL has a language code appended to it
|
|
191
|
-
if (/[-|_][a-z]{2,}[-|_][A-Z]{2,}$/.test(
|
|
361
|
+
if (/[-|_][a-z]{2,}[-|_][A-Z]{2,}$/.test(httpApplicationUrl)) {
|
|
192
362
|
Console.warn("Since v1.0.0, the language code is no longer added at the end of the application URL. Please remove the modifier (e.g. '-en-US') from the URL, and specify it in the `languageCode` parameter instead.");
|
|
193
363
|
}
|
|
194
|
-
const eventListeners = {
|
|
364
|
+
const eventListeners = {
|
|
365
|
+
voicePlusCommand: [],
|
|
366
|
+
interimMessage: [],
|
|
367
|
+
};
|
|
195
368
|
const initialConversationId = configuration.conversationId ?? v4();
|
|
196
369
|
let state = {
|
|
197
370
|
responses: configuration.responses ?? [],
|
|
@@ -199,7 +372,7 @@ function createConversation(configuration) {
|
|
|
199
372
|
userId: configuration.userId,
|
|
200
373
|
conversationId: initialConversationId,
|
|
201
374
|
};
|
|
202
|
-
const fullApplicationHttpUrl = () => `${
|
|
375
|
+
const fullApplicationHttpUrl = () => `${httpApplicationUrl}${configuration.experimental?.completeApplicationUrl === true
|
|
203
376
|
? ""
|
|
204
377
|
: `-${state.languageCode}`}`;
|
|
205
378
|
const setState = (change,
|
|
@@ -293,7 +466,7 @@ function createConversation(configuration) {
|
|
|
293
466
|
channelType: configuration.experimental?.channelType,
|
|
294
467
|
environment: configuration.environment,
|
|
295
468
|
};
|
|
296
|
-
if (
|
|
469
|
+
if (connection?.protocol === Protocol.Websocket) {
|
|
297
470
|
if (socket?.readyState === 1) {
|
|
298
471
|
socket.send(JSON.stringify(bodyWithContext));
|
|
299
472
|
}
|
|
@@ -303,20 +476,14 @@ function createConversation(configuration) {
|
|
|
303
476
|
}
|
|
304
477
|
else {
|
|
305
478
|
try {
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
},
|
|
314
|
-
body: JSON.stringify(bodyWithContext),
|
|
479
|
+
const json = await fetchUserMessage({
|
|
480
|
+
fullApplicationUrl: fullApplicationHttpUrl(),
|
|
481
|
+
apiKey: connection?.apiKey ?? "",
|
|
482
|
+
headers: configuration.headers ?? {},
|
|
483
|
+
stream: connection?.protocol === Protocol.HttpsWithStreaming,
|
|
484
|
+
eventListeners,
|
|
485
|
+
body: bodyWithContext,
|
|
315
486
|
});
|
|
316
|
-
if (res.status >= 400) {
|
|
317
|
-
throw new Error(`Responded with ${res.status}`);
|
|
318
|
-
}
|
|
319
|
-
const json = await res.json();
|
|
320
487
|
messageResponseHandler(json);
|
|
321
488
|
}
|
|
322
489
|
catch (err) {
|
|
@@ -342,7 +509,7 @@ function createConversation(configuration) {
|
|
|
342
509
|
const setupWebsocket = () => {
|
|
343
510
|
// If the socket is already set up, tear it down first
|
|
344
511
|
teardownWebsocket();
|
|
345
|
-
const url = new URL(
|
|
512
|
+
const url = new URL(websocketApplicationUrl);
|
|
346
513
|
if (configuration.experimental?.completeApplicationUrl !== true) {
|
|
347
514
|
url.searchParams.set("languageCode", state.languageCode);
|
|
348
515
|
url.searchParams.set("channelKey", `${url.searchParams.get("channelKey") ?? ""}-${state.languageCode}`);
|
|
@@ -357,21 +524,6 @@ function createConversation(configuration) {
|
|
|
357
524
|
messageResponseHandler(safeJsonParse(e.data));
|
|
358
525
|
}
|
|
359
526
|
};
|
|
360
|
-
url.searchParams.set("voice-plus", "true");
|
|
361
|
-
voicePlusSocket = new ReconnectingWebSocket(url.href);
|
|
362
|
-
voicePlusSocketMessageQueueCheckInterval = setInterval(() => {
|
|
363
|
-
checkVoicePlusSocketQueue();
|
|
364
|
-
}, 500);
|
|
365
|
-
voicePlusSocket.onmessage = (e) => {
|
|
366
|
-
if (typeof e?.data === "string") {
|
|
367
|
-
const command = safeJsonParse(e.data);
|
|
368
|
-
if (command != null) {
|
|
369
|
-
eventListeners.voicePlusCommand.forEach((listener) => {
|
|
370
|
-
listener(command);
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
527
|
};
|
|
376
528
|
const setupCommandWebsocket = () => {
|
|
377
529
|
// If the socket is already set up, tear it down first
|
|
@@ -379,16 +531,15 @@ function createConversation(configuration) {
|
|
|
379
531
|
if (configuration.bidirectional !== true) {
|
|
380
532
|
return;
|
|
381
533
|
}
|
|
382
|
-
const url = new URL(
|
|
534
|
+
const url = new URL(websocketApplicationUrl);
|
|
383
535
|
if (configuration.experimental?.completeApplicationUrl !== true) {
|
|
384
536
|
url.searchParams.set("languageCode", state.languageCode);
|
|
385
537
|
url.searchParams.set("channelKey", `${url.searchParams.get("channelKey") ?? ""}-${state.languageCode}`);
|
|
386
538
|
}
|
|
387
539
|
url.searchParams.set("conversationId", state.conversationId);
|
|
388
540
|
url.searchParams.set("type", "voice-plus");
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
url.searchParams.set("apiKey", apiKey);
|
|
541
|
+
if (connection?.apiKey != null) {
|
|
542
|
+
url.searchParams.set("apiKey", connection.apiKey);
|
|
392
543
|
}
|
|
393
544
|
voicePlusSocket = new ReconnectingWebSocket(url.href);
|
|
394
545
|
voicePlusSocketMessageQueueCheckInterval = setInterval(() => {
|
|
@@ -425,7 +576,7 @@ function createConversation(configuration) {
|
|
|
425
576
|
voicePlusSocket = undefined;
|
|
426
577
|
}
|
|
427
578
|
};
|
|
428
|
-
if (
|
|
579
|
+
if (connection?.protocol === Protocol.Websocket) {
|
|
429
580
|
setupWebsocket();
|
|
430
581
|
}
|
|
431
582
|
setupCommandWebsocket();
|
|
@@ -531,10 +682,13 @@ function createConversation(configuration) {
|
|
|
531
682
|
method: "POST",
|
|
532
683
|
headers: {
|
|
533
684
|
...(configuration.headers ?? {}),
|
|
685
|
+
"nlx-api-key": connection?.apiKey ?? "",
|
|
534
686
|
Accept: "application/json",
|
|
535
687
|
"Content-Type": "application/json",
|
|
536
688
|
"nlx-conversation-id": state.conversationId,
|
|
689
|
+
// Legacy header
|
|
537
690
|
"nlx-sdk-version": packageJson.version,
|
|
691
|
+
"nlx-core-version": packageJson.version,
|
|
538
692
|
},
|
|
539
693
|
body: JSON.stringify({
|
|
540
694
|
languageCode: state.languageCode,
|
|
@@ -589,6 +743,23 @@ function createConversation(configuration) {
|
|
|
589
743
|
sendFlow(welcomeIntent, context);
|
|
590
744
|
},
|
|
591
745
|
sendChoice,
|
|
746
|
+
submitFeedback: async (feedbackUrl, feedback) => {
|
|
747
|
+
const res = await fetch(feedbackUrl, {
|
|
748
|
+
method: "POST",
|
|
749
|
+
headers: {
|
|
750
|
+
"Content-Type": "application/json",
|
|
751
|
+
},
|
|
752
|
+
body: JSON.stringify({
|
|
753
|
+
languageCode: state.languageCode,
|
|
754
|
+
conversationId: state.conversationId,
|
|
755
|
+
userId: state.userId,
|
|
756
|
+
...feedback,
|
|
757
|
+
}),
|
|
758
|
+
});
|
|
759
|
+
if (res.status >= 400) {
|
|
760
|
+
throw new Error(`Responded with ${res.status}`);
|
|
761
|
+
}
|
|
762
|
+
},
|
|
592
763
|
currentConversationId: () => {
|
|
593
764
|
return state.conversationId;
|
|
594
765
|
},
|
|
@@ -597,7 +768,7 @@ function createConversation(configuration) {
|
|
|
597
768
|
Console.warn("Attempted to set language code to the one already active.");
|
|
598
769
|
return;
|
|
599
770
|
}
|
|
600
|
-
if (
|
|
771
|
+
if (connection?.protocol === Protocol.Websocket) {
|
|
601
772
|
setupWebsocket();
|
|
602
773
|
}
|
|
603
774
|
setupCommandWebsocket();
|
|
@@ -607,15 +778,17 @@ function createConversation(configuration) {
|
|
|
607
778
|
return state.languageCode;
|
|
608
779
|
},
|
|
609
780
|
getVoiceCredentials: async (context, options) => {
|
|
610
|
-
const
|
|
611
|
-
const res = await fetch(`${url}-${state.languageCode}/requestToken`, {
|
|
781
|
+
const res = await fetch(`${httpApplicationUrl}-${state.languageCode}/requestToken`, {
|
|
612
782
|
method: "POST",
|
|
613
783
|
headers: {
|
|
614
784
|
...(configuration.headers ?? {}),
|
|
785
|
+
"nlx-api-key": connection?.apiKey ?? "",
|
|
615
786
|
Accept: "application/json",
|
|
616
787
|
"Content-Type": "application/json",
|
|
617
788
|
"nlx-conversation-id": state.conversationId,
|
|
789
|
+
// Legacy header
|
|
618
790
|
"nlx-sdk-version": packageJson.version,
|
|
791
|
+
"nlx-core-version": packageJson.version,
|
|
619
792
|
},
|
|
620
793
|
body: JSON.stringify({
|
|
621
794
|
languageCode: state.languageCode,
|
|
@@ -645,14 +818,14 @@ function createConversation(configuration) {
|
|
|
645
818
|
conversationId: v4(),
|
|
646
819
|
responses: options?.clearResponses === true ? [] : state.responses,
|
|
647
820
|
});
|
|
648
|
-
if (
|
|
821
|
+
if (connection?.protocol === Protocol.Websocket) {
|
|
649
822
|
setupWebsocket();
|
|
650
823
|
}
|
|
651
824
|
setupCommandWebsocket();
|
|
652
825
|
},
|
|
653
826
|
destroy: () => {
|
|
654
827
|
subscribers = [];
|
|
655
|
-
if (
|
|
828
|
+
if (connection?.protocol === Protocol.Websocket) {
|
|
656
829
|
teardownWebsocket();
|
|
657
830
|
}
|
|
658
831
|
teardownCommandWebsocket();
|
|
@@ -677,8 +850,7 @@ function createConversation(configuration) {
|
|
|
677
850
|
* @returns Whether the configuration is valid?
|
|
678
851
|
*/
|
|
679
852
|
const isConfigValid = (configuration) => {
|
|
680
|
-
|
|
681
|
-
return applicationUrl.length > 0;
|
|
853
|
+
return parseConnection(configuration) != null;
|
|
682
854
|
};
|
|
683
855
|
/**
|
|
684
856
|
* Helper method to decide when a new {@link Config} requires creating a new {@link ConversationHandler} or whether the old `Config`'s
|
|
@@ -771,5 +943,71 @@ function promisify(fn, convo, timeout = 10000) {
|
|
|
771
943
|
});
|
|
772
944
|
};
|
|
773
945
|
}
|
|
946
|
+
/**
|
|
947
|
+
* Use this function when using **Voice+ scripts** to advance the conversation to the step specified.
|
|
948
|
+
*
|
|
949
|
+
* This functionality is orthogonal from other usage of the core SDK, as it may be used either using standard SDK communication channels or it can be used to provide a Voice+ script experience with for instance a telephony based channel.
|
|
950
|
+
* @example
|
|
951
|
+
* ```typescript
|
|
952
|
+
* import { sendVoicePlusStep } from "@nlxai/core";
|
|
953
|
+
*
|
|
954
|
+
* await sendVoicePlusStep({
|
|
955
|
+
* // hard-coded params
|
|
956
|
+
* apiKey: "REPLACE_WITH_API_KEY",
|
|
957
|
+
* workspaceId: "REPLACE_WITH_WORKSPACE_ID",
|
|
958
|
+
* scriptId: "REPLACE_WITH_SCRIPT_ID",
|
|
959
|
+
* step: "REPLACE_WITH_STEP_ID",
|
|
960
|
+
* // dynamic params
|
|
961
|
+
* conversationId: "REPLACE_WITH_CONVERSATION_ID",
|
|
962
|
+
* languageCode: "en-US",
|
|
963
|
+
* });
|
|
964
|
+
* ```
|
|
965
|
+
* @param configuration - Configuration for sending the step. Many of the values can be found on the deployment modal of the Voice+ script.
|
|
966
|
+
*/
|
|
967
|
+
const sendVoicePlusStep = async ({ apiKey, workspaceId, conversationId, scriptId, languageCode, step, context, debug = false, dev = false, }) => {
|
|
968
|
+
if (scriptId == null) {
|
|
969
|
+
throw new Error("Voice+ scriptId is not defined.");
|
|
970
|
+
}
|
|
971
|
+
if (typeof conversationId !== "string" || conversationId.length === 0) {
|
|
972
|
+
throw new Error("Voice+ conversationId is not defined.");
|
|
973
|
+
}
|
|
974
|
+
const [stepId, stepTriggerDescription] = typeof step === "string"
|
|
975
|
+
? [step, undefined]
|
|
976
|
+
: [step.stepId, step.stepTriggerDescription];
|
|
977
|
+
if (!stepIdRegex.test(stepId)) {
|
|
978
|
+
throw new Error("Invalid stepId. It should be formatted as a UUID.");
|
|
979
|
+
}
|
|
980
|
+
const payload = {
|
|
981
|
+
stepId,
|
|
982
|
+
context,
|
|
983
|
+
conversationId,
|
|
984
|
+
journeyId: scriptId,
|
|
985
|
+
languageCode,
|
|
986
|
+
stepTriggerDescription,
|
|
987
|
+
};
|
|
988
|
+
try {
|
|
989
|
+
await fetch(`https://${dev ? "dev." : ""}mm.nlx.ai/v1/track`, {
|
|
990
|
+
method: "POST",
|
|
991
|
+
headers: {
|
|
992
|
+
"x-api-key": apiKey,
|
|
993
|
+
"x-nlx-id": workspaceId,
|
|
994
|
+
"Content-Type": "application/json",
|
|
995
|
+
"nlx-sdk-version": packageJson.version,
|
|
996
|
+
"nlx-core-version": packageJson.version,
|
|
997
|
+
},
|
|
998
|
+
body: JSON.stringify(payload),
|
|
999
|
+
});
|
|
1000
|
+
if (debug) {
|
|
1001
|
+
Console.info(`✓ step: ${stepId}`, payload);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
catch (err) {
|
|
1005
|
+
if (debug) {
|
|
1006
|
+
Console.error(`× step: ${stepId}`, err, payload);
|
|
1007
|
+
}
|
|
1008
|
+
throw err;
|
|
1009
|
+
}
|
|
1010
|
+
};
|
|
1011
|
+
const stepIdRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
774
1012
|
|
|
775
|
-
export { ResponseType, createConversation, getCurrentExpirationTimestamp, isConfigValid, promisify, shouldReinitialize, version };
|
|
1013
|
+
export { Protocol, ResponseType, createConversation, getCurrentExpirationTimestamp, isConfigValid, promisify, sendVoicePlusStep, shouldReinitialize, version };
|