Skip to content
Draft
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
108 changes: 44 additions & 64 deletions packages/@webex/internal-plugin-llm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,92 +24,72 @@ npm install --save @webex/internal-plugin-llm
```js
import '@webex/internal-plugin-llm';
import WebexCore from '@webex/webex-core';
// Optional: import enum from package internals if needed in your app setup
// import {DataChannelTokenType} from '@webex/internal-plugin-llm/src/llm.types';

const webex = new WebexCore();

// Create a new LLM connection (each meeting owns its own channel)
const llmChannel = webex.internal.llm.createConnection();

// locusUrl and datachannelUrl are from meeting.locusInfo
const locusUrl = meeting.locusInfo.url;
const datachannelUrl = meeting.locusInfo.info.datachannelUrl;

// Optional JWT token for data channel auth
const datachannelToken = '<jwt-token>';

// Default session (no token)
await webex.internal.llm.registerAndConnect(locusUrl, datachannelUrl);

// Default session (with JWT token)
await webex.internal.llm.registerAndConnect(locusUrl, datachannelUrl, datachannelToken);

// Multiple named sessions
await webex.internal.llm.registerAndConnect(locusUrlA, datachannelUrlA, undefined, 'session-a');
await webex.internal.llm.registerAndConnect(
locusUrlB,
datachannelUrlB,
datachannelToken,
'session-b'
);

// Listen across multiple connections
const llm = webex.internal.llm;
const sessionA = 'session-a';
const sessionB = 'session-b';

// Default session events use the base event name.
llm.on('online', () => {
console.log('[default] connected');
// Optional: set up token refresh handler before connecting
llmChannel.setRefreshHandler(async () => {
// Return shape must match plugin expectation
return {
body: {
datachannelToken: '<refreshed-jwt-token>',
datachannelTokenType: 'llm-default-session', // or 'llm-practice-session'
},
};
});

llm.on('event', (envelope) => {
console.log('[default] event', envelope.data?.eventType, envelope.sessionId);
});
// Connect (with optional JWT token for data channel auth)
const datachannelToken = '<jwt-token>';
await llmChannel.registerAndConnect(locusUrl, datachannelUrl, datachannelToken);

// Non-default sessions emit events with :<sessionId> suffix.
llm.on(`online:${sessionA}`, () => {
console.log(`[${sessionA}] connected`);
// Subscribe to events directly on the channel
llmChannel.on('online', () => {
console.log('LLM connected');
});

llm.on(`event:${sessionA}`, (envelope) => {
console.log(`[${sessionA}] event`, envelope.data?.eventType, envelope.sessionId);
llmChannel.on('event:relay.event', (envelope) => {
console.log('LLM event', envelope.data?.eventType);
});

llm.on(`event:${sessionB}`, (envelope) => {
console.log(`[${sessionB}] event`, envelope.data?.eventType, envelope.sessionId);
});
// Channel status and metadata
llmChannel.isConnected();
llmChannel.isConnecting();
llmChannel.getBinding();
llmChannel.getLocusUrl();
llmChannel.getDatachannelUrl();
llmChannel.getSocket();

// Optional: store/retrieve token by token type
webex.internal.llm.setDatachannelToken(datachannelToken, 'llm-default-session');
webex.internal.llm.getDatachannelToken('llm-default-session');
// Token management
llmChannel.setDatachannelToken(datachannelToken);
llmChannel.getDatachannelToken();
llmChannel.clearDatachannelToken();

// Optional: inject token refresh handler
webex.internal.llm.setRefreshHandler(async () => {
// Return shape must match plugin expectation
return {
body: {
datachannelToken: '<refreshed-jwt-token>',
datachannelTokenType: 'llm-default-session',
},
};
}, 'llm-default-session');
// Manually trigger token refresh (if needed by your flow)
await llmChannel.refreshDataChannelToken();

// Disconnect when done (owner is responsible for cleanup)
await llmChannel.disconnect({code: 1000, reason: 'done'});

// Optional: manually trigger refresh (if needed by your flow)
await webex.internal.llm.refreshDataChannelToken();
// --- Plugin-level methods ---

// Per-session status and metadata
webex.internal.llm.isConnected('session-a');
webex.internal.llm.getBinding('session-a');
webex.internal.llm.getLocusUrl('session-a');
webex.internal.llm.getDatachannelUrl('session-a');
// Check if data channel token feature flag is enabled
await webex.internal.llm.isDataChannelTokenEnabled();

// All active sessions
// Get all active connections (useful for diagnostics)
webex.internal.llm.getAllConnections();

// Disconnect one session
await webex.internal.llm.disconnectLLM({code: 1000, reason: 'done'}, 'session-a', 'meeting-id');
// Find a channel by datachannel URL (used by interceptors)
webex.internal.llm.getConnectionByDatachannelUrl(datachannelUrl);

// Disconnect all sessions
await webex.internal.llm.disconnectAllLLM({code: 1000, reason: 'shutdown'});
// Disconnect all connections (useful for cleanup on logout)
await webex.internal.llm.disconnectAll({code: 1000, reason: 'shutdown'});
```

## Maintainers
Expand Down
3 changes: 2 additions & 1 deletion packages/@webex/internal-plugin-llm/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import {DataChannelTokenType} from './llm.types';
WebexCore.registerInternalPlugin('llm', LLMPlugin, {
config,
onBeforeLogout() {
return this.disconnectAllLLM();
return this.disconnectAll();
},
});

export {DataChannelTokenType};
export {LLM_DEFAULT_SESSION, LLM_PRACTICE_SESSION} from './constants';
export {default} from './llm';
export {default as LLMPlugin} from './llm-plugin';
export type {ILLMChannel, ILLMPlugin} from './llm.types';
Loading