From a36cd9a299e19ebce3653e46ac7f5a077aa78107 Mon Sep 17 00:00:00 2001 From: Daniel Onyejesi Date: Wed, 25 Mar 2026 18:29:48 -0400 Subject: [PATCH] Improve Transcribe error diagnostics, add minimum audio check - Log $response status/headers/body on deserialization errors - Add 10ms delay between audio chunks to prevent stream overload - Skip transcription if audio < 0.5s (too short for recognition) - Cleaner error logging with dedicated logTranscribeError helper --- src/utils/transcribeAWS.js | 74 ++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/src/utils/transcribeAWS.js b/src/utils/transcribeAWS.js index 2bf5ee6..8ce550e 100644 --- a/src/utils/transcribeAWS.js +++ b/src/utils/transcribeAWS.js @@ -61,7 +61,23 @@ function convertToPCM(inputBuffer) { async function* makeAudioStream(buffer) { for (var i = 0; i < buffer.length; i += CHUNK_SIZE) { yield { AudioEvent: { AudioChunk: buffer.slice(i, i + CHUNK_SIZE) } }; - await new Promise(function(r) { setTimeout(r, 0); }); + // Small delay between chunks to prevent overwhelming the stream + await new Promise(function(r) { setTimeout(r, 10); }); + } +} + +function logTranscribeError(label, err) { + console.error('[Transcribe] ' + label + ':', err.message); + if (err.name) console.error('[Transcribe] name:', err.name); + if (err.$metadata) console.error('[Transcribe] metadata:', JSON.stringify(err.$metadata)); + if (err.cause) console.error('[Transcribe] cause:', err.cause.message || JSON.stringify(err.cause)); + if (err.$response) { + try { + var resp = err.$response; + console.error('[Transcribe] status:', resp.statusCode); + if (resp.headers) console.error('[Transcribe] headers:', JSON.stringify(resp.headers)); + if (resp.body) console.error('[Transcribe] body:', typeof resp.body === 'string' ? resp.body.slice(0, 500) : '(stream)'); + } catch (e) { /* ignore logging errors */ } } } @@ -75,8 +91,6 @@ async function transcribeWithAWS(audioBuffer, mimeType) { secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY } : undefined; - var client = new TranscribeStreamingClient({ region: region, credentials: credentials }); - // Try ffmpeg conversion to PCM (most reliable with AWS Transcribe). // Falls back to ogg-opus passthrough if ffmpeg is not installed. var audioToSend = audioBuffer; @@ -90,13 +104,18 @@ async function transcribeWithAWS(audioBuffer, mimeType) { console.log('[Transcribe] ffmpeg conversion OK — PCM 16kHz, ' + audioToSend.length + ' bytes'); } catch (ffErr) { console.warn('[Transcribe] ffmpeg unavailable, sending ogg-opus directly:', ffErr.message); - // For WAV input, try pcm anyway without ffmpeg if (mimeType && mimeType.indexOf('wav') !== -1) { mediaEncoding = 'pcm'; sampleRate = 16000; } } + // Minimum ~0.5s of PCM audio at 16kHz mono 16-bit = 16000 bytes + if (mediaEncoding === 'pcm' && audioToSend.length < 16000) { + console.warn('[Transcribe] Audio too short (' + audioToSend.length + ' bytes), skipping'); + return ''; + } + var useMedical = process.env.AWS_TRANSCRIBE_MEDICAL === 'true'; var specialty = process.env.AWS_TRANSCRIBE_SPECIALTY || 'PRIMARYCARE'; var transcript = ''; @@ -118,10 +137,11 @@ async function transcribeWithAWS(audioBuffer, mimeType) { return text; } + // Try Medical first if enabled, with fallback to Standard if (useMedical) { try { - var StartMedicalStreamTranscriptionCommand = TranscribeModule.StartMedicalStreamTranscriptionCommand; - var medCmd = new StartMedicalStreamTranscriptionCommand({ + var client = new TranscribeStreamingClient({ region: region, credentials: credentials }); + var medCmd = new TranscribeModule.StartMedicalStreamTranscriptionCommand({ LanguageCode: 'en-US', MediaSampleRateHertz: sampleRate, MediaEncoding: mediaEncoding, @@ -131,29 +151,29 @@ async function transcribeWithAWS(audioBuffer, mimeType) { }); var medResp = await client.send(medCmd); transcript = await collectTranscript(medResp.TranscriptResultStream); - console.log('[Transcribe] Medical transcription OK (' + transcript.length + ' chars)'); + console.log('[Transcribe] Medical OK (' + transcript.length + ' chars)'); } catch (medErr) { - console.warn('[Transcribe] Medical failed:', medErr.message); - if (medErr.name) console.warn('[Transcribe] Error name:', medErr.name); - if (medErr.$metadata) console.warn('[Transcribe] Metadata:', JSON.stringify(medErr.$metadata)); - if (medErr.cause) console.warn('[Transcribe] Cause:', medErr.cause.message || medErr.cause); - console.warn('[Transcribe] Falling back to standard Transcribe...'); - // Fallback to standard Transcribe - var client2 = new TranscribeStreamingClient({ region: region, credentials: credentials }); - var StartStreamTranscriptionCommand = TranscribeModule.StartStreamTranscriptionCommand; - var cmd = new StartStreamTranscriptionCommand({ - LanguageCode: 'en-US', - MediaSampleRateHertz: sampleRate, - MediaEncoding: mediaEncoding, - AudioStream: makeAudioStream(audioToSend) - }); - var resp = await client2.send(cmd); - transcript = await collectTranscript(resp.TranscriptResultStream); - console.log('[Transcribe] Standard fallback OK (' + transcript.length + ' chars)'); + logTranscribeError('Medical failed', medErr); + console.warn('[Transcribe] Falling back to standard...'); + try { + var client2 = new TranscribeStreamingClient({ region: region, credentials: credentials }); + var stdCmd = new TranscribeModule.StartStreamTranscriptionCommand({ + LanguageCode: 'en-US', + MediaSampleRateHertz: sampleRate, + MediaEncoding: mediaEncoding, + AudioStream: makeAudioStream(audioToSend) + }); + var stdResp = await client2.send(stdCmd); + transcript = await collectTranscript(stdResp.TranscriptResultStream); + console.log('[Transcribe] Standard fallback OK (' + transcript.length + ' chars)'); + } catch (stdErr) { + logTranscribeError('Standard also failed', stdErr); + throw stdErr; + } } } else { - var StartStreamTranscriptionCommand = TranscribeModule.StartStreamTranscriptionCommand; - var cmd = new StartStreamTranscriptionCommand({ + var client = new TranscribeStreamingClient({ region: region, credentials: credentials }); + var cmd = new TranscribeModule.StartStreamTranscriptionCommand({ LanguageCode: 'en-US', MediaSampleRateHertz: sampleRate, MediaEncoding: mediaEncoding, @@ -161,7 +181,7 @@ async function transcribeWithAWS(audioBuffer, mimeType) { }); var resp = await client.send(cmd); transcript = await collectTranscript(resp.TranscriptResultStream); - console.log('[Transcribe] Standard transcription OK (' + transcript.length + ' chars)'); + console.log('[Transcribe] Standard OK (' + transcript.length + ' chars)'); } return transcript.trim();