@unboundcx/sdk 4.0.12 → 4.0.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unboundcx/sdk",
3
- "version": "4.0.12",
3
+ "version": "4.0.14",
4
4
  "description": "Official JavaScript SDK for the Unbound API - A comprehensive toolkit for integrating with Unbound's communication, AI, and data management services",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -108,6 +108,10 @@ message AudioRequest {
108
108
  // SilenceDetector), instead of relying on client-sent vad_event fields.
109
109
  // Defaults to false/unset: existing (non-Meet) callers are unaffected.
110
110
  bool server_vad = 26;
111
+
112
+ // Optional custom vocabulary terms to bias transcription toward
113
+ // (e.g. product names, jargon). Merged account + room terms, capped.
114
+ repeated string vocabulary = 27;
111
115
  }
112
116
 
113
117
  // Audio configuration
@@ -167,6 +167,8 @@ export class SttStream extends EventEmitter {
167
167
  * Meet (video-room) session metadata is not per-chunk — it is sent once in the
168
168
  * first-chunk session config, read from stream options: options.videoRoomId,
169
169
  * options.participantId, options.displayName, options.serverVad. See constructor.
170
+ * @param {string[]} [options.vocabulary] - Custom vocabulary terms to bias
171
+ * transcription toward (sent once in the first-chunk session config).
170
172
  */
171
173
  write(audioChunk, streamMetadata = {}) {
172
174
  if (this.isClosed) {
@@ -223,6 +225,7 @@ export class SttStream extends EventEmitter {
223
225
  participant_id: this.options.participantId || '',
224
226
  display_name: this.options.displayName || '',
225
227
  server_vad: this.options.serverVad || false,
228
+ vocabulary: this.options.vocabulary || [],
226
229
  };
227
230
 
228
231
  this.grpcCall.write(request);
@@ -0,0 +1,37 @@
1
+ /**
2
+ * AI Settings helpers - Manage account-level AI feature settings
3
+ * Exposed directly on AIService as getSettings()/updateSettings()
4
+ */
5
+
6
+ /**
7
+ * Get account AI settings
8
+ * @param {Object} sdk
9
+ * @returns {Promise<Object>} { settings: { shareOcrEnabled } }
10
+ */
11
+ export async function getSettings(sdk) {
12
+ const result = await sdk._fetch('/ai/settings', 'GET');
13
+ return result;
14
+ }
15
+
16
+ /**
17
+ * Update account AI settings
18
+ * @param {Object} sdk
19
+ * @param {Object} options
20
+ * @param {boolean} options.shareOcrEnabled - Whether shared-content OCR is enabled
21
+ * @returns {Promise<Object>} { settings }
22
+ */
23
+ export async function updateSettings(sdk, { shareOcrEnabled }) {
24
+ sdk.validateParams(
25
+ { shareOcrEnabled },
26
+ {
27
+ shareOcrEnabled: { type: 'boolean', required: true },
28
+ },
29
+ );
30
+
31
+ const params = {
32
+ body: { shareOcrEnabled },
33
+ };
34
+
35
+ const result = await sdk._fetch('/ai/settings', 'PUT', params);
36
+ return result;
37
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * AI Translate helper - Batch-translate arbitrary text items
3
+ * Exposed directly on AIService as translate()
4
+ */
5
+
6
+ /**
7
+ * Translate a batch of text items
8
+ * @param {Object} sdk
9
+ * @param {Object} options
10
+ * @param {Array<{id: string, text: string}>} options.items - Items to translate
11
+ * @param {string} options.targetLanguage - Target language code
12
+ * @param {string} [options.sourceLanguage] - Source language code (auto-detect if omitted)
13
+ * @param {string} [options.domain] - Optional domain hint for translation quality
14
+ * @param {string} [options.context] - Optional additional context
15
+ * @returns {Promise<Object>} { items: [{id, text}] }
16
+ */
17
+ export async function translate(
18
+ sdk,
19
+ { items, targetLanguage, sourceLanguage, domain, context },
20
+ ) {
21
+ sdk.validateParams(
22
+ { items, targetLanguage, sourceLanguage, domain, context },
23
+ {
24
+ items: { type: 'array', required: true },
25
+ targetLanguage: { type: 'string', required: true },
26
+ sourceLanguage: { type: 'string', required: false },
27
+ domain: { type: 'string', required: false },
28
+ context: { type: 'string', required: false },
29
+ },
30
+ );
31
+
32
+ const params = {
33
+ body: { items, targetLanguage, sourceLanguage, domain, context },
34
+ };
35
+
36
+ const result = await sdk._fetch('/ai/translate', 'POST', params);
37
+ return result;
38
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Vocabulary Service - Manage account-level custom transcription vocabulary
3
+ */
4
+ export class VocabularyService {
5
+ constructor(sdk) {
6
+ this.sdk = sdk;
7
+ }
8
+
9
+ /**
10
+ * List account custom vocabulary terms
11
+ * @returns {Promise<Object>} { terms: [{id, term, createdBy, createdAt}] }
12
+ */
13
+ async list() {
14
+ const result = await this.sdk._fetch('/ai/vocabulary', 'GET');
15
+ return result;
16
+ }
17
+
18
+ /**
19
+ * Add a custom vocabulary term (deduped case-insensitive, max 120 chars)
20
+ * @param {string} term - The vocabulary term to add
21
+ * @returns {Promise<Object>} { term: {id, term, createdBy, createdAt} }
22
+ */
23
+ async add(term) {
24
+ this.sdk.validateParams(
25
+ { term },
26
+ {
27
+ term: { type: 'string', required: true },
28
+ },
29
+ );
30
+
31
+ const params = {
32
+ body: { term },
33
+ };
34
+
35
+ const result = await this.sdk._fetch('/ai/vocabulary', 'POST', params);
36
+ return result;
37
+ }
38
+
39
+ /**
40
+ * Remove a custom vocabulary term
41
+ * @param {string} id - The vocabulary term ID
42
+ * @returns {Promise} 204-style response
43
+ */
44
+ async remove(id) {
45
+ this.sdk.validateParams(
46
+ { id },
47
+ {
48
+ id: { type: 'string', required: true },
49
+ },
50
+ );
51
+
52
+ const result = await this.sdk._fetch(`/ai/vocabulary/${id}`, 'DELETE');
53
+ return result;
54
+ }
55
+ }
package/services/ai.js CHANGED
@@ -1,4 +1,10 @@
1
1
  import { PlaybooksService } from './ai/playbooks.js';
2
+ import { VocabularyService } from './ai/vocabulary.js';
3
+ import { translate as translateItems } from './ai/translate.js';
4
+ import {
5
+ getSettings as getAiSettings,
6
+ updateSettings as updateAiSettings,
7
+ } from './ai/settings.js';
2
8
 
3
9
  export class AIService {
4
10
  constructor(sdk) {
@@ -8,6 +14,33 @@ export class AIService {
8
14
  this.stt = new SpeechToTextService(sdk);
9
15
  this.extract = new ExtractService(sdk);
10
16
  this.playbooks = new PlaybooksService(sdk);
17
+ this.vocabulary = new VocabularyService(sdk);
18
+ }
19
+
20
+ /**
21
+ * Translate a batch of text items
22
+ * @param {Object} params - { items, targetLanguage, sourceLanguage?, domain?, context? }
23
+ * @returns {Promise<Object>} { items: [{id, text}] }
24
+ */
25
+ async translate(params) {
26
+ return translateItems(this.sdk, params);
27
+ }
28
+
29
+ /**
30
+ * Get account AI settings
31
+ * @returns {Promise<Object>} { settings: { shareOcrEnabled } }
32
+ */
33
+ async getSettings() {
34
+ return getAiSettings(this.sdk);
35
+ }
36
+
37
+ /**
38
+ * Update account AI settings
39
+ * @param {Object} options - { shareOcrEnabled }
40
+ * @returns {Promise<Object>} { settings }
41
+ */
42
+ async updateSettings(options) {
43
+ return updateAiSettings(this.sdk, options);
11
44
  }
12
45
  }
13
46
 
package/services/video.js CHANGED
@@ -228,6 +228,8 @@ export class VideoService {
228
228
  calendarId,
229
229
  eventId,
230
230
  calendarProvider,
231
+ vocabularyTerms,
232
+ shareOcrEnabled,
231
233
  }) {
232
234
  this.sdk.validateParams(
233
235
  {
@@ -255,6 +257,8 @@ export class VideoService {
255
257
  calendarId,
256
258
  eventId,
257
259
  calendarProvider,
260
+ vocabularyTerms,
261
+ shareOcrEnabled,
258
262
  },
259
263
  {
260
264
  name: { type: 'string', required: false },
@@ -281,6 +285,8 @@ export class VideoService {
281
285
  calendarId: { type: 'string', required: false },
282
286
  eventId: { type: 'string', required: false },
283
287
  calendarProvider: { type: 'string', required: false },
288
+ vocabularyTerms: { type: 'array', required: false },
289
+ shareOcrEnabled: { type: 'boolean', required: false },
284
290
  },
285
291
  );
286
292
  const params = {
@@ -309,6 +315,8 @@ export class VideoService {
309
315
  calendarId,
310
316
  eventId,
311
317
  calendarProvider,
318
+ vocabularyTerms,
319
+ shareOcrEnabled,
312
320
  },
313
321
  };
314
322
  const result = await this.sdk._fetch(`/video`, 'POST', params);
@@ -350,6 +358,10 @@ export class VideoService {
350
358
  validationSchema.startTranscribingOn = { type: 'boolean' };
351
359
  if ('enableChat' in update)
352
360
  validationSchema.enableChat = { type: 'boolean' };
361
+ if ('vocabularyTerms' in update)
362
+ validationSchema.vocabularyTerms = { type: 'array' };
363
+ if ('shareOcrEnabled' in update)
364
+ validationSchema.shareOcrEnabled = { type: 'boolean' };
353
365
 
354
366
  if (Object.keys(validationSchema).length > 0) {
355
367
  this.sdk.validateParams(update, validationSchema);
@@ -1208,4 +1220,177 @@ export class VideoService {
1208
1220
  );
1209
1221
  return result;
1210
1222
  }
1223
+
1224
+ /**
1225
+ * Host-only manual retry for a stuck/failed webm->mp4 recording conversion
1226
+ * @param {string} roomId - The video room ID
1227
+ * @returns {Promise} { launched: true } on success
1228
+ */
1229
+ async retryRecordingConvert(roomId) {
1230
+ this.sdk.validateParams(
1231
+ { roomId },
1232
+ {
1233
+ roomId: { type: 'string', required: true },
1234
+ },
1235
+ );
1236
+
1237
+ const result = await this.sdk._fetch(
1238
+ `/video/${roomId}/recording-convert-retry`,
1239
+ 'POST',
1240
+ {},
1241
+ );
1242
+ return result;
1243
+ }
1244
+
1245
+ /**
1246
+ * Host-triggered "generate meeting name from transcript" action. Always
1247
+ * force-regenerates the room name.
1248
+ * @param {string} roomId - The video room ID
1249
+ * @returns {Promise} { ok: true, name: string } on success
1250
+ */
1251
+ async autoNameMeeting(roomId) {
1252
+ this.sdk.validateParams(
1253
+ { roomId },
1254
+ {
1255
+ roomId: { type: 'string', required: true },
1256
+ },
1257
+ );
1258
+
1259
+ const result = await this.sdk._fetch(`/video/${roomId}/auto-name`, 'POST', {});
1260
+ return result;
1261
+ }
1262
+
1263
+ /**
1264
+ * Host-authorized live-transcription toggle, mirrors the recording control
1265
+ * @param {string} roomId - The video room ID
1266
+ * @param {'start'|'stop'} action - Whether to start or stop transcription
1267
+ * @returns {Promise} { action, ok: true } on success
1268
+ */
1269
+ async controlTranscription(roomId, action) {
1270
+ this.sdk.validateParams(
1271
+ { roomId, action },
1272
+ {
1273
+ roomId: { type: 'string', required: true },
1274
+ action: { type: 'string', required: true },
1275
+ },
1276
+ );
1277
+
1278
+ const params = {
1279
+ body: { action },
1280
+ };
1281
+
1282
+ const result = await this.sdk._fetch(
1283
+ `/video/${roomId}/transcription`,
1284
+ 'POST',
1285
+ params,
1286
+ );
1287
+ return result;
1288
+ }
1289
+
1290
+ /**
1291
+ * Set the caption language for the calling participant in a video room
1292
+ * @param {string} roomId - The video room ID
1293
+ * @param {Object} options
1294
+ * @param {string} options.language - Language code, or 'original' for no translation
1295
+ * @returns {Promise} { ok: true } on success
1296
+ */
1297
+ async setCaptionLanguage(roomId, { language }) {
1298
+ this.sdk.validateParams(
1299
+ { roomId, language },
1300
+ {
1301
+ roomId: { type: 'string', required: true },
1302
+ language: { type: 'string', required: true },
1303
+ },
1304
+ );
1305
+
1306
+ const params = {
1307
+ body: { language },
1308
+ };
1309
+
1310
+ const result = await this.sdk._fetch(
1311
+ `/video/${roomId}/caption-language`,
1312
+ 'POST',
1313
+ params,
1314
+ );
1315
+ return result;
1316
+ }
1317
+
1318
+ /**
1319
+ * Translate a video room's full transcript into a target language (account users).
1320
+ * Batches the full transcript and caches translations server-side.
1321
+ * @param {string} roomId - The video room ID
1322
+ * @param {Object} options
1323
+ * @param {string} options.targetLanguage - Target language code
1324
+ * @returns {Promise} { items: [{messageId, text}] }
1325
+ */
1326
+ async translateTranscript(roomId, { targetLanguage }) {
1327
+ this.sdk.validateParams(
1328
+ { roomId, targetLanguage },
1329
+ {
1330
+ roomId: { type: 'string', required: true },
1331
+ targetLanguage: { type: 'string', required: true },
1332
+ },
1333
+ );
1334
+
1335
+ const params = {
1336
+ body: { targetLanguage },
1337
+ };
1338
+
1339
+ const result = await this.sdk._fetch(
1340
+ `/video/${roomId}/transcript/translate`,
1341
+ 'POST',
1342
+ params,
1343
+ );
1344
+ return result;
1345
+ }
1346
+
1347
+ /**
1348
+ * Submit a captured content-share keyframe for OCR/indexing
1349
+ * @param {string} roomId - The video room ID
1350
+ * @param {Object} options
1351
+ * @param {string} options.image - Base64 jpeg image data (no data: prefix)
1352
+ * @param {string} [options.title] - Optional title for the content frame
1353
+ * @returns {Promise} { event }
1354
+ */
1355
+ async submitContentFrame(roomId, { image, title }) {
1356
+ this.sdk.validateParams(
1357
+ { roomId, image, title },
1358
+ {
1359
+ roomId: { type: 'string', required: true },
1360
+ image: { type: 'string', required: true },
1361
+ title: { type: 'string', required: false },
1362
+ },
1363
+ );
1364
+
1365
+ const params = {
1366
+ body: { image, title },
1367
+ };
1368
+
1369
+ const result = await this.sdk._fetch(
1370
+ `/video/${roomId}/content-frame`,
1371
+ 'POST',
1372
+ params,
1373
+ );
1374
+ return result;
1375
+ }
1376
+
1377
+ /**
1378
+ * Get captured content-share events for a video room
1379
+ * @param {string} roomId - The video room ID
1380
+ * @returns {Promise} { events: [{id, participantId, displayName, timestamp, title, text, fileId}] }
1381
+ */
1382
+ async getContentEvents(roomId) {
1383
+ this.sdk.validateParams(
1384
+ { roomId },
1385
+ {
1386
+ roomId: { type: 'string', required: true },
1387
+ },
1388
+ );
1389
+
1390
+ const result = await this.sdk._fetch(
1391
+ `/video/${roomId}/content-events`,
1392
+ 'GET',
1393
+ );
1394
+ return result;
1395
+ }
1211
1396
  }
package/services/voice.js CHANGED
@@ -22,9 +22,9 @@ export class VoiceService {
22
22
  return result;
23
23
  }
24
24
 
25
- async call({ to, from, destination, app, timeout, customHeaders }) {
25
+ async call({ to, from, destination, app, timeout, customHeaders, statusWebhook }) {
26
26
  this.sdk.validateParams(
27
- { to, from, destination, app, timeout, customHeaders },
27
+ { to, from, destination, app, timeout, customHeaders, statusWebhook },
28
28
  {
29
29
  to: { type: 'string', required: true },
30
30
  from: { type: 'string', required: true },
@@ -32,6 +32,10 @@ export class VoiceService {
32
32
  app: { type: 'object', required: false },
33
33
  timeout: { type: 'number', required: false },
34
34
  customHeaders: { type: 'object', required: false },
35
+ // { url, static } — internal endpoint that receives call progress
36
+ // events (trying/ringing/answered/failed) with `static` fields
37
+ // merged into each POST body
38
+ statusWebhook: { type: 'object', required: false },
35
39
  },
36
40
  );
37
41
 
@@ -43,6 +47,7 @@ export class VoiceService {
43
47
  app,
44
48
  timeout,
45
49
  customHeaders,
50
+ statusWebhook,
46
51
  },
47
52
  };
48
53