Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/samples/meetings.min.js

Large diffs are not rendered by default.

38 changes: 38 additions & 0 deletions packages/@webex/plugin-meetings/src/meeting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ import {
STAGE_MANAGER_TYPE,
LOCUSEVENT,
LOCUS_LLM_EVENT,
LLM_PRACTICE_SESSION,
} from '../constants';
import BEHAVIORAL_METRICS from '../metrics/constants';
import ParameterError from '../common/errors/parameter';
Expand Down Expand Up @@ -5931,13 +5932,50 @@ export default class Meeting extends StatelessWebexPlugin {
}
};

/**
* Verifies the relay event was delivered for the active LLM session binding.
* @param {RelayEvent} event Event object coming from LLM Connection
* @returns {boolean}
*/
private isRelayEventRouteValid(event: RelayEvent): boolean {
const route = event?.headers?.route;

if (!route) {
return true;
}

const {llm} = (this as any).webex.internal;
const isPracticeSession = llm.isConnected(LLM_PRACTICE_SESSION);
const expectedBinding = isPracticeSession
? llm.getBinding(LLM_PRACTICE_SESSION)
: llm.getBinding();
Comment on lines +5948 to +5951

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Validate against the event's session binding

When the practice-session socket is connected, this always compares every relay event to the practice binding, even events delivered on the default LLM channel. Mercury tags each incoming envelope with its sessionId and emits default events on event:relay.event while practice events use event:relay.event:llm-practice-session; because both listeners remain registered, a default-session reaction with headers.route === llm.getBinding() will now be skipped whenever the practice channel is also connected. Use the event/session that delivered the relay event to choose the binding instead of global practice connectivity.

Useful? React with 👍 / 👎.


if (!expectedBinding || route === expectedBinding) {
return true;
}

LoggerProxy.logger.warn(
`Meeting:index#processRelayEvent --> Skipping handling of relay event for ${
this.id
}. route ${route} does not match ${
isPracticeSession ? 'practice session' : 'default session'
} LLM binding ${expectedBinding}.`
);

return false;
}

/**
* Callback called when a relay event is received from meeting LLM Connection
* @param {RelayEvent} e Event object coming from LLM Connection
* @private
* @returns {void}
*/
private processRelayEvent = (e: RelayEvent): void => {
if (!this.isRelayEventRouteValid(e)) {
return;
}

switch (e.data.relayType) {
case REACTION_RELAY_TYPES.REACTION:
if (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,7 @@ type RelayEventData = {

export type RelayEvent = {
data: RelayEventData;
headers?: {
route?: string;
};
};
116 changes: 116 additions & 0 deletions packages/@webex/plugin-meetings/test/unit/spec/meeting/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
OFFLINE,
ROAP_OFFER_ANSWER_EXCHANGE_TIMEOUT,
LOCUS_LLM_EVENT,
LLM_PRACTICE_SESSION,
RECORDING_STATE,
} from '@webex/plugin-meetings/src/constants';
import {
Expand Down Expand Up @@ -1979,6 +1980,121 @@ describe('plugin-meetings', () => {
fakeProcessedReaction
);
});

[
{
title: 'should skip a reaction when the default relay route does not match the LLM binding',
isPracticeSessionConnected: false,
route: 'wrong-default-route',
defaultBinding: 'default-route',
practiceBinding: 'practice-route',
shouldProcess: false,
expectedSessionLabel: 'default session',
},
{
title: 'should process a reaction when the default relay route matches the LLM binding',
isPracticeSessionConnected: false,
route: 'default-route',
defaultBinding: 'default-route',
practiceBinding: 'practice-route',
shouldProcess: true,
},
{
title:
'should process a reaction when the practice-session relay route matches the practice-session LLM binding',
isPracticeSessionConnected: true,
route: 'practice-route',
defaultBinding: 'default-route',
practiceBinding: 'practice-route',
shouldProcess: true,
},
{
title:
'should skip a reaction when the practice-session relay route does not match the practice-session LLM binding',
isPracticeSessionConnected: true,
route: 'default-route',
defaultBinding: 'default-route',
practiceBinding: 'practice-route',
shouldProcess: false,
expectedSessionLabel: 'practice session',
},
].forEach(
({
title,
isPracticeSessionConnected,
route,
defaultBinding,
practiceBinding,
shouldProcess,
expectedSessionLabel,
}) => {
it(title, () => {
LoggerProxy.logger.warn = sinon.stub();
meeting.isReactionsSupported = sinon.stub().returns(true);
meeting.config.receiveReactions = true;
const fakeSendersName = 'Fake reactors name';
meeting.members.membersCollection.get = sinon.stub().returns({name: fakeSendersName});
webex.internal.llm.isConnected = sinon.stub().callsFake((llmSessionId) => {
return llmSessionId === LLM_PRACTICE_SESSION && isPracticeSessionConnected;
});
webex.internal.llm.getBinding = sinon.stub().callsFake((llmSessionId) => {
if (llmSessionId === LLM_PRACTICE_SESSION) {
return practiceBinding;
}

return defaultBinding;
});
const fakeReactionPayload = {
type: 'fake_type',
codepoints: 'fake_codepoints',
shortcodes: 'fake_shortcodes',
};
const fakeSenderPayload = {
participantId: 'fake_participant_id',
};
const fakeRelayEvent = {
headers: {route},
data: {
relayType: REACTION_RELAY_TYPES.REACTION,
reaction: fakeReactionPayload,
sender: fakeSenderPayload,
},
};
const fakeProcessedReaction = {
reaction: fakeReactionPayload,
sender: {
id: fakeSenderPayload.participantId,
name: fakeSendersName,
},
};

TriggerProxy.trigger.resetHistory();
meeting.processRelayEvent(fakeRelayEvent);

if (shouldProcess) {
assert.calledWith(
TriggerProxy.trigger,
sinon.match.instanceOf(Meeting),
{
file: 'meeting/index',
function: 'join',
},
EVENT_TRIGGERS.MEETING_RECEIVE_REACTIONS,
fakeProcessedReaction
);
assert.notCalled(LoggerProxy.logger.warn);
} else {
assert.calledWith(
LoggerProxy.logger.warn,
`Meeting:index#processRelayEvent --> Skipping handling of relay event for ${meeting.id}. route ${route} does not match ${expectedSessionLabel} LLM binding ${
isPracticeSessionConnected ? practiceBinding : defaultBinding
}.`
);
assert.notCalled(TriggerProxy.trigger);
}
});
}
);
});

describe('#handleLLMOnline', () => {
Expand Down
Loading