initial changes for soniox (#270)

* initial changes for soniox

* changes to gather for soniox

* parse soniox stt results

* handle <end> token for soniox

* soniox: handle empty array of words

* support for soniox hints

* add soniox storage options

* update to verb specs

* add support for transcribe

* compile soniox transcripts

* gather: kill no input timer for soniox when we get interim results

* fix buffering of soniox transcripts

* fix for compiling soniox transcript

* another fix for compiling soniox transcript

* another fix

* handling of <end> token

* fix soniox bug

* gather: fixes for soniox continous asr

* fix undefined variable reference

* fix prev commit

* bugfix: allow verb_status requests

* gather: for soniox no need to restart transcription after final transcription received

* update verb specs

* update verb specs, fixes for continuous asr:
This commit is contained in:
Dave Horton
2023-03-03 13:37:55 -05:00
committed by GitHub
parent ab1947e23e
commit 1c683f1142
10 changed files with 272 additions and 33 deletions

View File

@@ -645,6 +645,12 @@ class CallSession extends Emitter {
api_key: credential.api_key api_key: credential.api_key
}; };
} }
else if ('soniox' === vendor) {
return {
speech_credential_sid: credential.speech_credential_sid,
api_key: credential.api_key
};
}
else if ('ibm' === vendor) { else if ('ibm' === vendor) {
return { return {
speech_credential_sid: credential.speech_credential_sid, speech_credential_sid: credential.speech_credential_sid,

View File

@@ -7,6 +7,7 @@ const {
AwsTranscriptionEvents, AwsTranscriptionEvents,
AzureTranscriptionEvents, AzureTranscriptionEvents,
DeepgramTranscriptionEvents, DeepgramTranscriptionEvents,
SonioxTranscriptionEvents,
IbmTranscriptionEvents, IbmTranscriptionEvents,
NvidiaTranscriptionEvents NvidiaTranscriptionEvents
} = require('../utils/constants'); } = require('../utils/constants');
@@ -33,11 +34,13 @@ class TaskGather 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;
[ [
'finishOnKey', 'input', 'numDigits', 'minDigits', 'maxDigits', 'finishOnKey', 'input', 'numDigits', 'minDigits', 'maxDigits',
@@ -85,6 +88,9 @@ class TaskGather extends Task {
/* buffer speech for continuous asr */ /* buffer speech for continuous asr */
this._bufferedTranscripts = []; this._bufferedTranscripts = [];
/* buffer for soniox transcripts */
this._sonioxTranscripts = [];
this.parentTask = parentTask; this.parentTask = parentTask;
} }
@@ -288,6 +294,7 @@ class TaskGather extends Task {
this._killAudio(cs); this._killAudio(cs);
this.ep.removeAllListeners('dtmf'); this.ep.removeAllListeners('dtmf');
clearTimeout(this.interDigitTimer); clearTimeout(this.interDigitTimer);
this._clearAsrTimer();
this.playTask?.span.end(); this.playTask?.span.end();
this.sayTask?.span.end(); this.sayTask?.span.end();
this._resolve('killed'); this._resolve('killed');
@@ -389,6 +396,13 @@ class TaskGather extends Task {
this._onDeepGramConnectFailure.bind(this, cs, ep)); this._onDeepGramConnectFailure.bind(this, cs, ep));
break; break;
case 'soniox':
this.bugname = 'soniox_transcribe';
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(SonioxTranscriptionEvents.Error,
this._onSonioxError.bind(this, cs, ep));
break;
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));
@@ -523,7 +537,7 @@ class TaskGather extends Task {
// make sure this is not a transcript from answering machine detection // make sure this is not a transcript from answering machine detection
const bugname = fsEvent.getHeader('media-bugname'); const bugname = fsEvent.getHeader('media-bugname');
const finished = fsEvent.getHeader('transcription-session-finished'); const finished = fsEvent.getHeader('transcription-session-finished');
this.logger.debug({evt, bugname, finished}, 'Gather:_onTranscription'); this.logger.debug({evt, bugname, finished}, `Gather:_onTranscription for vendor ${this.vendor}`);
if (bugname && this.bugname !== bugname) return; if (bugname && this.bugname !== bugname) return;
if (this.vendor === 'ibm') { if (this.vendor === 'ibm') {
@@ -544,9 +558,8 @@ class TaskGather extends Task {
/* count words for bargein feature */ /* count words for bargein feature */
const words = evt.alternatives[0]?.transcript.split(' ').length; const words = evt.alternatives[0]?.transcript.split(' ').length;
const bufferedWords = this._bufferedTranscripts.reduce((count, e) => { const bufferedWords = this._sonioxTranscripts.length +
return count + e.alternatives[0]?.transcript.split(' ').length; this._bufferedTranscripts.reduce((count, e) => count + e.alternatives[0]?.transcript.split(' ').length, 0);
}, 0);
if (evt.is_final) { if (evt.is_final) {
if (evt.alternatives[0].transcript === '' && !this.callSession.callGone && !this.killed) { if (evt.alternatives[0].transcript === '' && !this.callSession.callGone && !this.killed) {
@@ -555,7 +568,6 @@ class TaskGather extends Task {
} }
else { else {
this.logger.info({evt}, 'TaskGather:_onTranscription - got empty transcript, continue listening'); this.logger.info({evt}, 'TaskGather:_onTranscription - got empty transcript, continue listening');
//this._startTranscribing(ep);
} }
return; return;
} }
@@ -579,7 +591,9 @@ class TaskGather extends Task {
return this._resolve(this._bufferedTranscripts.length > 0 ? 'speech' : 'timeout'); return this._resolve(this._bufferedTranscripts.length > 0 ? 'speech' : 'timeout');
} }
this._startAsrTimer(); this._startAsrTimer();
return this._startTranscribing(ep);
/* some STT engines will keep listening after a final response, so no need to restart */
if (!['soniox', 'aws', 'microsoft', 'deepgram'].includes(this.vendor)) this._startTranscribing(ep);
} }
else { else {
if (this.bargein && (words + bufferedWords) < this.minBargeinWordCount) { if (this.bargein && (words + bufferedWords) < this.minBargeinWordCount) {
@@ -590,6 +604,12 @@ class TaskGather extends Task {
return; return;
} }
else { else {
if (this.vendor === 'soniox') {
/* compile transcripts into one */
this._sonioxTranscripts.push(evt.vendor.finalWords);
evt = this.compileSonioxTranscripts(this._sonioxTranscripts, 1, this.language);
this._sonioxTranscripts = [];
}
this._resolve('speech', evt); this._resolve('speech', evt);
} }
} }
@@ -613,6 +633,13 @@ class TaskGather extends Task {
this.cs.requestor.request('verb:hook', this.partialResultHook, Object.assign({speech: evt}, this.cs.requestor.request('verb:hook', this.partialResultHook, Object.assign({speech: evt},
this.cs.callInfo, httpHeaders)); this.cs.callInfo, httpHeaders));
} }
if (this.vendor === 'soniox') {
this._clearTimer();
if (evt.vendor.finalWords.length) {
this.logger.debug({evt}, 'TaskGather:_onTranscription - buffering soniox transcript');
this._sonioxTranscripts.push(evt.vendor.finalWords);
}
}
} }
} }
_onEndOfUtterance(cs, ep) { _onEndOfUtterance(cs, ep) {
@@ -643,6 +670,9 @@ class TaskGather extends Task {
return this._resolve('timeout'); return this._resolve('timeout');
} }
} }
_onSonioxError(cs, ep, evt) {
this.logger.info({evt}, 'TaskGather:_onSonioxError');
}
_onNvidiaError(cs, ep, evt) { _onNvidiaError(cs, ep, evt) {
this.logger.info({evt}, 'TaskGather:_onNvidiaError'); this.logger.info({evt}, 'TaskGather:_onNvidiaError');
} }

