@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/src/constants.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export {LLM_PRACTICE_SESSION} from '@webex/internal-plugin-llm';
2
+
1
3
  export const EVENT_TRIGGERS = {
2
4
  VOICEA_ANNOUNCEMENT: 'voicea:announcement',
3
5
  CAPTION_LANGUAGE_UPDATE: 'voicea:captionLanguageUpdate',
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
- // @ts-ignore
268
- this.webex.internal.llm.socket.send({
308
+ const {socket, binding} = this.getPublishTransport();
309
+ socket.send({
269
310
  id: `${this.seqNum}`,
270
311
  type: 'publishRequest',
271
- recipients: {
272
- // @ts-ignore
273
- route: this.webex.internal.llm.getBinding(),
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
- // @ts-ignore
322
- if (!this.webex.internal.llm.isConnected()) return;
323
- // @ts-ignore
324
- this.webex.internal.llm.socket.send({
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
- // @ts-ignore
329
- route: this.webex.internal.llm.getBinding(),
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
- // @ts-ignore
364
- if (!this.webex.internal.llm.isConnected()) return;
408
+ if (!this.isLLMConnected()) {
409
+ return;
410
+ }
365
411
 
366
- // @ts-ignore
367
- this.webex.internal.llm.socket.send({
412
+ const {socket, binding} = this.getPublishTransport();
413
+
414
+ socket?.send({
368
415
  id: `${this.seqNum}`,
369
416
  type: 'publishRequest',
370
- recipients: {
371
- // @ts-ignore
372
- route: this.webex.internal.llm.getBinding(),
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.isAnnounceProcessing()) return;
448
- // @ts-ignore
449
- if (!this.webex.internal.llm.isConnected()) {
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
- // @ts-ignore
470
- if (!this.webex.internal.llm.isConnected()) {
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;