@nuancelabs/js-sdk 0.13.0 → 0.14.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/README.md +1 -0
- package/index.d.ts +37 -1
- package/index.js +86 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,6 +18,7 @@ const conversation = createNuanceConversation({
|
|
|
18
18
|
// Your endpoint, which mints a session token with your API key.
|
|
19
19
|
token: async () => {
|
|
20
20
|
const response = await fetch("/api/nuance-token", { method: "POST" });
|
|
21
|
+
if (!response.ok) throw new Error(await response.text());
|
|
21
22
|
return (await response.json()).token;
|
|
22
23
|
},
|
|
23
24
|
});
|
package/index.d.ts
CHANGED
|
@@ -86,6 +86,38 @@ export declare interface AgentTurnStartedEvent extends SessionEventBase {
|
|
|
86
86
|
source: "reply" | "say" | (string & {});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* An error answer from the Nuance API that the SDK has no more specific error
|
|
91
|
+
* for, such as a `401` for an expired or used session token, a `429` when this
|
|
92
|
+
* month's conversation time is used, or a `503` once the SDK has stopped
|
|
93
|
+
* retrying a busy service. See the Errors guide for each status and `code`.
|
|
94
|
+
*
|
|
95
|
+
* Act on `status` and `code`, never on `message`: the message is the server's
|
|
96
|
+
* `detail`, written for you, and it can change.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* try {
|
|
101
|
+
* await conversation.connect();
|
|
102
|
+
* } catch (error) {
|
|
103
|
+
* if (error instanceof ApiError && error.status === 401) {
|
|
104
|
+
* // The session token expired or was used: the next connect() fetches a new one.
|
|
105
|
+
* }
|
|
106
|
+
* }
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
export declare class ApiError extends Error {
|
|
110
|
+
/** The HTTP status, such as `401` or `503`. */
|
|
111
|
+
readonly status: number;
|
|
112
|
+
/** The error's `code` when the server sent one, such as `"busy"` or `"unavailable"`. */
|
|
113
|
+
readonly code: string | null;
|
|
114
|
+
/** How long to wait before trying again, when the server said. */
|
|
115
|
+
readonly retryAfterSeconds: number | null;
|
|
116
|
+
/** The parsed JSON body of the answer, or `null` when it had none. */
|
|
117
|
+
readonly body: unknown;
|
|
118
|
+
constructor(status: number, detail: string, body?: unknown);
|
|
119
|
+
}
|
|
120
|
+
|
|
89
121
|
/**
|
|
90
122
|
* An audio track that can be attached to or detached from an audio element.
|
|
91
123
|
*/
|
|
@@ -362,7 +394,8 @@ export declare type ConversationEvent =
|
|
|
362
394
|
* `"time-limit"` when the session was capped and ran out its clock,
|
|
363
395
|
* `"policy-violation"` when the runtime ended it for content, `"refused"`
|
|
364
396
|
* when the runtime would not start it, with its reason in `message` when it
|
|
365
|
-
* gave one.
|
|
397
|
+
* gave one. Call {@link Conversation.disconnect} then: after the runtime's
|
|
398
|
+
* own end the call stays connected, with the camera and microphone on.
|
|
366
399
|
*/
|
|
367
400
|
| {
|
|
368
401
|
type: "session-ended";
|
|
@@ -416,6 +449,7 @@ export declare type ConversationStateValue = `${ConversationState}`;
|
|
|
416
449
|
* // Your server mints a session token with its API key.
|
|
417
450
|
* token: async () => {
|
|
418
451
|
* const response = await fetch("/api/nuance-token", { method: "POST" });
|
|
452
|
+
* if (!response.ok) throw new Error(await response.text());
|
|
419
453
|
* return (await response.json()).token;
|
|
420
454
|
* },
|
|
421
455
|
* });
|
|
@@ -448,6 +482,7 @@ export declare interface CreateNuanceConversationOptions {
|
|
|
448
482
|
* ```ts
|
|
449
483
|
* token: async () => {
|
|
450
484
|
* const response = await fetch("/api/nuance-token", { method: "POST" });
|
|
485
|
+
* if (!response.ok) throw new Error(await response.text());
|
|
451
486
|
* return (await response.json()).token;
|
|
452
487
|
* }
|
|
453
488
|
* ```
|
|
@@ -557,6 +592,7 @@ export declare class GuestLimitError extends Error {
|
|
|
557
592
|
constructor(reason: string, message: string);
|
|
558
593
|
}
|
|
559
594
|
|
|
595
|
+
|
|
560
596
|
/**
|
|
561
597
|
* Raised by {@link Conversation.connect} when the server refuses a new session
|
|
562
598
|
* because the account has not completed onboarding or accepted the current
|
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.14.1"}`;
|
|
8
8
|
|
|
9
9
|
// src/utils.ts
|
|
10
10
|
function normalizeHost(host) {
|
|
@@ -14,12 +14,16 @@ function normalizeHost(host) {
|
|
|
14
14
|
// src/api/client.ts
|
|
15
15
|
class ApiError extends Error {
|
|
16
16
|
status;
|
|
17
|
+
code;
|
|
18
|
+
retryAfterSeconds;
|
|
17
19
|
body;
|
|
18
20
|
constructor(status, detail, body = null) {
|
|
19
21
|
super(detail);
|
|
20
22
|
this.name = "ApiError";
|
|
21
23
|
this.status = status;
|
|
22
24
|
this.body = body;
|
|
25
|
+
this.code = isRecord(body) && typeof body.code === "string" ? body.code : null;
|
|
26
|
+
this.retryAfterSeconds = isRecord(body) && typeof body.retry_after_seconds === "number" ? body.retry_after_seconds : null;
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
|
|
@@ -99,7 +103,6 @@ class NuanceApiClient {
|
|
|
99
103
|
return this.headersProvider ? await this.headersProvider() : {};
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
|
-
|
|
103
106
|
// src/core/active-session-error.ts
|
|
104
107
|
class ActiveSessionError extends Error {
|
|
105
108
|
constructor() {
|
|
@@ -1676,6 +1679,12 @@ var COMMAND_TIMEOUT_MS = 5000;
|
|
|
1676
1679
|
var STREAM_TIMEOUT_MS = 30000;
|
|
1677
1680
|
var BACKGROUND_ID = /^[a-z0-9][a-z0-9_-]{0,63}$/;
|
|
1678
1681
|
var AVATAR_RETURN_TIMEOUT_MS = 8000;
|
|
1682
|
+
var RUNTIME_RETURN_TIMEOUT_MS = 20000;
|
|
1683
|
+
var RUNTIME_RECHECK_MS = 1000;
|
|
1684
|
+
function contextItem(item) {
|
|
1685
|
+
const name = item.name === undefined ? {} : { name: item.name };
|
|
1686
|
+
return item.type === "text" ? { type: "text", text: item.text, ...name } : { type: "image", media_type: item.mediaType, data: item.data, ...name };
|
|
1687
|
+
}
|
|
1679
1688
|
function isReconnecting(lkState) {
|
|
1680
1689
|
return lkState === ConnectionState2.Reconnecting || lkState === ConnectionState2.SignalReconnecting;
|
|
1681
1690
|
}
|
|
@@ -1734,8 +1743,10 @@ class LiveKitConversation {
|
|
|
1734
1743
|
everConnected = false;
|
|
1735
1744
|
away = false;
|
|
1736
1745
|
awayTimer = null;
|
|
1746
|
+
runtimeTimer = null;
|
|
1737
1747
|
presentationPaused = false;
|
|
1738
1748
|
readySending = false;
|
|
1749
|
+
contextSent = null;
|
|
1739
1750
|
leaving = null;
|
|
1740
1751
|
connectGeneration = 0;
|
|
1741
1752
|
_id;
|
|
@@ -1744,6 +1755,8 @@ class LiveKitConversation {
|
|
|
1744
1755
|
cameraFacing;
|
|
1745
1756
|
tokenProvider;
|
|
1746
1757
|
opensOnReady;
|
|
1758
|
+
presentsAtFullSize;
|
|
1759
|
+
initialContext;
|
|
1747
1760
|
resilient;
|
|
1748
1761
|
constructor(config) {
|
|
1749
1762
|
this.room = new Room({
|
|
@@ -1765,6 +1778,8 @@ class LiveKitConversation {
|
|
|
1765
1778
|
this.cameraFacing = config.mediaDevices?.cameraFacing;
|
|
1766
1779
|
this.tokenProvider = config.tokenProvider;
|
|
1767
1780
|
this.opensOnReady = config.opensOnReady ?? false;
|
|
1781
|
+
this.presentsAtFullSize = config.presentsAtFullSize ?? this.opensOnReady;
|
|
1782
|
+
this.initialContext = config.initialContext ?? [];
|
|
1768
1783
|
this.resilient = config.resilientReconnect ?? false;
|
|
1769
1784
|
}
|
|
1770
1785
|
get id() {
|
|
@@ -1780,6 +1795,7 @@ class LiveKitConversation {
|
|
|
1780
1795
|
this.sequence.stop();
|
|
1781
1796
|
this.sequence = this.newSequence();
|
|
1782
1797
|
this.readyOwed = false;
|
|
1798
|
+
this.contextSent = null;
|
|
1783
1799
|
this.everConnected = false;
|
|
1784
1800
|
if (!this.eventsRegistered) {
|
|
1785
1801
|
this.registerRoomEvents();
|
|
@@ -2039,10 +2055,14 @@ class LiveKitConversation {
|
|
|
2039
2055
|
queueMicrotask(() => {
|
|
2040
2056
|
if (this.room.state === ConnectionState2.Disconnected)
|
|
2041
2057
|
return;
|
|
2042
|
-
if (isReconnecting(this.room.state))
|
|
2058
|
+
if (isReconnecting(this.room.state)) {
|
|
2043
2059
|
this.avatarAway();
|
|
2044
|
-
else
|
|
2060
|
+
} else if (this.sessionEnded) {
|
|
2045
2061
|
this.avatarLeft();
|
|
2062
|
+
} else if (this.avatarInRoom()) {} else {
|
|
2063
|
+
this.avatarAway();
|
|
2064
|
+
this.awaitRuntimeReturn();
|
|
2065
|
+
}
|
|
2046
2066
|
});
|
|
2047
2067
|
});
|
|
2048
2068
|
this.room.on(RoomEvent3.Disconnected, () => {
|
|
@@ -2150,6 +2170,7 @@ class LiveKitConversation {
|
|
|
2150
2170
|
if (packet === null)
|
|
2151
2171
|
return;
|
|
2152
2172
|
this.speaksProtocol = true;
|
|
2173
|
+
this.sendContext();
|
|
2153
2174
|
const { seq, event } = packet;
|
|
2154
2175
|
if (this.sequence.repeats(seq))
|
|
2155
2176
|
return;
|
|
@@ -2243,6 +2264,10 @@ class LiveKitConversation {
|
|
|
2243
2264
|
this.emit({ type: "avatar-presented" });
|
|
2244
2265
|
return;
|
|
2245
2266
|
}
|
|
2267
|
+
if (!this.presentsAtFullSize) {
|
|
2268
|
+
this.markAvatarPresented();
|
|
2269
|
+
return;
|
|
2270
|
+
}
|
|
2246
2271
|
this.startPresentation();
|
|
2247
2272
|
}
|
|
2248
2273
|
startPresentation() {
|
|
@@ -2253,7 +2278,7 @@ class LiveKitConversation {
|
|
|
2253
2278
|
markAvatarPresented() {
|
|
2254
2279
|
this.presentation = null;
|
|
2255
2280
|
if (this.speaksProtocol && this.room.state === ConnectionState2.Connected) {
|
|
2256
|
-
this.
|
|
2281
|
+
this.ready().catch(() => {
|
|
2257
2282
|
return;
|
|
2258
2283
|
});
|
|
2259
2284
|
} else if (this.resilient && this.speaksProtocol && isReconnecting(this.room.state)) {
|
|
@@ -2261,11 +2286,28 @@ class LiveKitConversation {
|
|
|
2261
2286
|
}
|
|
2262
2287
|
this.emit({ type: "avatar-presented" });
|
|
2263
2288
|
}
|
|
2289
|
+
sendContext() {
|
|
2290
|
+
if (this.contextSent !== null || this.initialContext.length === 0)
|
|
2291
|
+
return;
|
|
2292
|
+
this.contextSent = this.command({
|
|
2293
|
+
type: "session.context",
|
|
2294
|
+
items: this.initialContext.map(contextItem)
|
|
2295
|
+
}).then(() => {
|
|
2296
|
+
return;
|
|
2297
|
+
}, () => {
|
|
2298
|
+
return;
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2301
|
+
ready() {
|
|
2302
|
+
this.sendContext();
|
|
2303
|
+
const ready = () => this.command({ type: "session.ready" });
|
|
2304
|
+
return this.contextSent === null ? ready() : this.contextSent.then(ready);
|
|
2305
|
+
}
|
|
2264
2306
|
sendOwedReady() {
|
|
2265
2307
|
if (!this.readyOwed || !this.speaksProtocol || this.readySending)
|
|
2266
2308
|
return;
|
|
2267
2309
|
this.readySending = true;
|
|
2268
|
-
this.
|
|
2310
|
+
this.ready().then(() => {
|
|
2269
2311
|
this.readyOwed = false;
|
|
2270
2312
|
}).catch(() => {
|
|
2271
2313
|
return;
|
|
@@ -2307,6 +2349,33 @@ class LiveKitConversation {
|
|
|
2307
2349
|
this.avatarLeft();
|
|
2308
2350
|
}, AVATAR_RETURN_TIMEOUT_MS);
|
|
2309
2351
|
}
|
|
2352
|
+
awaitRuntimeReturn(delay = RUNTIME_RETURN_TIMEOUT_MS) {
|
|
2353
|
+
if (this.runtimeTimer !== null)
|
|
2354
|
+
return;
|
|
2355
|
+
this.runtimeTimer = setTimeout(() => {
|
|
2356
|
+
this.runtimeTimer = null;
|
|
2357
|
+
if (!this.away)
|
|
2358
|
+
return;
|
|
2359
|
+
if (isReconnecting(this.room.state)) {
|
|
2360
|
+
this.awaitRuntimeReturn(RUNTIME_RECHECK_MS);
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
if (this.room.state === ConnectionState2.Disconnected)
|
|
2364
|
+
return;
|
|
2365
|
+
if (this.avatarInRoom())
|
|
2366
|
+
return;
|
|
2367
|
+
this.clearAway();
|
|
2368
|
+
this.avatarLeft();
|
|
2369
|
+
this.reportEnd("connection-lost");
|
|
2370
|
+
}, delay);
|
|
2371
|
+
}
|
|
2372
|
+
avatarInRoom() {
|
|
2373
|
+
for (const [, participant] of this.room.remoteParticipants) {
|
|
2374
|
+
if (this.isAvatarParticipant(participant))
|
|
2375
|
+
return true;
|
|
2376
|
+
}
|
|
2377
|
+
return false;
|
|
2378
|
+
}
|
|
2310
2379
|
avatarBack() {
|
|
2311
2380
|
const resume = this.presentationPaused && this._avatar.isReady;
|
|
2312
2381
|
this.clearAway();
|
|
@@ -2322,6 +2391,10 @@ class LiveKitConversation {
|
|
|
2322
2391
|
clearTimeout(this.awayTimer);
|
|
2323
2392
|
this.awayTimer = null;
|
|
2324
2393
|
}
|
|
2394
|
+
if (this.runtimeTimer !== null) {
|
|
2395
|
+
clearTimeout(this.runtimeTimer);
|
|
2396
|
+
this.runtimeTimer = null;
|
|
2397
|
+
}
|
|
2325
2398
|
}
|
|
2326
2399
|
startLayerFallback() {
|
|
2327
2400
|
if (this.layerFallback !== null)
|
|
@@ -2429,6 +2502,7 @@ class NuanceConversation {
|
|
|
2429
2502
|
sessionOverrides;
|
|
2430
2503
|
personality;
|
|
2431
2504
|
tools;
|
|
2505
|
+
initialContext;
|
|
2432
2506
|
variant;
|
|
2433
2507
|
maxSessionSeconds;
|
|
2434
2508
|
videoCapture;
|
|
@@ -2468,6 +2542,7 @@ class NuanceConversation {
|
|
|
2468
2542
|
this.sessionOverrides = options.sessionOverrides ?? {};
|
|
2469
2543
|
this.personality = options.personality;
|
|
2470
2544
|
this.tools = options.tools;
|
|
2545
|
+
this.initialContext = options.initialContext ?? [];
|
|
2471
2546
|
this.variant = options.variant;
|
|
2472
2547
|
this.maxSessionSeconds = options.maxSessionSeconds;
|
|
2473
2548
|
this.videoCapture = options.videoCapture;
|
|
@@ -2497,7 +2572,7 @@ class NuanceConversation {
|
|
|
2497
2572
|
try {
|
|
2498
2573
|
const request = {
|
|
2499
2574
|
session_overrides: this.sessionOverrides,
|
|
2500
|
-
client_ready: this.startOn === "full-quality"
|
|
2575
|
+
client_ready: this.startOn === "full-quality" || this.initialContext.length > 0
|
|
2501
2576
|
};
|
|
2502
2577
|
const timezone = this.timezone();
|
|
2503
2578
|
if (timezone)
|
|
@@ -2778,7 +2853,7 @@ class NuanceConversation {
|
|
|
2778
2853
|
return "time-limit";
|
|
2779
2854
|
if (reason === "policy_violation")
|
|
2780
2855
|
return "policy-violation";
|
|
2781
|
-
if (this.cutOff())
|
|
2856
|
+
if (reason === "runtime_silent" || this.cutOff())
|
|
2782
2857
|
return "connection-lost";
|
|
2783
2858
|
return "ended";
|
|
2784
2859
|
}
|
|
@@ -2809,6 +2884,8 @@ class NuanceConversation {
|
|
|
2809
2884
|
cameraEnabled: !this.sessionOverrides.disable_camera && mediaDevices.cameraEnabled !== false,
|
|
2810
2885
|
videoCapture: this.videoCapture,
|
|
2811
2886
|
opensOnReady: conversation.provider_metadata?.client_ready === true,
|
|
2887
|
+
presentsAtFullSize: this.startOn === "full-quality",
|
|
2888
|
+
initialContext: this.initialContext,
|
|
2812
2889
|
resilientReconnect: this.resilient
|
|
2813
2890
|
});
|
|
2814
2891
|
}
|
|
@@ -3012,5 +3089,6 @@ export {
|
|
|
3012
3089
|
LegalRequiredError,
|
|
3013
3090
|
GuestLimitError,
|
|
3014
3091
|
ConversationState,
|
|
3092
|
+
ApiError,
|
|
3015
3093
|
ActiveSessionError
|
|
3016
3094
|
};
|