View File

@@ -3,10 +3,11 @@ const {
TaskName, TaskName,
TaskPreconditions, TaskPreconditions,
GoogleTranscriptionEvents, GoogleTranscriptionEvents,
AzureTranscriptionEvents,
AwsTranscriptionEvents,
NuanceTranscriptionEvents, NuanceTranscriptionEvents,
AwsTranscriptionEvents,
AzureTranscriptionEvents,
DeepgramTranscriptionEvents, DeepgramTranscriptionEvents,
SonioxTranscriptionEvents,
IbmTranscriptionEvents, IbmTranscriptionEvents,
NvidiaTranscriptionEvents NvidiaTranscriptionEvents
} = require('../utils/constants'); } = require('../utils/constants');
@@ -195,7 +196,12 @@ class TaskTranscribe extends Task {
ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure, ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure,
this._onDeepGramConnectFailure.bind(this, cs, ep, channel)); this._onDeepGramConnectFailure.bind(this, cs, ep, channel));
break; break;
case 'soniox':
this.bugname = 'soniox_transcribe';
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
ep.addCustomEventListener(SonioxTranscriptionEvents.Error,
this._onSonioxError.bind(this, cs, ep));
break;
case 'ibm': case 'ibm':
this.bugname = 'ibm_transcribe'; this.bugname = 'ibm_transcribe';
ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, ep.addCustomEventListener(IbmTranscriptionEvents.Transcription,
@@ -326,8 +332,11 @@ class TaskTranscribe extends Task {
return this._resolve('timeout'); return this._resolve('timeout');
} }
} }
_onSonioxError(cs, ep, evt) {
this.logger.info({evt}, 'TaskTranscribe:_onSonioxError');
}
_onNvidiaError(cs, ep, evt) { _onNvidiaError(cs, ep, evt) {
this.logger.info({evt}, 'TaskGather:_onNvidiaError'); this.logger.info({evt}, 'TaskTranscribe:_onNvidiaError');
} }
_onDeepgramConnect(_cs, _ep) { _onDeepgramConnect(_cs, _ep) {
this.logger.debug('TaskTranscribe:_onDeepgramConnect'); this.logger.debug('TaskTranscribe:_onDeepgramConnect');
@@ -365,7 +374,7 @@ class TaskTranscribe extends Task {
this.notifyTaskDone(); this.notifyTaskDone();
} }
_onIbmError(cs, _ep, _channel, evt) { _onIbmError(cs, _ep, _channel, evt) {
this.logger.info({evt}, 'TaskGather:_onIbmError'); this.logger.info({evt}, 'TaskTranscribe:_onIbmError');
} }

View File

@@ -86,6 +86,10 @@
"ConnectFailure": "deepgram_transcribe::connect_failed", "ConnectFailure": "deepgram_transcribe::connect_failed",
"Connect": "deepgram_transcribe::connect" "Connect": "deepgram_transcribe::connect"
}, },
"SonioxTranscriptionEvents": {
"Transcription": "soniox_transcribe::transcription",
"Error": "soniox_transcribe::error"
},
"IbmTranscriptionEvents": { "IbmTranscriptionEvents": {
"Transcription": "ibm_transcribe::transcription", "Transcription": "ibm_transcribe::transcription",
"ConnectFailure": "ibm_transcribe::connect_failed", "ConnectFailure": "ibm_transcribe::connect_failed",
@@ -147,6 +151,7 @@
"queue:status", "queue:status",
"dial:confirm", "dial:confirm",
"verb:hook", "verb:hook",
"verb:status",
"jambonz:error" "jambonz:error"
], ],
"RecordState": { "RecordState": {

View File

@@ -62,6 +62,10 @@ 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 ('soniox' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = o.api_key;
}
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@@ -86,8 +90,10 @@ module.exports = (logger, srf) => {
const haveWellsaid = speech.find((s) => s.vendor === 'wellsaid'); const haveWellsaid = speech.find((s) => s.vendor === 'wellsaid');
const haveNuance = speech.find((s) => s.vendor === 'nuance'); const haveNuance = speech.find((s) => s.vendor === 'nuance');
const haveDeepgram = speech.find((s) => s.vendor === 'deepgram'); const haveDeepgram = speech.find((s) => s.vendor === 'deepgram');
const haveSoniox = speech.find((s) => s.vendor === 'soniox');
const haveIbm = speech.find((s) => s.vendor === 'ibm'); const haveIbm = speech.find((s) => s.vendor === 'ibm');
if (!haveGoogle || !haveAws || !haveMicrosoft || !haveWellsaid || !haveNuance || !haveIbm || !haveDeepgram) { 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) { if (r3.length) {
if (!haveGoogle) { if (!haveGoogle) {
@@ -114,6 +120,10 @@ module.exports = (logger, srf) => {
const deepgram = r3.find((s) => s.vendor === 'deepgram'); const deepgram = r3.find((s) => s.vendor === 'deepgram');
if (deepgram) speech.push(speechMapper(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) { if (!haveIbm) {
const ibm = r3.find((s) => s.vendor === 'ibm'); const ibm = r3.find((s) => s.vendor === 'ibm');
if (ibm) speech.push(speechMapper(ibm)); if (ibm) speech.push(speechMapper(ibm));

View File

@@ -5,6 +5,7 @@ const {
AwsTranscriptionEvents, AwsTranscriptionEvents,
NuanceTranscriptionEvents, NuanceTranscriptionEvents,
DeepgramTranscriptionEvents, DeepgramTranscriptionEvents,
SonioxTranscriptionEvents,
NvidiaTranscriptionEvents NvidiaTranscriptionEvents
} = require('./constants'); } = require('./constants');
@@ -88,9 +89,70 @@ const stickyVars = {
], ],
nvidia: [ nvidia: [
'NVIDIA_HINTS' 'NVIDIA_HINTS'
],
soniox: [
'SONIOX_PROFANITY_FILTER',
'SONIOX_MODEL'
] ]
}; };
const compileSonioxTranscripts = (finalWordChunks, channel, language) => {
const words = finalWordChunks.flat();
const transcript = words.reduce((acc, word) => {
if (word.text === '<end>') return acc;
if ([',', '.', '?', '!'].includes(word.text)) return `${acc}${word.text}`;
return `${acc} ${word.text}`;
}, '').trim();
const realWords = words.filter((word) => ![',.!?;'].includes(word.text) && word.text !== '<end>');
const confidence = realWords.reduce((acc, word) => acc + word.confidence, 0) / realWords.length;
const alternatives = [{transcript, confidence}];
return {
language_code: language,
channel_tag: channel,
is_final: true,
alternatives,
vendor: {
name: 'soniox',
evt: words
}
};
};
const normalizeSoniox = (evt, channel, language) => {
const copy = JSON.parse(JSON.stringify(evt));
/* an <end> token indicates the end of an utterance */
const endTokenPos = evt.words.map((w) => w.text).indexOf('<end>');
const endpointReached = endTokenPos !== -1;
const words = endpointReached ? evt.words.slice(0, endTokenPos) : evt.words;
/* note: we can safely ignore words after the <end> token as they will be returned again */
const finalWords = words.filter((word) => word.is_final);
const nonFinalWords = words.filter((word) => !word.is_final);
const is_final = endpointReached && finalWords.length > 0;
const transcript = words.reduce((acc, word) => {
if ([',', '.', '?', '!'].includes(word.text)) return `${acc}${word.text}`;
else return `${acc} ${word.text}`;
}, '').trim();
const realWords = words.filter((word) => ![',.!?;'].includes(word.text) && word.text !== '<end>');
const confidence = realWords.reduce((acc, word) => acc + word.confidence, 0) / realWords.length;
const alternatives = [{transcript, confidence}];
return {
language_code: language,
channel_tag: channel,
is_final,
alternatives,
vendor: {
name: 'soniox',
endpointReached,
evt: copy,
finalWords,
nonFinalWords
}
};
};
const normalizeDeepgram = (evt, channel, language) => { const normalizeDeepgram = (evt, channel, language) => {
const copy = JSON.parse(JSON.stringify(evt)); const copy = JSON.parse(JSON.stringify(evt));
const alternatives = (evt.channel?.alternatives || []) const alternatives = (evt.channel?.alternatives || [])
@@ -237,6 +299,8 @@ module.exports = (logger) => {
return normalizeIbm(evt, channel, language); return normalizeIbm(evt, channel, language);
case 'nvidia': case 'nvidia':
return normalizeNvidia(evt, channel, language); return normalizeNvidia(evt, channel, language);
case 'soniox':
return normalizeSoniox(evt, channel, language);
default: default:
logger.error(`Unknown vendor ${vendor}`); logger.error(`Unknown vendor ${vendor}`);
return evt; return evt;
@@ -441,6 +505,29 @@ module.exports = (logger) => {
{DEEPGRAM_SPEECH_TAG: deepgramOptions.tag} {DEEPGRAM_SPEECH_TAG: deepgramOptions.tag}
}; };
} }
else if ('soniox' === rOpts.vendor) {
const {sonioxOptions = {}} = rOpts;
const {storage = {}} = sonioxOptions;
opts = {
...opts,
...(sttCredentials.api_key) &&
{SONIOX_API_KEY: sttCredentials.api_key},
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'string' &&
{SONIOX_HINTS: rOpts.hints.join(',')}),
...(rOpts.hints.length > 0 && typeof rOpts.hints[0] === 'object' &&
{SONIOX_HINTS: JSON.stringify(rOpts.hints)}),
...(typeof rOpts.hintsBoost === 'number' &&
{SONIOX_HINTS_BOOST: rOpts.hintsBoost}),
...(sonioxOptions.model) &&
{SONIOX_MODEL: sonioxOptions.model},
...((sonioxOptions.profanityFilter || rOpts.profanityFilter) && {SONIOX_PROFANITY_FILTER: 1}),
...(storage?.id && {SONIOX_STORAGE_ID: storage.id}),
...(storage?.id && storage?.title && {SONIOX_STORAGE_TITLE: storage.title}),
...(storage?.id && storage?.disableStoreAudio && {SONIOX_STORAGE_DISABLE_AUDIO: 1}),
...(storage?.id && storage?.disableStoreTranscript && {SONIOX_STORAGE_DISABLE_TRANSCRIPT: 1}),
...(storage?.id && storage?.disableSearch && {SONIOX_STORAGE_DISABLE_SEARCH: 1})
};
}
else if ('ibm' === rOpts.vendor) { else if ('ibm' === rOpts.vendor) {
const {ibmOptions = {}} = rOpts; const {ibmOptions = {}} = rOpts;
opts = { opts = {
@@ -524,6 +611,9 @@ module.exports = (logger) => {
ep.removeCustomEventListener(DeepgramTranscriptionEvents.Connect); ep.removeCustomEventListener(DeepgramTranscriptionEvents.Connect);
ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure); ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure);
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);
@@ -534,8 +624,9 @@ module.exports = (logger) => {
const setSpeechCredentialsAtRuntime = (recognizer) => { const setSpeechCredentialsAtRuntime = (recognizer) => {
if (!recognizer) return; if (!recognizer) return;
if (recognizer.vendor === 'nuance') { if (recognizer.vendor === 'nuance') {
const {clientId, secret} = recognizer.nuanceOptions || {}; const {clientId, secret, kryptonEndpoint} = recognizer.nuanceOptions || {};
if (clientId && secret) return {client_id: clientId, secret}; if (clientId && secret) return {client_id: clientId, secret};
if (kryptonEndpoint) return {krypton_endpoint: kryptonEndpoint};
} }
else if (recognizer.vendor === 'nvidia') { else if (recognizer.vendor === 'nvidia') {
const {rivaUri} = recognizer.nvidiaOptions || {}; const {rivaUri} = recognizer.nvidiaOptions || {};
@@ -545,6 +636,10 @@ module.exports = (logger) => {
const {apiKey} = recognizer.deepgramOptions || {}; const {apiKey} = recognizer.deepgramOptions || {};
if (apiKey) return {api_key: apiKey}; if (apiKey) return {api_key: apiKey};
} }
else if (recognizer.vendor === 'soniox') {
const {apiKey} = recognizer.sonioxOptions || {};
if (apiKey) return {api_key: apiKey};
}
else if (recognizer.vendor === 'ibm') { else if (recognizer.vendor === 'ibm') {
const {ttsApiKey, ttsRegion, sttApiKey, sttRegion, instanceId} = recognizer.ibmOptions || {}; const {ttsApiKey, ttsRegion, sttApiKey, sttRegion, instanceId} = recognizer.ibmOptions || {};
if (ttsApiKey || sttApiKey) return { if (ttsApiKey || sttApiKey) return {
@@ -561,6 +656,7 @@ module.exports = (logger) => {
normalizeTranscription, normalizeTranscription,
setChannelVarsForStt, setChannelVarsForStt,
removeSpeechListeners, removeSpeechListeners,
setSpeechCredentialsAtRuntime setSpeechCredentialsAtRuntime,
compileSonioxTranscripts
}; };
}; };

28
package-lock.json generated
View File

@@ -14,7 +14,7 @@
"@jambonz/realtimedb-helpers": "^0.6.5", "@jambonz/realtimedb-helpers": "^0.6.5",
"@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.4", "@jambonz/verb-specifications": "^0.0.11",
"@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",
@@ -28,7 +28,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.18", "drachtio-fsmrf": "^3.0.19",
"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",
@@ -675,9 +675,9 @@
} }
}, },
"node_modules/@jambonz/verb-specifications": { "node_modules/@jambonz/verb-specifications": {
"version": "0.0.4", "version": "0.0.11",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.4.tgz", "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.11.tgz",
"integrity": "sha512-Y3aN6NyiTqi0AdGFG9/sZwurG2ogCYANJpekVThlUNHTtg6UVIY6Tt+ND8+Hfc6UvNRZWAQIkugzpZvJFJ0faA==", "integrity": "sha512-T5ZITmOkcXOIBSH/vSGIQ1PXyZfOtdpo/DpJQ82gdIfYhm4AotMVbS5YABj1RFj4ergNnkoHmmhgJttgpMf0dw==",
"dependencies": { "dependencies": {
"debug": "^4.3.4", "debug": "^4.3.4",
"pino": "^8.8.0" "pino": "^8.8.0"
@@ -2228,9 +2228,9 @@
} }
}, },
"node_modules/drachtio-fsmrf": { "node_modules/drachtio-fsmrf": {
"version": "3.0.18", "version": "3.0.19",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.18.tgz", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.19.tgz",
"integrity": "sha512-vWltwmIYeAapE2R5ise2HZgRS+8aGlQMXN6aTUxKGnvzGPD9+D29or+Y8HtodYG/2RKaPcaeUOjjsmnUaoWU2A==", "integrity": "sha512-MMofkpNCMxk58mIaXOe2jwTPwZ5f9AEH0eqqBaBR5a9qP8n7/xES18Qd7KaqDAOocTn99t0cl1k8Y+54F+lwfQ==",
"dependencies": { "dependencies": {
"camel-case": "^4.1.2", "camel-case": "^4.1.2",
"debug": "^2.6.9", "debug": "^2.6.9",
@@ -8132,9 +8132,9 @@
} }
}, },
"@jambonz/verb-specifications": { "@jambonz/verb-specifications": {
"version": "0.0.4", "version": "0.0.11",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.4.tgz", "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.11.tgz",
"integrity": "sha512-Y3aN6NyiTqi0AdGFG9/sZwurG2ogCYANJpekVThlUNHTtg6UVIY6Tt+ND8+Hfc6UvNRZWAQIkugzpZvJFJ0faA==", "integrity": "sha512-T5ZITmOkcXOIBSH/vSGIQ1PXyZfOtdpo/DpJQ82gdIfYhm4AotMVbS5YABj1RFj4ergNnkoHmmhgJttgpMf0dw==",
"requires": { "requires": {
"debug": "^4.3.4", "debug": "^4.3.4",
"pino": "^8.8.0" "pino": "^8.8.0"
@@ -9324,9 +9324,9 @@
} }
}, },
"drachtio-fsmrf": { "drachtio-fsmrf": {
"version": "3.0.18", "version": "3.0.19",
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.18.tgz", "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.19.tgz",
"integrity": "sha512-vWltwmIYeAapE2R5ise2HZgRS+8aGlQMXN6aTUxKGnvzGPD9+D29or+Y8HtodYG/2RKaPcaeUOjjsmnUaoWU2A==", "integrity": "sha512-MMofkpNCMxk58mIaXOe2jwTPwZ5f9AEH0eqqBaBR5a9qP8n7/xES18Qd7KaqDAOocTn99t0cl1k8Y+54F+lwfQ==",
"requires": { "requires": {
"camel-case": "^4.1.2", "camel-case": "^4.1.2",
"debug": "^2.6.9", "debug": "^2.6.9",

View File

@@ -29,7 +29,7 @@
"@jambonz/realtimedb-helpers": "^0.6.5", "@jambonz/realtimedb-helpers": "^0.6.5",
"@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.4", "@jambonz/verb-specifications": "^0.0.11",
"@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",
@@ -43,7 +43,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.18", "drachtio-fsmrf": "^3.0.19",
"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",

View File

@@ -207,6 +207,48 @@ test('\'gather\' test - deepgram', async(t) => {
//console.log(JSON.stringify(obj)); //console.log(JSON.stringify(obj));
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'), t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
'gather: succeeds when using deepgram credentials'); 'gather: succeeds when using deepgram credentials');
disconnect();
} catch (err) {
console.log(`error received: ${err}`);
disconnect();
t.error(err);
}
});
test('\'gather\' test - soniox', async(t) => {
if (!process.env.SONIOX_API_KEY ) {
t.pass('skipping soniox tests');
return t.end();
}
clearModule.all();
const {srf, disconnect} = require('../app');
try {
await connect(srf);
// GIVEN
let verbs = [
{
"verb": "gather",
"input": ["speech"],
"recognizer": {
"vendor": "deepgram",
"hints": ["customer support", "sales", "human resources", "HR"],
"deepgramOptions": {
"apiKey": process.env.SONIOX_API_KEY
}
},
"timeout": 10,
"actionHook": "/actionHook"
}
];
let from = "gather_success";
provisionCallHook(from, verbs);
// THEN
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`);
console.log(JSON.stringify(obj));
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
'gather: succeeds when using soniox credentials');
disconnect(); disconnect();
} catch (err) { } catch (err) {

View File

@@ -143,7 +143,7 @@ test('\'transcribe\' test - deepgram', async(t) => {
{ {
"verb": "transcribe", "verb": "transcribe",
"recognizer": { "recognizer": {
"vendor": "aws", "vendor": "deepgram",
"hints": ["customer support", "sales", "human resources", "HR"], "hints": ["customer support", "sales", "human resources", "HR"],
"deepgramOptions": { "deepgramOptions": {
"apiKey": process.env.DEEPGRAM_API_KEY "apiKey": process.env.DEEPGRAM_API_KEY
@@ -167,3 +167,44 @@ test('\'transcribe\' test - deepgram', async(t) => {
t.error(err); t.error(err);
} }
}); });
test('\'transcribe\' test - soniox', async(t) => {
if (!process.env.SONIOX_API_KEY ) {
t.pass('skipping soniox tests');
return t.end();
}
clearModule.all();
const {srf, disconnect} = require('../app');
try {
await connect(srf);
// GIVEN
let verbs = [
{
"verb": "transcribe",
"recognizer": {
"vendor": "soniox",
"hints": ["customer support", "sales", "human resources", "HR"],
"deepgramOptions": {
"apiKey": process.env.SONIOX_API_KEY
}
},
"transcriptionHook": "/transcriptionHook"
}
];
let from = "gather_success";
provisionCallHook(from, verbs);
// THEN
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
let obj = await getJSON(`http://127.0.0.1:3100/lastRequest/${from}_actionHook`);
console.log(JSON.stringify(obj));
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
'transcribe: succeeds when using soniox credentials');
disconnect();
} catch (err) {
console.log(`error received: ${err}`);
disconnect();
t.error(err);
}
});