first implementation of nextTurn and gather task cognigy

This commit is contained in:
akirilyuk
2022-02-02 14:59:04 +01:00
parent f1f21fb23b
commit 83c015c839
4 changed files with 68 additions and 26 deletions

View File

@@ -176,8 +176,8 @@ class Cognigy extends Task {
this.notifyTaskDone(); this.notifyTaskDone();
} }
_makeGatherTask({textPrompt, urlPrompt} = {}) { _makeGatherTask({textPrompt, urlPrompt, turnConfig} = {}) {
const config = this.config.makeGatherTaskConfig({textPrompt, urlPrompt}); const config = this.config.makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig});
const {retry, ...rest} = config; const {retry, ...rest} = config;
this.retry = retry; this.retry = retry;
const gather = makeTask(this.logger, {gather: rest}, this); const gather = makeTask(this.logger, {gather: rest}, this);
@@ -199,19 +199,22 @@ class Cognigy extends Task {
} }
_makeReferTask(referTo) { _makeReferTask(referTo) {
const refer = makeTask(this.logger, {'sip:refer': { return makeTask(this.logger, {'sip:refer': {
referTo referTo
}}); }}
return refer; );
} }
_makeHangupTask(reason) { _makeHangupTask(reason) {
const hangup = makeTask(this.logger, {hangup: { return makeTask(this.logger, {hangup: {
headers: { headers: {
'X-Reason': reason 'X-Reason': reason
} }
}}); }});
return hangup; }
_makePlayTask(url, loop) {
} }
/* if we need to interrupt the currently-running say task(s), call this */ /* if we need to interrupt the currently-running say task(s), call this */
@@ -235,11 +238,16 @@ class Cognigy extends Task {
async _onBotFinalPing(cs, ep) { async _onBotFinalPing(cs, ep) {
this.logger.info({prompts: this.prompts}, 'Cognigy:_onBotFinalPing'); this.logger.info({prompts: this.prompts}, 'Cognigy:_onBotFinalPing');
try { try {
await this.taskQueue.lastPromise; if (!this.gatherTaskExists) {
this.gatherTask = this._makeGatherTask(); await this.taskQueue.lastPromise;
this.gatherTask.exec(cs, ep, this) this.gatherTask = this._makeGatherTask();
.catch((err) => this.logger.info({err}, 'Cognigy gather task returned error')); this.gatherTask.exec(cs, ep, this)
this.prompts = []; .catch((err) => this.logger.info({err}, 'Cognigy gather task returned error'));
this.prompts = [];
} else {
this.logger.info({}, 'no need to create a gather task, it already exists!');
}
} catch (err) { } catch (err) {
this.logger.error({err}, 'Could not execute bot final ping!'); this.logger.error({err}, 'Could not execute bot final ping!');
} }
@@ -264,8 +272,10 @@ class Cognigy extends Task {
} }
const text = parseBotText(evt); const text = parseBotText(evt);
if (evt.data) this.config.update(evt.data); if (evt.data && evt.data.config && evt.data.config.session) this.config.update(evt.data.config.session);
if (text) {
// only add say task if its a normal cognigy node and not a "gather task"
if (text && (!evt.data || !evt.data.type || !evt.data.type !== 'gather')) {
this._enqueueTask(async() => { this._enqueueTask(async() => {
this.logger.info({text}, 'received text'); this.logger.info({text}, 'received text');
const sayTask = this._makeSayTask(text); const sayTask = this._makeSayTask(text);
@@ -293,6 +303,22 @@ class Cognigy extends Task {
cs.replaceApplication([this._makeReferTask(evt.data.referTo)]); cs.replaceApplication([this._makeReferTask(evt.data.referTo)]);
}); });
return; return;
case 'gather':
this.gatherTaskExists = true;
this._enqueueTask(async() => {
this.gatherTask = this._makeGatherTask({
textPrompt: evt.data.text,
urlPrompt: evt.data.url,
nextTurnConfig: evt.data.config && evt.data.config.nextTurn
});
try {
this.gatherTask.exec(cs, ep, this);
} catch (err) {
this.logger.info({err}, 'Cognigy gather task returned error');
}
this.gatherTaskExists = false;
});
return;
default: default:
break; break;
} }

View File

@@ -1,5 +1,5 @@
const Emitter = require('events'); const Emitter = require('events');
const lodash = require('lodash');
const hasKeys = (obj) => typeof obj === 'object' && Object.keys(obj) > 0; const hasKeys = (obj) => typeof obj === 'object' && Object.keys(obj) > 0;
const stripNulls = (obj) => { const stripNulls = (obj) => {
@@ -13,19 +13,33 @@ class SpeechConfig extends Emitter {
this.logger = logger; this.logger = logger;
this.ep = ep; this.ep = ep;
this.sessionConfig = opts.session || {}; this.sessionConfig = opts.session || {};
this.turnConfig = opts.nextTurn || {};
this.update(opts); this.update(opts);
} }
update(opts = {}) { update(opts = {}) {
const {session, nextTurn = {}} = opts; // TODO validation of session params?
if (session) this.sessionConfig = {...this.sessionConfig, ...session}; const {session } = opts;
this.turnConfig = nextTurn; if (session) {
this.logger.debug({opts, sessionLevel: this.sessionConfig, turnLevel: this.turnConfig}, 'SpeechConfig updated'); this.sessionConfig = lodash.merge(
{},
this.sessionConfig,
session
);
}
//this.turnConfig = nextTurn;
this.logger.debug({opts, sessionLevel: this.sessionConfig}, 'SpeechConfig updated');
} }
makeGatherTaskConfig({textPrompt, urlPrompt} = {}) { makeGatherTaskConfig({textPrompt, urlPrompt, turnConfig} = {}) {
const opts = JSON.parse(JSON.stringify(this.sessionConfig || {})); // we merge from top to bottom deeply so we wil have
// defaults from session config and then will override them via turn config
const opts = lodash.merge(
{},
this.sessionConfig || {}, // this should not be undefined ever
turnConfig
);
/*
const nextTurnKeys = Object.keys(this.turnConfig || {}); const nextTurnKeys = Object.keys(this.turnConfig || {});
const newKeys = nextTurnKeys.filter((k) => !(k in opts)); const newKeys = nextTurnKeys.filter((k) => !(k in opts));
const bothKeys = nextTurnKeys.filter((k) => k in opts); const bothKeys = nextTurnKeys.filter((k) => k in opts);
@@ -33,10 +47,12 @@ class SpeechConfig extends Emitter {
for (const key of newKeys) opts[key] = this.turnConfig[key]; for (const key of newKeys) opts[key] = this.turnConfig[key];
for (const key of bothKeys) opts[key] = {...opts[key], ...this.turnConfig[key]}; for (const key of bothKeys) opts[key] = {...opts[key], ...this.turnConfig[key]};
*/
this.logger.debug({ this.logger.debug({
opts, opts,
sessionConfig: this.sessionConfig, sessionConfig: this.sessionConfig,
turnConfig: this.turnConfig, turnConfig
}, 'Congigy SpeechConfig:_makeGatherTask current options'); }, 'Congigy SpeechConfig:_makeGatherTask current options');
/* input type: speech and/or dtmf entry */ /* input type: speech and/or dtmf entry */
@@ -62,6 +78,7 @@ class SpeechConfig extends Emitter {
}; };
} }
// todo what is the logic here if we put both? play over say or say over play?
if (urlPrompt) { if (urlPrompt) {
playConfig = { playConfig = {
url: urlPrompt url: urlPrompt
@@ -89,9 +106,6 @@ class SpeechConfig extends Emitter {
const final = stripNulls(config); const final = stripNulls(config);
/* turn config can now be emptied for next turn of conversation */
this.turnConfig = {};
const finalConfig = final; const finalConfig = final;
if (sayConfig) { if (sayConfig) {
finalConfig.say = sayConfig; finalConfig.say = sayConfig;

1
package-lock.json generated
View File

@@ -25,6 +25,7 @@
"drachtio-srf": "^4.4.55", "drachtio-srf": "^4.4.55",
"express": "^4.17.1", "express": "^4.17.1",
"ip": "^1.1.5", "ip": "^1.1.5",
"lodash": "^4.17.21",
"moment": "^2.29.1", "moment": "^2.29.1",
"parse-url": "^5.0.7", "parse-url": "^5.0.7",
"pino": "^6.13.2", "pino": "^6.13.2",

View File

@@ -43,6 +43,7 @@
"drachtio-srf": "^4.4.55", "drachtio-srf": "^4.4.55",
"express": "^4.17.1", "express": "^4.17.1",
"ip": "^1.1.5", "ip": "^1.1.5",
"lodash": "^4.17.21",
"moment": "^2.29.1", "moment": "^2.29.1",
"parse-url": "^5.0.7", "parse-url": "^5.0.7",
"pino": "^6.13.2", "pino": "^6.13.2",