UNPKG

@anam-ai/js-sdk

Version:

Client side JavaScript SDK for Anam AI

290 lines 13 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import { ClientError, ErrorCode } from './lib/ClientError'; import { CoreApiRestClient, InternalEventEmitter, MessageHistoryClient, PublicEventEmitter, StreamingClient, } from './modules'; import { AnamEvent, } from './types'; export default class AnamClient { constructor(sessionToken, personaConfig, options) { this.inputAudioState = { isMuted: false }; this.sessionId = null; this.streamingClient = null; this._isStreaming = false; const configError = this.validateClientConfig(sessionToken, personaConfig, options); if (configError) { throw new Error(configError); } this.personaConfig = personaConfig; this.clientOptions = options; this.publicEventEmitter = new PublicEventEmitter(); this.internalEventEmitter = new InternalEventEmitter(); this.apiClient = new CoreApiRestClient(sessionToken, options === null || options === void 0 ? void 0 : options.apiKey, options === null || options === void 0 ? void 0 : options.api); this.messageHistoryClient = new MessageHistoryClient(this.publicEventEmitter, this.internalEventEmitter); } decodeJwt(token) { try { const base64Payload = token.split('.')[1]; const payload = JSON.parse(atob(base64Payload)); return payload; } catch (error) { throw new Error('Invalid session token format'); } } validateClientConfig(sessionToken, personaConfig, options) { var _a; // Validate authentication configuration if (!sessionToken && !(options === null || options === void 0 ? void 0 : options.apiKey)) { return 'Either sessionToken or apiKey must be provided'; } if ((options === null || options === void 0 ? void 0 : options.apiKey) && sessionToken) { return 'Only one of sessionToken or apiKey should be used'; } // Validate persona configuration based on session token if (sessionToken) { const decodedToken = this.decodeJwt(sessionToken); const tokenType = (_a = decodedToken.type) === null || _a === void 0 ? void 0 : _a.toLowerCase(); if (tokenType === 'legacy') { if (!personaConfig || !('personaId' in personaConfig)) { return 'Both session token and client are missing a persona configuration. Please provide a persona ID of a saved persona in the personaConfig parameter.'; } } else if (tokenType === 'ephemeral' || tokenType === 'stateful') { if (personaConfig) { return 'This session token already contains a persona configuration. Please remove the personaConfig parameter.'; } } } else { // No session token (using apiKey) if (!personaConfig) { return 'Missing persona config. Persona configuration must be provided when using apiKey'; } } // Validate voice detection configuration if (options === null || options === void 0 ? void 0 : options.voiceDetection) { // End of speech sensitivity must be a number between 0 and 1 if (options.voiceDetection.endOfSpeechSensitivity !== undefined) { if (typeof options.voiceDetection.endOfSpeechSensitivity !== 'number') { return 'End of speech sensitivity must be a number'; } if (options.voiceDetection.endOfSpeechSensitivity < 0 || options.voiceDetection.endOfSpeechSensitivity > 1) { return 'End of speech sensitivity must be between 0 and 1'; } } } return undefined; } buildStartSessionOptionsForClient() { var _a; const sessionOptions = {}; if ((_a = this.clientOptions) === null || _a === void 0 ? void 0 : _a.voiceDetection) { sessionOptions.voiceDetection = this.clientOptions.voiceDetection; } // return undefined if no options are set if (Object.keys(sessionOptions).length === 0) { return undefined; } return sessionOptions; } startSession(userProvidedAudioStream) { return __awaiter(this, void 0, void 0, function* () { var _a; const config = this.personaConfig; // build session options from client options const sessionOptions = this.buildStartSessionOptionsForClient(); // start a new session const response = yield this.apiClient.startSession(config, sessionOptions); const { sessionId, clientConfig, engineHost, engineProtocol, signallingEndpoint, } = response; const { heartbeatIntervalSeconds, maxWsReconnectionAttempts, iceServers } = clientConfig; try { this.streamingClient = new StreamingClient(sessionId, { engine: { baseUrl: `${engineProtocol}://${engineHost}`, }, signalling: { heartbeatIntervalSeconds, maxWsReconnectionAttempts, url: { baseUrl: engineHost, protocol: engineProtocol, signallingPath: signallingEndpoint, }, }, iceServers, inputAudio: { inputAudioState: this.inputAudioState, userProvidedMediaStream: userProvidedAudioStream, audioDeviceId: (_a = this.clientOptions) === null || _a === void 0 ? void 0 : _a.audioDeviceId, }, }, this.publicEventEmitter, this.internalEventEmitter); } catch (error) { throw new ClientError('Failed to initialize streaming client', ErrorCode.CLIENT_ERROR_CODE_SERVER_ERROR, 500, { cause: error instanceof Error ? error.message : String(error) }); } this.sessionId = sessionId; return sessionId; }); } startSessionIfNeeded(userProvidedMediaStream) { return __awaiter(this, void 0, void 0, function* () { if (!this.sessionId || !this.streamingClient) { yield this.startSession(userProvidedMediaStream); if (!this.sessionId || !this.streamingClient) { throw new ClientError('Session ID or streaming client is not available after starting session', ErrorCode.CLIENT_ERROR_CODE_SERVER_ERROR, 500, { cause: 'Failed to initialize session properly', }); } } }); } stream(userProvidedAudioStream) { return __awaiter(this, void 0, void 0, function* () { yield this.startSessionIfNeeded(userProvidedAudioStream); if (this._isStreaming) { throw new Error('Already streaming'); } this._isStreaming = true; return new Promise((resolve) => { var _a; // set stream callbacks to capture the stream const streams = []; let videoReceived = false; let audioReceived = false; this.publicEventEmitter.addListener(AnamEvent.VIDEO_STREAM_STARTED, (videoStream) => { streams.push(videoStream); videoReceived = true; if (audioReceived) { resolve(streams); } }); this.publicEventEmitter.addListener(AnamEvent.AUDIO_STREAM_STARTED, (audioStream) => { streams.push(audioStream); audioReceived = true; if (videoReceived) { resolve(streams); } }); // start streaming (_a = this.streamingClient) === null || _a === void 0 ? void 0 : _a.startConnection(); }); }); } streamToVideoAndAudioElements(videoElementId, audioElementId, userProvidedMediaStream) { return __awaiter(this, void 0, void 0, function* () { try { yield this.startSessionIfNeeded(userProvidedMediaStream); } catch (error) { if (error instanceof ClientError) { throw error; } throw new ClientError('Failed to start session', ErrorCode.CLIENT_ERROR_CODE_SERVER_ERROR, 500, { cause: error instanceof Error ? error.message : String(error), }); } if (this._isStreaming) { throw new Error('Already streaming'); } this._isStreaming = true; if (!this.streamingClient) { throw new Error('Failed to stream: streaming client is not available'); } this.streamingClient.setMediaStreamTargetsById(videoElementId, audioElementId); this.streamingClient.startConnection(); }); } talk(content) { return __awaiter(this, void 0, void 0, function* () { if (!this.streamingClient) { throw new Error('Failed to send talk command: session is not started. Have you called startSession?'); } if (!this._isStreaming) { throw new Error('Failed to send talk command: not currently streaming. Have you called stream?'); } yield this.streamingClient.sendTalkCommand(content); return; }); } sendDataMessage(message) { if (this.streamingClient) { this.streamingClient.sendDataMessage(message); } else { throw new Error('Failed to send message: session is not started.'); } } stopStreaming() { return __awaiter(this, void 0, void 0, function* () { if (this.streamingClient) { this.streamingClient.stopConnection(); this.streamingClient = null; this.sessionId = null; this._isStreaming = false; } }); } isStreaming() { return this._isStreaming; } setPersonaConfig(personaConfig) { this.personaConfig = personaConfig; } getPersonaConfig() { return this.personaConfig; } getInputAudioState() { // if streaming client is available, make sure our state is up to date if (this.streamingClient) { this.inputAudioState = this.streamingClient.getInputAudioState(); } return this.inputAudioState; } muteInputAudio() { if (this.streamingClient) { this.inputAudioState = this.streamingClient.muteInputAudio(); } else { this.inputAudioState = Object.assign(Object.assign({}, this.inputAudioState), { isMuted: true }); } return this.inputAudioState; } unmuteInputAudio() { if (this.streamingClient) { this.inputAudioState = this.streamingClient.unmuteInputAudio(); } else { this.inputAudioState = Object.assign(Object.assign({}, this.inputAudioState), { isMuted: false }); } return this.inputAudioState; } createTalkMessageStream(correlationId) { if (!this.streamingClient) { throw new Error('Failed to start talk message stream: session is not started.'); } if (correlationId && correlationId.trim() === '') { throw new Error('Failed to start talk message stream: correlationId is empty'); } return this.streamingClient.startTalkMessageStream(correlationId); } /** * Event handling */ addListener(event, callback) { this.publicEventEmitter.addListener(event, callback); } removeListener(event, callback) { this.publicEventEmitter.removeListener(event, callback); } getActiveSessionId() { return this.sessionId; } } //# sourceMappingURL=AnamClient.js.map