Compare commits

..

6 Commits

Author SHA1 Message Date
Hoan Luu Huu
31559cbb3b user restriction (#520) 2023-11-08 12:39:56 -05:00
Dave Horton
1156bae2de fix for #521 - allow pause in confirmHook applications (#522) 2023-11-07 09:46:41 -05:00
Dave Horton
c6c599ab99 fix dialogflow tts bug (tts not working due to 'default' being assigned to label) and update to drachtio-srf with fix for parsing sip:1234@feature-server (#518) 2023-11-06 15:05:17 -05:00
Dave Horton
4d0f0fe75f prevent exception referencing user.uri (#517) 2023-11-06 13:42:37 -05:00
Dave Horton
6d625d87ad Feat/assemblyai testing (#516)
* handle errors from assemblyai

* wip

* fix alert

* normalizeAssemblyai
2023-11-02 17:05:28 -04:00
Hoan Luu Huu
7fee2ba2dc feat assembly (#515)
* fix

* wip

* wip

* wip

* wip

* fix review comments
2023-11-02 09:25:04 -04:00
13 changed files with 178 additions and 160 deletions

View File

@@ -23,7 +23,8 @@ module.exports = function(srf, logger) {
lookupAppBySid, lookupAppBySid,
lookupAppByRealm, lookupAppByRealm,
lookupAppByTeamsTenant, lookupAppByTeamsTenant,
registrar registrar,
lookupClientByAccountAndUsername
} = srf.locals.dbHelpers; } = srf.locals.dbHelpers;
const { const {
writeAlerts, writeAlerts,
@@ -35,6 +36,7 @@ module.exports = function(srf, logger) {
const callId = req.get('Call-ID'); const callId = req.get('Call-ID');
const uri = parseUri(req.uri); const uri = parseUri(req.uri);
logger.info({ logger.info({
uri,
callId, callId,
callingNumber: req.callingNumber, callingNumber: req.callingNumber,
calledNumber: req.calledNumber calledNumber: req.calledNumber
@@ -47,10 +49,17 @@ module.exports = function(srf, logger) {
const account_sid = req.get('X-Account-Sid'); const account_sid = req.get('X-Account-Sid');
req.locals = {callSid, account_sid, callId}; req.locals = {callSid, account_sid, callId};
if (req.has('X-Authenticated-User')) req.locals.originatingUser = req.get('X-Authenticated-User'); let clientDb = null;
if (req.has('X-Authenticated-User')) {
req.locals.originatingUser = req.get('X-Authenticated-User');
const arr = /^(.*)@(.*)/.exec(req.locals.originatingUser);
if (arr) {
[clientDb] = await lookupClientByAccountAndUsername(account_sid, arr[1]);
}
}
// check for call to application // check for call to application
if (uri.user.startsWith('app-') && req.locals.originatingUser) { if (uri.user?.startsWith('app-') && req.locals.originatingUser && clientDb.allow_direct_app_calling) {
const application_sid = uri.user.match(/app-(.*)/)[1]; const application_sid = uri.user.match(/app-(.*)/)[1];
logger.debug(`got application from Request URI header: ${application_sid}`); logger.debug(`got application from Request URI header: ${application_sid}`);
req.locals.application_sid = application_sid; req.locals.application_sid = application_sid;
@@ -60,13 +69,13 @@ module.exports = function(srf, logger) {
req.locals.application_sid = application_sid; req.locals.application_sid = application_sid;
} }
// check for call to queue // check for call to queue
if (uri.user.startsWith('queue-') && req.locals.originatingUser) { if (uri.user?.startsWith('queue-') && req.locals.originatingUser && clientDb.allow_direct_queue_calling) {
const queue_name = uri.user.match(/queue-(.*)/)[1]; const queue_name = uri.user.match(/queue-(.*)/)[1];
logger.debug(`got Queue from Request URI header: ${queue_name}`); logger.debug(`got Queue from Request URI header: ${queue_name}`);
req.locals.queue_name = queue_name; req.locals.queue_name = queue_name;
} }
// check for call to registered user // check for call to registered user
if (!JAMBONES_DISABLE_DIRECT_P2P_CALL && req.locals.originatingUser) { if (!JAMBONES_DISABLE_DIRECT_P2P_CALL && req.locals.originatingUser && clientDb.allow_direct_user_calling) {
const arr = /^(.*)@(.*)/.exec(req.locals.originatingUser); const arr = /^(.*)@(.*)/.exec(req.locals.originatingUser);
if (arr) { if (arr) {
const sipRealm = arr[2]; const sipRealm = arr[2];

View File

@@ -835,6 +835,11 @@ class CallSession extends Emitter {
api_key: credential.api_key, api_key: credential.api_key,
model_id: credential.model_id model_id: credential.model_id
}; };
} else if ('assemblyai' === vendor) {
return {
speech_credential_sid: credential.speech_credential_sid,
api_key: credential.api_key
};
} else if (vendor.startsWith('custom:')) { } else if (vendor.startsWith('custom:')) {
return { return {
speech_credential_sid: credential.speech_credential_sid, speech_credential_sid: credential.speech_credential_sid,

View File

@@ -58,13 +58,13 @@ class Dialogflow extends Task {
this.vendor = this.data.tts.vendor || 'default'; this.vendor = this.data.tts.vendor || 'default';
this.language = this.data.tts.language || 'default'; this.language = this.data.tts.language || 'default';
this.voice = this.data.tts.voice || 'default'; this.voice = this.data.tts.voice || 'default';
this.speechSynthesisLabel = this.data.tts.label || 'default'; this.speechSynthesisLabel = this.data.tts.label;
// fallback tts // fallback tts
this.fallbackVendor = this.data.tts.fallbackVendor || 'default'; this.fallbackVendor = this.data.tts.fallbackVendor || 'default';
this.fallbackLanguage = this.data.tts.fallbackLanguage || 'default'; this.fallbackLanguage = this.data.tts.fallbackLanguage || 'default';
this.fallbackVoice = this.data.tts.fallbackLanguage || 'default'; this.fallbackVoice = this.data.tts.fallbackLanguage || 'default';
this.fallbackLabel = this.data.tts.fallbackLabel || 'default'; this.fallbackLabel = this.data.tts.fallbackLabel;
} }
this.bargein = this.data.bargein; this.bargein = this.data.bargein;
} }

View File

@@ -9,8 +9,9 @@ const {
CobaltTranscriptionEvents, CobaltTranscriptionEvents,
IbmTranscriptionEvents, IbmTranscriptionEvents,
NvidiaTranscriptionEvents, NvidiaTranscriptionEvents,
JambonzTranscriptionEvents JambonzTranscriptionEvents,
} = require('../utils/constants'); AssemblyAiTranscriptionEvents
} = require('../utils/constants.json');
const { const {
JAMBONES_GATHER_EARLY_HINTS_MATCH, JAMBONES_GATHER_EARLY_HINTS_MATCH,
JAMBONZ_GATHER_EARLY_HINTS_MATCH, JAMBONZ_GATHER_EARLY_HINTS_MATCH,
@@ -392,9 +393,9 @@ class TaskGather extends SttTask {
case 'deepgram': case 'deepgram':
this.bugname = 'deepgram_transcribe'; this.bugname = 'deepgram_transcribe';
ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect, this._onDeepgramConnect.bind(this, cs, ep)); ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure,
this._onDeepGramConnectFailure.bind(this, cs, ep)); this._onVendorConnectFailure.bind(this, cs, ep));
/* if app sets deepgramOptions.utteranceEndMs they essentially want continuous asr */ /* if app sets deepgramOptions.utteranceEndMs they essentially want continuous asr */
if (opts.DEEPGRAM_SPEECH_UTTERANCE_END_MS) this.isContinuousAsr = true; if (opts.DEEPGRAM_SPEECH_UTTERANCE_END_MS) this.isContinuousAsr = true;
@@ -438,9 +439,9 @@ class TaskGather extends SttTask {
case 'ibm': case 'ibm':
this.bugname = 'ibm_transcribe'; this.bugname = 'ibm_transcribe';
ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(IbmTranscriptionEvents.Connect, this._onIbmConnect.bind(this, cs, ep)); ep.addCustomEventListener(IbmTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
this._onIbmConnectFailure.bind(this, cs, ep)); this._onVendorConnectFailure.bind(this, cs, ep));
break; break;
case 'nvidia': case 'nvidia':
@@ -460,13 +461,22 @@ class TaskGather extends SttTask {
} }
break; break;
case 'assemblyai':
this.bugname = 'assemblyai_transcribe';
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Transcription,
this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure,
this._onVendorConnectFailure.bind(this, cs, ep));
break;
default: default:
if (this.vendor.startsWith('custom:')) { if (this.vendor.startsWith('custom:')) {
this.bugname = `${this.vendor}_transcribe`; this.bugname = `${this.vendor}_transcribe`;
ep.addCustomEventListener(JambonzTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep)); ep.addCustomEventListener(JambonzTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(JambonzTranscriptionEvents.Connect, this._onJambonzConnect.bind(this, cs, ep)); ep.addCustomEventListener(JambonzTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(JambonzTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(JambonzTranscriptionEvents.ConnectFailure,
this._onJambonzConnectFailure.bind(this, cs, ep)); this._onVendorConnectFailure.bind(this, cs, ep));
break; break;
} }
else { else {
@@ -788,12 +798,6 @@ class TaskGather extends SttTask {
_onTranscriptionComplete(cs, ep) { _onTranscriptionComplete(cs, ep) {
this.logger.debug('TaskGather:_onTranscriptionComplete'); this.logger.debug('TaskGather:_onTranscriptionComplete');
} }
_onDeepgramConnect(_cs, _ep) {
this.logger.debug('TaskGather:_onDeepgramConnect');
}
_onJambonzConnect(_cs, _ep) {
this.logger.debug('TaskGather:_onJambonzConnect');
}
async _onJambonzError(cs, ep, evt) { async _onJambonzError(cs, ep, evt) {
this.logger.info({evt}, 'TaskGather:_onJambonzError'); this.logger.info({evt}, 'TaskGather:_onJambonzError');
if (this.isHandledByPrimaryProvider && this.fallbackVendor) { if (this.isHandledByPrimaryProvider && this.fallbackVendor) {
@@ -827,54 +831,16 @@ class TaskGather extends SttTask {
this.notifyError({msg: 'ASR error', details:`Custom speech vendor ${this.vendor} error: ${evt.error}`}); this.notifyError({msg: 'ASR error', details:`Custom speech vendor ${this.vendor} error: ${evt.error}`});
} }
_onDeepGramConnectFailure(cs, _ep, evt) { _onVendorConnectFailure(cs, _ep, evt) {
const {reason} = evt; super._onVendorConnectFailure(cs, _ep, evt);
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, 'TaskGather:_onDeepgramConnectFailure');
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_FAILURE,
message: `Failed connecting to Deepgram speech recognizer: ${reason}`,
vendor: 'deepgram',
}).catch((err) => this.logger.info({err}, 'Error generating alert for deepgram connection failure'));
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor deepgram: ${reason}`});
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(); this.notifyTaskDone();
} }
_onIbmConnect(_cs, _ep) { _onVendorError(cs, _ep, evt) {
this.logger.debug('TaskGather:_onIbmConnect'); super._onVendorError(cs, _ep, evt);
this._resolve('stt-error', evt);
} }
_onIbmConnectFailure(cs, _ep, evt) {
const {reason} = evt;
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, 'TaskGather:_onIbmConnectFailure');
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_FAILURE,
message: `Failed connecting to IBM watson speech recognizer: ${reason}`,
vendor: 'ibm',
}).catch((err) => this.logger.info({err}, 'Error generating alert for IBM connection failure'));
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor IBM: ${reason}`});
this.notifyTaskDone();
}
_onIbmError(cs, _ep, evt) {
this.logger.info({evt}, 'TaskGather:_onIbmError'); }
_onVadDetected(cs, ep) { _onVadDetected(cs, ep) {
if (this.bargein && this.minBargeinWordCount === 0) { if (this.bargein && this.minBargeinWordCount === 0) {
this.logger.debug('TaskGather:_onVadDetected'); this.logger.debug('TaskGather:_onVadDetected');
@@ -948,6 +914,13 @@ class TaskGather extends SttTask {
await this.performAction({reason: 'timeout'}); await this.performAction({reason: 'timeout'});
} }
} }
else if (reason.startsWith('stt-error')) {
if (this.parentTask) this.parentTask.emit('stt-error', evt);
else {
this.emit('stt-error', evt);
await this.performAction({reason: 'error', details: evt.error});
}
}
} catch (err) { /*already logged error*/ } } catch (err) { /*already logged error*/ }
this.notifyTaskDone(); this.notifyTaskDone();
} }

View File

@@ -148,6 +148,36 @@ class SttTask extends Task {
const dgOptions = this.data.recognizer.deepgramOptions = this.data.recognizer.deepgramOptions || {}; const dgOptions = this.data.recognizer.deepgramOptions = this.data.recognizer.deepgramOptions || {};
dgOptions.utteranceEndMs = dgOptions.utteranceEndMs || asrTimeout; dgOptions.utteranceEndMs = dgOptions.utteranceEndMs || asrTimeout;
} }
_onVendorConnect(_cs, _ep) {
this.logger.debug(`TaskGather:_on${this.vendor}Connect`);
}
_onVendorError(cs, _ep, evt) {
this.logger.info({evt}, `${this.name}:_on${this.vendor}Error`);
const {writeAlerts, AlertType} = cs.srf.locals;
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_FAILURE,
message: 'STT failure reported by vendor',
detail: evt.error,
vendor: this.vendor,
}).catch((err) => this.logger.info({err}, `Error generating alert for ${this.vendor} connection failure`));
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor ${this.vendor}: ${evt.error}`});
}
_onVendorConnectFailure(cs, _ep, evt) {
const {reason} = evt;
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, `${this.name}:_on${this.vendor}ConnectFailure`);
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 ${this.vendor} connection failure`));
this.notifyError({msg: 'ASR error', details:`Failed connecting to speech vendor ${this.vendor}: ${reason}`});
}
} }
module.exports = SttTask; module.exports = SttTask;

View File

@@ -11,8 +11,9 @@ const {
IbmTranscriptionEvents, IbmTranscriptionEvents,
NvidiaTranscriptionEvents, NvidiaTranscriptionEvents,
JambonzTranscriptionEvents, JambonzTranscriptionEvents,
TranscribeStatus TranscribeStatus,
} = require('../utils/constants'); AssemblyAiTranscriptionEvents
} = require('../utils/constants.json');
const { normalizeJambones } = require('@jambonz/verb-specifications'); const { normalizeJambones } = require('@jambonz/verb-specifications');
const SttTask = require('./stt-task'); const SttTask = require('./stt-task');
@@ -228,9 +229,9 @@ class TaskTranscribe extends SttTask {
ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription, ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription,
this._onTranscription.bind(this, cs, ep, channel)); this._onTranscription.bind(this, cs, ep, channel));
ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect, ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect,
this._onDeepgramConnect.bind(this, cs, ep, channel)); this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure,
this._onDeepGramConnectFailure.bind(this, cs, ep, channel)); this._onVendorConnectFailure.bind(this, cs, ep, channel));
/* if app sets deepgramOptions.utteranceEndMs they essentially want continuous asr */ /* if app sets deepgramOptions.utteranceEndMs they essentially want continuous asr */
if (opts.DEEPGRAM_SPEECH_UTTERANCE_END_MS) this.isContinuousAsr = true; if (opts.DEEPGRAM_SPEECH_UTTERANCE_END_MS) this.isContinuousAsr = true;
@@ -276,9 +277,9 @@ class TaskTranscribe extends SttTask {
ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, ep.addCustomEventListener(IbmTranscriptionEvents.Transcription,
this._onTranscription.bind(this, cs, ep, channel)); this._onTranscription.bind(this, cs, ep, channel));
ep.addCustomEventListener(IbmTranscriptionEvents.Connect, ep.addCustomEventListener(IbmTranscriptionEvents.Connect,
this._onIbmConnect.bind(this, cs, ep, channel)); this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
this._onIbmConnectFailure.bind(this, cs, ep, channel)); this._onVendorConnectFailure.bind(this, cs, ep, channel));
break; break;
case 'nvidia': case 'nvidia':
@@ -293,6 +294,16 @@ class TaskTranscribe extends SttTask {
this._onVadDetected.bind(this, cs, ep)); this._onVadDetected.bind(this, cs, ep));
break; break;
case 'assemblyai':
this.bugname = 'assemblyai_transcribe';
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Transcription,
this._onTranscription.bind(this, cs, ep, channel));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure,
this._onVendorConnectFailure.bind(this, cs, ep, channel));
break;
default: default:
if (this.vendor.startsWith('custom:')) { if (this.vendor.startsWith('custom:')) {
this.bugname = `${this.vendor}_transcribe`; this.bugname = `${this.vendor}_transcribe`;
@@ -480,78 +491,7 @@ class TaskTranscribe extends SttTask {
this._timer = null; this._timer = null;
} }
} }
_onDeepgramConnect(_cs, _ep) {
this.logger.debug('TaskTranscribe:_onDeepgramConnect');
}
_onDeepGramConnectFailure(cs, _ep, channel, evt) {
const {reason} = evt;
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, 'TaskTranscribe:_onDeepgramConnectFailure');
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_FAILURE,
message: `Failed connecting to Deepgram speech recognizer: ${reason}`,
vendor: 'deepgram',
}).catch((err) => this.logger.info({err}, 'Error generating alert for deepgram connection failure'));
this.notifyError(`Failed connecting to speech vendor deepgram: ${reason}`);
if (this.childSpan[channel - 1] && this.childSpan[channel - 1].span) {
this.childSpan[channel - 1].span.setAttributes({
channel,
'stt.resolve': 'connection failure'
});
this.childSpan[channel - 1].span.end();
}
this.notifyTaskDone();
}
_onJambonzConnect(_cs, _ep) {
this.logger.debug('TaskTranscribe:_onJambonzConnect');
}
_onJambonzConnectFailure(cs, _ep, evt) {
const {reason} = evt;
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, 'TaskTranscribe:_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) {
this.logger.debug('TaskTranscribe:_onIbmConnect');
}
_onIbmConnectFailure(cs, _ep, channel, evt) {
const {reason} = evt;
const {writeAlerts, AlertType} = cs.srf.locals;
this.logger.info({evt}, 'TaskTranscribe:_onIbmConnectFailure');
writeAlerts({
account_sid: cs.accountSid,
alert_type: AlertType.STT_FAILURE,
message: `Failed connecting to IBM watson speech recognizer: ${reason}`,
vendor: 'ibm',
}).catch((err) => this.logger.info({err}, 'Error generating alert for IBM connection failure'));
this.notifyError(`Failed connecting to speech vendor IBM: ${reason}`);
if (this.childSpan[channel - 1] && this.childSpan[channel - 1].span) {
this.childSpan[channel - 1].span.setAttributes({
channel,
'stt.resolve': 'connection failure'
});
this.childSpan[channel - 1].span.end();
}
this.notifyTaskDone();
}
_onIbmError(cs, _ep, _channel, evt) {
this.logger.info({evt}, 'TaskTranscribe:_onIbmError');
}
async _onJambonzError(cs, _ep, evt) { async _onJambonzError(cs, _ep, evt) {
this.logger.info({evt}, 'TaskTranscribe:_onJambonzError'); this.logger.info({evt}, 'TaskTranscribe:_onJambonzError');
if (this.isHandledByPrimaryProvider && this.fallbackVendor) { if (this.isHandledByPrimaryProvider && this.fallbackVendor) {
@@ -589,6 +529,18 @@ class TaskTranscribe extends SttTask {
} }
} }
_onVendorConnectFailure(cs, _ep, channel, evt) {
super._onVendorConnectFailure(cs, _ep, evt);
if (this.childSpan[channel - 1] && this.childSpan[channel - 1].span) {
this.childSpan[channel - 1].span.setAttributes({
channel,
'stt.resolve': 'connection failure'
});
this.childSpan[channel - 1].span.end();
}
this.notifyTaskDone();
}
_startAsrTimer(channel) { _startAsrTimer(channel) {
if (this.vendor === 'deepgram') return; // no need if (this.vendor === 'deepgram') return; // no need
assert(this.isContinuousAsr); assert(this.isContinuousAsr);

View File

@@ -126,6 +126,12 @@
"Connect": "jambonz_transcribe::connect", "Connect": "jambonz_transcribe::connect",
"Error": "jambonz_transcribe::error" "Error": "jambonz_transcribe::error"
}, },
"AssemblyAiTranscriptionEvents": {
"Transcription": "assemblyai_transcribe::transcription",
"Error": "assemblyai_transcribe::error",
"ConnectFailure": "assemblyai_transcribe::connect_failed",
"Connect": "assemblyai_transcribe::connect"
},
"ListenEvents": { "ListenEvents": {
"Connect": "mod_audio_fork::connect", "Connect": "mod_audio_fork::connect",
"ConnectFailure": "mod_audio_fork::connect_failed", "ConnectFailure": "mod_audio_fork::connect_failed",

View File

@@ -91,6 +91,9 @@ 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;
obj.model_id = o.model_id; obj.model_id = o.model_id;
} else if ('assemblyai' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = o.api_key;
} else if (obj.vendor.startsWith('custom:')) { } else if (obj.vendor.startsWith('custom:')) {
const o = JSON.parse(decrypt(credential)); const o = JSON.parse(decrypt(credential));
obj.auth_token = o.auth_token; obj.auth_token = o.auth_token;

View File

@@ -140,7 +140,8 @@ function installSrfLocals(srf, logger) {
lookupTeamsByAccount, lookupTeamsByAccount,
lookupAccountBySid, lookupAccountBySid,
lookupAccountCapacitiesBySid, lookupAccountCapacitiesBySid,
lookupSmppGateways lookupSmppGateways,
lookupClientByAccountAndUsername
} = require('@jambonz/db-helpers')({ } = require('@jambonz/db-helpers')({
host: JAMBONES_MYSQL_HOST, host: JAMBONES_MYSQL_HOST,
user: JAMBONES_MYSQL_USER, user: JAMBONES_MYSQL_USER,
@@ -217,6 +218,7 @@ function installSrfLocals(srf, logger) {
lookupAccountBySid, lookupAccountBySid,
lookupAccountCapacitiesBySid, lookupAccountCapacitiesBySid,
lookupSmppGateways, lookupSmppGateways,
lookupClientByAccountAndUsername,
updateCallStatus, updateCallStatus,
retrieveCall, retrieveCall,
listCalls, listCalls,

View File

@@ -334,6 +334,7 @@ class SingleDialer extends Emitter {
// verify it contains only allowed verbs // verify it contains only allowed verbs
const allowedTasks = tasks.filter((task) => { const allowedTasks = tasks.filter((task) => {
return [ return [
TaskPreconditions.None,
TaskPreconditions.StableCall, TaskPreconditions.StableCall,
TaskPreconditions.Endpoint TaskPreconditions.Endpoint
].includes(task.preconditions); ].includes(task.preconditions);

View File

@@ -8,8 +8,9 @@ const {
SonioxTranscriptionEvents, SonioxTranscriptionEvents,
NvidiaTranscriptionEvents, NvidiaTranscriptionEvents,
CobaltTranscriptionEvents, CobaltTranscriptionEvents,
JambonzTranscriptionEvents JambonzTranscriptionEvents,
} = require('./constants'); AssemblyAiTranscriptionEvents
} = require('./constants.json');
const stickyVars = { const stickyVars = {
google: [ google: [
@@ -104,6 +105,10 @@ const stickyVars = {
soniox: [ soniox: [
'SONIOX_PROFANITY_FILTER', 'SONIOX_PROFANITY_FILTER',
'SONIOX_MODEL' 'SONIOX_MODEL'
],
assemblyai: [
'ASSEMBLYAI_API_KEY',
'ASSEMBLYAI_WORD_BOOST'
] ]
}; };
@@ -375,6 +380,24 @@ const normalizeAws = (evt, channel, language) => {
}; };
}; };
const normalizeAssemblyAi = (evt, channel, language) => {
const copy = JSON.parse(JSON.stringify(evt));
return {
language_code: language,
channel_tag: channel,
is_final: evt.message_type === 'FinalTranscript',
alternatives: [
{
confidence: evt.confidence,
transcript: evt.text,
}
],
vendor: {
name: 'ASSEMBLYAI',
evt: copy
}
};
};
module.exports = (logger) => { module.exports = (logger) => {
const normalizeTranscription = (evt, vendor, channel, language, shortUtterance) => { const normalizeTranscription = (evt, vendor, channel, language, shortUtterance) => {
@@ -399,6 +422,8 @@ module.exports = (logger) => {
return normalizeSoniox(evt, channel, language); return normalizeSoniox(evt, channel, language);
case 'cobalt': case 'cobalt':
return normalizeCobalt(evt, channel, language); return normalizeCobalt(evt, channel, language);
case 'assemblyai':
return normalizeAssemblyAi(evt, channel, language, shortUtterance);
default: default:
if (vendor.startsWith('custom:')) { if (vendor.startsWith('custom:')) {
return normalizeCustom(evt, channel, language, vendor); return normalizeCustom(evt, channel, language, vendor);
@@ -692,6 +717,14 @@ module.exports = (logger) => {
...(cobaltOptions.enableConfusionNetwork && {COBALT_ENABLE_CONFUSION_NETWORK: 1}), ...(cobaltOptions.enableConfusionNetwork && {COBALT_ENABLE_CONFUSION_NETWORK: 1}),
...(cobaltOptions.compiledContextData && {COBALT_COMPILED_CONTEXT_DATA: cobaltOptions.compiledContextData}), ...(cobaltOptions.compiledContextData && {COBALT_COMPILED_CONTEXT_DATA: cobaltOptions.compiledContextData}),
}; };
} else if ('assemblyai' === vendor) {
opts = {
...opts,
...(sttCredentials.api_key) &&
{ASSEMBLYAI_API_KEY: sttCredentials.api_key},
...(rOpts.hints?.length > 0 &&
{ASSEMBLYAI_WORD_BOOST: JSON.stringify(rOpts.hints)})
};
} }
else if (vendor.startsWith('custom:')) { else if (vendor.startsWith('custom:')) {
let {options = {}} = rOpts; let {options = {}} = rOpts;
@@ -755,6 +788,10 @@ module.exports = (logger) => {
ep.removeCustomEventListener(JambonzTranscriptionEvents.ConnectFailure); ep.removeCustomEventListener(JambonzTranscriptionEvents.ConnectFailure);
ep.removeCustomEventListener(JambonzTranscriptionEvents.Error); ep.removeCustomEventListener(JambonzTranscriptionEvents.Error);
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.Transcription);
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.Connect);
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure);
}; };
const setSpeechCredentialsAtRuntime = (recognizer) => { const setSpeechCredentialsAtRuntime = (recognizer) => {

28
package-lock.json generated
View File

@@ -18,7 +18,7 @@
"@jambonz/speech-utils": "^0.0.24", "@jambonz/speech-utils": "^0.0.24",
"@jambonz/stats-collector": "^0.1.9", "@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.8", "@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.44", "@jambonz/verb-specifications": "^0.0.45",
"@opentelemetry/api": "^1.4.0", "@opentelemetry/api": "^1.4.0",
"@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-jaeger": "^1.9.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.35.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0",
@@ -32,7 +32,7 @@
"debug": "^4.3.4", "debug": "^4.3.4",
"deepcopy": "^2.1.0", "deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.27", "drachtio-fsmrf": "^3.0.27",
"drachtio-srf": "^4.5.29", "drachtio-srf": "^4.5.31",
"express": "^4.18.2", "express": "^4.18.2",
"express-validator": "^7.0.1", "express-validator": "^7.0.1",
"ip": "^1.1.8", "ip": "^1.1.8",
@@ -3181,9 +3181,9 @@
} }
}, },
"node_modules/@jambonz/verb-specifications": { "node_modules/@jambonz/verb-specifications": {
"version": "0.0.44", "version": "0.0.45",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.44.tgz", "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.45.tgz",
"integrity": "sha512-mXTbZlJ3AprxooSNvEHYt/9wsky4wHT4mJmL2XrkZGQY6fG/LzVNFVy0Tvx0xZzAVJMY9SmNcDiM0HBNnAufIg==", "integrity": "sha512-0cC7cfyXuOlqjfrtA9GC7A84efInj4z+ZSsibONqHMw3FVJE5IvcvabRojarDHooIn9Uw6AEX/zZ7BZqfgVmJw==",
"dependencies": { "dependencies": {
"debug": "^4.3.4", "debug": "^4.3.4",
"pino": "^8.8.0" "pino": "^8.8.0"
@@ -5360,9 +5360,9 @@
} }
}, },
"node_modules/drachtio-srf": { "node_modules/drachtio-srf": {
"version": "4.5.29", "version": "4.5.31",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.29.tgz", "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.31.tgz",
"integrity": "sha512-Hj2OW+SyQxAyLpyHngJwW26FX9V4fDYZGX0mlU4YOF4ml7I7b7XITcRPxKhroYDOrLgKqhNeh5BcPoKqPhEK7A==", "integrity": "sha512-/M4J8h2aqHtMXWr8/UHngKQsY9sQQxjdd23jDTSpNVpCwgZ2/xZFhbg/B/UCjrarSRzbyDCvuluOAtaPRSw7Hw==",
"dependencies": { "dependencies": {
"debug": "^3.2.7", "debug": "^3.2.7",
"delegates": "^0.1.0", "delegates": "^0.1.0",
@@ -13275,9 +13275,9 @@
} }
}, },
"@jambonz/verb-specifications": { "@jambonz/verb-specifications": {
"version": "0.0.44", "version": "0.0.45",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.44.tgz", "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.45.tgz",
"integrity": "sha512-mXTbZlJ3AprxooSNvEHYt/9wsky4wHT4mJmL2XrkZGQY6fG/LzVNFVy0Tvx0xZzAVJMY9SmNcDiM0HBNnAufIg==", "integrity": "sha512-0cC7cfyXuOlqjfrtA9GC7A84efInj4z+ZSsibONqHMw3FVJE5IvcvabRojarDHooIn9Uw6AEX/zZ7BZqfgVmJw==",
"requires": { "requires": {
"debug": "^4.3.4", "debug": "^4.3.4",
"pino": "^8.8.0" "pino": "^8.8.0"
@@ -14944,9 +14944,9 @@
} }
}, },
"drachtio-srf": { "drachtio-srf": {
"version": "4.5.29", "version": "4.5.31",
"resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.29.tgz", "resolved": "https://registry.npmjs.org/drachtio-srf/-/drachtio-srf-4.5.31.tgz",
"integrity": "sha512-Hj2OW+SyQxAyLpyHngJwW26FX9V4fDYZGX0mlU4YOF4ml7I7b7XITcRPxKhroYDOrLgKqhNeh5BcPoKqPhEK7A==", "integrity": "sha512-/M4J8h2aqHtMXWr8/UHngKQsY9sQQxjdd23jDTSpNVpCwgZ2/xZFhbg/B/UCjrarSRzbyDCvuluOAtaPRSw7Hw==",
"requires": { "requires": {
"debug": "^3.2.7", "debug": "^3.2.7",
"delegates": "^0.1.0", "delegates": "^0.1.0",

View File

@@ -34,7 +34,7 @@
"@jambonz/speech-utils": "^0.0.24", "@jambonz/speech-utils": "^0.0.24",
"@jambonz/stats-collector": "^0.1.9", "@jambonz/stats-collector": "^0.1.9",
"@jambonz/time-series": "^0.2.8", "@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.44", "@jambonz/verb-specifications": "^0.0.45",
"@opentelemetry/api": "^1.4.0", "@opentelemetry/api": "^1.4.0",
"@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-jaeger": "^1.9.0",
"@opentelemetry/exporter-trace-otlp-http": "^0.35.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0",
@@ -48,7 +48,7 @@
"debug": "^4.3.4", "debug": "^4.3.4",
"deepcopy": "^2.1.0", "deepcopy": "^2.1.0",
"drachtio-fsmrf": "^3.0.27", "drachtio-fsmrf": "^3.0.27",
"drachtio-srf": "^4.5.29", "drachtio-srf": "^4.5.31",
"express": "^4.18.2", "express": "^4.18.2",
"express-validator": "^7.0.1", "express-validator": "^7.0.1",
"ip": "^1.1.8", "ip": "^1.1.8",