From 96b3b0fe07720be172235c6585beff789d274508 Mon Sep 17 00:00:00 2001 From: Hoan Luu Huu <110280845+xquanluu@users.noreply.github.com> Date: Wed, 2 Oct 2024 00:40:41 +0700 Subject: [PATCH] Allow Say, Gather, Transcribe is able to finished if there is error for speech credential (#910) * allow move to next task if say verb is failed because of speech credential * allow move to next task if say verb is failed because of speech credential * allow move to next task if say verb is failed because of speech credential * wip * wip --- lib/tasks/gather.js | 16 +++++++++++++++- lib/tasks/say.js | 23 ++++++++++++++++++++--- lib/tasks/stt-task.js | 3 ++- lib/tasks/transcribe.js | 16 +++++++++++++++- lib/tasks/tts-task.js | 6 ++++-- lib/utils/error.js | 9 +++++++++ 6 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 lib/utils/error.js diff --git a/lib/tasks/gather.js b/lib/tasks/gather.js index ee219803..dcab1abc 100644 --- a/lib/tasks/gather.js +++ b/lib/tasks/gather.js @@ -22,6 +22,7 @@ const { const makeTask = require('./make_task'); const assert = require('assert'); const SttTask = require('./stt-task'); +const { SpeechCredentialError } = require('../utils/error'); class TaskGather extends SttTask { constructor(logger, opts, parentTask) { @@ -122,7 +123,20 @@ class TaskGather extends SttTask { return s; } - async exec(cs, {ep}) { + async exec(cs, obj) { + try { + await this.handling(cs, obj); + } catch (error) { + if (error instanceof SpeechCredentialError) { + this.logger.info('Gather failed due to SpeechCredentialError, finished!'); + this.notifyTaskDone(); + return; + } + throw error; + } + } + + async handling(cs, {ep}) { this.logger.debug({options: this.data}, 'Gather:exec'); await super.exec(cs, {ep}); const {updateSpeechCredentialLastUsed} = require('../utils/db-utils')(this.logger, cs.srf); diff --git a/lib/tasks/say.js b/lib/tasks/say.js index f80a1062..241a3f93 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -1,6 +1,7 @@ const TtsTask = require('./tts-task'); const {TaskName, TaskPreconditions} = require('../utils/constants'); const pollySSMLSplit = require('polly-ssml-split'); +const { SpeechCredentialError } = require('../utils/error'); const breakLengthyTextIfNeeded = (logger, text) => { const chunkSize = 1000; @@ -61,7 +62,23 @@ class TaskSay extends TtsTask { } } - async exec(cs, {ep}) { + async exec(cs, obj) { + try { + await this.handling(cs, obj); + this.emit('playDone'); + } catch (error) { + if (error instanceof SpeechCredentialError) { + // if say failed due to speech credentials, alarm is writtern and error notification is sent + // finished this say to move to next task. + this.logger.info('Say failed due to SpeechCredentialError, finished!'); + this.emit('playDone'); + return; + } + throw error; + } + } + + async handling(cs, {ep}) { const {srf, accountSid:account_sid, callSid:target_sid} = cs; const {writeAlerts, AlertType} = srf.locals; const {addFileToCache} = srf.locals.dbHelpers; @@ -117,7 +134,7 @@ class TaskSay extends TtsTask { } else { this.notifyError( { msg: 'TTS error', details:`TTS vendor ${vendor} error: ${error}`, failover: 'not available'}); - throw error; + throw new SpeechCredentialError(error.message); } }; let filepath; @@ -206,6 +223,7 @@ class TaskSay extends TtsTask { continue; } catch (err) { this.logger.info({err}, 'Error waiting for playback-stop event'); + throw err; } } finally { this._playPromise = null; @@ -223,7 +241,6 @@ class TaskSay extends TtsTask { segment++; } } - this.emit('playDone'); } async kill(cs) { diff --git a/lib/tasks/stt-task.js b/lib/tasks/stt-task.js index 273bcc98..8ba70bdf 100644 --- a/lib/tasks/stt-task.js +++ b/lib/tasks/stt-task.js @@ -2,6 +2,7 @@ const Task = require('./task'); const assert = require('assert'); const crypto = require('crypto'); const { TaskPreconditions, CobaltTranscriptionEvents } = require('../utils/constants'); +const { SpeechCredentialError } = require('../utils/error'); class SttTask extends Task { @@ -190,7 +191,7 @@ class SttTask extends Task { target_sid: cs.callSid }).catch((err) => this.logger.info({err}, 'Error generating alert for no stt')); // the ASR might have fallback configuration, should not done task here. - throw new Error(`No speech-to-text service credentials for ${vendor} have been configured`); + throw new SpeechCredentialError(`No speech-to-text service credentials for ${vendor} have been configured`); } if (vendor === 'nuance' && credentials.client_id) { diff --git a/lib/tasks/transcribe.js b/lib/tasks/transcribe.js index f86b403b..51d97881 100644 --- a/lib/tasks/transcribe.js +++ b/lib/tasks/transcribe.js @@ -16,6 +16,7 @@ const { } = require('../utils/constants.json'); const { normalizeJambones } = require('@jambonz/verb-specifications'); const SttTask = require('./stt-task'); +const { SpeechCredentialError } = require('../utils/error'); const STT_LISTEN_SPAN_NAME = 'stt-listen'; @@ -73,7 +74,20 @@ class TaskTranscribe extends SttTask { return this.channel === 2 || this.separateRecognitionPerChannel && this.ep2; } - async exec(cs, {ep, ep2}) { + async exec(cs, obj) { + try { + await this.handling(cs, obj); + } catch (error) { + if (error instanceof SpeechCredentialError) { + this.logger.info('Transcribe failed due to SpeechCredentialError, finished!'); + this.notifyTaskDone(); + return; + } + throw error; + } + } + + async handling(cs, {ep, ep2}) { await super.exec(cs, {ep, ep2}); if (this.data.recognizer.vendor === 'nuance') { diff --git a/lib/tasks/tts-task.js b/lib/tasks/tts-task.js index be085841..a8b5d517 100644 --- a/lib/tasks/tts-task.js +++ b/lib/tasks/tts-task.js @@ -1,5 +1,6 @@ const Task = require('./task'); const { TaskPreconditions } = require('../utils/constants'); +const { SpeechCredentialError } = require('../utils/error'); class TtsTask extends Task { @@ -52,7 +53,8 @@ class TtsTask extends Task { let credentials = cs.getSpeechCredentials(vendor, 'tts', label); if (!credentials) { - throw new Error(`No text-to-speech service credentials for ${vendor} with labels: ${label} have been configured`); + throw new SpeechCredentialError( + `No text-to-speech service credentials for ${vendor} with labels: ${label} have been configured`); } /* parse Nuance voices into name and model */ let model; @@ -124,7 +126,7 @@ class TtsTask extends Task { vendor, target_sid: cs.callSid }).catch((err) => this.logger.info({err}, 'Error generating alert for no tts')); - throw new Error('no provisioned speech credentials for TTS'); + throw new SpeechCredentialError('no provisioned speech credentials for TTS'); } // synthesize all of the text elements let lastUpdated = false; diff --git a/lib/utils/error.js b/lib/utils/error.js new file mode 100644 index 00000000..7b47cf17 --- /dev/null +++ b/lib/utils/error.js @@ -0,0 +1,9 @@ +class SpeechCredentialError extends Error { + constructor(msg) { + super(msg); + } +} + +module.exports = { + SpeechCredentialError +};