import LoggerProxy from '../logger-proxy'; import MetricsManager from '../metrics/MetricsManager'; import {METRIC_EVENT_NAMES} from '../metrics/constants'; import {CC_FILE, METHODS} from '../constants'; import { HTTP_METHODS, WebexSDK, IHttpResponse, TranscriptAction, AIAssistantEventType, AIAssistantEventName, HistoricTranscriptsResponse, } from '../types'; import {getErrorDetails} from './core/Utils'; import { AI_ASSISTANT_BASE_URL_TEMPLATE, AI_ASSISTANT_ENV_MAP, AI_ASSISTANT_API_URLS, WCC_API_GATEWAY, } from './constants'; import {AIFeatureFlags} from './config/types'; /** * ApiAIAssistant provides AI Assistant APIs for transcript controls. * @public */ export class ApiAIAssistant { private webex: WebexSDK; private metricsManager: MetricsManager; public aiFeature: AIFeatureFlags; constructor(webex: WebexSDK) { this.webex = webex; this.metricsManager = MetricsManager.getInstance({webex}); } public setAIFeatureFlags(aiFeature: AIFeatureFlags): void { this.aiFeature = aiFeature; } private getBaseUrl(): string { const wccApiGatewayUrl = this.webex.internal.services.get(WCC_API_GATEWAY) || ''; if (!wccApiGatewayUrl) { const {error: detailedError} = getErrorDetails( new Error('AI_ASSISTANT_BASE_URL_NOT_AVAILABLE'), METHODS.GET_BASE_URL, CC_FILE ); throw detailedError; } let hostname = ''; try { hostname = new URL(wccApiGatewayUrl).hostname.toLowerCase(); } catch (error) { hostname = wccApiGatewayUrl.toLowerCase(); } const resolvedEnv = AI_ASSISTANT_ENV_MAP[hostname]; if (!resolvedEnv) { const {error: detailedError} = getErrorDetails( new Error('AI_ASSISTANT_BASE_URL_NOT_AVAILABLE'), METHODS.GET_BASE_URL, CC_FILE ); throw detailedError; } return AI_ASSISTANT_BASE_URL_TEMPLATE.replace('%s', resolvedEnv); } /** * Sends an event to the AI Assistant service. * @param agentId - agent identifier * @param interactionId - interaction/conversation identifier * @param eventType - the type of event (e.g. 'CUSTOM_EVENT') * @param eventName - the name of the event (e.g. 'GET_TRANSCRIPTS') * @param action - action within eventDetails (e.g. 'START' or 'STOP') */ public async sendEvent( agentId: string, interactionId: string, eventType: AIAssistantEventType, eventName: AIAssistantEventName, action: TranscriptAction ): Promise> { LoggerProxy.info('Sending event', { module: CC_FILE, method: METHODS.SEND_EVENT, interactionId, data: {eventType, eventName, action}, }); this.metricsManager.timeEvent([ METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_SUCCESS, METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_FAILED, ]); try { const baseUrl = this.getBaseUrl(); const orgId = this.webex.credentials.getOrgId(); const response = (await this.webex.request({ uri: `${baseUrl}${AI_ASSISTANT_API_URLS.EVENT}`, method: HTTP_METHODS.POST, addAuthHeader: true, body: { agentId, orgId, eventType, eventName, eventDetails: { data: { interactionId, action, actionTimeStamp: String(Date.now()), }, }, }, })) as IHttpResponse; this.metricsManager.trackEvent( METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_SUCCESS, {agentId, orgId, interactionId, eventType, eventName, action}, ['operational'] ); return response?.body || {}; } catch (error) { this.metricsManager.trackEvent( METRIC_EVENT_NAMES.AI_ASSISTANT_SEND_EVENT_FAILED, { interactionId, eventType, eventName, action, error: error instanceof Error ? error.message : String(error), }, ['operational'] ); const {error: detailedError} = getErrorDetails(error, METHODS.SEND_EVENT, CC_FILE); throw detailedError; } } /** * Fetches historic transcripts for an interaction. * This API is allowed only when real-time transcription feature is enabled. * * @param interactionId - interaction/conversation identifier */ public async fetchHistoricTranscripts( agentId: string, interactionId: string ): Promise { LoggerProxy.info('Fetching historic transcripts', { module: CC_FILE, method: METHODS.FETCH_HISTORIC_TRANSCRIPTS, interactionId, }); this.metricsManager.timeEvent([ METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS, METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED, ]); if (!this.aiFeature?.realtimeTranscripts?.enable) { const {error: detailedError} = getErrorDetails( new Error('REAL_TIME_TRANSCRIPTION_NOT_ENABLED'), METHODS.FETCH_HISTORIC_TRANSCRIPTS, CC_FILE ); throw detailedError; } try { const baseUrl = this.getBaseUrl(); const orgId = this.webex.credentials.getOrgId(); const response = (await this.webex.request({ uri: `${baseUrl}${AI_ASSISTANT_API_URLS.TRANSCRIPTS_LIST}`, method: HTTP_METHODS.POST, addAuthHeader: true, body: { agentId, orgId, interactionId, }, })) as IHttpResponse; this.metricsManager.trackEvent( METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_SUCCESS, {agentId, orgId, interactionId}, ['operational'] ); return response.body as HistoricTranscriptsResponse; } catch (error) { this.metricsManager.trackEvent( METRIC_EVENT_NAMES.AI_ASSISTANT_FETCH_HISTORIC_TRANSCRIPTS_FAILED, { interactionId, error: error instanceof Error ? error.message : String(error), }, ['operational'] ); if (error instanceof Error) { throw error; } const {error: detailedError} = getErrorDetails( error, METHODS.FETCH_HISTORIC_TRANSCRIPTS, CC_FILE ); throw detailedError; } } } export default ApiAIAssistant;