@unboundcx/sdk 2.8.6 → 2.8.8

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/services/ai.js CHANGED
@@ -1,8 +1,13 @@
1
+ import { PlaybooksService } from './ai/playbooks.js';
2
+
1
3
  export class AIService {
2
4
  constructor(sdk) {
3
5
  this.sdk = sdk;
4
6
  this.generative = new GenerativeService(sdk);
5
7
  this.tts = new TextToSpeechService(sdk);
8
+ this.stt = new SpeechToTextService(sdk);
9
+ this.extract = new ExtractService(sdk);
10
+ this.playbooks = new PlaybooksService(sdk);
6
11
  }
7
12
  }
8
13
 
@@ -14,43 +19,129 @@ export class GenerativeService {
14
19
  async chat({
15
20
  prompt,
16
21
  messages,
22
+ systemPrompt,
23
+ appendSystemPrompt,
17
24
  relatedId,
25
+ peopleId,
26
+ companyId,
27
+ provider,
18
28
  model,
19
29
  temperature,
30
+ maxTokens,
20
31
  subscriptionId,
21
32
  stream,
22
- method,
33
+ isPlayground = false,
34
+ responseFormat,
35
+ sessionId,
36
+ scope,
37
+ manageSession,
38
+ tools,
39
+ toolConfig,
40
+ maxToolRounds,
41
+ recordTypeId,
42
+ meta,
43
+ qualityCheck,
44
+ isPublic,
23
45
  }) {
24
46
  this.sdk.validateParams(
25
- { method, stream, temperature, subscriptionId, model, prompt, messages },
47
+ {
48
+ stream,
49
+ temperature,
50
+ maxTokens,
51
+ subscriptionId,
52
+ provider,
53
+ model,
54
+ prompt,
55
+ messages,
56
+ systemPrompt,
57
+ appendSystemPrompt,
58
+ isPlayground,
59
+ responseFormat,
60
+ sessionId,
61
+ scope,
62
+ manageSession,
63
+ tools,
64
+ toolConfig,
65
+ maxToolRounds,
66
+ recordTypeId,
67
+ peopleId,
68
+ companyId,
69
+ meta,
70
+ qualityCheck,
71
+ isPublic,
72
+ },
26
73
  {
27
74
  prompt: { type: 'string', required: false },
28
75
  messages: { type: 'array', required: false },
76
+ systemPrompt: { type: 'string', required: false },
77
+ appendSystemPrompt: { type: 'string', required: false },
78
+ provider: { type: 'string', required: false },
29
79
  model: { type: 'string', required: false },
30
80
  temperature: { type: 'number', required: false },
81
+ maxTokens: { type: 'number', required: false },
31
82
  subscriptionId: { type: 'string', required: false },
32
83
  stream: { type: 'boolean', required: false },
33
- method: { type: 'string', required: true },
84
+ isPlayground: { type: 'boolean', required: false },
85
+ responseFormat: { type: 'object', required: false },
86
+ sessionId: { type: 'string', required: false },
87
+ scope: { type: 'string', required: false },
88
+ manageSession: { type: 'boolean', required: false },
89
+ tools: { type: 'array', required: false },
90
+ toolConfig: { type: 'object', required: false },
91
+ maxToolRounds: { type: 'number', required: false },
92
+ recordTypeId: { type: 'string', required: false },
93
+ peopleId: { type: 'string', required: false },
94
+ companyId: { type: 'string', required: false },
95
+ meta: { type: 'object', required: false },
96
+ qualityCheck: { type: 'boolean', required: false },
97
+ isPublic: { type: 'boolean', required: false },
34
98
  },
35
99
  );
36
100
 
37
101
  const params = {
38
102
  body: {
103
+ isPlayground,
39
104
  prompt,
40
105
  messages,
106
+ systemPrompt,
107
+ appendSystemPrompt,
41
108
  relatedId,
109
+ peopleId,
110
+ companyId,
111
+ provider,
42
112
  model,
43
113
  temperature,
114
+ maxTokens,
44
115
  subscriptionId,
45
116
  stream,
46
- method,
117
+ responseFormat,
118
+ sessionId,
119
+ scope,
120
+ manageSession,
121
+ tools,
122
+ toolConfig,
123
+ maxToolRounds,
124
+ recordTypeId,
125
+ meta,
126
+ qualityCheck,
127
+ isPublic,
47
128
  },
48
129
  // Return raw response for streaming to allow client-side stream handling
49
130
  returnRawResponse: stream === true,
50
131
  };
51
132
 
52
- // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
53
- const forceFetch = stream === true;
133
+ // Force HTTP transport when streaming or when messages contain large content (too large for NATS)
134
+ const hasLargeContent = messages?.some(
135
+ (m) =>
136
+ Array.isArray(m.content) &&
137
+ m.content.some(
138
+ (c) =>
139
+ c.type === 'image' ||
140
+ c.type === 'image_url' ||
141
+ c.type === 'document',
142
+ ),
143
+ );
144
+ const forceFetch = stream === true || hasLargeContent;
54
145
  const result = await this.sdk._fetch(
55
146
  '/ai/generative/chat',
56
147
  'POST',
@@ -60,55 +151,181 @@ export class GenerativeService {
60
151
  return result;
61
152
  }
62
153
 
63
- async playbook({
64
- prompt,
65
- messages,
66
- relatedId,
154
+ /**
155
+ * Clone an existing chat session into a new one, importing conversation context.
156
+ * Filters out system prompts and tool calls, keeps user/assistant turns.
157
+ * If the transcript exceeds ~50k tokens it is summarized automatically.
158
+ *
159
+ * @param {Object} options
160
+ * @param {string} options.sourceSessionId - Session to clone from (required)
161
+ * @param {string} [options.systemPrompt] - New system prompt (appended after conversation context)
162
+ * @param {string} [options.provider] - LLM provider (falls back to source session)
163
+ * @param {string} [options.model] - Model ID (falls back to source session)
164
+ * @param {Array} [options.tools] - Tool names (falls back to source session)
165
+ * @param {Object} [options.toolConfig] - Tool config (falls back to source session)
166
+ * @param {string} [options.scope] - 'user' or 'account'
167
+ * @param {string} [options.userId] - Owner user ID
168
+ * @param {string} [options.peopleId] - Linked people record
169
+ * @param {string} [options.companyId] - Linked company record
170
+ * @param {string} [options.relatedId] - Linked related record
171
+ * @returns {Promise<Object>} { id, sourceSessionId, wasSummarized }
172
+ */
173
+ async cloneSession({
174
+ sourceSessionId,
175
+ systemPrompt,
176
+ provider,
67
177
  model,
68
- temperature,
69
- subscriptionId,
70
- stream,
71
- playbookId,
72
- sessionId,
178
+ tools,
179
+ toolConfig,
180
+ scope,
181
+ userId,
182
+ peopleId,
183
+ companyId,
184
+ relatedId,
73
185
  }) {
74
186
  this.sdk.validateParams(
75
- { playbookId },
187
+ { sourceSessionId },
76
188
  {
77
- prompt: { type: 'string', required: false },
78
- messages: { type: 'array', required: false },
79
- relatedId: { type: 'string', required: false },
189
+ sourceSessionId: { type: 'string', required: true },
190
+ systemPrompt: { type: 'string', required: false },
191
+ provider: { type: 'string', required: false },
80
192
  model: { type: 'string', required: false },
81
- temperature: { type: 'number', required: false },
82
- subscriptionId: { type: 'string', required: false },
83
- stream: { type: 'boolean', required: false },
84
- playbookId: { type: 'string', required: true },
85
- sessionId: { type: 'string', required: false },
193
+ tools: { type: 'array', required: false },
194
+ toolConfig: { type: 'object', required: false },
195
+ scope: { type: 'string', required: false },
196
+ userId: { type: 'string', required: false },
197
+ peopleId: { type: 'string', required: false },
198
+ companyId: { type: 'string', required: false },
199
+ relatedId: { type: 'string', required: false },
86
200
  },
87
201
  );
88
202
 
89
203
  const params = {
90
204
  body: {
91
- prompt,
92
- messages,
93
- relatedId,
205
+ sourceSessionId,
206
+ systemPrompt,
207
+ provider,
94
208
  model,
95
- temperature,
96
- subscriptionId,
97
- stream,
98
- playbookId,
99
- sessionId,
209
+ tools,
210
+ toolConfig,
211
+ scope,
212
+ userId,
213
+ peopleId,
214
+ companyId,
215
+ relatedId,
100
216
  },
101
217
  };
102
218
 
103
219
  const result = await this.sdk._fetch(
104
- '/ai/generative/playbook',
220
+ '/ai/generative/chat/clone',
105
221
  'POST',
106
222
  params,
107
223
  );
108
224
  return result;
109
225
  }
110
226
 
111
- async chatOllama({
227
+ /**
228
+ * List available chat tools and their metadata
229
+ * @returns {Promise<Object>} { tools: Array<{ name, label, description, configRequirements }>, count: number }
230
+ */
231
+ async listTools() {
232
+ return await this.sdk._fetch('/ai/generative/tools', 'GET');
233
+ }
234
+
235
+ /**
236
+ * List chat sessions visible to the current user
237
+ * @param {Object} [options] - Filter options
238
+ * @param {string} [options.relatedId] - Filter by related object ID
239
+ * @param {string} [options.scope] - Filter by scope ('user' or 'account')
240
+ * @param {number} [options.limit=25] - Results limit
241
+ * @param {number} [options.offset=0] - Pagination offset
242
+ * @returns {Promise<Object>} { sessions: Array }
243
+ */
244
+ async listSessions({ relatedId, scope, limit, offset } = {}) {
245
+ this.sdk.validateParams(
246
+ { relatedId, scope, limit, offset },
247
+ {
248
+ relatedId: { type: 'string', required: false },
249
+ scope: { type: 'string', required: false },
250
+ limit: { type: 'number', required: false },
251
+ offset: { type: 'number', required: false },
252
+ },
253
+ );
254
+
255
+ const params = { query: { relatedId, scope, limit, offset } };
256
+ const result = await this.sdk._fetch(
257
+ '/ai/generative/sessions',
258
+ 'GET',
259
+ params,
260
+ );
261
+ return result;
262
+ }
263
+
264
+ /**
265
+ * Get a chat session with its message history
266
+ * @param {Object} options
267
+ * @param {string} options.sessionId - Session ID to retrieve
268
+ * @returns {Promise<Object>} { session: { id, name, scope, relatedId, provider, model, messages, messageCount, lastMessageAt, createdAt } }
269
+ */
270
+ async getSession({ sessionId }) {
271
+ this.sdk.validateParams(
272
+ { sessionId },
273
+ { sessionId: { type: 'string', required: true } },
274
+ );
275
+
276
+ const result = await this.sdk._fetch(
277
+ `/ai/generative/sessions/${sessionId}`,
278
+ 'GET',
279
+ );
280
+ return result;
281
+ }
282
+
283
+ /**
284
+ * Update a chat session (name, scope)
285
+ * @param {Object} options
286
+ * @param {string} options.sessionId - Session ID to update
287
+ * @param {string} [options.name] - New session name
288
+ * @param {string} [options.scope] - New scope ('user' or 'account')
289
+ * @returns {Promise<Object>} { success: true }
290
+ */
291
+ async updateSession({ sessionId, name, scope }) {
292
+ this.sdk.validateParams(
293
+ { sessionId, name, scope },
294
+ {
295
+ sessionId: { type: 'string', required: true },
296
+ name: { type: 'string', required: false },
297
+ scope: { type: 'string', required: false },
298
+ },
299
+ );
300
+
301
+ const result = await this.sdk._fetch(
302
+ `/ai/generative/sessions/${sessionId}`,
303
+ 'PUT',
304
+ { body: { name, scope } },
305
+ );
306
+ return result;
307
+ }
308
+
309
+ /**
310
+ * Delete a chat session
311
+ * @param {Object} options
312
+ * @param {string} options.sessionId - Session ID to delete
313
+ * @returns {Promise<Object>} { success: true }
314
+ */
315
+ async deleteSession({ sessionId }) {
316
+ this.sdk.validateParams(
317
+ { sessionId },
318
+ { sessionId: { type: 'string', required: true } },
319
+ );
320
+
321
+ const result = await this.sdk._fetch(
322
+ `/ai/generative/sessions/${sessionId}`,
323
+ 'DELETE',
324
+ );
325
+ return result;
326
+ }
327
+
328
+ async playbook({
112
329
  prompt,
113
330
  messages,
114
331
  relatedId,
@@ -116,10 +333,11 @@ export class GenerativeService {
116
333
  temperature,
117
334
  subscriptionId,
118
335
  stream,
119
- method,
336
+ playbookId,
337
+ sessionId,
120
338
  }) {
121
339
  this.sdk.validateParams(
122
- { method },
340
+ { playbookId },
123
341
  {
124
342
  prompt: { type: 'string', required: false },
125
343
  messages: { type: 'array', required: false },
@@ -128,7 +346,8 @@ export class GenerativeService {
128
346
  temperature: { type: 'number', required: false },
129
347
  subscriptionId: { type: 'string', required: false },
130
348
  stream: { type: 'boolean', required: false },
131
- method: { type: 'string', required: true },
349
+ playbookId: { type: 'string', required: true },
350
+ sessionId: { type: 'string', required: false },
132
351
  },
133
352
  );
134
353
 
@@ -141,22 +360,68 @@ export class GenerativeService {
141
360
  temperature,
142
361
  subscriptionId,
143
362
  stream,
144
- method,
363
+ playbookId,
364
+ sessionId,
145
365
  },
146
- // Return raw response for streaming to allow client-side stream handling
147
- returnRawResponse: stream === true,
148
366
  };
149
367
 
150
- // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
151
- const forceFetch = stream === true;
152
368
  const result = await this.sdk._fetch(
153
- '/ai/generative/ollama',
369
+ '/ai/generative/playbook',
154
370
  'POST',
155
371
  params,
156
- forceFetch,
157
372
  );
158
373
  return result;
159
374
  }
375
+
376
+ // async chatOllama({
377
+ // prompt,
378
+ // messages,
379
+ // relatedId,
380
+ // model,
381
+ // temperature,
382
+ // subscriptionId,
383
+ // stream,
384
+ // method,
385
+ // }) {
386
+ // this.sdk.validateParams(
387
+ // { method },
388
+ // {
389
+ // prompt: { type: 'string', required: false },
390
+ // messages: { type: 'array', required: false },
391
+ // relatedId: { type: 'string', required: false },
392
+ // model: { type: 'string', required: false },
393
+ // temperature: { type: 'number', required: false },
394
+ // subscriptionId: { type: 'string', required: false },
395
+ // stream: { type: 'boolean', required: false },
396
+ // method: { type: 'string', required: true },
397
+ // },
398
+ // );
399
+
400
+ // const params = {
401
+ // body: {
402
+ // prompt,
403
+ // messages,
404
+ // relatedId,
405
+ // model,
406
+ // temperature,
407
+ // subscriptionId,
408
+ // stream,
409
+ // method,
410
+ // },
411
+ // // Return raw response for streaming to allow client-side stream handling
412
+ // returnRawResponse: stream === true,
413
+ // };
414
+
415
+ // // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
416
+ // const forceFetch = stream === true;
417
+ // const result = await this.sdk._fetch(
418
+ // '/ai/generative/ollama',
419
+ // 'POST',
420
+ // params,
421
+ // forceFetch,
422
+ // );
423
+ // return result;
424
+ // }
160
425
  }
161
426
 
162
427
  export class TextToSpeechService {
@@ -174,9 +439,21 @@ export class TextToSpeechService {
174
439
  pitch,
175
440
  volumeGainDb,
176
441
  effectsProfileIds,
442
+ createAccessKey,
177
443
  }) {
178
444
  this.sdk.validateParams(
179
- { text },
445
+ {
446
+ text,
447
+ voice,
448
+ languageCode,
449
+ ssmlGender,
450
+ audioEncoding,
451
+ speakingRate,
452
+ pitch,
453
+ volumeGainDb,
454
+ effectsProfileIds,
455
+ createAccessKey,
456
+ },
180
457
  {
181
458
  text: { type: 'string', required: true },
182
459
  voice: { type: 'string', required: false },
@@ -187,6 +464,7 @@ export class TextToSpeechService {
187
464
  pitch: { type: 'number', required: false },
188
465
  volumeGainDb: { type: 'number', required: false },
189
466
  effectsProfileIds: { type: 'array', required: false },
467
+ createAccessKey: { type: 'boolean', required: false },
190
468
  },
191
469
  );
192
470
 
@@ -199,6 +477,7 @@ export class TextToSpeechService {
199
477
  if (pitch) ttsData.pitch = pitch;
200
478
  if (volumeGainDb) ttsData.volumeGainDb = volumeGainDb;
201
479
  if (effectsProfileIds) ttsData.effectsProfileIds = effectsProfileIds;
480
+ if (createAccessKey) ttsData.createAccessKey = createAccessKey;
202
481
 
203
482
  const params = {
204
483
  body: ttsData,
@@ -207,4 +486,686 @@ export class TextToSpeechService {
207
486
  const result = await this.sdk._fetch('/ai/tts', 'POST', params);
208
487
  return result;
209
488
  }
489
+
490
+ /**
491
+ * List available TTS voices
492
+ * @returns {Promise<Object>} { voices: Array, count: number, supportedEncodings: Array, supportedLanguages: Array }
493
+ */
494
+ async list() {
495
+ const result = await this.sdk._fetch('/ai/tts', 'GET');
496
+ return result;
497
+ }
498
+ }
499
+
500
+ export class SpeechToTextService {
501
+ constructor(sdk) {
502
+ this.sdk = sdk;
503
+ }
504
+
505
+ /**
506
+ * Create a transcription from file or storage
507
+ * @param {Object} options - Transcription options
508
+ * @param {string} options.sourceType - 'file', 'storage', 'stream', or 'url'
509
+ * @param {string} [options.sourceId] - Source identifier
510
+ * @param {string} [options.storageId] - Storage ID if using storage
511
+ * @param {string} [options.engine='google'] - STT engine ('google', 'deepgram', 'whisper')
512
+ * @param {string} [options.languageCode='en-US'] - BCP-47 language code
513
+ * @param {Object} [options.metadata] - Engine-specific configuration
514
+ * @param {string} [options.engagementSessionId] - Engagement session ID
515
+ * @param {string} [options.playbookId] - Playbook ID
516
+ * @param {string} [options.name] - Transcription name
517
+ * @param {string} [options.role] - Speaker role
518
+ * @param {string} [options.direction] - Call direction
519
+ * @returns {Promise<Object>} Transcription result
520
+ */
521
+ async create({
522
+ sourceType,
523
+ sourceId,
524
+ sipCallId,
525
+ cdrId,
526
+ storageId,
527
+ engine,
528
+ languageCode,
529
+ metadata,
530
+ engagementSessionId,
531
+ playbookId,
532
+ name,
533
+ role,
534
+ direction,
535
+ }) {
536
+ this.sdk.validateParams(
537
+ {
538
+ sourceType,
539
+ sourceId,
540
+ sipCallId,
541
+ cdrId,
542
+ storageId,
543
+ engine,
544
+ languageCode,
545
+ metadata,
546
+ engagementSessionId,
547
+ playbookId,
548
+ name,
549
+ role,
550
+ direction,
551
+ },
552
+ {
553
+ sourceType: { type: 'string', required: true },
554
+ sourceId: { type: 'string', required: false },
555
+ sipCallId: { type: 'string', required: false },
556
+ cdrId: { type: 'string', required: false },
557
+ storageId: { type: 'string', required: false },
558
+ engine: { type: 'string', required: false },
559
+ languageCode: { type: 'string', required: false },
560
+ metadata: { type: 'object', required: false },
561
+ engagementSessionId: { type: 'string', required: false },
562
+ playbookId: { type: 'string', required: false },
563
+ name: { type: 'string', required: false },
564
+ role: { type: 'string', required: false },
565
+ direction: { type: 'string', required: false },
566
+ },
567
+ );
568
+
569
+ const params = {
570
+ body: {
571
+ sourceType,
572
+ sourceId,
573
+ sipCallId,
574
+ cdrId,
575
+ storageId,
576
+ engine,
577
+ languageCode,
578
+ metadata,
579
+ engagementSessionId,
580
+ playbookId,
581
+ name,
582
+ role,
583
+ direction,
584
+ },
585
+ };
586
+
587
+ const result = await this.sdk._fetch('/ai/stt', 'POST', params);
588
+ return result;
589
+ }
590
+
591
+ /**
592
+ * Create a real-time streaming transcription session
593
+ * Returns an EventEmitter-based stream for sending audio and receiving transcripts
594
+ *
595
+ * @param {Object} options - Stream options
596
+ * @param {string} [options.engine='google'] - STT engine ('google' or 'whisper')
597
+ * @param {string} [options.model] - STT model (e.g., 'phone_call' for Google)
598
+ * @param {string} [options.languageCode='en-US'] - BCP-47 language code
599
+ * @param {string} [options.encoding='LINEAR16'] - Audio encoding format
600
+ * @param {number} [options.sampleRateHertz=16000] - Sample rate in Hertz
601
+ * @param {number} [options.audioChannelCount=1] - Number of audio channels (1=mono, 2=stereo)
602
+ * @param {boolean} [options.singleUtterance=false] - Stop after first utterance
603
+ * @param {boolean} [options.interimResults=true] - Return interim results
604
+ * @param {boolean} [options.enableAutomaticPunctuation=true] - Enable automatic punctuation
605
+ * @param {boolean} [options.diarizationEnabled=false] - Enable speaker diarization
606
+ * @param {number} [options.speakerCount] - Number of speakers (if diarization enabled)
607
+ * @param {boolean} [options.vadEnabled=false] - Enable Voice Activity Detection
608
+ * @param {number} [options.minSilenceDuration=500] - Min silence duration in ms (for VAD)
609
+ * @param {number} [options.speechPadMs=400] - Speech padding in ms (for VAD)
610
+ * @param {string} [options.engagementSessionId] - Engagement session ID
611
+ * @param {string} [options.playbookId] - Playbook ID
612
+ * @param {string} [options.name] - Session name
613
+ * @param {Object} [options.metadata] - Additional metadata
614
+ * @returns {Promise<SttStream>} Stream object with write() method and transcript events
615
+ *
616
+ * @example
617
+ * const stream = await sdk.ai.stt.stream({ engine: 'google', languageCode: 'en-US' });
618
+ * stream.on('transcript', (result) => console.log(result.text, result.isFinal));
619
+ * stream.on('error', (error) => console.error(error));
620
+ * stream.on('close', () => console.log('Stream closed'));
621
+ * stream.write(audioChunk);
622
+ * stream.end();
623
+ */
624
+ async stream(options = {}) {
625
+ // Environment check - STT streaming only works in Node.js
626
+ if (this.sdk.environment !== 'node') {
627
+ throw new Error(
628
+ 'STT streaming requires Node.js environment. ' +
629
+ 'This feature is only available in backend services. ' +
630
+ 'Browser-based streaming is not supported.',
631
+ );
632
+ }
633
+
634
+ const {
635
+ engine = 'google',
636
+ model,
637
+ languageCode = 'en-US',
638
+ encoding = 'LINEAR16',
639
+ sampleRateHertz = 16000,
640
+ audioChannelCount = 1,
641
+ singleUtterance = false,
642
+ interimResults = true,
643
+ enableAutomaticPunctuation = true,
644
+ diarizationEnabled = false,
645
+ speakerCount,
646
+ vadEnabled = false,
647
+ minSilenceDuration = 500,
648
+ speechPadMs = 400,
649
+ engagementSessionId,
650
+ playbookId,
651
+ taskId,
652
+ workerId,
653
+ generateSubject,
654
+ generateTranscriptSummary,
655
+ generateSentiment,
656
+ bridgeId,
657
+ name,
658
+ metadata,
659
+ sipCallId,
660
+ cdrId,
661
+ } = options;
662
+
663
+ // Validate parameters
664
+ this.sdk.validateParams(
665
+ {
666
+ engine,
667
+ model,
668
+ languageCode,
669
+ encoding,
670
+ sampleRateHertz,
671
+ audioChannelCount,
672
+ singleUtterance,
673
+ interimResults,
674
+ enableAutomaticPunctuation,
675
+ diarizationEnabled,
676
+ speakerCount,
677
+ vadEnabled,
678
+ minSilenceDuration,
679
+ speechPadMs,
680
+ engagementSessionId,
681
+ playbookId,
682
+ taskId,
683
+ workerId,
684
+ generateSubject,
685
+ generateTranscriptSummary,
686
+ generateSentiment,
687
+ bridgeId,
688
+ name,
689
+ metadata,
690
+ sipCallId,
691
+ cdrId,
692
+ },
693
+ {
694
+ engine: { type: 'string', required: false },
695
+ model: { type: 'string', required: false },
696
+ languageCode: { type: 'string', required: false },
697
+ encoding: { type: 'string', required: false },
698
+ sampleRateHertz: { type: 'number', required: false },
699
+ audioChannelCount: { type: 'number', required: false },
700
+ singleUtterance: { type: 'boolean', required: false },
701
+ interimResults: { type: 'boolean', required: false },
702
+ enableAutomaticPunctuation: { type: 'boolean', required: false },
703
+ diarizationEnabled: { type: 'boolean', required: false },
704
+ speakerCount: { type: 'number', required: false },
705
+ vadEnabled: { type: 'boolean', required: false },
706
+ minSilenceDuration: { type: 'number', required: false },
707
+ speechPadMs: { type: 'number', required: false },
708
+ engagementSessionId: { type: 'string', required: false },
709
+ playbookId: { type: 'string', required: false },
710
+ taskId: { type: 'string', required: false },
711
+ workerId: { type: 'string', required: false },
712
+ generateSubject: { type: 'boolean', required: false },
713
+ generateTranscriptSummary: { type: 'boolean', required: false },
714
+ generateSentiment: { type: 'boolean', required: false },
715
+ bridgeId: { type: 'string', required: false },
716
+ name: { type: 'string', required: false },
717
+ metadata: { type: 'object', required: false },
718
+ sipCallId: { type: 'string', required: false },
719
+ cdrId: { type: 'string', required: false },
720
+ },
721
+ );
722
+
723
+ // Create session via API
724
+ const sessionParams = {
725
+ body: {
726
+ engine,
727
+ model,
728
+ engagementSessionId,
729
+ playbookId,
730
+ taskId,
731
+ workerId,
732
+ generateSubject,
733
+ generateTranscriptSummary,
734
+ generateSentiment,
735
+ bridgeId,
736
+ sipCallId,
737
+ cdrId,
738
+ name,
739
+ metadata: {
740
+ ...metadata,
741
+ languageCode,
742
+ encoding,
743
+ sampleRateHertz,
744
+ audioChannelCount,
745
+ singleUtterance,
746
+ interimResults,
747
+ enableAutomaticPunctuation,
748
+ diarizationEnabled,
749
+ speakerCount,
750
+ vadEnabled,
751
+ minSilenceDuration,
752
+ speechPadMs,
753
+ },
754
+ },
755
+ };
756
+
757
+ const session = await this.sdk._fetch(
758
+ '/ai/stt/stream',
759
+ 'POST',
760
+ sessionParams,
761
+ );
762
+
763
+ // Dynamic import of SttStream (only loads when needed)
764
+ const { SttStream } = await import('./ai/SttStream.js');
765
+
766
+ // Create and return stream instance
767
+ const streamOptions = {
768
+ ...options,
769
+ languageCode,
770
+ encoding,
771
+ sampleRateHertz,
772
+ audioChannelCount,
773
+ vadEnabled,
774
+ minSilenceDuration,
775
+ speechPadMs,
776
+ };
777
+
778
+ return new SttStream(this.sdk, session, streamOptions);
779
+ }
780
+
781
+ /**
782
+ * Get a transcription by ID
783
+ * @param {string} id - Transcription ID
784
+ * @param {Object} [options] - Retrieval options
785
+ * @param {boolean} [options.includeMessages=true] - Include transcription messages
786
+ * @returns {Promise<Object>} Transcription data
787
+ */
788
+ async get(id, { includeMessages = true } = {}) {
789
+ this.sdk.validateParams(
790
+ { id },
791
+ {
792
+ id: { type: 'string', required: true },
793
+ includeMessages: { type: 'boolean', required: false },
794
+ },
795
+ );
796
+
797
+ const params = {
798
+ query: { includeMessages: includeMessages.toString() },
799
+ };
800
+
801
+ const result = await this.sdk._fetch(`/ai/stt/${id}`, 'GET', params);
802
+ return result;
803
+ }
804
+
805
+ /**
806
+ * List transcriptions with filters
807
+ * @param {Object} [filters] - Filter options
808
+ * @param {string} [filters.engagementSessionId] - Filter by engagement session
809
+ * @param {string} [filters.userId] - Filter by user
810
+ * @param {string} [filters.status] - Filter by status
811
+ * @param {string} [filters.engine] - Filter by engine
812
+ * @param {string} [filters.sourceType] - Filter by source type
813
+ * @param {string} [filters.playbookId] - Filter by playbook
814
+ * @param {string} [filters.startDate] - Filter by start date
815
+ * @param {string} [filters.endDate] - Filter by end date
816
+ * @param {number} [filters.limit=50] - Limit results
817
+ * @param {number} [filters.offset=0] - Offset for pagination
818
+ * @returns {Promise<Object>} List of transcriptions
819
+ */
820
+ async list(filters = {}) {
821
+ this.sdk.validateParams(filters, {
822
+ engagementSessionId: { type: 'string', required: false },
823
+ userId: { type: 'string', required: false },
824
+ status: { type: 'string', required: false },
825
+ engine: { type: 'string', required: false },
826
+ sourceType: { type: 'string', required: false },
827
+ playbookId: { type: 'string', required: false },
828
+ startDate: { type: 'string', required: false },
829
+ endDate: { type: 'string', required: false },
830
+ limit: { type: 'number', required: false },
831
+ offset: { type: 'number', required: false },
832
+ });
833
+
834
+ const params = {
835
+ query: filters,
836
+ };
837
+
838
+ const result = await this.sdk._fetch('/ai/stt', 'GET', params);
839
+ return result;
840
+ }
841
+
842
+ /**
843
+ * Log a transcription message for a streaming session
844
+ * @param {string} sessionId - Transcription session ID
845
+ * @param {string} messageId - message id for the transcription
846
+ * @param {number} timestamp - message timestamp (unix)
847
+ * @param {Object} message - Message data
848
+ * @param {string} message.text - Transcribed text
849
+ * @param {Object} [message.transcriptionJson] - Full transcription metadata
850
+ * @param {number} [message.duration] - Duration of this segment in seconds
851
+ * @param {number} [message.confidence] - Confidence score (0-1)
852
+ * @param {string} [message.languageCode] - Language code for this segment
853
+ * @param {string} [message.userId] - User associated with this message
854
+ * @param {string} [message.role] - Speaker role
855
+ * @param {string} [message.sipCallId] - SIP call identifier
856
+ * @param {string} [message.side] - Stream side ('send' or 'recv')
857
+ * @param {string} [message.bridgeId] - bridge id
858
+ * @param {Object} [message.sentiment] - Sentiment analysis data
859
+ * @param {number} [message.sentiment.score] - Overall sentiment score (-100 to +100)
860
+ * @param {number} [message.sentiment.previousScore] - Previous sentiment score (-100 to +100)
861
+ * @param {number} [message.sentiment.delta] - Score change (score - previousScore)
862
+ * @param {number} [message.sentiment.customerScore] - Customer sentiment score (-100 to +100)
863
+ * @param {number} [message.sentiment.agentScore] - Agent sentiment score (-100 to +100)
864
+ * @param {number} [message.sentiment.intensity] - Sentiment intensity (0 to 1)
865
+ * @param {string[]} [message.sentiment.emotions] - Detected emotions (up to 3)
866
+ * @param {string} [message.sentiment.trend] - Sentiment trend ('improving' | 'stable' | 'declining')
867
+ * @param {string} [message.sentiment.source] - Analysis source ('llm+acoustic' | 'acoustic_only' | 'carry_forward')
868
+ * @returns {Promise<Object>} Created message result
869
+ */
870
+ async logMessage(
871
+ sessionId,
872
+ {
873
+ messageId,
874
+ timestamp,
875
+ text,
876
+ transcriptionJson,
877
+ duration,
878
+ confidence,
879
+ languageCode,
880
+ userId,
881
+ role,
882
+ sipCallId,
883
+ side,
884
+ bridgeId,
885
+ sentiment,
886
+ },
887
+ ) {
888
+ this.sdk.validateParams(
889
+ { messageId, sessionId, text, bridgeId },
890
+ {
891
+ sessionId: { type: 'string', required: true },
892
+ messageId: { type: 'string', required: false },
893
+ timestamp: { type: 'number', required: false },
894
+ text: { type: 'string', required: true },
895
+ transcriptionJson: { type: 'object', required: false },
896
+ duration: { type: 'number', required: false },
897
+ confidence: { type: 'number', required: false },
898
+ languageCode: { type: 'string', required: false },
899
+ userId: { type: 'string', required: false },
900
+ role: { type: 'string', required: false },
901
+ sipCallId: { type: 'string', required: false },
902
+ side: { type: 'string', required: false },
903
+ bridgeId: { type: 'string', required: false },
904
+ sentiment: { type: 'object', required: false },
905
+ },
906
+ );
907
+
908
+ const params = {
909
+ body: {
910
+ messageId,
911
+ timestamp,
912
+ text,
913
+ transcriptionJson,
914
+ duration,
915
+ confidence,
916
+ languageCode,
917
+ userId,
918
+ role,
919
+ sipCallId,
920
+ side,
921
+ bridgeId,
922
+ sentiment,
923
+ },
924
+ };
925
+
926
+ const result = await this.sdk._fetch(
927
+ `/ai/stt/stream/${sessionId}/messages`,
928
+ 'POST',
929
+ params,
930
+ );
931
+ return result;
932
+ }
933
+
934
+ /**
935
+ * Complete a streaming transcription session
936
+ * @param {string} sessionId - Transcription session ID
937
+ * @param {Object} [options] - Completion options
938
+ * @param {string} [options.status='completed'] - Final status ('completed' or 'failed')
939
+ * @param {string} [options.error] - Error message if status is 'failed'
940
+ * @returns {Promise<Object>} Completion result
941
+ */
942
+ async complete(sessionId, { status = 'completed', error } = {}) {
943
+ this.sdk.validateParams(
944
+ { sessionId },
945
+ {
946
+ sessionId: { type: 'string', required: true },
947
+ status: { type: 'string', required: false },
948
+ error: { type: 'string', required: false },
949
+ },
950
+ );
951
+
952
+ const params = {
953
+ body: { status, error },
954
+ };
955
+
956
+ const result = await this.sdk._fetch(
957
+ `/ai/stt/stream/${sessionId}/complete`,
958
+ 'PUT',
959
+ params,
960
+ );
961
+ return result;
962
+ }
963
+ }
964
+
965
+ // STT Constants
966
+ export const STT = {
967
+ ENGINES: {
968
+ GOOGLE: 'google',
969
+ DEEPGRAM: 'deepgram',
970
+ WHISPER: 'whisper',
971
+ },
972
+ SOURCE_TYPES: {
973
+ FILE: 'file',
974
+ STORAGE: 'storage',
975
+ STREAM: 'stream',
976
+ URL: 'url',
977
+ },
978
+ STATUS: {
979
+ PENDING: 'pending',
980
+ PROCESSING: 'processing',
981
+ COMPLETED: 'completed',
982
+ FAILED: 'failed',
983
+ },
984
+ LANGUAGES: {
985
+ EN_US: 'en-US',
986
+ EN_GB: 'en-GB',
987
+ EN_AU: 'en-AU',
988
+ ES_ES: 'es-ES',
989
+ ES_MX: 'es-MX',
990
+ ES_US: 'es-US',
991
+ FR_FR: 'fr-FR',
992
+ FR_CA: 'fr-CA',
993
+ DE_DE: 'de-DE',
994
+ IT_IT: 'it-IT',
995
+ PT_BR: 'pt-BR',
996
+ PT_PT: 'pt-PT',
997
+ ZH_CN: 'zh-CN',
998
+ JA_JP: 'ja-JP',
999
+ KO_KR: 'ko-KR',
1000
+ },
1001
+ };
1002
+
1003
+ export class ExtractService {
1004
+ constructor(sdk) {
1005
+ this.sdk = sdk;
1006
+ }
1007
+
1008
+ /**
1009
+ * Extract phone number from text
1010
+ * @param {Object} options - Extraction options
1011
+ * @param {string} options.value - Text containing phone number
1012
+ * @param {string} [options.country='US'] - Country code for validation
1013
+ * @param {string} [options.format='E164'] - Output format: 'E164', 'national', 'international'
1014
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { country, type, nationalNumber, ... } }
1015
+ */
1016
+ async phone({ value, country = 'US', format = 'E164' }) {
1017
+ return this._extract('phone', value, { country, format });
1018
+ }
1019
+
1020
+ /**
1021
+ * Extract email address from text
1022
+ * @param {Object} options - Extraction options
1023
+ * @param {string} options.value - Text containing email
1024
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { domain } }
1025
+ */
1026
+ async email({ value }) {
1027
+ return this._extract('email', value);
1028
+ }
1029
+
1030
+ /**
1031
+ * Extract physical address from text
1032
+ * @param {Object} options - Extraction options
1033
+ * @param {string} options.value - Text containing address
1034
+ * @param {boolean} [options.useAI=false] - Use AI for better extraction from conversational text
1035
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { components } }
1036
+ */
1037
+ async address({ value, useAI = false }) {
1038
+ return this._extract('address', value, { useAI });
1039
+ }
1040
+
1041
+ /**
1042
+ * Extract person's name from text
1043
+ * @param {Object} options - Extraction options
1044
+ * @param {string} options.value - Text containing name
1045
+ * @param {boolean} [options.useAI=true] - Use AI for extraction from conversational text
1046
+ * @returns {Promise<Object>} { isValid, parsedValue, firstName, lastName, title }
1047
+ */
1048
+ async personName({ value, useAI = true }) {
1049
+ return this._extract('personName', value, { useAI });
1050
+ }
1051
+
1052
+ /**
1053
+ * Extract and validate credit card number (masked for security)
1054
+ * @param {Object} options - Extraction options
1055
+ * @param {string} options.value - Text containing credit card
1056
+ * @param {boolean} [options.maskOutput=true] - Mask all but last 4 digits
1057
+ * @returns {Promise<Object>} { isValid, parsedValue (masked), confidence, metadata: { cardType, length } }
1058
+ */
1059
+ async creditCard({ value, maskOutput = true }) {
1060
+ return this._extract('creditCard', value, { maskOutput });
1061
+ }
1062
+
1063
+ /**
1064
+ * Extract and validate SSN (masked for security)
1065
+ * @param {Object} options - Extraction options
1066
+ * @param {string} options.value - Text containing SSN
1067
+ * @param {boolean} [options.maskOutput=true] - Mask all but last 4 digits
1068
+ * @returns {Promise<Object>} { isValid, parsedValue (masked), confidence, metadata }
1069
+ */
1070
+ async ssn({ value, maskOutput = true }) {
1071
+ return this._extract('ssn', value, { maskOutput });
1072
+ }
1073
+
1074
+ /**
1075
+ * Extract phone extension
1076
+ * @param {Object} options - Extraction options
1077
+ * @param {string} options.value - Text containing extension
1078
+ * @param {number} [options.minLength=1] - Minimum digits
1079
+ * @param {number} [options.maxLength=10] - Maximum digits
1080
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence }
1081
+ */
1082
+ async extension({ value, minLength = 1, maxLength = 10 }) {
1083
+ return this._extract('extension', value, { minLength, maxLength });
1084
+ }
1085
+
1086
+ /**
1087
+ * Detect correct/incorrect response from natural language
1088
+ * @param {Object} options - Extraction options
1089
+ * @param {string} options.value - User's response (e.g., "yes", "no", "correct", "nope")
1090
+ * @returns {Promise<Object>} { isValid, parsedValue: 'correct'|'incorrect', booleanValue: true|false }
1091
+ */
1092
+ async correctIncorrect({ value }) {
1093
+ return this._extract('correctIncorrect', value);
1094
+ }
1095
+
1096
+ /**
1097
+ * Extract using custom regex pattern
1098
+ * @param {Object} options - Extraction options
1099
+ * @param {string} options.value - Text to match against
1100
+ * @param {string} options.pattern - Regex pattern
1101
+ * @param {number} [options.maxLength] - Maximum length of extracted value
1102
+ * @param {string} [options.flags='g'] - Regex flags
1103
+ * @param {boolean} [options.replaceNumbers=true] - Convert number words to digits first
1104
+ * @returns {Promise<Object>} { isValid, parsedValue, originalValue, processedValue }
1105
+ */
1106
+ async regex({
1107
+ value,
1108
+ pattern,
1109
+ maxLength,
1110
+ flags = 'g',
1111
+ replaceNumbers = true,
1112
+ }) {
1113
+ return this._extract('regex', value, {
1114
+ pattern,
1115
+ maxLength,
1116
+ flags,
1117
+ replaceNumbers,
1118
+ });
1119
+ }
1120
+
1121
+ /**
1122
+ * Detect intent response from natural language
1123
+ * @param {Object} options - Extraction options
1124
+ * @param {string} options.value - User's response (e.g., "I need help with my invoice")
1125
+ * @param {Object} options.params.question - The Question posed to the user to answer
1126
+ * @param {Object} options.params.validOptions - An array of objects with the valid intents
1127
+ *
1128
+ * @returns {Promise<Object>} { isValid, value: 'Billing', }
1129
+ */
1130
+ async intent({ value, params }) {
1131
+ return this._extract('intent', value, params);
1132
+ }
1133
+
1134
+ /**
1135
+ * Extract ALL entities from text (phone, email, address, name, etc.)
1136
+ * @param {Object} options - Extraction options
1137
+ * @param {string} options.value - Text to extract from
1138
+ * @param {array} [options.types] - Array of strings of the types you want to find ('correctIncorrect', 'phone', 'email', 'address', 'personName', 'extension', 'creditCard', 'ssn'). If not provided, all types will be extracted.
1139
+ * @param {string} [options.question] - The original question that was asked to the user. Providing this context significantly improves extraction accuracy by helping the AI understand what data entities to look for.
1140
+ * @returns {Promise<Object>} { isValid, extractions: [{ type, value, ...metadata }], metadata }
1141
+ */
1142
+ async all({ value, types = [], question }) {
1143
+ const params = {
1144
+ types,
1145
+ question,
1146
+ };
1147
+ return this._extract('all', value, params);
1148
+ }
1149
+
1150
+ // Internal method
1151
+ async _extract(type, value, params = {}) {
1152
+ this.sdk.validateParams(
1153
+ { type, value },
1154
+ {
1155
+ type: { type: 'string', required: true },
1156
+ value: { type: 'string', required: true },
1157
+ },
1158
+ );
1159
+
1160
+ const requestParams = {
1161
+ body: {
1162
+ type,
1163
+ value,
1164
+ params,
1165
+ },
1166
+ };
1167
+
1168
+ const result = await this.sdk._fetch('/ai/extract', 'POST', requestParams);
1169
+ return result;
1170
+ }
210
1171
  }