mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-10-04 02:04:12 +00:00
fix to be more precise about removing custom event handlers so that w… (#580)
* fix to be more precise about removing custom event handlers so that when we stop a gather we dont also inadvertently stop a background transcribe as well * test fixes * fix: endpointing=false was being ignored for Deepgram
This commit is contained in:
@@ -109,7 +109,7 @@ const disconnect = () => {
|
|||||||
httpServer?.on('close', resolve);
|
httpServer?.on('close', resolve);
|
||||||
httpServer?.close();
|
httpServer?.close();
|
||||||
srf.disconnect();
|
srf.disconnect();
|
||||||
srf.locals.mediaservers.forEach((ms) => ms.disconnect());
|
srf.locals.mediaservers?.forEach((ms) => ms.disconnect());
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+43
-34
@@ -221,7 +221,7 @@ class TaskGather extends SttTask {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error(err, 'TaskGather:exec error');
|
this.logger.error(err, 'TaskGather:exec error');
|
||||||
}
|
}
|
||||||
this.removeSpeechListeners(ep);
|
this.removeCustomEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
kill(cs) {
|
kill(cs) {
|
||||||
@@ -261,7 +261,7 @@ class TaskGather extends SttTask {
|
|||||||
else if (this.input.includes('digits')) {
|
else if (this.input.includes('digits')) {
|
||||||
if (this.digitBuffer.length === 0 && this.needsStt) {
|
if (this.digitBuffer.length === 0 && this.needsStt) {
|
||||||
// DTMF is higher priority than STT.
|
// DTMF is higher priority than STT.
|
||||||
this.removeSpeechListeners(ep);
|
this.removeCustomEventListeners();
|
||||||
ep.stopTranscription({
|
ep.stopTranscription({
|
||||||
vendor: this.vendor,
|
vendor: this.vendor,
|
||||||
bugname: this.bugname,
|
bugname: this.bugname,
|
||||||
@@ -305,33 +305,37 @@ class TaskGather extends SttTask {
|
|||||||
switch (this.vendor) {
|
switch (this.vendor) {
|
||||||
case 'google':
|
case 'google':
|
||||||
this.bugname = `${this.bugname_prefix}google_transcribe`;
|
this.bugname = `${this.bugname_prefix}google_transcribe`;
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep));
|
ep, GoogleTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
|
ep, GoogleTranscriptionEvents.EndOfUtterance, this._onEndOfUtterance.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(
|
||||||
|
ep, GoogleTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'aws':
|
case 'aws':
|
||||||
case 'polly':
|
case 'polly':
|
||||||
this.bugname = `${this.bugname_prefix}aws_transcribe`;
|
this.bugname = `${this.bugname_prefix}aws_transcribe`;
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(ep, AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
this.addCustomEventListener(ep, AwsTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
case 'microsoft':
|
case 'microsoft':
|
||||||
this.bugname = `${this.bugname_prefix}azure_transcribe`;
|
this.bugname = `${this.bugname_prefix}azure_transcribe`;
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected,
|
ep, AzureTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(ep, AzureTranscriptionEvents.NoSpeechDetected,
|
||||||
this._onNoSpeechDetected.bind(this, cs, ep));
|
this._onNoSpeechDetected.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
this.addCustomEventListener(ep, AzureTranscriptionEvents.VadDetected, this._onVadDetected.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
case 'nuance':
|
case 'nuance':
|
||||||
this.bugname = `${this.bugname_prefix}nuance_transcribe`;
|
this.bugname = `${this.bugname_prefix}nuance_transcribe`;
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, NuanceTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep));
|
this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech,
|
this.addCustomEventListener(ep, NuanceTranscriptionEvents.StartOfSpeech,
|
||||||
this._onStartOfSpeech.bind(this, cs, ep));
|
this._onStartOfSpeech.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete,
|
this.addCustomEventListener(ep, NuanceTranscriptionEvents.TranscriptionComplete,
|
||||||
this._onTranscriptionComplete.bind(this, cs, ep));
|
this._onTranscriptionComplete.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.VadDetected,
|
this.addCustomEventListener(ep, NuanceTranscriptionEvents.VadDetected,
|
||||||
this._onVadDetected.bind(this, cs, ep));
|
this._onVadDetected.bind(this, cs, ep));
|
||||||
|
|
||||||
/* stall timers until prompt finishes playing */
|
/* stall timers until prompt finishes playing */
|
||||||
@@ -342,9 +346,10 @@ class TaskGather extends SttTask {
|
|||||||
|
|
||||||
case 'deepgram':
|
case 'deepgram':
|
||||||
this.bugname = `${this.bugname_prefix}deepgram_transcribe`;
|
this.bugname = `${this.bugname_prefix}deepgram_transcribe`;
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
ep, DeepgramTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, DeepgramTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(ep, DeepgramTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.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 */
|
||||||
@@ -353,12 +358,14 @@ class TaskGather extends SttTask {
|
|||||||
|
|
||||||
case 'soniox':
|
case 'soniox':
|
||||||
this.bugname = `${this.bugname_prefix}soniox_transcribe`;
|
this.bugname = `${this.bugname_prefix}soniox_transcribe`;
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
|
ep, SonioxTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'cobalt':
|
case 'cobalt':
|
||||||
this.bugname = `${this.bugname_prefix}cobalt_transcribe`;
|
this.bugname = `${this.bugname_prefix}cobalt_transcribe`;
|
||||||
ep.addCustomEventListener(CobaltTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
|
ep, CobaltTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
|
|
||||||
/* cobalt doesnt have language, it has model, which is required */
|
/* cobalt doesnt have language, it has model, which is required */
|
||||||
if (!this.data.recognizer.model) {
|
if (!this.data.recognizer.model) {
|
||||||
@@ -388,21 +395,21 @@ class TaskGather extends SttTask {
|
|||||||
|
|
||||||
case 'ibm':
|
case 'ibm':
|
||||||
this.bugname = `${this.bugname_prefix}ibm_transcribe`;
|
this.bugname = `${this.bugname_prefix}ibm_transcribe`;
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep));
|
this._onVendorConnectFailure.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'nvidia':
|
case 'nvidia':
|
||||||
this.bugname = `${this.bugname_prefix}nvidia_transcribe`;
|
this.bugname = `${this.bugname_prefix}nvidia_transcribe`;
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, NvidiaTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep));
|
this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.StartOfSpeech,
|
this.addCustomEventListener(ep, NvidiaTranscriptionEvents.StartOfSpeech,
|
||||||
this._onStartOfSpeech.bind(this, cs, ep));
|
this._onStartOfSpeech.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.TranscriptionComplete,
|
this.addCustomEventListener(ep, NvidiaTranscriptionEvents.TranscriptionComplete,
|
||||||
this._onTranscriptionComplete.bind(this, cs, ep));
|
this._onTranscriptionComplete.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.VadDetected,
|
this.addCustomEventListener(ep, NvidiaTranscriptionEvents.VadDetected,
|
||||||
this._onVadDetected.bind(this, cs, ep));
|
this._onVadDetected.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 */
|
||||||
@@ -413,19 +420,21 @@ class TaskGather extends SttTask {
|
|||||||
|
|
||||||
case 'assemblyai':
|
case 'assemblyai':
|
||||||
this.bugname = `${this.bugname_prefix}assemblyai_transcribe`;
|
this.bugname = `${this.bugname_prefix}assemblyai_transcribe`;
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep));
|
this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
|
ep, AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep));
|
this._onVendorConnectFailure.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (this.vendor.startsWith('custom:')) {
|
if (this.vendor.startsWith('custom:')) {
|
||||||
this.bugname = `${this.bugname_prefix}${this.vendor}_transcribe`;
|
this.bugname = `${this.bugname_prefix}${this.vendor}_transcribe`;
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
this.addCustomEventListener(
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
ep, JambonzTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, JambonzTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(ep, JambonzTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep));
|
this._onVendorConnectFailure.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -437,7 +446,7 @@ class TaskGather extends SttTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* common handler for all stt engine errors */
|
/* common handler for all stt engine errors */
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Error, this._onJambonzError.bind(this, cs, ep));
|
this.addCustomEventListener(ep, 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'));
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-3
@@ -14,17 +14,15 @@ class SttTask extends Task {
|
|||||||
const {
|
const {
|
||||||
setChannelVarsForStt,
|
setChannelVarsForStt,
|
||||||
normalizeTranscription,
|
normalizeTranscription,
|
||||||
removeSpeechListeners,
|
|
||||||
setSpeechCredentialsAtRuntime,
|
setSpeechCredentialsAtRuntime,
|
||||||
compileSonioxTranscripts,
|
compileSonioxTranscripts,
|
||||||
consolidateTranscripts
|
consolidateTranscripts
|
||||||
} = 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.compileSonioxTranscripts = compileSonioxTranscripts;
|
this.compileSonioxTranscripts = compileSonioxTranscripts;
|
||||||
this.consolidateTranscripts = consolidateTranscripts;
|
this.consolidateTranscripts = consolidateTranscripts;
|
||||||
|
this.eventHandlers = [];
|
||||||
this.isHandledByPrimaryProvider = true;
|
this.isHandledByPrimaryProvider = true;
|
||||||
if (this.data.recognizer) {
|
if (this.data.recognizer) {
|
||||||
const recognizer = this.data.recognizer;
|
const recognizer = this.data.recognizer;
|
||||||
@@ -140,6 +138,15 @@ class SttTask extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addCustomEventListener(ep, event, handler) {
|
||||||
|
this.eventHandlers.push({ep, event, handler});
|
||||||
|
ep.addCustomEventListener(event, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCustomEventListeners() {
|
||||||
|
this.eventHandlers.forEach((h) => h.ep.removeCustomEventListener(h.event, h.handler));
|
||||||
|
}
|
||||||
|
|
||||||
async _initSpeechCredentials(cs, vendor, label) {
|
async _initSpeechCredentials(cs, vendor, label) {
|
||||||
const {getNuanceAccessToken, getIbmAccessToken} = cs.srf.locals.dbHelpers;
|
const {getNuanceAccessToken, getIbmAccessToken} = cs.srf.locals.dbHelpers;
|
||||||
let credentials = cs.getSpeechCredentials(vendor, 'stt', label);
|
let credentials = cs.getSpeechCredentials(vendor, 'stt', label);
|
||||||
|
|||||||
+28
-27
@@ -80,7 +80,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
this.logger.info(err, 'TaskTranscribe:exec - error');
|
this.logger.info(err, 'TaskTranscribe:exec - error');
|
||||||
this.parentTask && this.parentTask.emit('error', err);
|
this.parentTask && this.parentTask.emit('error', err);
|
||||||
}
|
}
|
||||||
this.removeSpeechListeners(ep);
|
this.removeCustomEventListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _stopTranscription() {
|
async _stopTranscription() {
|
||||||
@@ -142,43 +142,43 @@ class TaskTranscribe extends SttTask {
|
|||||||
switch (this.vendor) {
|
switch (this.vendor) {
|
||||||
case 'google':
|
case 'google':
|
||||||
this.bugname = `${this.bugname_prefix}google_transcribe`;
|
this.bugname = `${this.bugname_prefix}google_transcribe`;
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, GoogleTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.NoAudioDetected,
|
this.addCustomEventListener(ep, GoogleTranscriptionEvents.NoAudioDetected,
|
||||||
this._onNoAudio.bind(this, cs, ep, channel));
|
this._onNoAudio.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(GoogleTranscriptionEvents.MaxDurationExceeded,
|
this.addCustomEventListener(ep, GoogleTranscriptionEvents.MaxDurationExceeded,
|
||||||
this._onMaxDurationExceeded.bind(this, cs, ep, channel));
|
this._onMaxDurationExceeded.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'aws':
|
case 'aws':
|
||||||
case 'polly':
|
case 'polly':
|
||||||
this.bugname = `${this.bugname_prefix}aws_transcribe`;
|
this.bugname = `${this.bugname_prefix}aws_transcribe`;
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, AwsTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.NoAudioDetected,
|
this.addCustomEventListener(ep, AwsTranscriptionEvents.NoAudioDetected,
|
||||||
this._onNoAudio.bind(this, cs, ep, channel));
|
this._onNoAudio.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.MaxDurationExceeded,
|
this.addCustomEventListener(ep, AwsTranscriptionEvents.MaxDurationExceeded,
|
||||||
this._onMaxDurationExceeded.bind(this, cs, ep, channel));
|
this._onMaxDurationExceeded.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
case 'microsoft':
|
case 'microsoft':
|
||||||
this.bugname = `${this.bugname_prefix}azure_transcribe`;
|
this.bugname = `${this.bugname_prefix}azure_transcribe`;
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, AzureTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected,
|
this.addCustomEventListener(ep, AzureTranscriptionEvents.NoSpeechDetected,
|
||||||
this._onNoAudio.bind(this, cs, ep, channel));
|
this._onNoAudio.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
case 'nuance':
|
case 'nuance':
|
||||||
this.bugname = `${this.bugname_prefix}nuance_transcribe`;
|
this.bugname = `${this.bugname_prefix}nuance_transcribe`;
|
||||||
ep.addCustomEventListener(NuanceTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, NuanceTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
case 'deepgram':
|
case 'deepgram':
|
||||||
this.bugname = `${this.bugname_prefix}deepgram_transcribe`;
|
this.bugname = `${this.bugname_prefix}deepgram_transcribe`;
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, DeepgramTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.Connect,
|
this.addCustomEventListener(ep, DeepgramTranscriptionEvents.Connect,
|
||||||
this._onVendorConnect.bind(this, cs, ep));
|
this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, DeepgramTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.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 */
|
||||||
@@ -187,12 +187,12 @@ class TaskTranscribe extends SttTask {
|
|||||||
break;
|
break;
|
||||||
case 'soniox':
|
case 'soniox':
|
||||||
this.bugname = `${this.bugname_prefix}soniox_transcribe`;
|
this.bugname = `${this.bugname_prefix}soniox_transcribe`;
|
||||||
ep.addCustomEventListener(SonioxTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, SonioxTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
case 'cobalt':
|
case 'cobalt':
|
||||||
this.bugname = `${this.bugname_prefix}cobalt_transcribe`;
|
this.bugname = `${this.bugname_prefix}cobalt_transcribe`;
|
||||||
ep.addCustomEventListener(CobaltTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, CobaltTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
|
|
||||||
/* cobalt doesnt have language, it has model, which is required */
|
/* cobalt doesnt have language, it has model, which is required */
|
||||||
@@ -222,37 +222,38 @@ class TaskTranscribe extends SttTask {
|
|||||||
|
|
||||||
case 'ibm':
|
case 'ibm':
|
||||||
this.bugname = `${this.bugname_prefix}ibm_transcribe`;
|
this.bugname = `${this.bugname_prefix}ibm_transcribe`;
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.Connect,
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.Connect,
|
||||||
this._onVendorConnect.bind(this, cs, ep));
|
this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(IbmTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, IbmTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep, channel));
|
this._onVendorConnectFailure.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'nvidia':
|
case 'nvidia':
|
||||||
this.bugname = `${this.bugname_prefix}nvidia_transcribe`;
|
this.bugname = `${this.bugname_prefix}nvidia_transcribe`;
|
||||||
ep.addCustomEventListener(NvidiaTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, NvidiaTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'assemblyai':
|
case 'assemblyai':
|
||||||
this.bugname = `${this.bugname_prefix}assemblyai_transcribe`;
|
this.bugname = `${this.bugname_prefix}assemblyai_transcribe`;
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
this.addCustomEventListener(ep,
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
|
AssemblyAiTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.Error, this._onVendorError.bind(this, cs, ep));
|
||||||
|
this.addCustomEventListener(ep, AssemblyAiTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep, channel));
|
this._onVendorConnectFailure.bind(this, cs, ep, channel));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if (this.vendor.startsWith('custom:')) {
|
if (this.vendor.startsWith('custom:')) {
|
||||||
this.bugname = `${this.bugname_prefix}${this.vendor}_transcribe`;
|
this.bugname = `${this.bugname_prefix}${this.vendor}_transcribe`;
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Transcription,
|
this.addCustomEventListener(ep, JambonzTranscriptionEvents.Transcription,
|
||||||
this._onTranscription.bind(this, cs, ep, channel));
|
this._onTranscription.bind(this, cs, ep, channel));
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
this.addCustomEventListener(ep, JambonzTranscriptionEvents.Connect, this._onVendorConnect.bind(this, cs, ep));
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.ConnectFailure,
|
this.addCustomEventListener(ep, JambonzTranscriptionEvents.ConnectFailure,
|
||||||
this._onVendorConnectFailure.bind(this, cs, ep));
|
this._onVendorConnectFailure.bind(this, cs, ep));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -264,7 +265,7 @@ class TaskTranscribe extends SttTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* common handler for all stt engine errors */
|
/* common handler for all stt engine errors */
|
||||||
ep.addCustomEventListener(JambonzTranscriptionEvents.Error, this._onJambonzError.bind(this, cs, ep));
|
this.addCustomEventListener(ep, 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'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
const {
|
const {
|
||||||
TaskName,
|
TaskName,
|
||||||
AzureTranscriptionEvents,
|
|
||||||
GoogleTranscriptionEvents,
|
|
||||||
AwsTranscriptionEvents,
|
|
||||||
NuanceTranscriptionEvents,
|
|
||||||
DeepgramTranscriptionEvents,
|
|
||||||
SonioxTranscriptionEvents,
|
|
||||||
NvidiaTranscriptionEvents,
|
|
||||||
CobaltTranscriptionEvents,
|
|
||||||
JambonzTranscriptionEvents,
|
|
||||||
AssemblyAiTranscriptionEvents
|
|
||||||
} = require('./constants.json');
|
} = require('./constants.json');
|
||||||
|
|
||||||
const stickyVars = {
|
const stickyVars = {
|
||||||
@@ -615,7 +605,7 @@ module.exports = (logger) => {
|
|||||||
...(deepgramOptions.keywords) &&
|
...(deepgramOptions.keywords) &&
|
||||||
{DEEPGRAM_SPEECH_KEYWORDS: deepgramOptions.keywords.join(',')},
|
{DEEPGRAM_SPEECH_KEYWORDS: deepgramOptions.keywords.join(',')},
|
||||||
...('endpointing' in deepgramOptions) &&
|
...('endpointing' in deepgramOptions) &&
|
||||||
{DEEPGRAM_SPEECH_ENDPOINTING: deepgramOptions.endpointing},
|
{DEEPGRAM_SPEECH_ENDPOINTING: deepgramOptions.endpointing === false ? 'false' : deepgramOptions.endpointing},
|
||||||
...(deepgramOptions.utteranceEndMs) &&
|
...(deepgramOptions.utteranceEndMs) &&
|
||||||
{DEEPGRAM_SPEECH_UTTERANCE_END_MS: deepgramOptions.utteranceEndMs},
|
{DEEPGRAM_SPEECH_UTTERANCE_END_MS: deepgramOptions.utteranceEndMs},
|
||||||
...(deepgramOptions.vadTurnoff) &&
|
...(deepgramOptions.vadTurnoff) &&
|
||||||
@@ -755,48 +745,6 @@ module.exports = (logger) => {
|
|||||||
return opts;
|
return opts;
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeSpeechListeners = (ep) => {
|
|
||||||
ep.removeCustomEventListener(GoogleTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(GoogleTranscriptionEvents.EndOfUtterance);
|
|
||||||
ep.removeCustomEventListener(GoogleTranscriptionEvents.VadDetected);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(AwsTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(AwsTranscriptionEvents.VadDetected);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(AzureTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(AzureTranscriptionEvents.NoSpeechDetected);
|
|
||||||
ep.removeCustomEventListener(AzureTranscriptionEvents.VadDetected);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.TranscriptionComplete);
|
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.StartOfSpeech);
|
|
||||||
ep.removeCustomEventListener(NuanceTranscriptionEvents.VadDetected);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(DeepgramTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(DeepgramTranscriptionEvents.Connect);
|
|
||||||
ep.removeCustomEventListener(DeepgramTranscriptionEvents.ConnectFailure);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(SonioxTranscriptionEvents.Transcription);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(CobaltTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(CobaltTranscriptionEvents.CompileContext);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.TranscriptionComplete);
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.StartOfSpeech);
|
|
||||||
ep.removeCustomEventListener(NvidiaTranscriptionEvents.VadDetected);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(JambonzTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(JambonzTranscriptionEvents.Connect);
|
|
||||||
ep.removeCustomEventListener(JambonzTranscriptionEvents.ConnectFailure);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(JambonzTranscriptionEvents.Error);
|
|
||||||
|
|
||||||
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.Transcription);
|
|
||||||
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.Connect);
|
|
||||||
ep.removeCustomEventListener(AssemblyAiTranscriptionEvents.ConnectFailure);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setSpeechCredentialsAtRuntime = (recognizer) => {
|
const setSpeechCredentialsAtRuntime = (recognizer) => {
|
||||||
if (!recognizer) return;
|
if (!recognizer) return;
|
||||||
if (recognizer.vendor === 'nuance') {
|
if (recognizer.vendor === 'nuance') {
|
||||||
@@ -835,7 +783,6 @@ module.exports = (logger) => {
|
|||||||
return {
|
return {
|
||||||
normalizeTranscription,
|
normalizeTranscription,
|
||||||
setChannelVarsForStt,
|
setChannelVarsForStt,
|
||||||
removeSpeechListeners,
|
|
||||||
setSpeechCredentialsAtRuntime,
|
setSpeechCredentialsAtRuntime,
|
||||||
compileSonioxTranscripts,
|
compileSonioxTranscripts,
|
||||||
consolidateTranscripts
|
consolidateTranscripts
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ services:
|
|||||||
ipv4_address: 172.38.0.7
|
ipv4_address: 172.38.0.7
|
||||||
|
|
||||||
drachtio:
|
drachtio:
|
||||||
image: drachtio/drachtio-server:0.8.22
|
image: drachtio/drachtio-server:latest
|
||||||
restart: always
|
restart: always
|
||||||
command: drachtio --contact "sip:*;transport=udp" --mtu 4096 --address 0.0.0.0 --port 9022
|
command: drachtio --contact "sip:*;transport=udp" --mtu 4096 --address 0.0.0.0 --port 9022
|
||||||
ports:
|
ports:
|
||||||
@@ -57,7 +57,7 @@ services:
|
|||||||
condition: service_healthy
|
condition: service_healthy
|
||||||
|
|
||||||
freeswitch:
|
freeswitch:
|
||||||
image: drachtio/drachtio-freeswitch-mrf:0.5.6
|
image: drachtio/drachtio-freeswitch-mrf:latest
|
||||||
restart: always
|
restart: always
|
||||||
command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100
|
command: freeswitch --rtp-range-start 20000 --rtp-range-end 20100
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
+1
-1
@@ -188,7 +188,7 @@ test('\'play\' tests with seekOffset and actionHook', async(t) => {
|
|||||||
const seconds = parseInt(obj.body.playback_seconds);
|
const seconds = parseInt(obj.body.playback_seconds);
|
||||||
const milliseconds = parseInt(obj.body.playback_milliseconds);
|
const milliseconds = parseInt(obj.body.playback_milliseconds);
|
||||||
const lastOffsetPos = parseInt(obj.body.playback_last_offset_pos);
|
const lastOffsetPos = parseInt(obj.body.playback_last_offset_pos);
|
||||||
//console.log({obj}, 'lastRequest');
|
console.log({obj}, 'lastRequest');
|
||||||
t.ok(obj.body.reason === "playCompleted", "play: actionHook success received");
|
t.ok(obj.body.reason === "playCompleted", "play: actionHook success received");
|
||||||
t.ok(seconds === 2, "playback_seconds: actionHook success received");
|
t.ok(seconds === 2, "playback_seconds: actionHook success received");
|
||||||
t.ok(milliseconds === 2048, "playback_milliseconds: actionHook success received");
|
t.ok(milliseconds === 2048, "playback_milliseconds: actionHook success received");
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ test('\'transcribe\' test - google', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
|
||||||
'transcribe: succeeds when using google credentials');
|
'transcribe: succeeds when using google credentials');
|
||||||
|
|
||||||
@@ -89,6 +90,7 @@ test('\'transcribe\' test - microsoft', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
|
||||||
'transcribe: succeeds when using microsoft credentials');
|
'transcribe: succeeds when using microsoft credentials');
|
||||||
|
|
||||||
@@ -126,6 +128,7 @@ test('\'transcribe\' test - aws', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
|
||||||
'transcribe: succeeds when using aws credentials');
|
'transcribe: succeeds when using aws credentials');
|
||||||
|
|
||||||
@@ -190,6 +193,7 @@ test('\'transcribe\' test - deepgram config options', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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().includes('like to speak to customer support'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().includes('like to speak to customer support'),
|
||||||
'transcribe: succeeds when using deepgram credentials');
|
'transcribe: succeeds when using deepgram credentials');
|
||||||
|
|
||||||
@@ -230,6 +234,7 @@ test('\'transcribe\' test - deepgram', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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().includes('like to speak to customer support'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().includes('like to speak to customer support'),
|
||||||
'transcribe: succeeds when using deepgram credentials');
|
'transcribe: succeeds when using deepgram credentials');
|
||||||
|
|
||||||
@@ -309,6 +314,7 @@ test('\'transcribe\' test - google with asrTimeout', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().startsWith('i\'d like to speak to customer support'),
|
||||||
'transcribe: succeeds when using google credentials');
|
'transcribe: succeeds when using google credentials');
|
||||||
|
|
||||||
@@ -369,6 +375,7 @@ test('\'transcribe\' test - deepgram config options altLanguages', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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().includes('like to speak to customer support'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().includes('like to speak to customer support'),
|
||||||
'transcribe: succeeds when using deepgram credentials');
|
'transcribe: succeeds when using deepgram credentials');
|
||||||
|
|
||||||
@@ -430,6 +437,7 @@ test('\'transcribe\' test - deepgram config options altLanguages', async(t) => {
|
|||||||
// THEN
|
// THEN
|
||||||
await sippUac('uac-gather-account-creds-success.xml', '172.38.0.10', from);
|
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`);
|
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().includes('like to speak to customer support'),
|
t.ok(obj.body.speech.alternatives[0].transcript.toLowerCase().includes('like to speak to customer support'),
|
||||||
'transcribe: succeeds when using deepgram credentials');
|
'transcribe: succeeds when using deepgram credentials');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user