@nuancelabs/js-sdk 0.10.0 → 0.11.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.
Files changed (3) hide show
  1. package/index.d.ts +13 -0
  2. package/index.js +60 -5
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -499,6 +499,13 @@ export declare interface SessionPersonality {
499
499
  export declare interface SessionTokenRequest {
500
500
  /** Who the avatar is in the conversation. Leave it out for the default personality. */
501
501
  personality?: SessionPersonality;
502
+ /**
503
+ * Your own id for the person this conversation is for, such as a hash of
504
+ * your user id. After repeated policy violations, new conversations pause
505
+ * for this person only, not for your whole app. Letters, digits and
506
+ * `_ . : -`, up to 128 characters: never an email address or a name.
507
+ */
508
+ end_user_id?: string;
502
509
  }
503
510
 
504
511
  /** What `POST /session-tokens` returns. */
@@ -507,11 +514,17 @@ export declare interface SessionTokenResponse {
507
514
  token: string;
508
515
  /** When the token stops being able to start a conversation (UTC, ISO 8601). */
509
516
  expires_at: string;
517
+ /** What to change in how you mint tokens; empty when nothing. */
518
+ warnings?: string[];
510
519
  }
511
520
 
512
521
  /** When the avatar starts speaking: see {@link CreateNuanceConversationOptions.startOn}. */
513
522
  export declare type StartOn = "first-frame" | "full-quality";
514
523
 
524
+
525
+
526
+
527
+
515
528
  /**
516
529
  * The local user participant in a conversation.
517
530
  */
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.10.0"}`;
7
+ var SDK_HEADER = `js/${"0.11.0"}`;
8
8
 
9
9
  // src/utils.ts
10
10
  function normalizeHost(host) {
@@ -1098,6 +1098,17 @@ function parseSessionEvent(payload) {
1098
1098
  if (decoded.type === "floor") {
1099
1099
  return { type: "floor", speaking: decoded.state === "speaking" };
1100
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
+ }
1101
1112
  if (decoded.type === "session.status" && decoded.status === "ended") {
1102
1113
  return {
1103
1114
  type: "ended",
@@ -1544,6 +1555,7 @@ class LiveKitUserParticipant {
1544
1555
  // src/providers/livekit/conversation.ts
1545
1556
  var DATA_EVENT_TOPIC = "nuance.data";
1546
1557
  var SESSION_TOPIC = "nuance.v1";
1558
+ var PACKET_MAX_BYTES = 14000;
1547
1559
  var RUNTIME_TOPIC = "runtime";
1548
1560
  var CLOCK_SYNC_TOPIC = "clock-sync";
1549
1561
  var STATS_FLUSH_TIMEOUT_MS = 300;
@@ -1551,6 +1563,7 @@ var SESSION_END_TIMEOUT_MS = 300;
1551
1563
  var SESSION_END_ID = "client-session-end";
1552
1564
  var REFUSAL_REASON_WAIT_MS = 500;
1553
1565
  var COMMAND_TIMEOUT_MS = 5000;
1566
+ var STREAM_TIMEOUT_MS = 30000;
1554
1567
  var BACKGROUND_ID = /^[a-z0-9][a-z0-9_-]{0,63}$/;
1555
1568
  var AVATAR_RETURN_TIMEOUT_MS = 8000;
1556
1569
  function isReconnecting(lkState) {
@@ -1764,9 +1777,24 @@ class LiveKitConversation {
1764
1777
  async releaseAgent() {
1765
1778
  return this.command({ type: "floor.release" });
1766
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
+ }
1767
1795
  onChange(handler) {
1768
1796
  return this.onEvent((event) => {
1769
- 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")
1770
1798
  handler(event);
1771
1799
  });
1772
1800
  }
@@ -1783,6 +1811,12 @@ class LiveKitConversation {
1783
1811
  }
1784
1812
  });
1785
1813
  }
1814
+ onToolCall(handler) {
1815
+ return this.onEvent((event) => {
1816
+ if (event.type === "tool-call")
1817
+ handler(event.call);
1818
+ });
1819
+ }
1786
1820
  async command(command) {
1787
1821
  if (this.room.state !== ConnectionState2.Connected) {
1788
1822
  throw new Error("Not connected — call connect() first");
@@ -1794,11 +1828,18 @@ class LiveKitConversation {
1794
1828
  const answer = Promise.withResolvers();
1795
1829
  this.commands.set(id, { type: command.type, settle: answer.resolve });
1796
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
+ });
1797
1838
  try {
1798
1839
  return await Promise.race([
1799
- this.room.localParticipant.publishData(new TextEncoder().encode(JSON.stringify({ ...command, id })), { reliable: true, topic: SESSION_TOPIC }).then(() => answer.promise),
1840
+ sent.then(() => answer.promise),
1800
1841
  new Promise((_, reject) => {
1801
- 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);
1802
1843
  })
1803
1844
  ]);
1804
1845
  } finally {
@@ -2006,6 +2047,8 @@ class LiveKitConversation {
2006
2047
  }
2007
2048
  } else if (event.type === "floor") {
2008
2049
  this.setAvatarSpeaking(event.speaking);
2050
+ } else if (event.type === "tool-call") {
2051
+ this.emit({ type: "tool-call", call: event.call });
2009
2052
  } else if (event.type === "ended") {
2010
2053
  this.answered?.();
2011
2054
  if (!event.byClient)
@@ -2446,9 +2489,15 @@ class NuanceConversation {
2446
2489
  }
2447
2490
  return this.delegate.releaseAgent();
2448
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
+ }
2449
2498
  onChange(handler) {
2450
2499
  return this.onEvent((event) => {
2451
- 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")
2452
2501
  handler(event);
2453
2502
  });
2454
2503
  }
@@ -2465,6 +2514,12 @@ class NuanceConversation {
2465
2514
  }
2466
2515
  });
2467
2516
  }
2517
+ onToolCall(handler) {
2518
+ return this.onEvent((event) => {
2519
+ if (event.type === "tool-call")
2520
+ handler(event.call);
2521
+ });
2522
+ }
2468
2523
  async waitForSlot(conversationId, attempt) {
2469
2524
  const admission = await this.followEvents(conversationId, attempt);
2470
2525
  if (admission !== null) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuancelabs/js-sdk",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "description": "JavaScript SDK for Nuance Labs conversation video AI avatar",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {