diff --git a/lib/session/call-session.js b/lib/session/call-session.js index 0475d388..f5545b15 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -645,6 +645,12 @@ class CallSession extends Emitter { 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) { return { speech_credential_sid: credential.speech_credential_sid, diff --git a/lib/tasks/gather.js b/lib/tasks/gather.js index 5aaec43c..1476fb82 100644 --- a/lib/tasks/gather.js +++ b/lib/tasks/gather.js @@ -7,6 +7,7 @@ const { AwsTranscriptionEvents, AzureTranscriptionEvents, DeepgramTranscriptionEvents, + SonioxTranscriptionEvents, IbmTranscriptionEvents, NvidiaTranscriptionEvents } = require('../utils/constants'); @@ -33,11 +34,13 @@ class TaskGather extends Task { setChannelVarsForStt, normalizeTranscription, removeSpeechListeners, - setSpeechCredentialsAtRuntime + setSpeechCredentialsAtRuntime, + compileSonioxTranscripts } = require('../utils/transcription-utils')(logger); this.setChannelVarsForStt = setChannelVarsForStt; this.normalizeTranscription = normalizeTranscription; this.removeSpeechListeners = removeSpeechListeners; + this.compileSonioxTranscripts = compileSonioxTranscripts; [ 'finishOnKey', 'input', 'numDigits', 'minDigits', 'maxDigits', @@ -85,6 +88,9 @@ class TaskGather extends Task { /* buffer speech for continuous asr */ this._bufferedTranscripts = []; + /* buffer for soniox transcripts */ + this._sonioxTranscripts = []; + this.parentTask = parentTask; } @@ -288,6 +294,7 @@ class TaskGather extends Task { this._killAudio(cs); this.ep.removeAllListeners('dtmf'); clearTimeout(this.interDigitTimer); + this._clearAsrTimer(); this.playTask?.span.end(); this.sayTask?.span.end(); this._resolve('killed'); @@ -389,6 +396,13 @@ class TaskGather extends Task { this._onDeepGramConnectFailure.bind(this, cs, ep)); 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': this.bugname = 'ibm_transcribe'; 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 const bugname = fsEvent.getHeader('media-bugname'); 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 (this.vendor === 'ibm') { @@ -544,9 +558,8 @@ class TaskGather extends Task { /* count words for bargein feature */ const words = evt.alternatives[0]?.transcript.split(' ').length; - const bufferedWords = this._bufferedTranscripts.reduce((count, e) => { - return count + e.alternatives[0]?.transcript.split(' ').length; - }, 0); + const bufferedWords = this._sonioxTranscripts.length + + this._bufferedTranscripts.reduce((count, e) => count + e.alternatives[0]?.transcript.split(' ').length, 0); if (evt.is_final) { if (evt.alternatives[0].transcript === '' && !this.callSession.callGone && !this.killed) { @@ -555,7 +568,6 @@ class TaskGather extends Task { } else { this.logger.info({evt}, 'TaskGather:_onTranscription - got empty transcript, continue listening'); - //this._startTranscribing(ep); } return; } @@ -579,7 +591,9 @@ class TaskGather extends Task { return this._resolve(this._bufferedTranscripts.length > 0 ? 'speech' : 'timeout'); } 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 { if (this.bargein && (words + bufferedWords) < this.minBargeinWordCount) { @@ -590,6 +604,12 @@ class TaskGather extends Task { return; } 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); } } @@ -613,6 +633,13 @@ class TaskGather extends Task { this.cs.requestor.request('verb:hook', this.partialResultHook, Object.assign({speech: evt}, 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) { @@ -643,6 +670,9 @@ class TaskGather extends Task { return this._resolve('timeout'); } } + _onSonioxError(cs, ep, evt) { + this.logger.info({evt}, 'TaskGather:_onSonioxError'); + } _onNvidiaError(cs, ep, evt) { this.logger.info({evt}, 'TaskGather:_onNvidiaError'); } diff --git a/lib/tasks/transcribe.js b/lib/tasks/transcribe.js index 3c672d01..45341612 100644 --- a/lib/tasks/transcribe.js +++ b/lib/tasks/transcribe.js @@ -3,10 +3,11 @@ const { TaskName, TaskPreconditions, GoogleTranscriptionEvents, - AzureTranscriptionEvents, - AwsTranscriptionEvents, NuanceTranscriptionEvents, + AwsTranscriptionEvents, + AzureTranscriptionEvents, DeepgramTranscriptionEvents, + SonioxTranscriptionEvents, IbmTranscriptionEvents, NvidiaTranscriptionEvents } = require('../utils/constants'); @@ -195,7 +196,12 @@ class TaskTranscribe extends Task { ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure, this._onDeepGramConnectFailure.bind(this, cs, ep, channel)); 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': this.bugname = 'ibm_transcribe'; ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, @@ -326,8 +332,11 @@ class TaskTranscribe extends Task { return this._resolve('timeout'); } } + _onSonioxError(cs, ep, evt) { + this.logger.info({evt}, 'TaskTranscribe:_onSonioxError'); + } _onNvidiaError(cs, ep, evt) { - this.logger.info({evt}, 'TaskGather:_onNvidiaError'); + this.logger.info({evt}, 'TaskTranscribe:_onNvidiaError'); } _onDeepgramConnect(_cs, _ep) { this.logger.debug('TaskTranscribe:_onDeepgramConnect'); @@ -365,7 +374,7 @@ class TaskTranscribe extends Task { this.notifyTaskDone(); } _onIbmError(cs, _ep, _channel, evt) { - this.logger.info({evt}, 'TaskGather:_onIbmError'); + this.logger.info({evt}, 'TaskTranscribe:_onIbmError'); } diff --git a/lib/utils/constants.json b/lib/utils/constants.json index 3f7dd82d..21462d57 100644 --- a/lib/utils/constants.json +++ b/lib/utils/constants.json @@ -86,6 +86,10 @@ "ConnectFailure": "deepgram_transcribe::connect_failed", "Connect": "deepgram_transcribe::connect" }, + "SonioxTranscriptionEvents": { + "Transcription": "soniox_transcribe::transcription", + "Error": "soniox_transcribe::error" + }, "IbmTranscriptionEvents": { "Transcription": "ibm_transcribe::transcription", "ConnectFailure": "ibm_transcribe::connect_failed", @@ -147,6 +151,7 @@ "queue:status", "dial:confirm", "verb:hook", + "verb:status", "jambonz:error" ], "RecordState": { diff --git a/lib/utils/db-utils.js b/lib/utils/db-utils.js index d4fcd680..9e4f3d7d 100644 --- a/lib/utils/db-utils.js +++ b/lib/utils/db-utils.js @@ -62,6 +62,10 @@ const speechMapper = (cred) => { const o = JSON.parse(decrypt(credential)); 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) { console.log(err); } @@ -86,8 +90,10 @@ module.exports = (logger, srf) => { 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) { + if (!haveGoogle || !haveAws || !haveMicrosoft || !haveWellsaid || + !haveNuance || !haveIbm || !haveDeepgram || !haveSoniox) { const [r3] = await pp.query(sqlSpeechCredentialsForSP, account_sid); if (r3.length) { if (!haveGoogle) { @@ -114,6 +120,10 @@ module.exports = (logger, srf) => { 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)); diff --git a/lib/utils/transcription-utils.js b/lib/utils/transcription-utils.js index ca2e5c72..300ca546 100644 --- a/lib/utils/transcription-utils.js +++ b/lib/utils/transcription-utils.js @@ -5,6 +5,7 @@ const { AwsTranscriptionEvents, NuanceTranscriptionEvents, DeepgramTranscriptionEvents, + SonioxTranscriptionEvents, NvidiaTranscriptionEvents } = require('./constants'); @@ -88,9 +89,70 @@ const stickyVars = { ], nvidia: [ '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 === '') 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 !== ''); + 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 token indicates the end of an utterance */ + const endTokenPos = evt.words.map((w) => w.text).indexOf(''); + const endpointReached = endTokenPos !== -1; + const words = endpointReached ? evt.words.slice(0, endTokenPos) : evt.words; + + /* note: we can safely ignore words after the 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 !== ''); + 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 copy = JSON.parse(JSON.stringify(evt)); const alternatives = (evt.channel?.alternatives || []) @@ -237,6 +299,8 @@ module.exports = (logger) => { return normalizeIbm(evt, channel, language); case 'nvidia': return normalizeNvidia(evt, channel, language); + case 'soniox': + return normalizeSoniox(evt, channel, language); default: logger.error(`Unknown vendor ${vendor}`); return evt; @@ -441,6 +505,29 @@ module.exports = (logger) => { {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) { const {ibmOptions = {}} = rOpts; opts = { @@ -524,6 +611,9 @@ module.exports = (logger) => { ep.removeCustomEventListener(DeepgramTranscriptionEvents.Connect); ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure); + ep.removeCustomEventListener(SonioxTranscriptionEvents.Transcription); + ep.removeCustomEventListener(SonioxTranscriptionEvents.Error); + ep.removeCustomEventListener(NvidiaTranscriptionEvents.Transcription); ep.removeCustomEventListener(NvidiaTranscriptionEvents.TranscriptionComplete); ep.removeCustomEventListener(NvidiaTranscriptionEvents.StartOfSpeech); @@ -534,8 +624,9 @@ module.exports = (logger) => { const setSpeechCredentialsAtRuntime = (recognizer) => { if (!recognizer) return; if (recognizer.vendor === 'nuance') { - const {clientId, secret} = recognizer.nuanceOptions || {}; + const {clientId, secret, kryptonEndpoint} = recognizer.nuanceOptions || {}; if (clientId && secret) return {client_id: clientId, secret}; + if (kryptonEndpoint) return {krypton_endpoint: kryptonEndpoint}; } else if (recognizer.vendor === 'nvidia') { const {rivaUri} = recognizer.nvidiaOptions || {}; @@ -545,6 +636,10 @@ module.exports = (logger) => { const {apiKey} = recognizer.deepgramOptions || {}; 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') { const {ttsApiKey, ttsRegion, sttApiKey, sttRegion, instanceId} = recognizer.ibmOptions || {}; if (ttsApiKey || sttApiKey) return { @@ -561,6 +656,7 @@ module.exports = (logger) => { normalizeTranscription, setChannelVarsForStt, removeSpeechListeners, - setSpeechCredentialsAtRuntime + setSpeechCredentialsAtRuntime, + compileSonioxTranscripts }; }; diff --git a/package-lock.json b/package-lock.json index 846def2d..f4d37384 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@jambonz/realtimedb-helpers": "^0.6.5", "@jambonz/stats-collector": "^0.1.6", "@jambonz/time-series": "^0.2.5", - "@jambonz/verb-specifications": "^0.0.4", + "@jambonz/verb-specifications": "^0.0.11", "@opentelemetry/api": "^1.4.0", "@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0", @@ -28,7 +28,7 @@ "bent": "^7.3.12", "debug": "^4.3.4", "deepcopy": "^2.1.0", - "drachtio-fsmrf": "^3.0.18", + "drachtio-fsmrf": "^3.0.19", "drachtio-srf": "^4.5.23", "express": "^4.18.2", "ip": "^1.1.8", @@ -675,9 +675,9 @@ } }, "node_modules/@jambonz/verb-specifications": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.4.tgz", - "integrity": "sha512-Y3aN6NyiTqi0AdGFG9/sZwurG2ogCYANJpekVThlUNHTtg6UVIY6Tt+ND8+Hfc6UvNRZWAQIkugzpZvJFJ0faA==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.11.tgz", + "integrity": "sha512-T5ZITmOkcXOIBSH/vSGIQ1PXyZfOtdpo/DpJQ82gdIfYhm4AotMVbS5YABj1RFj4ergNnkoHmmhgJttgpMf0dw==", "dependencies": { "debug": "^4.3.4", "pino": "^8.8.0" @@ -2228,9 +2228,9 @@ } }, "node_modules/drachtio-fsmrf": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.18.tgz", - "integrity": "sha512-vWltwmIYeAapE2R5ise2HZgRS+8aGlQMXN6aTUxKGnvzGPD9+D29or+Y8HtodYG/2RKaPcaeUOjjsmnUaoWU2A==", + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.19.tgz", + "integrity": "sha512-MMofkpNCMxk58mIaXOe2jwTPwZ5f9AEH0eqqBaBR5a9qP8n7/xES18Qd7KaqDAOocTn99t0cl1k8Y+54F+lwfQ==", "dependencies": { "camel-case": "^4.1.2", "debug": "^2.6.9", @@ -8132,9 +8132,9 @@ } }, "@jambonz/verb-specifications": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.4.tgz", - "integrity": "sha512-Y3aN6NyiTqi0AdGFG9/sZwurG2ogCYANJpekVThlUNHTtg6UVIY6Tt+ND8+Hfc6UvNRZWAQIkugzpZvJFJ0faA==", + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.11.tgz", + "integrity": "sha512-T5ZITmOkcXOIBSH/vSGIQ1PXyZfOtdpo/DpJQ82gdIfYhm4AotMVbS5YABj1RFj4ergNnkoHmmhgJttgpMf0dw==", "requires": { "debug": "^4.3.4", "pino": "^8.8.0" @@ -9324,9 +9324,9 @@ } }, "drachtio-fsmrf": { - "version": "3.0.18", - "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.18.tgz", - "integrity": "sha512-vWltwmIYeAapE2R5ise2HZgRS+8aGlQMXN6aTUxKGnvzGPD9+D29or+Y8HtodYG/2RKaPcaeUOjjsmnUaoWU2A==", + "version": "3.0.19", + "resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.19.tgz", + "integrity": "sha512-MMofkpNCMxk58mIaXOe2jwTPwZ5f9AEH0eqqBaBR5a9qP8n7/xES18Qd7KaqDAOocTn99t0cl1k8Y+54F+lwfQ==", "requires": { "camel-case": "^4.1.2", "debug": "^2.6.9", diff --git a/package.json b/package.json index 1348db38..4bcefbb9 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "@jambonz/realtimedb-helpers": "^0.6.5", "@jambonz/stats-collector": "^0.1.6", "@jambonz/time-series": "^0.2.5", - "@jambonz/verb-specifications": "^0.0.4", + "@jambonz/verb-specifications": "^0.0.11", "@opentelemetry/api": "^1.4.0", "@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0", @@ -43,7 +43,7 @@ "bent": "^7.3.12", "debug": "^4.3.4", "deepcopy": "^2.1.0", - "drachtio-fsmrf": "^3.0.18", + "drachtio-fsmrf": "^3.0.19", "drachtio-srf": "^4.5.23", "express": "^4.18.2", "ip": "^1.1.8", diff --git a/test/gather-tests.js b/test/gather-tests.js index 3fdf755f..4e6aa0ff 100644 --- a/test/gather-tests.js +++ b/test/gather-tests.js @@ -206,7 +206,49 @@ test('\'gather\' test - deepgram', async(t) => { 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 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(); } catch (err) { diff --git a/test/transcribe-tests.js b/test/transcribe-tests.js index 0af21feb..e2daea7f 100644 --- a/test/transcribe-tests.js +++ b/test/transcribe-tests.js @@ -143,7 +143,7 @@ test('\'transcribe\' test - deepgram', async(t) => { { "verb": "transcribe", "recognizer": { - "vendor": "aws", + "vendor": "deepgram", "hints": ["customer support", "sales", "human resources", "HR"], "deepgramOptions": { "apiKey": process.env.DEEPGRAM_API_KEY @@ -160,6 +160,47 @@ test('\'transcribe\' test - deepgram', async(t) => { t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'), 'transcribe: succeeds when using deepgram credentials'); + disconnect(); + } catch (err) { + console.log(`error received: ${err}`); + disconnect(); + 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}`);