UNPKG

@volley/recognition-client-sdk

Version:

Recognition Service TypeScript/Node.js Client SDK

131 lines (113 loc) 4.13 kB
/** * Message Handler for Recognition Client * Routes incoming WebSocket messages to appropriate callbacks */ import { RecognitionResultTypeV1, ClientControlActionV1, type TranscriptionResultV1, type FunctionCallResultV1, type MetadataResultV1, type ErrorResultV1, type ClientControlMessageV1 } from '@recog/shared-types'; export interface MessageHandlerCallbacks { onTranscript: (result: TranscriptionResultV1) => void; onFunctionCall: (result: FunctionCallResultV1) => void; onMetadata: (metadata: MetadataResultV1) => void; onError: (error: ErrorResultV1) => void; onControlMessage: (msg: ClientControlMessageV1) => void; logger?: (level: 'debug' | 'info' | 'warn' | 'error', message: string, data?: any) => void; } export class MessageHandler { private firstTranscriptTime: number | null = null; private sessionStartTime: number | null = null; private callbacks: MessageHandlerCallbacks; constructor(callbacks: MessageHandlerCallbacks) { this.callbacks = callbacks; } /** * Set session start time for performance tracking */ setSessionStartTime(time: number): void { this.sessionStartTime = time; } /** * Handle incoming WebSocket message */ handleMessage(msg: { v: number; type: string; data: any }): void { // Log ALL incoming messages for debugging if (this.callbacks.logger) { this.callbacks.logger('debug', 'Received WebSocket message', { msgType: msg.type, msgDataType: msg.data && typeof msg.data === 'object' && 'type' in msg.data ? msg.data.type : 'N/A', fullMessage: msg }); } // Safely check for type in msg.data - guard against primitives // Log error if we receive primitive data (indicates server issue) if (msg.data && typeof msg.data !== 'object') { if (this.callbacks.logger) { this.callbacks.logger('error', 'Received primitive msg.data from server', { dataType: typeof msg.data, data: msg.data, fullMessage: msg }); } } const msgType = (msg.data && typeof msg.data === 'object' && 'type' in msg.data ? msg.data.type : undefined) || msg.type; const msgData = msg.data || msg; switch (msgType) { case RecognitionResultTypeV1.TRANSCRIPTION: this.handleTranscription(msgData as TranscriptionResultV1); break; case RecognitionResultTypeV1.FUNCTION_CALL: this.callbacks.onFunctionCall(msgData as FunctionCallResultV1); break; case RecognitionResultTypeV1.METADATA: this.callbacks.onMetadata(msgData as MetadataResultV1); break; case RecognitionResultTypeV1.ERROR: this.callbacks.onError(msgData as ErrorResultV1); break; case RecognitionResultTypeV1.CLIENT_CONTROL_MESSAGE: this.callbacks.onControlMessage(msgData as ClientControlMessageV1); break; default: // Unknown message type - log if logger available if (this.callbacks.logger) { this.callbacks.logger('debug', 'Unknown message type', { type: msgType }); } } } /** * Handle transcript message and track performance metrics * @param result - The transcription result from the server */ private handleTranscription(result: TranscriptionResultV1): void { // Track time to first transcript if (!this.firstTranscriptTime && this.sessionStartTime) { this.firstTranscriptTime = Date.now(); const timeToFirstTranscript = this.firstTranscriptTime - this.sessionStartTime; if (this.callbacks.logger) { this.callbacks.logger('debug', 'First transcript received', { timeToFirstTranscriptMs: timeToFirstTranscript }); } } this.callbacks.onTranscript(result); } /** * Get performance metrics */ getMetrics() { return { sessionStartTime: this.sessionStartTime, firstTranscriptTime: this.firstTranscriptTime, timeToFirstTranscript: this.firstTranscriptTime && this.sessionStartTime ? this.firstTranscriptTime - this.sessionStartTime : null }; } }