@nuancelabs/js-sdk 0.9.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +16 -0
- package/index.js +77 -5
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -376,6 +376,18 @@ export declare interface CreateNuanceConversationOptions {
|
|
|
376
376
|
}
|
|
377
377
|
|
|
378
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Raised by {@link Conversation.connect} when the server refuses a guest's new
|
|
381
|
+
* session because a guest limit is used up. `reason` says which: `used` (this
|
|
382
|
+
* guest has had its conversations), `device` (so has this browser) or `network`
|
|
383
|
+
* (so has this network today). The message is the server's, written for the
|
|
384
|
+
* person.
|
|
385
|
+
*/
|
|
386
|
+
export declare class GuestLimitError extends Error {
|
|
387
|
+
readonly reason: string;
|
|
388
|
+
constructor(reason: string, message: string);
|
|
389
|
+
}
|
|
390
|
+
|
|
379
391
|
/**
|
|
380
392
|
* Raised by {@link Conversation.connect} when the server refuses a new session
|
|
381
393
|
* because the account has not completed onboarding or accepted the current
|
|
@@ -500,6 +512,10 @@ export declare interface SessionTokenResponse {
|
|
|
500
512
|
/** When the avatar starts speaking: see {@link CreateNuanceConversationOptions.startOn}. */
|
|
501
513
|
export declare type StartOn = "first-frame" | "full-quality";
|
|
502
514
|
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
|
|
503
519
|
/**
|
|
504
520
|
* The local user participant in a conversation.
|
|
505
521
|
*/
|
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ function isRecord(value) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/core/sdk-version.ts
|
|
7
|
-
var SDK_HEADER = `js/${"0.
|
|
7
|
+
var SDK_HEADER = `js/${"0.10.1"}`;
|
|
8
8
|
|
|
9
9
|
// src/utils.ts
|
|
10
10
|
function normalizeHost(host) {
|
|
@@ -108,6 +108,16 @@ class ActiveSessionError extends Error {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// src/core/guest-limit-error.ts
|
|
112
|
+
class GuestLimitError extends Error {
|
|
113
|
+
reason;
|
|
114
|
+
constructor(reason, message) {
|
|
115
|
+
super(message);
|
|
116
|
+
this.name = "GuestLimitError";
|
|
117
|
+
this.reason = reason;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
111
121
|
// src/core/legal-required-error.ts
|
|
112
122
|
class LegalRequiredError extends Error {
|
|
113
123
|
constructor() {
|
|
@@ -1088,6 +1098,17 @@ function parseSessionEvent(payload) {
|
|
|
1088
1098
|
if (decoded.type === "floor") {
|
|
1089
1099
|
return { type: "floor", speaking: decoded.state === "speaking" };
|
|
1090
1100
|
}
|
|
1101
|
+
if (decoded.type === "tool.call" && typeof decoded.call_id === "string" && typeof decoded.name === "string" && isRecord(decoded.arguments)) {
|
|
1102
|
+
return {
|
|
1103
|
+
type: "tool-call",
|
|
1104
|
+
call: {
|
|
1105
|
+
callId: decoded.call_id,
|
|
1106
|
+
name: decoded.name,
|
|
1107
|
+
label: typeof decoded.label === "string" ? decoded.label : "",
|
|
1108
|
+
arguments: decoded.arguments
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1091
1112
|
if (decoded.type === "session.status" && decoded.status === "ended") {
|
|
1092
1113
|
return {
|
|
1093
1114
|
type: "ended",
|
|
@@ -1534,6 +1555,7 @@ class LiveKitUserParticipant {
|
|
|
1534
1555
|
// src/providers/livekit/conversation.ts
|
|
1535
1556
|
var DATA_EVENT_TOPIC = "nuance.data";
|
|
1536
1557
|
var SESSION_TOPIC = "nuance.v1";
|
|
1558
|
+
var PACKET_MAX_BYTES = 14000;
|
|
1537
1559
|
var RUNTIME_TOPIC = "runtime";
|
|
1538
1560
|
var CLOCK_SYNC_TOPIC = "clock-sync";
|
|
1539
1561
|
var STATS_FLUSH_TIMEOUT_MS = 300;
|
|
@@ -1541,6 +1563,7 @@ var SESSION_END_TIMEOUT_MS = 300;
|
|
|
1541
1563
|
var SESSION_END_ID = "client-session-end";
|
|
1542
1564
|
var REFUSAL_REASON_WAIT_MS = 500;
|
|
1543
1565
|
var COMMAND_TIMEOUT_MS = 5000;
|
|
1566
|
+
var STREAM_TIMEOUT_MS = 30000;
|
|
1544
1567
|
var BACKGROUND_ID = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
1545
1568
|
var AVATAR_RETURN_TIMEOUT_MS = 8000;
|
|
1546
1569
|
function isReconnecting(lkState) {
|
|
@@ -1754,9 +1777,24 @@ class LiveKitConversation {
|
|
|
1754
1777
|
async releaseAgent() {
|
|
1755
1778
|
return this.command({ type: "floor.release" });
|
|
1756
1779
|
}
|
|
1780
|
+
async sendToolResult(callId, output, options = {}) {
|
|
1781
|
+
return this.command({
|
|
1782
|
+
type: "tool.result",
|
|
1783
|
+
call_id: callId,
|
|
1784
|
+
output,
|
|
1785
|
+
...options.images?.length && {
|
|
1786
|
+
images: options.images.map(({ mediaType, data }) => ({
|
|
1787
|
+
media_type: mediaType,
|
|
1788
|
+
data
|
|
1789
|
+
}))
|
|
1790
|
+
},
|
|
1791
|
+
is_error: options.isError ?? false,
|
|
1792
|
+
final: options.final ?? true
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1757
1795
|
onChange(handler) {
|
|
1758
1796
|
return this.onEvent((event) => {
|
|
1759
|
-
if (event.type !== "data-event" && event.type !== "screen-share-changed")
|
|
1797
|
+
if (event.type !== "data-event" && event.type !== "screen-share-changed" && event.type !== "tool-call")
|
|
1760
1798
|
handler(event);
|
|
1761
1799
|
});
|
|
1762
1800
|
}
|
|
@@ -1773,6 +1811,12 @@ class LiveKitConversation {
|
|
|
1773
1811
|
}
|
|
1774
1812
|
});
|
|
1775
1813
|
}
|
|
1814
|
+
onToolCall(handler) {
|
|
1815
|
+
return this.onEvent((event) => {
|
|
1816
|
+
if (event.type === "tool-call")
|
|
1817
|
+
handler(event.call);
|
|
1818
|
+
});
|
|
1819
|
+
}
|
|
1776
1820
|
async command(command) {
|
|
1777
1821
|
if (this.room.state !== ConnectionState2.Connected) {
|
|
1778
1822
|
throw new Error("Not connected — call connect() first");
|
|
@@ -1784,11 +1828,18 @@ class LiveKitConversation {
|
|
|
1784
1828
|
const answer = Promise.withResolvers();
|
|
1785
1829
|
this.commands.set(id, { type: command.type, settle: answer.resolve });
|
|
1786
1830
|
let timer;
|
|
1831
|
+
const text = JSON.stringify({ ...command, id });
|
|
1832
|
+
const payload = new TextEncoder().encode(text);
|
|
1833
|
+
const streamed = payload.byteLength > PACKET_MAX_BYTES;
|
|
1834
|
+
const sent = streamed ? this.room.localParticipant.sendText(text, { topic: SESSION_TOPIC }) : this.room.localParticipant.publishData(payload, {
|
|
1835
|
+
reliable: true,
|
|
1836
|
+
topic: SESSION_TOPIC
|
|
1837
|
+
});
|
|
1787
1838
|
try {
|
|
1788
1839
|
return await Promise.race([
|
|
1789
|
-
|
|
1840
|
+
sent.then(() => answer.promise),
|
|
1790
1841
|
new Promise((_, reject) => {
|
|
1791
|
-
timer = setTimeout(() => reject(new Error(`The runtime did not answer ${command.type}`)), COMMAND_TIMEOUT_MS);
|
|
1842
|
+
timer = setTimeout(() => reject(new Error(`The runtime did not answer ${command.type}`)), streamed ? STREAM_TIMEOUT_MS : COMMAND_TIMEOUT_MS);
|
|
1792
1843
|
})
|
|
1793
1844
|
]);
|
|
1794
1845
|
} finally {
|
|
@@ -1996,6 +2047,8 @@ class LiveKitConversation {
|
|
|
1996
2047
|
}
|
|
1997
2048
|
} else if (event.type === "floor") {
|
|
1998
2049
|
this.setAvatarSpeaking(event.speaking);
|
|
2050
|
+
} else if (event.type === "tool-call") {
|
|
2051
|
+
this.emit({ type: "tool-call", call: event.call });
|
|
1999
2052
|
} else if (event.type === "ended") {
|
|
2000
2053
|
this.answered?.();
|
|
2001
2054
|
if (!event.byClient)
|
|
@@ -2211,6 +2264,9 @@ function isMaintenanceBody(body) {
|
|
|
2211
2264
|
function isBusyBody(body) {
|
|
2212
2265
|
return isRecord(body) && body.code === "busy";
|
|
2213
2266
|
}
|
|
2267
|
+
function isGuestLimitBody(body) {
|
|
2268
|
+
return isRecord(body) && body.code === "guest_limit";
|
|
2269
|
+
}
|
|
2214
2270
|
function isCurfewBody(body) {
|
|
2215
2271
|
return isRecord(body) && body.code === "policy_curfew";
|
|
2216
2272
|
}
|
|
@@ -2325,6 +2381,9 @@ class NuanceConversation {
|
|
|
2325
2381
|
if (error instanceof ApiError && error.status === 503 && isMaintenanceBody(error.body)) {
|
|
2326
2382
|
throw new MaintenanceError(typeof error.body.detail === "string" ? error.body.detail : "The service is under maintenance");
|
|
2327
2383
|
}
|
|
2384
|
+
if (error instanceof ApiError && error.status === 429 && isGuestLimitBody(error.body)) {
|
|
2385
|
+
throw new GuestLimitError(typeof error.body.reason === "string" ? error.body.reason : "used", typeof error.body.detail === "string" ? error.body.detail : "You've had your free conversation");
|
|
2386
|
+
}
|
|
2328
2387
|
if (error instanceof ApiError && error.status === 403 && isCurfewBody(error.body)) {
|
|
2329
2388
|
throw new PolicyCurfewError(typeof error.body.retry_after_seconds === "number" ? error.body.retry_after_seconds : null);
|
|
2330
2389
|
}
|
|
@@ -2430,9 +2489,15 @@ class NuanceConversation {
|
|
|
2430
2489
|
}
|
|
2431
2490
|
return this.delegate.releaseAgent();
|
|
2432
2491
|
}
|
|
2492
|
+
async sendToolResult(callId, output, options) {
|
|
2493
|
+
if (!this.delegate) {
|
|
2494
|
+
throw new Error("Not connected — call connect() first");
|
|
2495
|
+
}
|
|
2496
|
+
return this.delegate.sendToolResult(callId, output, options);
|
|
2497
|
+
}
|
|
2433
2498
|
onChange(handler) {
|
|
2434
2499
|
return this.onEvent((event) => {
|
|
2435
|
-
if (event.type !== "data-event" && event.type !== "screen-share-changed")
|
|
2500
|
+
if (event.type !== "data-event" && event.type !== "screen-share-changed" && event.type !== "tool-call")
|
|
2436
2501
|
handler(event);
|
|
2437
2502
|
});
|
|
2438
2503
|
}
|
|
@@ -2449,6 +2514,12 @@ class NuanceConversation {
|
|
|
2449
2514
|
}
|
|
2450
2515
|
});
|
|
2451
2516
|
}
|
|
2517
|
+
onToolCall(handler) {
|
|
2518
|
+
return this.onEvent((event) => {
|
|
2519
|
+
if (event.type === "tool-call")
|
|
2520
|
+
handler(event.call);
|
|
2521
|
+
});
|
|
2522
|
+
}
|
|
2452
2523
|
async waitForSlot(conversationId, attempt) {
|
|
2453
2524
|
const admission = await this.followEvents(conversationId, attempt);
|
|
2454
2525
|
if (admission !== null) {
|
|
@@ -2795,6 +2866,7 @@ export {
|
|
|
2795
2866
|
MediaAccessError,
|
|
2796
2867
|
MaintenanceError,
|
|
2797
2868
|
LegalRequiredError,
|
|
2869
|
+
GuestLimitError,
|
|
2798
2870
|
ConversationState,
|
|
2799
2871
|
ActiveSessionError
|
|
2800
2872
|
};
|