@webex/internal-plugin-voicea 3.11.0 → 3.12.0-metrics-init-fix-2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.js +9 -1
- package/dist/constants.js.map +1 -1
- package/dist/types/constants.d.ts +1 -0
- package/dist/types/voicea.d.ts +48 -0
- package/dist/voicea.js +151 -27
- package/dist/voicea.js.map +1 -1
- package/package.json +9 -9
- package/src/constants.ts +2 -0
- package/src/voicea.ts +146 -27
- package/test/unit/spec/voicea.js +398 -24
package/src/constants.ts
CHANGED
package/src/voicea.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
AIBRIDGE_RELAY_TYPES,
|
|
7
7
|
TRANSCRIPTION_TYPE,
|
|
8
8
|
VOICEA,
|
|
9
|
+
LLM_PRACTICE_SESSION,
|
|
9
10
|
ANNOUNCE_STATUS,
|
|
10
11
|
TURN_ON_CAPTION_STATUS,
|
|
11
12
|
TOGGLE_MANUAL_CAPTION_STATUS,
|
|
@@ -41,6 +42,8 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
41
42
|
|
|
42
43
|
private captionStatus: string;
|
|
43
44
|
|
|
45
|
+
private keepTranscriptionSubscribed: boolean;
|
|
46
|
+
|
|
44
47
|
private toggleManualCaptionStatus: string;
|
|
45
48
|
|
|
46
49
|
private currentSpokenLanguage?: string;
|
|
@@ -89,6 +92,8 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
89
92
|
if (!this.hasSubscribedToEvents) {
|
|
90
93
|
// @ts-ignore
|
|
91
94
|
this.webex.internal.llm.on('event:relay.event', this.eventProcessor);
|
|
95
|
+
// @ts-ignore
|
|
96
|
+
this.webex.internal.llm.on(`event:relay.event:${LLM_PRACTICE_SESSION}`, this.eventProcessor);
|
|
92
97
|
this.hasSubscribedToEvents = true;
|
|
93
98
|
}
|
|
94
99
|
}
|
|
@@ -99,9 +104,12 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
99
104
|
*/
|
|
100
105
|
public deregisterEvents() {
|
|
101
106
|
this.areCaptionsEnabled = false;
|
|
107
|
+
this.keepTranscriptionSubscribed = false;
|
|
102
108
|
this.captionServiceId = undefined;
|
|
103
109
|
// @ts-ignore
|
|
104
110
|
this.webex.internal.llm.off('event:relay.event', this.eventProcessor);
|
|
111
|
+
// @ts-ignore
|
|
112
|
+
this.webex.internal.llm.off(`event:relay.event:${LLM_PRACTICE_SESSION}`, this.eventProcessor);
|
|
105
113
|
this.hasSubscribedToEvents = false;
|
|
106
114
|
this.announceStatus = ANNOUNCE_STATUS.IDLE;
|
|
107
115
|
this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
|
|
@@ -118,6 +126,7 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
118
126
|
super(...args);
|
|
119
127
|
this.seqNum = 1;
|
|
120
128
|
this.areCaptionsEnabled = false;
|
|
129
|
+
this.keepTranscriptionSubscribed = false;
|
|
121
130
|
this.captionServiceId = undefined;
|
|
122
131
|
this.announceStatus = ANNOUNCE_STATUS.IDLE;
|
|
123
132
|
this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
|
|
@@ -257,6 +266,38 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
257
266
|
this.trigger(EVENT_TRIGGERS.VOICEA_ANNOUNCEMENT, voiceaLanguageOptions);
|
|
258
267
|
};
|
|
259
268
|
|
|
269
|
+
/**
|
|
270
|
+
* Indicates whether the default or practice-session LLM connection is active.
|
|
271
|
+
* @returns {boolean}
|
|
272
|
+
*/
|
|
273
|
+
private isLLMConnected = (): boolean =>
|
|
274
|
+
// @ts-ignore
|
|
275
|
+
this.webex.internal.llm.isConnected() ||
|
|
276
|
+
// @ts-ignore
|
|
277
|
+
this.webex.internal.llm.isConnected(LLM_PRACTICE_SESSION);
|
|
278
|
+
|
|
279
|
+
public getKeepTranscriptionSubscribed = (): boolean => this.keepTranscriptionSubscribed;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Resolves the active LLM publish transport, preferring the practice-session
|
|
283
|
+
* connection only when that session is fully connected.
|
|
284
|
+
* @returns {Object}
|
|
285
|
+
*/
|
|
286
|
+
private getPublishTransport = () => {
|
|
287
|
+
// @ts-ignore
|
|
288
|
+
const {llm} = this.webex.internal;
|
|
289
|
+
const isPracticeSessionConnected = llm.isConnected(LLM_PRACTICE_SESSION);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
socket: (isPracticeSessionConnected && llm.getSocket(LLM_PRACTICE_SESSION)) || llm.socket,
|
|
293
|
+
binding:
|
|
294
|
+
(isPracticeSessionConnected && llm.getBinding(LLM_PRACTICE_SESSION)) || llm.getBinding(),
|
|
295
|
+
datachannelUrl:
|
|
296
|
+
(isPracticeSessionConnected && llm.getDatachannelUrl(LLM_PRACTICE_SESSION)) ||
|
|
297
|
+
llm.getDatachannelUrl(),
|
|
298
|
+
};
|
|
299
|
+
};
|
|
300
|
+
|
|
260
301
|
/**
|
|
261
302
|
* Sends Announcement to add voicea to the meeting
|
|
262
303
|
* @returns {void}
|
|
@@ -264,14 +305,15 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
264
305
|
private sendAnnouncement = (): void => {
|
|
265
306
|
this.announceStatus = ANNOUNCE_STATUS.JOINING;
|
|
266
307
|
this.listenToEvents();
|
|
267
|
-
|
|
268
|
-
|
|
308
|
+
const {socket, binding} = this.getPublishTransport();
|
|
309
|
+
socket.send({
|
|
269
310
|
id: `${this.seqNum}`,
|
|
270
311
|
type: 'publishRequest',
|
|
271
|
-
recipients:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
312
|
+
recipients: [
|
|
313
|
+
{
|
|
314
|
+
route: binding,
|
|
315
|
+
},
|
|
316
|
+
],
|
|
275
317
|
// If captionServiceId exists, send it as the 'to' header; otherwise keep headers empty.
|
|
276
318
|
headers: this.captionServiceId ? {to: this.captionServiceId} : {},
|
|
277
319
|
data: {
|
|
@@ -318,16 +360,19 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
318
360
|
* @returns {void}
|
|
319
361
|
*/
|
|
320
362
|
public requestLanguage = (languageCode: string): void => {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
363
|
+
if (!this.isLLMConnected()) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const {socket, binding} = this.getPublishTransport();
|
|
368
|
+
socket.send({
|
|
325
369
|
id: `${this.seqNum}`,
|
|
326
370
|
type: 'publishRequest',
|
|
327
|
-
recipients:
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
371
|
+
recipients: [
|
|
372
|
+
{
|
|
373
|
+
route: binding,
|
|
374
|
+
},
|
|
375
|
+
],
|
|
331
376
|
headers: {
|
|
332
377
|
to: this.captionServiceId,
|
|
333
378
|
},
|
|
@@ -360,17 +405,20 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
360
405
|
csis: number[],
|
|
361
406
|
isFinal: boolean
|
|
362
407
|
): void => {
|
|
363
|
-
|
|
364
|
-
|
|
408
|
+
if (!this.isLLMConnected()) {
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
365
411
|
|
|
366
|
-
|
|
367
|
-
|
|
412
|
+
const {socket, binding} = this.getPublishTransport();
|
|
413
|
+
|
|
414
|
+
socket?.send({
|
|
368
415
|
id: `${this.seqNum}`,
|
|
369
416
|
type: 'publishRequest',
|
|
370
|
-
recipients:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
417
|
+
recipients: [
|
|
418
|
+
{
|
|
419
|
+
route: binding,
|
|
420
|
+
},
|
|
421
|
+
],
|
|
374
422
|
headers: {},
|
|
375
423
|
data: {
|
|
376
424
|
eventType: 'relay.event',
|
|
@@ -425,6 +473,7 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
425
473
|
this.areCaptionsEnabled = true;
|
|
426
474
|
this.captionStatus = TURN_ON_CAPTION_STATUS.ENABLED;
|
|
427
475
|
this.announce();
|
|
476
|
+
this.updateSubchannelSubscriptionsAndSyncCaptionState({subscribe: ['transcription']}, true);
|
|
428
477
|
})
|
|
429
478
|
.catch(() => {
|
|
430
479
|
this.captionStatus = TURN_ON_CAPTION_STATUS.IDLE;
|
|
@@ -439,14 +488,21 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
439
488
|
private isAnnounceProcessing = () =>
|
|
440
489
|
[ANNOUNCE_STATUS.JOINING, ANNOUNCE_STATUS.JOINED].includes(this.announceStatus);
|
|
441
490
|
|
|
491
|
+
/**
|
|
492
|
+
* is announce processed
|
|
493
|
+
* @returns {boolean}
|
|
494
|
+
*/
|
|
495
|
+
private isAnnounceProcessed = () => this.announceStatus === ANNOUNCE_STATUS.JOINED;
|
|
496
|
+
|
|
442
497
|
/**
|
|
443
498
|
* announce to voicea data chanel
|
|
444
499
|
* @returns {void}
|
|
445
500
|
*/
|
|
446
501
|
public announce = () => {
|
|
447
|
-
if (this.
|
|
448
|
-
|
|
449
|
-
|
|
502
|
+
if (this.isAnnounceProcessed()) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (!this.isLLMConnected()) {
|
|
450
506
|
throw new Error('voicea can not announce before llm connected');
|
|
451
507
|
}
|
|
452
508
|
this.sendAnnouncement();
|
|
@@ -466,8 +522,8 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
466
522
|
*/
|
|
467
523
|
public turnOnCaptions = async (spokenLanguage?): undefined | Promise<void> => {
|
|
468
524
|
if (this.captionStatus === TURN_ON_CAPTION_STATUS.SENDING) return undefined;
|
|
469
|
-
|
|
470
|
-
if (!this.
|
|
525
|
+
|
|
526
|
+
if (!this.isLLMConnected()) {
|
|
471
527
|
throw new Error('can not turn on captions before llm connected');
|
|
472
528
|
}
|
|
473
529
|
|
|
@@ -577,6 +633,69 @@ export class VoiceaChannel extends WebexPlugin implements IVoiceaChannel {
|
|
|
577
633
|
* @returns {string}
|
|
578
634
|
*/
|
|
579
635
|
public getAnnounceStatus = () => this.announceStatus;
|
|
636
|
+
/**
|
|
637
|
+
* update LLM sub‑channel subscriptions.
|
|
638
|
+
*
|
|
639
|
+
* sends a single `subchannelSubscriptionRequest` to LLM,
|
|
640
|
+
* allowing subscribe and unsubscribe subchannel.
|
|
641
|
+
*
|
|
642
|
+
* @param {string[]} options.subscribe Sub‑channels to subscribe to.
|
|
643
|
+
* @param {string[]} options.unsubscribe Sub‑channels to unsubscribe from.
|
|
644
|
+
* @returns {Promise}
|
|
645
|
+
*/
|
|
646
|
+
public updateSubchannelSubscriptions = async ({
|
|
647
|
+
subscribe = [],
|
|
648
|
+
unsubscribe = [],
|
|
649
|
+
}: {
|
|
650
|
+
subscribe?: string[];
|
|
651
|
+
unsubscribe?: string[];
|
|
652
|
+
} = {}): Promise<void> => {
|
|
653
|
+
// @ts-ignore
|
|
654
|
+
const isDataChannelTokenEnabled = await this.webex.internal.llm.isDataChannelTokenEnabled();
|
|
655
|
+
// @ts-ignore
|
|
656
|
+
if (!this.isLLMConnected() || !isDataChannelTokenEnabled) return;
|
|
657
|
+
|
|
658
|
+
const {socket, datachannelUrl} = this.getPublishTransport();
|
|
659
|
+
|
|
660
|
+
// @ts-ignore
|
|
661
|
+
socket.send({
|
|
662
|
+
id: `${this.seqNum}`,
|
|
663
|
+
type: 'subchannelSubscriptionRequest',
|
|
664
|
+
data: {
|
|
665
|
+
// @ts-ignore
|
|
666
|
+
datachannelUri: datachannelUrl,
|
|
667
|
+
subscribe,
|
|
668
|
+
unsubscribe,
|
|
669
|
+
},
|
|
670
|
+
trackingId: `${config.trackingIdPrefix}_${uuid.v4().toString()}`,
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
this.seqNum += 1;
|
|
674
|
+
};
|
|
675
|
+
|
|
676
|
+
/**
|
|
677
|
+
* Updates transcription subchannel subscriptions and records whether the
|
|
678
|
+
* transcription subscription should be kept (and restored on reconnect).
|
|
679
|
+
*
|
|
680
|
+
* @param {Object} [options] - Subscription options.
|
|
681
|
+
* @param {string[]} [options.subscribe] - Subchannels to subscribe to.
|
|
682
|
+
* @param {string[]} [options.unsubscribe] - Subchannels to unsubscribe from.
|
|
683
|
+
* @param {boolean} [keepSubscribed=false] - Whether the transcription
|
|
684
|
+
* subscription should be kept and restored on reconnect.
|
|
685
|
+
*
|
|
686
|
+
* @returns {Promise<void>}
|
|
687
|
+
*/
|
|
688
|
+
public updateSubchannelSubscriptionsAndSyncCaptionState = (
|
|
689
|
+
options: {
|
|
690
|
+
subscribe?: string[];
|
|
691
|
+
unsubscribe?: string[];
|
|
692
|
+
} = {},
|
|
693
|
+
keepSubscribed = false
|
|
694
|
+
): Promise<void> => {
|
|
695
|
+
this.keepTranscriptionSubscribed = keepSubscribed;
|
|
696
|
+
|
|
697
|
+
return this.updateSubchannelSubscriptions(options);
|
|
698
|
+
};
|
|
580
699
|
}
|
|
581
700
|
|
|
582
701
|
export default VoiceaChannel;
|