mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 08:40:38 +00:00
feat: update speech-ultil version 1.0.1 (#275)
* feat: update speech-ultil version 1.0.1 * feat: update speech-ultil version 1.0.1 * more fixes for custom stt * more fixes * fixes * update drachtio-fsmrf * pass url to mod_jambonz_transcribe * transcription utils: handle custom results * handle custom speech vendor errors * add support for hints to custom speech * change to custom speech options * send hints as an array for custom speech * update latest speech-utils * transcribe: changes to support soniox * bugfix: soniox transcribe --------- Co-authored-by: Quan HL <quanluuhoang8@gmail.com> Co-authored-by: Dave Horton <daveh@beachdognet.com>
This commit is contained in:
@@ -660,6 +660,14 @@ class CallSession extends Emitter {
|
|||||||
stt_region: credential.stt_region
|
stt_region: credential.stt_region
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (vendor.startsWith('custom:')) {
|
||||||
|
return {
|
||||||
|
speech_credential_sid: credential.speech_credential_sid,
|
||||||
|
auth_token: credential.auth_token,
|
||||||
|
custom_stt_url: credential.custom_stt_url,
|
||||||
|
custom_tts_url: credential.custom_tts_url
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
writeAlerts({
|
writeAlerts({
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ const {
|
|||||||
DeepgramTranscriptionEvents,
|
DeepgramTranscriptionEvents,
|
||||||
SonioxTranscriptionEvents,
|
SonioxTranscriptionEvents,
|
||||||
IbmTranscriptionEvents,
|
IbmTranscriptionEvents,
|
||||||
NvidiaTranscriptionEvents
|
NvidiaTranscriptionEvents,
|
||||||
|
JambonzTranscriptionEvents
|
||||||
} = require('../utils/constants');
|
} = require('../utils/constants');
|
||||||
|
|
||||||
const makeTask = require('./make_task');
|
const makeTask = require('./make_task');
|
||||||
@@ -379,8 +380,6 @@ class TaskGather extends Task {
|
|||||||
this._onTranscriptionComplete.bind(this, cs, ep));
|
this._onTranscriptionComplete.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.VadDetected,
|
ep.addCustomEventListener(NuanceTranscriptionEvents.VadDetected,
|
||||||
this._onVadDetected.bind(this, cs, ep));
|
this._onVadDetected.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.Error,
|
|
||||||
this._onNuanceError.bind(this, cs, ep));
|
|
||||||
|
|
||||||
/* stall timers until prompt finishes playing */
|
/* stall timers until prompt finishes playing */
|
||||||
if ((this.sayTask || this.playTask) && this.listenDuringPrompt) {
|
if ((this.sayTask || this.playTask) && this.listenDuringPrompt) {
|
||||||
@@ -399,8 +398,6 @@ class TaskGather extends Task {
|
|||||||
case 'soniox':
|
case 'soniox':
|
||||||
this.bugname = 'soniox_transcribe';
|
this.bugname = 'soniox_transcribe';
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Error,
|
|
||||||
this._onSonioxError.bind(this, cs, ep));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'ibm':
|
case 'ibm':
|
||||||
@@ -409,8 +406,6 @@ class TaskGather extends Task {
|
|||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Connect, this._onIbmConnect.bind(this, cs, ep));
|
ep.addCustomEventListener(IbmTranscriptionEvents.Connect, this._onIbmConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
||||||
this._onIbmConnectFailure.bind(this, cs, ep));
|
this._onIbmConnectFailure.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Error,
|
|
||||||
this._onIbmError.bind(this, cs, ep));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'nvidia':
|
case 'nvidia':
|
||||||
@@ -423,8 +418,6 @@ class TaskGather extends Task {
|
|||||||
this._onTranscriptionComplete.bind(this, cs, ep));
|
this._onTranscriptionComplete.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.VadDetected,
|
ep.addCustomEventListener(NvidiaTranscriptionEvents.VadDetected,
|
||||||
this._onVadDetected.bind(this, cs, ep));
|
this._onVadDetected.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.Error,
|
|
||||||
this._onNvidiaError.bind(this, cs, ep));
|
|
||||||
|
|
||||||
/* I think nvidia has this (??) - stall timers until prompt finishes playing */
|
/* I think nvidia has this (??) - stall timers until prompt finishes playing */
|
||||||
if ((this.sayTask || this.playTask) && this.listenDuringPrompt) {
|
if ((this.sayTask || this.playTask) && this.listenDuringPrompt) {
|
||||||
@@ -433,11 +426,23 @@ class TaskGather extends Task {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if (this.vendor.startsWith('custom:')) {
|
||||||
|
this.bugname = `${this.vendor}_transcribe`;
|
||||||
|
ep.addCustomEventListener(JambonzTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
|
ep.addCustomEventListener(JambonzTranscriptionEvents.Connect, this._onJambonzConnect.bind(this, cs, ep));
|
||||||
|
ep.addCustomEventListener(JambonzTranscriptionEvents.ConnectFailure,
|
||||||
|
this._onJambonzConnectFailure.bind(this, cs, ep));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
this.notifyError({ msg: 'ASR error', details:`Invalid vendor ${this.vendor}`});
|
this.notifyError({ msg: 'ASR error', details:`Invalid vendor ${this.vendor}`});
|
||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
throw new Error(`Invalid vendor ${this.vendor}`);
|
throw new Error(`Invalid vendor ${this.vendor}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* common handler for all stt engine errors */
|
||||||
|
ep.addCustomEventListener(JambonzTranscriptionEvents.Error, this._onJambonzError.bind(this, cs, ep));
|
||||||
await ep.set(opts)
|
await ep.set(opts)
|
||||||
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
|
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
|
||||||
}
|
}
|
||||||
@@ -662,26 +667,30 @@ class TaskGather extends Task {
|
|||||||
_onTranscriptionComplete(cs, ep) {
|
_onTranscriptionComplete(cs, ep) {
|
||||||
this.logger.debug('TaskGather:_onTranscriptionComplete');
|
this.logger.debug('TaskGather:_onTranscriptionComplete');
|
||||||
}
|
}
|
||||||
_onNuanceError(cs, ep, evt) {
|
|
||||||
const {code, error, details} = evt;
|
|
||||||
if (code === 404 && error === 'No speech') {
|
|
||||||
this.logger.debug({code, error, details}, 'TaskGather:_onNuanceError');
|
|
||||||
return this._resolve('timeout');
|
|
||||||
}
|
|
||||||
this.logger.info({code, error, details}, 'TaskGather:_onNuanceError');
|
|
||||||
if (code === 413 && error === 'Too much speech') {
|
|
||||||
return this._resolve('timeout');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_onSonioxError(cs, ep, evt) {
|
|
||||||
this.logger.info({evt}, 'TaskGather:_onSonioxError');
|
|
||||||
}
|
|
||||||
_onNvidiaError(cs, ep, evt) {
|
|
||||||
this.logger.info({evt}, 'TaskGather:_onNvidiaError');
|
|
||||||
}
|
|
||||||
_onDeepgramConnect(_cs, _ep) {
|
_onDeepgramConnect(_cs, _ep) {
|
||||||
this.logger.debug('TaskGather:_onDeepgramConnect');
|
this.logger.debug('TaskGather:_onDeepgramConnect');
|
||||||
}
|
}
|
||||||
|
_onJambonzConnect(_cs, _ep) {
|
||||||
|
this.logger.debug('TaskGather:_onJambonzConnect');
|
||||||
|
}
|
||||||
|
_onJambonzError(cs, _ep, evt) {
|
||||||
|
this.logger.info({evt}, 'TaskGather:_onJambonzError');
|
||||||
|
const {writeAlerts, AlertType} = cs.srf.locals;
|
||||||
|
|
||||||
|
if (this.vendor === 'nuance') {
|
||||||
|
const {code, error} = evt;
|
||||||
|
if (code === 404 && error === 'No speech') return this._resolve('timeout');
|
||||||
|
if (code === 413 && error === 'Too much speech') return this._resolve('timeout');
|
||||||
|
}
|
||||||
|
this.logger.info({evt}, 'TaskGather:_onJambonzError');
|
||||||
|
writeAlerts({
|
||||||
|
account_sid: cs.accountSid,
|
||||||
|
alert_type: AlertType.STT_FAILURE,
|
||||||
|
message: `Custom speech vendor ${this.vendor} error: ${evt.error}`,
|
||||||
|
vendor: this.vendor,
|
||||||
|
}).catch((err) => this.logger.info({err}, 'Error generating alert for jambonz custom connection failure'));
|
||||||
|
this.notifyError({msg: 'ASR error', details:`Custom speech vendor ${this.vendor} error: ${evt.error}`});
|
||||||
|
}
|
||||||
|
|
||||||
_onDeepGramConnectFailure(cs, _ep, evt) {
|
_onDeepGramConnectFailure(cs, _ep, evt) {
|
||||||
const {reason} = evt;
|
const {reason} = evt;
|
||||||
@@ -696,6 +705,19 @@ class TaskGather extends Task {
|
|||||||
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor deepgram: ${reason}`});
|
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor deepgram: ${reason}`});
|
||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
}
|
}
|
||||||
|
_onJambonzConnectFailure(cs, _ep, evt) {
|
||||||
|
const {reason} = evt;
|
||||||
|
const {writeAlerts, AlertType} = cs.srf.locals;
|
||||||
|
this.logger.info({evt}, 'TaskGather:_onJambonzConnectFailure');
|
||||||
|
writeAlerts({
|
||||||
|
account_sid: cs.accountSid,
|
||||||
|
alert_type: AlertType.STT_FAILURE,
|
||||||
|
message: `Failed connecting to ${this.vendor} speech recognizer: ${reason}`,
|
||||||
|
vendor: this.vendor,
|
||||||
|
}).catch((err) => this.logger.info({err}, 'Error generating alert for jambonz custom connection failure'));
|
||||||
|
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor ${this.vendor}: ${reason}`});
|
||||||
|
this.notifyTaskDone();
|
||||||
|
}
|
||||||
|
|
||||||
_onIbmConnect(_cs, _ep) {
|
_onIbmConnect(_cs, _ep) {
|
||||||
this.logger.debug('TaskGather:_onIbmConnect');
|
this.logger.debug('TaskGather:_onIbmConnect');
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ const {
|
|||||||
DeepgramTranscriptionEvents,
|
DeepgramTranscriptionEvents,
|
||||||
SonioxTranscriptionEvents,
|
SonioxTranscriptionEvents,
|
||||||
IbmTranscriptionEvents,
|
IbmTranscriptionEvents,
|
||||||
NvidiaTranscriptionEvents
|
NvidiaTranscriptionEvents,
|
||||||
|
JambonzTranscriptionEvents
|
||||||
} = require('../utils/constants');
|
} = require('../utils/constants');
|
||||||
const { normalizeJambones } = require('@jambonz/verb-specifications');
|
const { normalizeJambones } = require('@jambonz/verb-specifications');
|
||||||
|
|
||||||
@@ -23,11 +24,13 @@ class TaskTranscribe extends Task {
|
|||||||
setChannelVarsForStt,
|
setChannelVarsForStt,
|
||||||
normalizeTranscription,
|
normalizeTranscription,
|
||||||
removeSpeechListeners,
|
removeSpeechListeners,
|
||||||
setSpeechCredentialsAtRuntime
|
setSpeechCredentialsAtRuntime,
|
||||||
|
compileSonioxTranscripts
|
||||||
} = require('../utils/transcription-utils')(logger);
|
} = require('../utils/transcription-utils')(logger);
|
||||||
this.setChannelVarsForStt = setChannelVarsForStt;
|
this.setChannelVarsForStt = setChannelVarsForStt;
|
||||||
this.normalizeTranscription = normalizeTranscription;
|
this.normalizeTranscription = normalizeTranscription;
|
||||||
this.removeSpeechListeners = removeSpeechListeners;
|
this.removeSpeechListeners = removeSpeechListeners;
|
||||||
|
this.compileSonioxTranscripts = compileSonioxTranscripts;
|
||||||
|
|
||||||
this.transcriptionHook = this.data.transcriptionHook;
|
this.transcriptionHook = this.data.transcriptionHook;
|
||||||
this.earlyMedia = this.data.earlyMedia === true || (parentTask && parentTask.earlyMedia);
|
this.earlyMedia = this.data.earlyMedia === true || (parentTask && parentTask.earlyMedia);
|
||||||
@@ -41,6 +44,9 @@ class TaskTranscribe extends Task {
|
|||||||
/* let credentials be supplied in the recognizer object at runtime */
|
/* let credentials be supplied in the recognizer object at runtime */
|
||||||
this.sttCredentials = setSpeechCredentialsAtRuntime(recognizer);
|
this.sttCredentials = setSpeechCredentialsAtRuntime(recognizer);
|
||||||
|
|
||||||
|
/* buffer for soniox transcripts */
|
||||||
|
this._sonioxTranscripts = [];
|
||||||
|
|
||||||
recognizer.hints = recognizer.hints || [];
|
recognizer.hints = recognizer.hints || [];
|
||||||
recognizer.altLanguages = recognizer.altLanguages || [];
|
recognizer.altLanguages = recognizer.altLanguages || [];
|
||||||
}
|
}
|
||||||
@@ -184,8 +190,6 @@ class TaskTranscribe extends Task {
|
|||||||
this._onStartOfSpeech.bind(this, cs, ep, channel));
|
this._onStartOfSpeech.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete,
|
ep.addCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete,
|
||||||
this._onTranscriptionComplete.bind(this, cs, ep, channel));
|
this._onTranscriptionComplete.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.Error,
|
|
||||||
this._onNuanceError.bind(this, cs, ep, channel));
|
|
||||||
break;
|
break;
|
||||||
case 'deepgram':
|
case 'deepgram':
|
||||||
this.bugname = 'deepgram_transcribe';
|
this.bugname = 'deepgram_transcribe';
|
||||||
@@ -198,9 +202,8 @@ class TaskTranscribe extends Task {
|
|||||||
break;
|
break;
|
||||||
case 'soniox':
|
case 'soniox':
|
||||||
this.bugname = 'soniox_transcribe';
|
this.bugname = 'soniox_transcribe';
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription,
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Error,
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
this._onSonioxError.bind(this, cs, ep));
|
|
||||||
break;
|
break;
|
||||||
case 'ibm':
|
case 'ibm':
|
||||||
this.bugname = 'ibm_transcribe';
|
this.bugname = 'ibm_transcribe';
|
||||||
@@ -210,8 +213,6 @@ class TaskTranscribe extends Task {
|
|||||||
this._onIbmConnect.bind(this, cs, ep, channel));
|
this._onIbmConnect.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
||||||
this._onIbmConnectFailure.bind(this, cs, ep, channel));
|
this._onIbmConnectFailure.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Error,
|
|
||||||
this._onIbmError.bind(this, cs, ep, channel));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'nvidia':
|
case 'nvidia':
|
||||||
@@ -224,14 +225,13 @@ class TaskTranscribe extends Task {
|
|||||||
this._onTranscriptionComplete.bind(this, cs, ep));
|
this._onTranscriptionComplete.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.VadDetected,
|
ep.addCustomEventListener(NvidiaTranscriptionEvents.VadDetected,
|
||||||
this._onVadDetected.bind(this, cs, ep));
|
this._onVadDetected.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.Error,
|
|
||||||
this._onNvidiaError.bind(this, cs, ep));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Invalid vendor ${this.vendor}`);
|
throw new Error(`Invalid vendor ${this.vendor}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* common handler for all stt engine errors */
|
||||||
|
ep.addCustomEventListener(JambonzTranscriptionEvents.Error, this._onJambonzError.bind(this, cs, ep));
|
||||||
await ep.set(opts)
|
await ep.set(opts)
|
||||||
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
|
.catch((err) => this.logger.info(err, 'Error setting channel variables'));
|
||||||
|
|
||||||
@@ -273,6 +273,15 @@ class TaskTranscribe extends Task {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.vendor === 'soniox') {
|
||||||
|
/* compile transcripts into one */
|
||||||
|
this._sonioxTranscripts.push(evt.vendor.finalWords);
|
||||||
|
if (evt.is_final) {
|
||||||
|
evt = this.compileSonioxTranscripts(this._sonioxTranscripts, 1, this.language);
|
||||||
|
this._sonioxTranscripts = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.transcriptionHook) {
|
if (this.transcriptionHook) {
|
||||||
const b3 = this.getTracingPropagation();
|
const b3 = this.getTracingPropagation();
|
||||||
const httpHeaders = b3 && {b3};
|
const httpHeaders = b3 && {b3};
|
||||||
@@ -321,23 +330,6 @@ class TaskTranscribe extends Task {
|
|||||||
this._timer = null;
|
this._timer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_onNuanceError(_cs, _ep, _channel, evt) {
|
|
||||||
const {code, error, details} = evt;
|
|
||||||
if (code === 404 && error === 'No speech') {
|
|
||||||
this.logger.debug({code, error, details}, 'TaskTranscribe:_onNuanceError');
|
|
||||||
return this._resolve('timeout');
|
|
||||||
}
|
|
||||||
this.logger.info({code, error, details}, 'TaskTranscribe:_onNuanceError');
|
|
||||||
if (code === 413 && error === 'Too much speech') {
|
|
||||||
return this._resolve('timeout');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_onSonioxError(cs, ep, evt) {
|
|
||||||
this.logger.info({evt}, 'TaskTranscribe:_onSonioxError');
|
|
||||||
}
|
|
||||||
_onNvidiaError(cs, ep, evt) {
|
|
||||||
this.logger.info({evt}, 'TaskTranscribe:_onNvidiaError');
|
|
||||||
}
|
|
||||||
_onDeepgramConnect(_cs, _ep) {
|
_onDeepgramConnect(_cs, _ep) {
|
||||||
this.logger.debug('TaskTranscribe:_onDeepgramConnect');
|
this.logger.debug('TaskTranscribe:_onDeepgramConnect');
|
||||||
}
|
}
|
||||||
@@ -376,6 +368,24 @@ class TaskTranscribe extends Task {
|
|||||||
_onIbmError(cs, _ep, _channel, evt) {
|
_onIbmError(cs, _ep, _channel, evt) {
|
||||||
this.logger.info({evt}, 'TaskTranscribe:_onIbmError');
|
this.logger.info({evt}, 'TaskTranscribe:_onIbmError');
|
||||||
}
|
}
|
||||||
|
_onJambonzError(cs, _ep, evt) {
|
||||||
|
this.logger.info({evt}, 'TaskTranscribe:_onJambonzError');
|
||||||
|
const {writeAlerts, AlertType} = cs.srf.locals;
|
||||||
|
|
||||||
|
if (this.vendor === 'nuance') {
|
||||||
|
const {code, error} = evt;
|
||||||
|
if (code === 404 && error === 'No speech') return this._resolve('timeout');
|
||||||
|
if (code === 413 && error === 'Too much speech') return this._resolve('timeout');
|
||||||
|
}
|
||||||
|
this.logger.info({evt}, 'TaskTranscribe:_onJambonzError');
|
||||||
|
writeAlerts({
|
||||||
|
account_sid: cs.accountSid,
|
||||||
|
alert_type: AlertType.STT_FAILURE,
|
||||||
|
message: `Custom speech vendor ${this.vendor} error: ${evt.error}`,
|
||||||
|
vendor: this.vendor,
|
||||||
|
}).catch((err) => this.logger.info({err}, 'Error generating alert for jambonz custom connection failure'));
|
||||||
|
this.notifyError({msg: 'ASR error', details:`Custom speech vendor ${this.vendor} error: ${evt.error}`});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,6 +110,12 @@
|
|||||||
"NoSpeechDetected": "azure_transcribe::no_speech_detected",
|
"NoSpeechDetected": "azure_transcribe::no_speech_detected",
|
||||||
"VadDetected": "azure_transcribe::vad_detected"
|
"VadDetected": "azure_transcribe::vad_detected"
|
||||||
},
|
},
|
||||||
|
"JambonzTranscriptionEvents": {
|
||||||
|
"Transcription": "jambonz_transcribe::transcription",
|
||||||
|
"ConnectFailure": "jambonz_transcribe::connect_failed",
|
||||||
|
"Connect": "jambonz_transcribe::connect",
|
||||||
|
"Error": "jambonz_transcribe::error"
|
||||||
|
},
|
||||||
"ListenEvents": {
|
"ListenEvents": {
|
||||||
"Connect": "mod_audio_fork::connect",
|
"Connect": "mod_audio_fork::connect",
|
||||||
"ConnectFailure": "mod_audio_fork::connect_failed",
|
"ConnectFailure": "mod_audio_fork::connect_failed",
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ const speechMapper = (cred) => {
|
|||||||
const o = JSON.parse(decrypt(credential));
|
const o = JSON.parse(decrypt(credential));
|
||||||
obj.api_key = o.api_key;
|
obj.api_key = o.api_key;
|
||||||
}
|
}
|
||||||
|
else if (obj.vendor.startsWith('custom:')) {
|
||||||
|
const o = JSON.parse(decrypt(credential));
|
||||||
|
obj.auth_token = o.auth_token;
|
||||||
|
obj.custom_stt_url = o.custom_stt_url;
|
||||||
|
obj.custom_tts_url = o.custom_tts_url;
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
}
|
}
|
||||||
@@ -83,53 +89,13 @@ module.exports = (logger, srf) => {
|
|||||||
const [r2] = await pp.query(sqlSpeechCredentials, account_sid);
|
const [r2] = await pp.query(sqlSpeechCredentials, account_sid);
|
||||||
const speech = r2.map(speechMapper);
|
const speech = r2.map(speechMapper);
|
||||||
|
|
||||||
/* search at the service provider level if we don't find it at the account level */
|
/* add service provider creds unless we have that vendor at the account level */
|
||||||
const haveGoogle = speech.find((s) => s.vendor === 'google');
|
|
||||||
const haveAws = speech.find((s) => s.vendor === 'aws');
|
|
||||||
const haveMicrosoft = speech.find((s) => s.vendor === 'microsoft');
|
|
||||||
const haveWellsaid = speech.find((s) => s.vendor === 'wellsaid');
|
|
||||||
const haveNuance = speech.find((s) => s.vendor === 'nuance');
|
|
||||||
const haveDeepgram = speech.find((s) => s.vendor === 'deepgram');
|
|
||||||
const haveSoniox = speech.find((s) => s.vendor === 'soniox');
|
|
||||||
const haveIbm = speech.find((s) => s.vendor === 'ibm');
|
|
||||||
if (!haveGoogle || !haveAws || !haveMicrosoft || !haveWellsaid ||
|
|
||||||
!haveNuance || !haveIbm || !haveDeepgram || !haveSoniox) {
|
|
||||||
const [r3] = await pp.query(sqlSpeechCredentialsForSP, account_sid);
|
const [r3] = await pp.query(sqlSpeechCredentialsForSP, account_sid);
|
||||||
if (r3.length) {
|
r3.forEach((s) => {
|
||||||
if (!haveGoogle) {
|
if (!speech.find((s2) => s2.vendor === s.vendor)) {
|
||||||
const google = r3.find((s) => s.vendor === 'google');
|
speech.push(speechMapper(s));
|
||||||
if (google) speech.push(speechMapper(google));
|
|
||||||
}
|
|
||||||
if (!haveAws) {
|
|
||||||
const aws = r3.find((s) => s.vendor === 'aws');
|
|
||||||
if (aws) speech.push(speechMapper(aws));
|
|
||||||
}
|
|
||||||
if (!haveMicrosoft) {
|
|
||||||
const ms = r3.find((s) => s.vendor === 'microsoft');
|
|
||||||
if (ms) speech.push(speechMapper(ms));
|
|
||||||
}
|
|
||||||
if (!haveWellsaid) {
|
|
||||||
const wellsaid = r3.find((s) => s.vendor === 'wellsaid');
|
|
||||||
if (wellsaid) speech.push(speechMapper(wellsaid));
|
|
||||||
}
|
|
||||||
if (!haveNuance) {
|
|
||||||
const nuance = r3.find((s) => s.vendor === 'nuance');
|
|
||||||
if (nuance) speech.push(speechMapper(nuance));
|
|
||||||
}
|
|
||||||
if (!haveDeepgram) {
|
|
||||||
const deepgram = r3.find((s) => s.vendor === 'deepgram');
|
|
||||||
if (deepgram) speech.push(speechMapper(deepgram));
|
|
||||||
}
|
|
||||||
if (!haveSoniox) {
|
|
||||||
const soniox = r3.find((s) => s.vendor === 'soniox');
|
|
||||||
if (soniox) speech.push(speechMapper(soniox));
|
|
||||||
}
|
|
||||||
if (!haveIbm) {
|
|
||||||
const ibm = r3.find((s) => s.vendor === 'ibm');
|
|
||||||
if (ibm) speech.push(speechMapper(ibm));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...r[0],
|
...r[0],
|
||||||
|
|||||||
@@ -138,7 +138,6 @@ function installSrfLocals(srf, logger) {
|
|||||||
retrieveCall,
|
retrieveCall,
|
||||||
listCalls,
|
listCalls,
|
||||||
deleteCall,
|
deleteCall,
|
||||||
synthAudio,
|
|
||||||
createHash,
|
createHash,
|
||||||
retrieveHash,
|
retrieveHash,
|
||||||
deleteKey,
|
deleteKey,
|
||||||
@@ -151,11 +150,17 @@ function installSrfLocals(srf, logger) {
|
|||||||
pushBack,
|
pushBack,
|
||||||
popFront,
|
popFront,
|
||||||
removeFromList,
|
removeFromList,
|
||||||
lengthOfList,
|
|
||||||
getListPosition,
|
getListPosition,
|
||||||
|
lengthOfList,
|
||||||
|
} = require('@jambonz/realtimedb-helpers')({
|
||||||
|
host: process.env.JAMBONES_REDIS_HOST,
|
||||||
|
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||||
|
}, logger, tracer);
|
||||||
|
const {
|
||||||
|
synthAudio,
|
||||||
getNuanceAccessToken,
|
getNuanceAccessToken,
|
||||||
getIbmAccessToken,
|
getIbmAccessToken,
|
||||||
} = require('@jambonz/realtimedb-helpers')({
|
} = require('@jambonz/speech-utils')({
|
||||||
host: process.env.JAMBONES_REDIS_HOST,
|
host: process.env.JAMBONES_REDIS_HOST,
|
||||||
port: process.env.JAMBONES_REDIS_PORT || 6379
|
port: process.env.JAMBONES_REDIS_PORT || 6379
|
||||||
}, logger, tracer);
|
}, logger, tracer);
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ const {
|
|||||||
NuanceTranscriptionEvents,
|
NuanceTranscriptionEvents,
|
||||||
DeepgramTranscriptionEvents,
|
DeepgramTranscriptionEvents,
|
||||||
SonioxTranscriptionEvents,
|
SonioxTranscriptionEvents,
|
||||||
NvidiaTranscriptionEvents
|
NvidiaTranscriptionEvents,
|
||||||
|
JambonzTranscriptionEvents
|
||||||
} = require('./constants');
|
} = require('./constants');
|
||||||
|
|
||||||
const stickyVars = {
|
const stickyVars = {
|
||||||
@@ -223,6 +224,15 @@ const normalizeGoogle = (evt, channel, language) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const normalizeCustom = (evt, channel, language) => {
|
||||||
|
return {
|
||||||
|
language_code: language,
|
||||||
|
channel_tag: channel,
|
||||||
|
is_final: evt.is_final,
|
||||||
|
alternatives: [evt.alternatives[0]]
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeNuance = (evt, channel, language) => {
|
const normalizeNuance = (evt, channel, language) => {
|
||||||
const copy = JSON.parse(JSON.stringify(evt));
|
const copy = JSON.parse(JSON.stringify(evt));
|
||||||
return {
|
return {
|
||||||
@@ -302,6 +312,9 @@ module.exports = (logger) => {
|
|||||||
case 'soniox':
|
case 'soniox':
|
||||||
return normalizeSoniox(evt, channel, language);
|
return normalizeSoniox(evt, channel, language);
|
||||||
default:
|
default:
|
||||||
|
if (vendor.startsWith('custom:')) {
|
||||||
|
return normalizeCustom(evt, channel, language);
|
||||||
|
}
|
||||||
logger.error(`Unknown vendor ${vendor}`);
|
logger.error(`Unknown vendor ${vendor}`);
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
@@ -311,6 +324,7 @@ module.exports = (logger) => {
|
|||||||
let opts = {};
|
let opts = {};
|
||||||
const {enable, voiceMs = 0, mode = -1} = rOpts.vad || {};
|
const {enable, voiceMs = 0, mode = -1} = rOpts.vad || {};
|
||||||
const vad = {enable, voiceMs, mode};
|
const vad = {enable, voiceMs, mode};
|
||||||
|
const vendor = rOpts.vendor;
|
||||||
|
|
||||||
/* voice activity detection works across vendors */
|
/* voice activity detection works across vendors */
|
||||||
opts = {
|
opts = {
|
||||||
@@ -320,7 +334,7 @@ module.exports = (logger) => {
|
|||||||
...(vad.enable && typeof vad.mode === 'number' && {RECOGNIZER_VAD_MODE: vad.mode}),
|
...(vad.enable && typeof vad.mode === 'number' && {RECOGNIZER_VAD_MODE: vad.mode}),
|
||||||
};
|
};
|
||||||
|
|
||||||
if ('google' === rOpts.vendor) {
|
if ('google' === vendor) {
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
...(sttCredentials &&
|
...(sttCredentials &&
|
||||||
@@ -372,7 +386,7 @@ module.exports = (logger) => {
|
|||||||
{GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE: rOpts.naicsCode}),
|
{GOOGLE_SPEECH_METADATA_INDUSTRY_NAICS_CODE: rOpts.naicsCode}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (['aws', 'polly'].includes(rOpts.vendor)) {
|
else if (['aws', 'polly'].includes(vendor)) {
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
...(rOpts.vocabularyName && {AWS_VOCABULARY_NAME: rOpts.vocabularyName}),
|
...(rOpts.vocabularyName && {AWS_VOCABULARY_NAME: rOpts.vocabularyName}),
|
||||||
@@ -385,7 +399,7 @@ module.exports = (logger) => {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('microsoft' === rOpts.vendor) {
|
else if ('microsoft' === vendor) {
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'string' &&
|
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'string' &&
|
||||||
@@ -410,7 +424,7 @@ module.exports = (logger) => {
|
|||||||
{AZURE_SERVICE_ENDPOINT_ID: sttCredentials.custom_stt_endpoint})
|
{AZURE_SERVICE_ENDPOINT_ID: sttCredentials.custom_stt_endpoint})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('nuance' === rOpts.vendor) {
|
else if ('nuance' === vendor) {
|
||||||
/**
|
/**
|
||||||
* Note: all nuance options are in recognizer.nuanceOptions, should migrate
|
* Note: all nuance options are in recognizer.nuanceOptions, should migrate
|
||||||
* other vendor settings to similar nested structure
|
* other vendor settings to similar nested structure
|
||||||
@@ -461,7 +475,7 @@ module.exports = (logger) => {
|
|||||||
{NUANCE_RESOURCES: JSON.stringify(nuanceOptions.resources)},
|
{NUANCE_RESOURCES: JSON.stringify(nuanceOptions.resources)},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('deepgram' === rOpts.vendor) {
|
else if ('deepgram' === vendor) {
|
||||||
const {deepgramOptions = {}} = rOpts;
|
const {deepgramOptions = {}} = rOpts;
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
@@ -505,7 +519,7 @@ module.exports = (logger) => {
|
|||||||
{DEEPGRAM_SPEECH_TAG: deepgramOptions.tag}
|
{DEEPGRAM_SPEECH_TAG: deepgramOptions.tag}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('soniox' === rOpts.vendor) {
|
else if ('soniox' === vendor) {
|
||||||
const {sonioxOptions = {}} = rOpts;
|
const {sonioxOptions = {}} = rOpts;
|
||||||
const {storage = {}} = sonioxOptions;
|
const {storage = {}} = sonioxOptions;
|
||||||
opts = {
|
opts = {
|
||||||
@@ -528,7 +542,7 @@ module.exports = (logger) => {
|
|||||||
...(storage?.id && storage?.disableSearch && {SONIOX_STORAGE_DISABLE_SEARCH: 1})
|
...(storage?.id && storage?.disableSearch && {SONIOX_STORAGE_DISABLE_SEARCH: 1})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('ibm' === rOpts.vendor) {
|
else if ('ibm' === vendor) {
|
||||||
const {ibmOptions = {}} = rOpts;
|
const {ibmOptions = {}} = rOpts;
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
@@ -552,7 +566,7 @@ module.exports = (logger) => {
|
|||||||
{IBM_SPEECH_WATSON_LEARNING_OPT_OUT: ibmOptions.watsonLearningOptOut}
|
{IBM_SPEECH_WATSON_LEARNING_OPT_OUT: ibmOptions.watsonLearningOptOut}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if ('nvidia' === rOpts.vendor) {
|
else if ('nvidia' === vendor) {
|
||||||
const {nvidiaOptions = {}} = rOpts;
|
const {nvidiaOptions = {}} = rOpts;
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
@@ -581,11 +595,29 @@ module.exports = (logger) => {
|
|||||||
{NVIDIA_CUSTOM_CONFIGURATION: JSON.stringify(nvidiaOptions.customConfiguration)}),
|
{NVIDIA_CUSTOM_CONFIGURATION: JSON.stringify(nvidiaOptions.customConfiguration)}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
else if (vendor.startsWith('custom:')) {
|
||||||
|
let {options = {}} = rOpts;
|
||||||
|
const {auth_token, custom_stt_url} = sttCredentials;
|
||||||
|
options = {
|
||||||
|
...options,
|
||||||
|
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'string' &&
|
||||||
|
{hints: rOpts.hints}),
|
||||||
|
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'object' &&
|
||||||
|
{hints: JSON.stringify(rOpts.hints)}),
|
||||||
|
...(typeof rOpts.hintsBoost === 'number' && {hintsBoost: rOpts.hintsBoost})
|
||||||
|
};
|
||||||
|
|
||||||
stickyVars[rOpts.vendor].forEach((key) => {
|
opts = {
|
||||||
|
...opts,
|
||||||
|
JAMBONZ_STT_API_KEY: auth_token,
|
||||||
|
JAMBONZ_STT_URL: custom_stt_url,
|
||||||
|
...(Object.keys(options).length > 0 && {JAMBONZ_STT_OPTIONS: JSON.stringify(options)}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
(stickyVars[vendor] || []).forEach((key) => {
|
||||||
if (!opts[key]) opts[key] = '';
|
if (!opts[key]) opts[key] = '';
|
||||||
});
|
});
|
||||||
//logger.debug({opts}, 'recognizer channel vars');
|
|
||||||
return opts;
|
return opts;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -604,7 +636,6 @@ module.exports = (logger) => {
|
|||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.Transcription);
|
ep.removeCustomEventListener(NuanceTranscriptionEvents.Transcription);
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete);
|
ep.removeCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete);
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech);
|
ep.removeCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech);
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.Error);
|
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.VadDetected);
|
ep.removeCustomEventListener(NuanceTranscriptionEvents.VadDetected);
|
||||||
|
|
||||||
ep.removeCustomEventListener(DeepgramTranscriptionEvents.Transcription);
|
ep.removeCustomEventListener(DeepgramTranscriptionEvents.Transcription);
|
||||||
@@ -612,13 +643,17 @@ module.exports = (logger) => {
|
|||||||
ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure);
|
ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure);
|
||||||
|
|
||||||
ep.removeCustomEventListener(SonioxTranscriptionEvents.Transcription);
|
ep.removeCustomEventListener(SonioxTranscriptionEvents.Transcription);
|
||||||
ep.removeCustomEventListener(SonioxTranscriptionEvents.Error);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.Transcription);
|
ep.removeCustomEventListener(NvidiaTranscriptionEvents.Transcription);
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.TranscriptionComplete);
|
ep.removeCustomEventListener(NvidiaTranscriptionEvents.TranscriptionComplete);
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.StartOfSpeech);
|
ep.removeCustomEventListener(NvidiaTranscriptionEvents.StartOfSpeech);
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.Error);
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.VadDetected);
|
ep.removeCustomEventListener(NvidiaTranscriptionEvents.VadDetected);
|
||||||
|
|
||||||
|
ep.removeCustomEventListener(JambonzTranscriptionEvents.Transcription);
|
||||||
|
ep.removeCustomEventListener(JambonzTranscriptionEvents.Connect);
|
||||||
|
ep.removeCustomEventListener(JambonzTranscriptionEvents.ConnectFailure);
|
||||||
|
|
||||||
|
ep.removeCustomEventListener(JambonzTranscriptionEvents.Error);
|
||||||
};
|
};
|
||||||
|
|
||||||
const setSpeechCredentialsAtRuntime = (recognizer) => {
|
const setSpeechCredentialsAtRuntime = (recognizer) => {
|
||||||
|
|||||||
1907
package-lock.json
generated
1907
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@
|
|||||||
"@jambonz/db-helpers": "^0.7.4",
|
"@jambonz/db-helpers": "^0.7.4",
|
||||||
"@jambonz/http-health-check": "^0.0.1",
|
"@jambonz/http-health-check": "^0.0.1",
|
||||||
"@jambonz/realtimedb-helpers": "^0.6.5",
|
"@jambonz/realtimedb-helpers": "^0.6.5",
|
||||||
|
"@jambonz/speech-utils": "^0.0.2",
|
||||||
"@jambonz/stats-collector": "^0.1.6",
|
"@jambonz/stats-collector": "^0.1.6",
|
||||||
"@jambonz/time-series": "^0.2.5",
|
"@jambonz/time-series": "^0.2.5",
|
||||||
"@jambonz/verb-specifications": "^0.0.11",
|
"@jambonz/verb-specifications": "^0.0.11",
|
||||||
@@ -43,7 +44,7 @@
|
|||||||
"bent": "^7.3.12",
|
"bent": "^7.3.12",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"deepcopy": "^2.1.0",
|
"deepcopy": "^2.1.0",
|
||||||
"drachtio-fsmrf": "^3.0.19",
|
"drachtio-fsmrf": "^3.0.20",
|
||||||
"drachtio-srf": "^4.5.23",
|
"drachtio-srf": "^4.5.23",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"ip": "^1.1.8",
|
"ip": "^1.1.8",
|
||||||
|
|||||||
Reference in New Issue
Block a user