mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-21 17:17:58 +00:00
drop queue speech on barge in
This commit is contained in:
@@ -73,6 +73,7 @@ class Cognigy extends Task {
|
|||||||
resolver = resolve;
|
resolver = resolve;
|
||||||
rejector = reject;
|
rejector = reject;
|
||||||
});
|
});
|
||||||
|
taskPromise.resolve = resolver;
|
||||||
this.taskQueue.push(async(cb) => {
|
this.taskQueue.push(async(cb) => {
|
||||||
this.logger.debug('executing task from queue');
|
this.logger.debug('executing task from queue');
|
||||||
try {
|
try {
|
||||||
@@ -86,6 +87,10 @@ class Cognigy extends Task {
|
|||||||
}
|
}
|
||||||
this.logger.debug('say task executed from queue');
|
this.logger.debug('say task executed from queue');
|
||||||
});
|
});
|
||||||
|
if(this.taskQueue.lastPromise){
|
||||||
|
// resolve the previous promise for cleanup
|
||||||
|
this.taskQueue.lastPromise.resolve({});
|
||||||
|
}
|
||||||
this.taskQueue.lastPromise = taskPromise;
|
this.taskQueue.lastPromise = taskPromise;
|
||||||
return taskPromise;
|
return taskPromise;
|
||||||
}
|
}
|
||||||
@@ -180,6 +185,19 @@ class Cognigy extends Task {
|
|||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a promt which will be sent to the consumer. We will create a say task if bargein is disabled
|
||||||
|
* for session and nextTurn, else create a gather task.
|
||||||
|
*/
|
||||||
|
_createPromtTask({text, url, turnConfig, dontListenAfterSpeech} = {}){
|
||||||
|
const bargeInOnNextTurn = turnConfig?.bargein?.enable?.length>0;
|
||||||
|
const bargeInSession = this.config.bargeInEnabled;
|
||||||
|
if(bargeInOnNextTurn || bargeInSession){
|
||||||
|
return this._makeGatherTask({textPrompt: text, url: urlPrompt, turnConfig, dontListenAfterSpeech});
|
||||||
|
}
|
||||||
|
return this._makeSayTask({text, turnConfig});
|
||||||
|
}
|
||||||
|
|
||||||
_makeGatherTask({textPrompt, urlPrompt, turnConfig} = {}) {
|
_makeGatherTask({textPrompt, urlPrompt, turnConfig} = {}) {
|
||||||
this.logger.debug({textPrompt, urlPrompt, turnConfig}, '_makeGatherTask');
|
this.logger.debug({textPrompt, urlPrompt, turnConfig}, '_makeGatherTask');
|
||||||
const config = this.config.makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig});
|
const config = this.config.makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig});
|
||||||
@@ -274,11 +292,11 @@ class Cognigy extends Task {
|
|||||||
const text = parseBotText(evt);
|
const text = parseBotText(evt);
|
||||||
|
|
||||||
// only add say task if its a normal cognigy node and not a "gather task"
|
// only add say task if its a normal cognigy node and not a "gather task"
|
||||||
if (text && (evt?.data?.type !== 'gather')) {
|
if (text && (evt?.data?.type !== 'promt')) {
|
||||||
this.logger.info({text}, 'received text');
|
this.logger.info({text}, 'received text');
|
||||||
this._enqueueTask(async() => {
|
this._enqueueTask(async() => {
|
||||||
// todo inject the session config into the say task
|
// todo inject the session config into the say task
|
||||||
const sayTask = this._makeSayTask({text});
|
const sayTask = this._createPromtTask({ text, dontListenAfterSpeech: true });
|
||||||
await sayTask.exec(cs, ep, this);
|
await sayTask.exec(cs, ep, this);
|
||||||
this.logger.debug({text}, 'executed say task');
|
this.logger.debug({text}, 'executed say task');
|
||||||
});
|
});
|
||||||
@@ -304,9 +322,9 @@ class Cognigy extends Task {
|
|||||||
return;
|
return;
|
||||||
case 'promt':
|
case 'promt':
|
||||||
this._enqueueTask(async() => {
|
this._enqueueTask(async() => {
|
||||||
const sayTask = this._makeSayTask({
|
const sayTask = this._createPromtTask({
|
||||||
text: evt.data.text,
|
text: evt.data.text,
|
||||||
nextTurn: evt?.data?.config?.nextTurn
|
turnConfig: evt?.data?.config?.nextTurn
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
await sayTask.exec(cs, ep, this);
|
await sayTask.exec(cs, ep, this);
|
||||||
@@ -337,6 +355,17 @@ class Cognigy extends Task {
|
|||||||
this.logger.debug({evt}, `Cognigy: got transcription for callSid ${cs.callSid}`);
|
this.logger.debug({evt}, `Cognigy: got transcription for callSid ${cs.callSid}`);
|
||||||
const utterance = evt.alternatives[0].transcript;
|
const utterance = evt.alternatives[0].transcript;
|
||||||
|
|
||||||
|
//if we have barge in enabled AND we enabled skipping until next question
|
||||||
|
//then stop execution of currently queues bot output before sending the
|
||||||
|
//response to waiting bot since otherwise we could stop upcoming bot output
|
||||||
|
|
||||||
|
if(this.config.skipUntilBotInput){
|
||||||
|
// clear task queue, resolve the last promise and cleanup;
|
||||||
|
this.taskQueue.end();
|
||||||
|
this.taskQueue.lastPromise.resolve();
|
||||||
|
this.taskQueue.autostart = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.eventHook) {
|
if (this.eventHook) {
|
||||||
this.performHook(cs, this.eventHook, {event: 'userMessage', message: utterance})
|
this.performHook(cs, this.eventHook, {event: 'userMessage', message: utterance})
|
||||||
.then((redirected) => {
|
.then((redirected) => {
|
||||||
@@ -376,7 +405,7 @@ class Cognigy extends Task {
|
|||||||
/* send dtmf to bot */
|
/* send dtmf to bot */
|
||||||
try {
|
try {
|
||||||
if (this.client && this.client.connected) {
|
if (this.client && this.client.connected) {
|
||||||
this.client.sendMessage(evt.digits);
|
this.client.sendMessage(String(evt.digits));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// if the bot is not connected, should we maybe throw an error here?
|
// if the bot is not connected, should we maybe throw an error here?
|
||||||
|
|||||||
@@ -28,6 +28,19 @@ class SpeechConfig extends Emitter {
|
|||||||
this.logger.debug({sessionLevel: this.sessionConfig}, 'SpeechConfig updated');
|
this.logger.debug({sessionLevel: this.sessionConfig}, 'SpeechConfig updated');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if we should skip all nodes until next bot input
|
||||||
|
*/
|
||||||
|
get skipUntilBotInput(){
|
||||||
|
return this.sessionConfig.bargein?.skipUntilBotInput === false;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Check if barge is enabled on session level
|
||||||
|
*/
|
||||||
|
get bargeInEnabled(){
|
||||||
|
return this.sessionConfig.bargein?.enable?.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
makeSayTaskConfig({text, turnConfig = {}} = {}) {
|
makeSayTaskConfig({text, turnConfig = {}} = {}) {
|
||||||
const synthesizer = lodash.merge({}, this.sessionConfig.synthesizer, turnConfig.synthesizer);
|
const synthesizer = lodash.merge({}, this.sessionConfig.synthesizer, turnConfig.synthesizer);
|
||||||
return {
|
return {
|
||||||
@@ -36,7 +49,7 @@ class SpeechConfig extends Emitter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig = {}} = {}) {
|
makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig = {}, dontListenAfterSpeech} = {}) {
|
||||||
// we merge from top to bottom deeply so we wil have
|
// we merge from top to bottom deeply so we wil have
|
||||||
// defaults from session config and then will override them via turn config
|
// defaults from session config and then will override them via turn config
|
||||||
const opts = lodash.merge(
|
const opts = lodash.merge(
|
||||||
@@ -112,7 +125,8 @@ class SpeechConfig extends Emitter {
|
|||||||
noInputRetries,
|
noInputRetries,
|
||||||
noInputSpeech,
|
noInputSpeech,
|
||||||
noInputUrl
|
noInputUrl
|
||||||
}
|
},
|
||||||
|
dontListenAfterSpeech
|
||||||
};
|
};
|
||||||
|
|
||||||
const final = stripNulls(config);
|
const final = stripNulls(config);
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ class TaskGather extends Task {
|
|||||||
].forEach((k) => this[k] = this.data[k]);
|
].forEach((k) => this[k] = this.data[k]);
|
||||||
this.listenDuringPrompt = this.data.listenDuringPrompt === false ? false : true;
|
this.listenDuringPrompt = this.data.listenDuringPrompt === false ? false : true;
|
||||||
this.minBargeinWordCount = this.data.minBargeinWordCount || 1;
|
this.minBargeinWordCount = this.data.minBargeinWordCount || 1;
|
||||||
|
// this is specially for barge in where we want to make a bargebale promt
|
||||||
|
// to a user without listening after the say task has finished
|
||||||
|
this.dontListenAfterSpeech = this.data.dontListenAfterSpeech === true;
|
||||||
this.timeout = (this.timeout || 15) * 1000;
|
this.timeout = (this.timeout || 15) * 1000;
|
||||||
this.interim = this.partialResultCallback || this.bargein;
|
this.interim = this.partialResultCallback || this.bargein;
|
||||||
if (this.data.recognizer) {
|
if (this.data.recognizer) {
|
||||||
@@ -101,14 +104,14 @@ class TaskGather extends Task {
|
|||||||
this.sayTask.on('playDone', async(err) => {
|
this.sayTask.on('playDone', async(err) => {
|
||||||
if (err) return this.logger.error({err}, 'Gather:exec Error playing tts');
|
if (err) return this.logger.error({err}, 'Gather:exec Error playing tts');
|
||||||
this.logger.debug('Gather: say task completed');
|
this.logger.debug('Gather: say task completed');
|
||||||
if (!this.killed) startListening(cs, ep);
|
if (!this.killed || !this.dontListenAfterSpeech) startListening(cs, ep);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (this.playTask) {
|
else if (this.playTask) {
|
||||||
this.playTask.exec(cs, ep); // kicked off, _not_ waiting for it to complete
|
this.playTask.exec(cs, ep); // kicked off, _not_ waiting for it to complete
|
||||||
this.playTask.on('playDone', async(err) => {
|
this.playTask.on('playDone', async(err) => {
|
||||||
if (err) return this.logger.error({err}, 'Gather:exec Error playing url');
|
if (err) return this.logger.error({err}, 'Gather:exec Error playing url');
|
||||||
if (!this.killed) startListening(cs, ep);
|
if (!this.killed || !this.dontListenAfterSpeech) startListening(cs, ep);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else startListening(cs, ep);
|
else startListening(cs, ep);
|
||||||
|
|||||||
@@ -22,11 +22,9 @@ class TaskSay extends Task {
|
|||||||
const {writeAlerts, AlertType, stats} = srf.locals;
|
const {writeAlerts, AlertType, stats} = srf.locals;
|
||||||
const {synthAudio} = srf.locals.dbHelpers;
|
const {synthAudio} = srf.locals.dbHelpers;
|
||||||
|
|
||||||
// todo maybe we should allow the user to only change the voice/language without changing the vendor?
|
const vendor = this.synthesizer.vendor || cs.speechSynthesisVendor;
|
||||||
const hasVerbLevelTts = this.synthesizer.vendor && this.synthesizer.vendor !== 'default';
|
const language = this.synthesizer.language || cs.speechSynthesisLanguage ;
|
||||||
const vendor = hasVerbLevelTts ? this.synthesizer.vendor : cs.speechSynthesisVendor ;
|
const voice = this.synthesizer.voice || cs.speechSynthesisVoice ;
|
||||||
const language = hasVerbLevelTts ? this.synthesizer.language : cs.speechSynthesisLanguage ;
|
|
||||||
const voice = hasVerbLevelTts ? this.synthesizer.voice : cs.speechSynthesisVoice ;
|
|
||||||
const engine = this.synthesizer.engine || 'standard';
|
const engine = this.synthesizer.engine || 'standard';
|
||||||
const salt = cs.callSid;
|
const salt = cs.callSid;
|
||||||
const credentials = cs.getSpeechCredentials(vendor, 'tts');
|
const credentials = cs.getSpeechCredentials(vendor, 'tts');
|
||||||
|
|||||||
Reference in New Issue
Block a user