@unboundcx/sdk 2.8.6 → 2.8.7

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,36 +19,56 @@ export class GenerativeService {
14
19
  async chat({
15
20
  prompt,
16
21
  messages,
22
+ systemPrompt,
17
23
  relatedId,
24
+ provider,
18
25
  model,
19
26
  temperature,
20
27
  subscriptionId,
21
28
  stream,
22
- method,
29
+ isPlayground = false,
30
+ responseFormat,
23
31
  }) {
24
32
  this.sdk.validateParams(
25
- { method, stream, temperature, subscriptionId, model, prompt, messages },
33
+ {
34
+ stream,
35
+ temperature,
36
+ subscriptionId,
37
+ provider,
38
+ model,
39
+ prompt,
40
+ messages,
41
+ systemPrompt,
42
+ isPlayground,
43
+ responseFormat,
44
+ },
26
45
  {
27
46
  prompt: { type: 'string', required: false },
28
47
  messages: { type: 'array', required: false },
48
+ systemPrompt: { type: 'string', required: false },
49
+ provider: { type: 'string', required: false },
29
50
  model: { type: 'string', required: false },
30
51
  temperature: { type: 'number', required: false },
31
52
  subscriptionId: { type: 'string', required: false },
32
53
  stream: { type: 'boolean', required: false },
33
- method: { type: 'string', required: true },
54
+ isPlayground: { type: 'boolean', required: false },
55
+ responseFormat: { type: 'object', required: false },
34
56
  },
35
57
  );
36
58
 
37
59
  const params = {
38
60
  body: {
61
+ isPlayground,
39
62
  prompt,
40
63
  messages,
64
+ systemPrompt,
41
65
  relatedId,
66
+ provider,
42
67
  model,
43
68
  temperature,
44
69
  subscriptionId,
45
70
  stream,
46
- method,
71
+ responseFormat,
47
72
  },
48
73
  // Return raw response for streaming to allow client-side stream handling
49
74
  returnRawResponse: stream === true,
@@ -108,55 +133,55 @@ export class GenerativeService {
108
133
  return result;
109
134
  }
110
135
 
111
- async chatOllama({
112
- prompt,
113
- messages,
114
- relatedId,
115
- model,
116
- temperature,
117
- subscriptionId,
118
- stream,
119
- method,
120
- }) {
121
- this.sdk.validateParams(
122
- { method },
123
- {
124
- prompt: { type: 'string', required: false },
125
- messages: { type: 'array', required: false },
126
- relatedId: { type: 'string', required: false },
127
- model: { type: 'string', required: false },
128
- temperature: { type: 'number', required: false },
129
- subscriptionId: { type: 'string', required: false },
130
- stream: { type: 'boolean', required: false },
131
- method: { type: 'string', required: true },
132
- },
133
- );
136
+ // async chatOllama({
137
+ // prompt,
138
+ // messages,
139
+ // relatedId,
140
+ // model,
141
+ // temperature,
142
+ // subscriptionId,
143
+ // stream,
144
+ // method,
145
+ // }) {
146
+ // this.sdk.validateParams(
147
+ // { method },
148
+ // {
149
+ // prompt: { type: 'string', required: false },
150
+ // messages: { type: 'array', required: false },
151
+ // relatedId: { type: 'string', required: false },
152
+ // model: { type: 'string', required: false },
153
+ // temperature: { type: 'number', required: false },
154
+ // subscriptionId: { type: 'string', required: false },
155
+ // stream: { type: 'boolean', required: false },
156
+ // method: { type: 'string', required: true },
157
+ // },
158
+ // );
134
159
 
135
- const params = {
136
- body: {
137
- prompt,
138
- messages,
139
- relatedId,
140
- model,
141
- temperature,
142
- subscriptionId,
143
- stream,
144
- method,
145
- },
146
- // Return raw response for streaming to allow client-side stream handling
147
- returnRawResponse: stream === true,
148
- };
160
+ // const params = {
161
+ // body: {
162
+ // prompt,
163
+ // messages,
164
+ // relatedId,
165
+ // model,
166
+ // temperature,
167
+ // subscriptionId,
168
+ // stream,
169
+ // method,
170
+ // },
171
+ // // Return raw response for streaming to allow client-side stream handling
172
+ // returnRawResponse: stream === true,
173
+ // };
149
174
 
150
- // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
151
- const forceFetch = stream === true;
152
- const result = await this.sdk._fetch(
153
- '/ai/generative/ollama',
154
- 'POST',
155
- params,
156
- forceFetch,
157
- );
158
- return result;
159
- }
175
+ // // Force HTTP transport when streaming is enabled since NATS doesn't support streaming responses
176
+ // const forceFetch = stream === true;
177
+ // const result = await this.sdk._fetch(
178
+ // '/ai/generative/ollama',
179
+ // 'POST',
180
+ // params,
181
+ // forceFetch,
182
+ // );
183
+ // return result;
184
+ // }
160
185
  }
161
186
 
162
187
  export class TextToSpeechService {
@@ -174,9 +199,21 @@ export class TextToSpeechService {
174
199
  pitch,
175
200
  volumeGainDb,
176
201
  effectsProfileIds,
202
+ createAccessKey,
177
203
  }) {
178
204
  this.sdk.validateParams(
179
- { text },
205
+ {
206
+ text,
207
+ voice,
208
+ languageCode,
209
+ ssmlGender,
210
+ audioEncoding,
211
+ speakingRate,
212
+ pitch,
213
+ volumeGainDb,
214
+ effectsProfileIds,
215
+ createAccessKey,
216
+ },
180
217
  {
181
218
  text: { type: 'string', required: true },
182
219
  voice: { type: 'string', required: false },
@@ -187,6 +224,7 @@ export class TextToSpeechService {
187
224
  pitch: { type: 'number', required: false },
188
225
  volumeGainDb: { type: 'number', required: false },
189
226
  effectsProfileIds: { type: 'array', required: false },
227
+ createAccessKey: { type: 'boolean', required: false },
190
228
  },
191
229
  );
192
230
 
@@ -199,6 +237,7 @@ export class TextToSpeechService {
199
237
  if (pitch) ttsData.pitch = pitch;
200
238
  if (volumeGainDb) ttsData.volumeGainDb = volumeGainDb;
201
239
  if (effectsProfileIds) ttsData.effectsProfileIds = effectsProfileIds;
240
+ if (createAccessKey) ttsData.createAccessKey = createAccessKey;
202
241
 
203
242
  const params = {
204
243
  body: ttsData,
@@ -207,4 +246,686 @@ export class TextToSpeechService {
207
246
  const result = await this.sdk._fetch('/ai/tts', 'POST', params);
208
247
  return result;
209
248
  }
249
+
250
+ /**
251
+ * List available TTS voices
252
+ * @returns {Promise<Object>} { voices: Array, count: number, supportedEncodings: Array, supportedLanguages: Array }
253
+ */
254
+ async list() {
255
+ const result = await this.sdk._fetch('/ai/tts', 'GET');
256
+ return result;
257
+ }
258
+ }
259
+
260
+ export class SpeechToTextService {
261
+ constructor(sdk) {
262
+ this.sdk = sdk;
263
+ }
264
+
265
+ /**
266
+ * Create a transcription from file or storage
267
+ * @param {Object} options - Transcription options
268
+ * @param {string} options.sourceType - 'file', 'storage', 'stream', or 'url'
269
+ * @param {string} [options.sourceId] - Source identifier
270
+ * @param {string} [options.storageId] - Storage ID if using storage
271
+ * @param {string} [options.engine='google'] - STT engine ('google', 'deepgram', 'whisper')
272
+ * @param {string} [options.languageCode='en-US'] - BCP-47 language code
273
+ * @param {Object} [options.metadata] - Engine-specific configuration
274
+ * @param {string} [options.engagementSessionId] - Engagement session ID
275
+ * @param {string} [options.playbookId] - Playbook ID
276
+ * @param {string} [options.name] - Transcription name
277
+ * @param {string} [options.role] - Speaker role
278
+ * @param {string} [options.direction] - Call direction
279
+ * @returns {Promise<Object>} Transcription result
280
+ */
281
+ async create({
282
+ sourceType,
283
+ sourceId,
284
+ sipCallId,
285
+ cdrId,
286
+ storageId,
287
+ engine,
288
+ languageCode,
289
+ metadata,
290
+ engagementSessionId,
291
+ playbookId,
292
+ name,
293
+ role,
294
+ direction,
295
+ }) {
296
+ this.sdk.validateParams(
297
+ {
298
+ sourceType,
299
+ sourceId,
300
+ sipCallId,
301
+ cdrId,
302
+ storageId,
303
+ engine,
304
+ languageCode,
305
+ metadata,
306
+ engagementSessionId,
307
+ playbookId,
308
+ name,
309
+ role,
310
+ direction,
311
+ },
312
+ {
313
+ sourceType: { type: 'string', required: true },
314
+ sourceId: { type: 'string', required: false },
315
+ sipCallId: { type: 'string', required: false },
316
+ cdrId: { type: 'string', required: false },
317
+ storageId: { type: 'string', required: false },
318
+ engine: { type: 'string', required: false },
319
+ languageCode: { type: 'string', required: false },
320
+ metadata: { type: 'object', required: false },
321
+ engagementSessionId: { type: 'string', required: false },
322
+ playbookId: { type: 'string', required: false },
323
+ name: { type: 'string', required: false },
324
+ role: { type: 'string', required: false },
325
+ direction: { type: 'string', required: false },
326
+ },
327
+ );
328
+
329
+ const params = {
330
+ body: {
331
+ sourceType,
332
+ sourceId,
333
+ sipCallId,
334
+ cdrId,
335
+ storageId,
336
+ engine,
337
+ languageCode,
338
+ metadata,
339
+ engagementSessionId,
340
+ playbookId,
341
+ name,
342
+ role,
343
+ direction,
344
+ },
345
+ };
346
+
347
+ const result = await this.sdk._fetch('/ai/stt', 'POST', params);
348
+ return result;
349
+ }
350
+
351
+ /**
352
+ * Create a real-time streaming transcription session
353
+ * Returns an EventEmitter-based stream for sending audio and receiving transcripts
354
+ *
355
+ * @param {Object} options - Stream options
356
+ * @param {string} [options.engine='google'] - STT engine ('google' or 'whisper')
357
+ * @param {string} [options.model] - STT model (e.g., 'phone_call' for Google)
358
+ * @param {string} [options.languageCode='en-US'] - BCP-47 language code
359
+ * @param {string} [options.encoding='LINEAR16'] - Audio encoding format
360
+ * @param {number} [options.sampleRateHertz=16000] - Sample rate in Hertz
361
+ * @param {number} [options.audioChannelCount=1] - Number of audio channels (1=mono, 2=stereo)
362
+ * @param {boolean} [options.singleUtterance=false] - Stop after first utterance
363
+ * @param {boolean} [options.interimResults=true] - Return interim results
364
+ * @param {boolean} [options.enableAutomaticPunctuation=true] - Enable automatic punctuation
365
+ * @param {boolean} [options.diarizationEnabled=false] - Enable speaker diarization
366
+ * @param {number} [options.speakerCount] - Number of speakers (if diarization enabled)
367
+ * @param {boolean} [options.vadEnabled=false] - Enable Voice Activity Detection
368
+ * @param {number} [options.minSilenceDuration=500] - Min silence duration in ms (for VAD)
369
+ * @param {number} [options.speechPadMs=400] - Speech padding in ms (for VAD)
370
+ * @param {string} [options.engagementSessionId] - Engagement session ID
371
+ * @param {string} [options.playbookId] - Playbook ID
372
+ * @param {string} [options.name] - Session name
373
+ * @param {Object} [options.metadata] - Additional metadata
374
+ * @returns {Promise<SttStream>} Stream object with write() method and transcript events
375
+ *
376
+ * @example
377
+ * const stream = await sdk.ai.stt.stream({ engine: 'google', languageCode: 'en-US' });
378
+ * stream.on('transcript', (result) => console.log(result.text, result.isFinal));
379
+ * stream.on('error', (error) => console.error(error));
380
+ * stream.on('close', () => console.log('Stream closed'));
381
+ * stream.write(audioChunk);
382
+ * stream.end();
383
+ */
384
+ async stream(options = {}) {
385
+ // Environment check - STT streaming only works in Node.js
386
+ if (this.sdk.environment !== 'node') {
387
+ throw new Error(
388
+ 'STT streaming requires Node.js environment. ' +
389
+ 'This feature is only available in backend services. ' +
390
+ 'Browser-based streaming is not supported.',
391
+ );
392
+ }
393
+
394
+ const {
395
+ engine = 'google',
396
+ model,
397
+ languageCode = 'en-US',
398
+ encoding = 'LINEAR16',
399
+ sampleRateHertz = 16000,
400
+ audioChannelCount = 1,
401
+ singleUtterance = false,
402
+ interimResults = true,
403
+ enableAutomaticPunctuation = true,
404
+ diarizationEnabled = false,
405
+ speakerCount,
406
+ vadEnabled = false,
407
+ minSilenceDuration = 500,
408
+ speechPadMs = 400,
409
+ engagementSessionId,
410
+ playbookId,
411
+ taskId,
412
+ workerId,
413
+ generateSubject,
414
+ generateTranscriptSummary,
415
+ generateSentiment,
416
+ bridgeId,
417
+ name,
418
+ metadata,
419
+ sipCallId,
420
+ cdrId,
421
+ } = options;
422
+
423
+ // Validate parameters
424
+ this.sdk.validateParams(
425
+ {
426
+ engine,
427
+ model,
428
+ languageCode,
429
+ encoding,
430
+ sampleRateHertz,
431
+ audioChannelCount,
432
+ singleUtterance,
433
+ interimResults,
434
+ enableAutomaticPunctuation,
435
+ diarizationEnabled,
436
+ speakerCount,
437
+ vadEnabled,
438
+ minSilenceDuration,
439
+ speechPadMs,
440
+ engagementSessionId,
441
+ playbookId,
442
+ taskId,
443
+ workerId,
444
+ generateSubject,
445
+ generateTranscriptSummary,
446
+ generateSentiment,
447
+ bridgeId,
448
+ name,
449
+ metadata,
450
+ sipCallId,
451
+ cdrId,
452
+ },
453
+ {
454
+ engine: { type: 'string', required: false },
455
+ model: { type: 'string', required: false },
456
+ languageCode: { type: 'string', required: false },
457
+ encoding: { type: 'string', required: false },
458
+ sampleRateHertz: { type: 'number', required: false },
459
+ audioChannelCount: { type: 'number', required: false },
460
+ singleUtterance: { type: 'boolean', required: false },
461
+ interimResults: { type: 'boolean', required: false },
462
+ enableAutomaticPunctuation: { type: 'boolean', required: false },
463
+ diarizationEnabled: { type: 'boolean', required: false },
464
+ speakerCount: { type: 'number', required: false },
465
+ vadEnabled: { type: 'boolean', required: false },
466
+ minSilenceDuration: { type: 'number', required: false },
467
+ speechPadMs: { type: 'number', required: false },
468
+ engagementSessionId: { type: 'string', required: false },
469
+ playbookId: { type: 'string', required: false },
470
+ taskId: { type: 'string', required: false },
471
+ workerId: { type: 'string', required: false },
472
+ generateSubject: { type: 'boolean', required: false },
473
+ generateTranscriptSummary: { type: 'boolean', required: false },
474
+ generateSentiment: { type: 'boolean', required: false },
475
+ bridgeId: { type: 'string', required: false },
476
+ name: { type: 'string', required: false },
477
+ metadata: { type: 'object', required: false },
478
+ sipCallId: { type: 'string', required: false },
479
+ cdrId: { type: 'string', required: false },
480
+ },
481
+ );
482
+
483
+ // Create session via API
484
+ const sessionParams = {
485
+ body: {
486
+ engine,
487
+ model,
488
+ engagementSessionId,
489
+ playbookId,
490
+ taskId,
491
+ workerId,
492
+ generateSubject,
493
+ generateTranscriptSummary,
494
+ generateSentiment,
495
+ bridgeId,
496
+ sipCallId,
497
+ cdrId,
498
+ name,
499
+ metadata: {
500
+ ...metadata,
501
+ languageCode,
502
+ encoding,
503
+ sampleRateHertz,
504
+ audioChannelCount,
505
+ singleUtterance,
506
+ interimResults,
507
+ enableAutomaticPunctuation,
508
+ diarizationEnabled,
509
+ speakerCount,
510
+ vadEnabled,
511
+ minSilenceDuration,
512
+ speechPadMs,
513
+ },
514
+ },
515
+ };
516
+
517
+ const session = await this.sdk._fetch(
518
+ '/ai/stt/stream',
519
+ 'POST',
520
+ sessionParams,
521
+ );
522
+
523
+ // Dynamic import of SttStream (only loads when needed)
524
+ const { SttStream } = await import('./ai/SttStream.js');
525
+
526
+ // Create and return stream instance
527
+ const streamOptions = {
528
+ ...options,
529
+ languageCode,
530
+ encoding,
531
+ sampleRateHertz,
532
+ audioChannelCount,
533
+ vadEnabled,
534
+ minSilenceDuration,
535
+ speechPadMs,
536
+ };
537
+
538
+ return new SttStream(this.sdk, session, streamOptions);
539
+ }
540
+
541
+ /**
542
+ * Get a transcription by ID
543
+ * @param {string} id - Transcription ID
544
+ * @param {Object} [options] - Retrieval options
545
+ * @param {boolean} [options.includeMessages=true] - Include transcription messages
546
+ * @returns {Promise<Object>} Transcription data
547
+ */
548
+ async get(id, { includeMessages = true } = {}) {
549
+ this.sdk.validateParams(
550
+ { id },
551
+ {
552
+ id: { type: 'string', required: true },
553
+ includeMessages: { type: 'boolean', required: false },
554
+ },
555
+ );
556
+
557
+ const params = {
558
+ query: { includeMessages: includeMessages.toString() },
559
+ };
560
+
561
+ const result = await this.sdk._fetch(`/ai/stt/${id}`, 'GET', params);
562
+ return result;
563
+ }
564
+
565
+ /**
566
+ * List transcriptions with filters
567
+ * @param {Object} [filters] - Filter options
568
+ * @param {string} [filters.engagementSessionId] - Filter by engagement session
569
+ * @param {string} [filters.userId] - Filter by user
570
+ * @param {string} [filters.status] - Filter by status
571
+ * @param {string} [filters.engine] - Filter by engine
572
+ * @param {string} [filters.sourceType] - Filter by source type
573
+ * @param {string} [filters.playbookId] - Filter by playbook
574
+ * @param {string} [filters.startDate] - Filter by start date
575
+ * @param {string} [filters.endDate] - Filter by end date
576
+ * @param {number} [filters.limit=50] - Limit results
577
+ * @param {number} [filters.offset=0] - Offset for pagination
578
+ * @returns {Promise<Object>} List of transcriptions
579
+ */
580
+ async list(filters = {}) {
581
+ this.sdk.validateParams(filters, {
582
+ engagementSessionId: { type: 'string', required: false },
583
+ userId: { type: 'string', required: false },
584
+ status: { type: 'string', required: false },
585
+ engine: { type: 'string', required: false },
586
+ sourceType: { type: 'string', required: false },
587
+ playbookId: { type: 'string', required: false },
588
+ startDate: { type: 'string', required: false },
589
+ endDate: { type: 'string', required: false },
590
+ limit: { type: 'number', required: false },
591
+ offset: { type: 'number', required: false },
592
+ });
593
+
594
+ const params = {
595
+ query: filters,
596
+ };
597
+
598
+ const result = await this.sdk._fetch('/ai/stt', 'GET', params);
599
+ return result;
600
+ }
601
+
602
+ /**
603
+ * Log a transcription message for a streaming session
604
+ * @param {string} sessionId - Transcription session ID
605
+ * @param {string} messageId - message id for the transcription
606
+ * @param {number} timestamp - message timestamp (unix)
607
+ * @param {Object} message - Message data
608
+ * @param {string} message.text - Transcribed text
609
+ * @param {Object} [message.transcriptionJson] - Full transcription metadata
610
+ * @param {number} [message.duration] - Duration of this segment in seconds
611
+ * @param {number} [message.confidence] - Confidence score (0-1)
612
+ * @param {string} [message.languageCode] - Language code for this segment
613
+ * @param {string} [message.userId] - User associated with this message
614
+ * @param {string} [message.role] - Speaker role
615
+ * @param {string} [message.sipCallId] - SIP call identifier
616
+ * @param {string} [message.side] - Stream side ('send' or 'recv')
617
+ * @param {string} [message.bridgeId] - bridge id
618
+ * @param {Object} [message.sentiment] - Sentiment analysis data
619
+ * @param {number} [message.sentiment.score] - Overall sentiment score (-100 to +100)
620
+ * @param {number} [message.sentiment.previousScore] - Previous sentiment score (-100 to +100)
621
+ * @param {number} [message.sentiment.delta] - Score change (score - previousScore)
622
+ * @param {number} [message.sentiment.customerScore] - Customer sentiment score (-100 to +100)
623
+ * @param {number} [message.sentiment.agentScore] - Agent sentiment score (-100 to +100)
624
+ * @param {number} [message.sentiment.intensity] - Sentiment intensity (0 to 1)
625
+ * @param {string[]} [message.sentiment.emotions] - Detected emotions (up to 3)
626
+ * @param {string} [message.sentiment.trend] - Sentiment trend ('improving' | 'stable' | 'declining')
627
+ * @param {string} [message.sentiment.source] - Analysis source ('llm+acoustic' | 'acoustic_only' | 'carry_forward')
628
+ * @returns {Promise<Object>} Created message result
629
+ */
630
+ async logMessage(
631
+ sessionId,
632
+ {
633
+ messageId,
634
+ timestamp,
635
+ text,
636
+ transcriptionJson,
637
+ duration,
638
+ confidence,
639
+ languageCode,
640
+ userId,
641
+ role,
642
+ sipCallId,
643
+ side,
644
+ bridgeId,
645
+ sentiment,
646
+ },
647
+ ) {
648
+ this.sdk.validateParams(
649
+ { messageId, sessionId, text, bridgeId },
650
+ {
651
+ sessionId: { type: 'string', required: true },
652
+ messageId: { type: 'string', required: false },
653
+ timestamp: { type: 'number', required: false },
654
+ text: { type: 'string', required: true },
655
+ transcriptionJson: { type: 'object', required: false },
656
+ duration: { type: 'number', required: false },
657
+ confidence: { type: 'number', required: false },
658
+ languageCode: { type: 'string', required: false },
659
+ userId: { type: 'string', required: false },
660
+ role: { type: 'string', required: false },
661
+ sipCallId: { type: 'string', required: false },
662
+ side: { type: 'string', required: false },
663
+ bridgeId: { type: 'string', required: false },
664
+ sentiment: { type: 'object', required: false },
665
+ },
666
+ );
667
+
668
+ const params = {
669
+ body: {
670
+ messageId,
671
+ timestamp,
672
+ text,
673
+ transcriptionJson,
674
+ duration,
675
+ confidence,
676
+ languageCode,
677
+ userId,
678
+ role,
679
+ sipCallId,
680
+ side,
681
+ bridgeId,
682
+ sentiment,
683
+ },
684
+ };
685
+
686
+ const result = await this.sdk._fetch(
687
+ `/ai/stt/stream/${sessionId}/messages`,
688
+ 'POST',
689
+ params,
690
+ );
691
+ return result;
692
+ }
693
+
694
+ /**
695
+ * Complete a streaming transcription session
696
+ * @param {string} sessionId - Transcription session ID
697
+ * @param {Object} [options] - Completion options
698
+ * @param {string} [options.status='completed'] - Final status ('completed' or 'failed')
699
+ * @param {string} [options.error] - Error message if status is 'failed'
700
+ * @returns {Promise<Object>} Completion result
701
+ */
702
+ async complete(sessionId, { status = 'completed', error } = {}) {
703
+ this.sdk.validateParams(
704
+ { sessionId },
705
+ {
706
+ sessionId: { type: 'string', required: true },
707
+ status: { type: 'string', required: false },
708
+ error: { type: 'string', required: false },
709
+ },
710
+ );
711
+
712
+ const params = {
713
+ body: { status, error },
714
+ };
715
+
716
+ const result = await this.sdk._fetch(
717
+ `/ai/stt/stream/${sessionId}/complete`,
718
+ 'PUT',
719
+ params,
720
+ );
721
+ return result;
722
+ }
723
+ }
724
+
725
+ // STT Constants
726
+ export const STT = {
727
+ ENGINES: {
728
+ GOOGLE: 'google',
729
+ DEEPGRAM: 'deepgram',
730
+ WHISPER: 'whisper',
731
+ },
732
+ SOURCE_TYPES: {
733
+ FILE: 'file',
734
+ STORAGE: 'storage',
735
+ STREAM: 'stream',
736
+ URL: 'url',
737
+ },
738
+ STATUS: {
739
+ PENDING: 'pending',
740
+ PROCESSING: 'processing',
741
+ COMPLETED: 'completed',
742
+ FAILED: 'failed',
743
+ },
744
+ LANGUAGES: {
745
+ EN_US: 'en-US',
746
+ EN_GB: 'en-GB',
747
+ EN_AU: 'en-AU',
748
+ ES_ES: 'es-ES',
749
+ ES_MX: 'es-MX',
750
+ ES_US: 'es-US',
751
+ FR_FR: 'fr-FR',
752
+ FR_CA: 'fr-CA',
753
+ DE_DE: 'de-DE',
754
+ IT_IT: 'it-IT',
755
+ PT_BR: 'pt-BR',
756
+ PT_PT: 'pt-PT',
757
+ ZH_CN: 'zh-CN',
758
+ JA_JP: 'ja-JP',
759
+ KO_KR: 'ko-KR',
760
+ },
761
+ };
762
+
763
+ export class ExtractService {
764
+ constructor(sdk) {
765
+ this.sdk = sdk;
766
+ }
767
+
768
+ /**
769
+ * Extract phone number from text
770
+ * @param {Object} options - Extraction options
771
+ * @param {string} options.value - Text containing phone number
772
+ * @param {string} [options.country='US'] - Country code for validation
773
+ * @param {string} [options.format='E164'] - Output format: 'E164', 'national', 'international'
774
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { country, type, nationalNumber, ... } }
775
+ */
776
+ async phone({ value, country = 'US', format = 'E164' }) {
777
+ return this._extract('phone', value, { country, format });
778
+ }
779
+
780
+ /**
781
+ * Extract email address from text
782
+ * @param {Object} options - Extraction options
783
+ * @param {string} options.value - Text containing email
784
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { domain } }
785
+ */
786
+ async email({ value }) {
787
+ return this._extract('email', value);
788
+ }
789
+
790
+ /**
791
+ * Extract physical address from text
792
+ * @param {Object} options - Extraction options
793
+ * @param {string} options.value - Text containing address
794
+ * @param {boolean} [options.useAI=false] - Use AI for better extraction from conversational text
795
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence, metadata: { components } }
796
+ */
797
+ async address({ value, useAI = false }) {
798
+ return this._extract('address', value, { useAI });
799
+ }
800
+
801
+ /**
802
+ * Extract person's name from text
803
+ * @param {Object} options - Extraction options
804
+ * @param {string} options.value - Text containing name
805
+ * @param {boolean} [options.useAI=true] - Use AI for extraction from conversational text
806
+ * @returns {Promise<Object>} { isValid, parsedValue, firstName, lastName, title }
807
+ */
808
+ async personName({ value, useAI = true }) {
809
+ return this._extract('personName', value, { useAI });
810
+ }
811
+
812
+ /**
813
+ * Extract and validate credit card number (masked for security)
814
+ * @param {Object} options - Extraction options
815
+ * @param {string} options.value - Text containing credit card
816
+ * @param {boolean} [options.maskOutput=true] - Mask all but last 4 digits
817
+ * @returns {Promise<Object>} { isValid, parsedValue (masked), confidence, metadata: { cardType, length } }
818
+ */
819
+ async creditCard({ value, maskOutput = true }) {
820
+ return this._extract('creditCard', value, { maskOutput });
821
+ }
822
+
823
+ /**
824
+ * Extract and validate SSN (masked for security)
825
+ * @param {Object} options - Extraction options
826
+ * @param {string} options.value - Text containing SSN
827
+ * @param {boolean} [options.maskOutput=true] - Mask all but last 4 digits
828
+ * @returns {Promise<Object>} { isValid, parsedValue (masked), confidence, metadata }
829
+ */
830
+ async ssn({ value, maskOutput = true }) {
831
+ return this._extract('ssn', value, { maskOutput });
832
+ }
833
+
834
+ /**
835
+ * Extract phone extension
836
+ * @param {Object} options - Extraction options
837
+ * @param {string} options.value - Text containing extension
838
+ * @param {number} [options.minLength=1] - Minimum digits
839
+ * @param {number} [options.maxLength=10] - Maximum digits
840
+ * @returns {Promise<Object>} { isValid, parsedValue, confidence }
841
+ */
842
+ async extension({ value, minLength = 1, maxLength = 10 }) {
843
+ return this._extract('extension', value, { minLength, maxLength });
844
+ }
845
+
846
+ /**
847
+ * Detect correct/incorrect response from natural language
848
+ * @param {Object} options - Extraction options
849
+ * @param {string} options.value - User's response (e.g., "yes", "no", "correct", "nope")
850
+ * @returns {Promise<Object>} { isValid, parsedValue: 'correct'|'incorrect', booleanValue: true|false }
851
+ */
852
+ async correctIncorrect({ value }) {
853
+ return this._extract('correctIncorrect', value);
854
+ }
855
+
856
+ /**
857
+ * Extract using custom regex pattern
858
+ * @param {Object} options - Extraction options
859
+ * @param {string} options.value - Text to match against
860
+ * @param {string} options.pattern - Regex pattern
861
+ * @param {number} [options.maxLength] - Maximum length of extracted value
862
+ * @param {string} [options.flags='g'] - Regex flags
863
+ * @param {boolean} [options.replaceNumbers=true] - Convert number words to digits first
864
+ * @returns {Promise<Object>} { isValid, parsedValue, originalValue, processedValue }
865
+ */
866
+ async regex({
867
+ value,
868
+ pattern,
869
+ maxLength,
870
+ flags = 'g',
871
+ replaceNumbers = true,
872
+ }) {
873
+ return this._extract('regex', value, {
874
+ pattern,
875
+ maxLength,
876
+ flags,
877
+ replaceNumbers,
878
+ });
879
+ }
880
+
881
+ /**
882
+ * Detect intent response from natural language
883
+ * @param {Object} options - Extraction options
884
+ * @param {string} options.value - User's response (e.g., "I need help with my invoice")
885
+ * @param {Object} options.params.question - The Question posed to the user to answer
886
+ * @param {Object} options.params.validOptions - An array of objects with the valid intents
887
+ *
888
+ * @returns {Promise<Object>} { isValid, value: 'Billing', }
889
+ */
890
+ async intent({ value, params }) {
891
+ return this._extract('intent', value, params);
892
+ }
893
+
894
+ /**
895
+ * Extract ALL entities from text (phone, email, address, name, etc.)
896
+ * @param {Object} options - Extraction options
897
+ * @param {string} options.value - Text to extract from
898
+ * @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.
899
+ * @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.
900
+ * @returns {Promise<Object>} { isValid, extractions: [{ type, value, ...metadata }], metadata }
901
+ */
902
+ async all({ value, types = [], question }) {
903
+ const params = {
904
+ types,
905
+ question,
906
+ };
907
+ return this._extract('all', value, params);
908
+ }
909
+
910
+ // Internal method
911
+ async _extract(type, value, params = {}) {
912
+ this.sdk.validateParams(
913
+ { type, value },
914
+ {
915
+ type: { type: 'string', required: true },
916
+ value: { type: 'string', required: true },
917
+ },
918
+ );
919
+
920
+ const requestParams = {
921
+ body: {
922
+ type,
923
+ value,
924
+ params,
925
+ },
926
+ };
927
+
928
+ const result = await this.sdk._fetch('/ai/extract', 'POST', requestParams);
929
+ return result;
930
+ }
210
931
  }