UNPKG

@anam-ai/js-sdk

Version:

Client side JavaScript SDK for Anam AI

287 lines 14.1 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createRTCStatsReport = exports.sendClientMetric = exports.setMetricsContext = exports.setClientMetricsBaseUrl = exports.ClientMetricMeasurement = exports.DEFAULT_ANAM_API_VERSION = exports.DEFAULT_ANAM_METRICS_BASE_URL = void 0; const constants_1 = require("./constants"); exports.DEFAULT_ANAM_METRICS_BASE_URL = 'https://api.anam.ai'; exports.DEFAULT_ANAM_API_VERSION = '/v1'; var ClientMetricMeasurement; (function (ClientMetricMeasurement) { ClientMetricMeasurement["CLIENT_METRIC_MEASUREMENT_ERROR"] = "client_error"; ClientMetricMeasurement["CLIENT_METRIC_MEASUREMENT_CONNECTION_CLOSED"] = "client_connection_closed"; ClientMetricMeasurement["CLIENT_METRIC_MEASUREMENT_CONNECTION_ESTABLISHED"] = "client_connection_established"; ClientMetricMeasurement["CLIENT_METRIC_MEASUREMENT_SESSION_ATTEMPT"] = "client_session_attempt"; ClientMetricMeasurement["CLIENT_METRIC_MEASUREMENT_SESSION_SUCCESS"] = "client_session_success"; })(ClientMetricMeasurement || (exports.ClientMetricMeasurement = ClientMetricMeasurement = {})); let anamCurrentBaseUrl = exports.DEFAULT_ANAM_METRICS_BASE_URL; let anamCurrentApiVersion = exports.DEFAULT_ANAM_API_VERSION; const setClientMetricsBaseUrl = (baseUrl, apiVersion = exports.DEFAULT_ANAM_API_VERSION) => { anamCurrentBaseUrl = baseUrl; anamCurrentApiVersion = apiVersion; }; exports.setClientMetricsBaseUrl = setClientMetricsBaseUrl; let anamMetricsContext = { sessionId: null, organizationId: null, attemptCorrelationId: null, }; const setMetricsContext = (context) => { anamMetricsContext = Object.assign(Object.assign({}, anamMetricsContext), context); }; exports.setMetricsContext = setMetricsContext; const sendClientMetric = (name, value, tags) => __awaiter(void 0, void 0, void 0, function* () { try { const metricTags = Object.assign(Object.assign({}, constants_1.CLIENT_METADATA), tags); // Add session and organization IDs if available if (anamMetricsContext.sessionId) { metricTags.sessionId = anamMetricsContext.sessionId; } if (anamMetricsContext.organizationId) { metricTags.organizationId = anamMetricsContext.organizationId; } if (anamMetricsContext.attemptCorrelationId) { metricTags.attemptCorrelationId = anamMetricsContext.attemptCorrelationId; } yield fetch(`${anamCurrentBaseUrl}${anamCurrentApiVersion}/metrics/client`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ name, value, tags: metricTags, }), }); } catch (error) { console.error('Failed to send error metric:', error); } }); exports.sendClientMetric = sendClientMetric; const createRTCStatsReport = (stats, outputFormat = 'console') => { /** * constructs a report of the RTC stats for logging to the console or returns as JSON */ var _a, _b, _c; // Collect stats by type for organized reporting const statsByType = {}; stats.forEach((report) => { if (!statsByType[report.type]) { statsByType[report.type] = []; } statsByType[report.type].push(report); }); // Initialize JSON report structure const jsonReport = { issues: [], }; // Build video statistics (Persona video output) const inboundVideo = ((_a = statsByType['inbound-rtp']) === null || _a === void 0 ? void 0 : _a.filter((r) => r.kind === 'video')) || []; if (inboundVideo.length > 0) { jsonReport.personaVideoStream = []; inboundVideo.forEach((report) => { var _a, _b, _c, _d, _e; const videoData = { framesReceived: (_a = report.framesReceived) !== null && _a !== void 0 ? _a : 'unknown', framesDropped: (_b = report.framesDropped) !== null && _b !== void 0 ? _b : 'unknown', framesPerSecond: (_c = report.framesPerSecond) !== null && _c !== void 0 ? _c : 'unknown', packetsReceived: (_d = report.packetsReceived) !== null && _d !== void 0 ? _d : 'unknown', packetsLost: (_e = report.packetsLost) !== null && _e !== void 0 ? _e : 'unknown', resolution: report.frameWidth && report.frameHeight ? `${report.frameWidth}x${report.frameHeight}` : undefined, jitter: report.jitter !== undefined ? report.jitter : undefined, }; jsonReport.personaVideoStream.push(videoData); }); } // Build audio statistics (Persona audio output) const inboundAudio = ((_b = statsByType['inbound-rtp']) === null || _b === void 0 ? void 0 : _b.filter((r) => r.kind === 'audio')) || []; if (inboundAudio.length > 0) { jsonReport.personaAudioStream = []; inboundAudio.forEach((report) => { var _a, _b, _c; const audioData = { packetsReceived: (_a = report.packetsReceived) !== null && _a !== void 0 ? _a : 'unknown', packetsLost: (_b = report.packetsLost) !== null && _b !== void 0 ? _b : 'unknown', audioLevel: (_c = report.audioLevel) !== null && _c !== void 0 ? _c : 'unknown', jitter: report.jitter !== undefined ? report.jitter : undefined, totalAudioEnergy: report.totalAudioEnergy !== undefined ? report.totalAudioEnergy : undefined, }; jsonReport.personaAudioStream.push(audioData); }); } // Build user audio input statistics const outboundAudio = ((_c = statsByType['outbound-rtp']) === null || _c === void 0 ? void 0 : _c.filter((r) => r.kind === 'audio')) || []; if (outboundAudio.length > 0) { jsonReport.userAudioInput = []; outboundAudio.forEach((report) => { var _a, _b; const userAudioData = { packetsSent: (_a = report.packetsSent) !== null && _a !== void 0 ? _a : 'unknown', retransmittedPackets: (_b = report.retransmittedPacketsSent) !== null && _b !== void 0 ? _b : undefined, avgPacketSendDelay: report.totalPacketSendDelay !== undefined ? (report.totalPacketSendDelay / (report.packetsSent || 1)) * 1000 : undefined, }; jsonReport.userAudioInput.push(userAudioData); }); } // Build codec information if (statsByType['codec']) { jsonReport.codecs = []; statsByType['codec'].forEach((report) => { const codecData = { status: report.payloadType ? 'Active' : 'Available', mimeType: report.mimeType || 'Unknown', payloadType: report.payloadType || 'N/A', clockRate: report.clockRate || undefined, channels: report.channels || undefined, }; jsonReport.codecs.push(codecData); }); } // Build transport layer information if (statsByType['transport']) { jsonReport.transportLayer = []; statsByType['transport'].forEach((report) => { const transportData = { dtlsState: report.dtlsState || 'unknown', iceState: report.iceState || 'unknown', bytesSent: report.bytesSent || undefined, bytesReceived: report.bytesReceived || undefined, }; jsonReport.transportLayer.push(transportData); }); } // Build issues summary const issues = []; // Check for video issues inboundVideo.forEach((report) => { if (typeof report.framesDropped === 'number' && report.framesDropped > 0) { issues.push(`Video: ${report.framesDropped} frames dropped`); } if (typeof report.packetsLost === 'number' && report.packetsLost > 0) { issues.push(`Video: ${report.packetsLost} packets lost`); } if (typeof report.framesPerSecond === 'number' && report.framesPerSecond < 23) { issues.push(`Video: Low frame rate (${report.framesPerSecond} fps)`); } }); // Check for audio issues inboundAudio.forEach((report) => { if (typeof report.packetsLost === 'number' && report.packetsLost > 0) { issues.push(`Audio: ${report.packetsLost} packets lost`); } if (typeof report.jitter === 'number' && report.jitter > 0.1) { issues.push(`Audio: High jitter (${(report.jitter * 1000).toFixed(1)}ms)`); } }); jsonReport.issues = issues; // Return JSON if requested if (outputFormat === 'json') { return jsonReport; } // Generate console output from JSON report console.group('📊 WebRTC Session Statistics Report'); // Console output for video stream if (jsonReport.personaVideoStream && jsonReport.personaVideoStream.length > 0) { console.group('📹 Persona Video Stream (Inbound)'); jsonReport.personaVideoStream.forEach((videoData) => { console.log(`Frames Received: ${videoData.framesReceived}`); console.log(`Frames Dropped: ${videoData.framesDropped}`); console.log(`Frames Per Second: ${videoData.framesPerSecond}`); console.log(`Packets Received: ${typeof videoData.packetsReceived === 'number' ? videoData.packetsReceived.toLocaleString() : videoData.packetsReceived}`); console.log(`Packets Lost: ${videoData.packetsLost}`); if (videoData.resolution) { console.log(`Resolution: ${videoData.resolution}`); } if (videoData.jitter !== undefined) { console.log(`Jitter: ${videoData.jitter.toFixed(5)}ms`); } }); console.groupEnd(); } // Console output for audio stream if (jsonReport.personaAudioStream && jsonReport.personaAudioStream.length > 0) { console.group('🔊 Persona Audio Stream (Inbound)'); jsonReport.personaAudioStream.forEach((audioData) => { console.log(`Packets Received: ${typeof audioData.packetsReceived === 'number' ? audioData.packetsReceived.toLocaleString() : audioData.packetsReceived}`); console.log(`Packets Lost: ${audioData.packetsLost}`); console.log(`Audio Level: ${audioData.audioLevel}`); if (audioData.jitter !== undefined) { console.log(`Jitter: ${audioData.jitter.toFixed(5)}ms`); } if (audioData.totalAudioEnergy !== undefined) { console.log(`Total Audio Energy: ${audioData.totalAudioEnergy.toFixed(6)}`); } }); console.groupEnd(); } // Console output for user audio input if (jsonReport.userAudioInput && jsonReport.userAudioInput.length > 0) { console.group('🎤 User Audio Input (Outbound)'); jsonReport.userAudioInput.forEach((userAudioData) => { console.log(`Packets Sent: ${typeof userAudioData.packetsSent === 'number' ? userAudioData.packetsSent.toLocaleString() : userAudioData.packetsSent}`); if (userAudioData.retransmittedPackets) { console.log(`Retransmitted Packets: ${userAudioData.retransmittedPackets}`); } if (userAudioData.avgPacketSendDelay !== undefined) { console.log(`Avg Packet Send Delay: ${userAudioData.avgPacketSendDelay.toFixed(5)}ms`); } }); console.groupEnd(); } // Console output for codecs if (jsonReport.codecs && jsonReport.codecs.length > 0) { console.group('🔧 Codecs Used'); jsonReport.codecs.forEach((codecData) => { console.log(`${codecData.status} ${codecData.mimeType} - Payload Type: ${codecData.payloadType}`); if (codecData.clockRate) { console.log(` Clock Rate: ${codecData.clockRate}Hz`); } if (codecData.channels) { console.log(` Channels: ${codecData.channels}`); } }); console.groupEnd(); } // Console output for transport layer if (jsonReport.transportLayer && jsonReport.transportLayer.length > 0) { console.group('🚚 Transport Layer'); jsonReport.transportLayer.forEach((transportData) => { console.log(`DTLS State: ${transportData.dtlsState}`); console.log(`ICE State: ${transportData.iceState}`); if (transportData.bytesReceived || transportData.bytesSent) { console.log(`Data Transfer (bytes) - Sent: ${(transportData.bytesSent || 0).toLocaleString()}, Received: ${(transportData.bytesReceived || 0).toLocaleString()}`); } }); console.groupEnd(); } // Console output for issues if (jsonReport.issues.length > 0) { console.group('⚠️ Potential Issues Detected'); jsonReport.issues.forEach((issue) => console.warn(issue)); console.groupEnd(); } else { console.log('✅ No significant issues detected'); } console.groupEnd(); }; exports.createRTCStatsReport = createRTCStatsReport; //# sourceMappingURL=ClientMetrics.js.map