mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2025-12-20 16:50:39 +00:00
initial support for Rasa
This commit is contained in:
@@ -202,7 +202,6 @@ class CallSession extends Emitter {
|
|||||||
*/
|
*/
|
||||||
getSpeechCredentials(vendor, type) {
|
getSpeechCredentials(vendor, type) {
|
||||||
const {writeAlerts, AlertType} = this.srf.locals;
|
const {writeAlerts, AlertType} = this.srf.locals;
|
||||||
this.logger.debug({vendor, type, speech: this.accountInfo.speech}, `searching for speech for vendor ${vendor}`);
|
|
||||||
if (this.accountInfo.speech && this.accountInfo.speech.length > 0) {
|
if (this.accountInfo.speech && this.accountInfo.speech.length > 0) {
|
||||||
const credential = this.accountInfo.speech.find((s) => s.vendor === vendor);
|
const credential = this.accountInfo.speech.find((s) => s.vendor === vendor);
|
||||||
if (credential && (
|
if (credential && (
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const makeTask = require('./make_task');
|
|||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
class TaskGather extends Task {
|
class TaskGather extends Task {
|
||||||
constructor(logger, opts) {
|
constructor(logger, opts, parentTask) {
|
||||||
super(logger, opts);
|
super(logger, opts);
|
||||||
this.preconditions = TaskPreconditions.Endpoint;
|
this.preconditions = TaskPreconditions.Endpoint;
|
||||||
|
|
||||||
@@ -40,6 +40,8 @@ class TaskGather extends Task {
|
|||||||
|
|
||||||
if (this.say) this.sayTask = makeTask(this.logger, {say: this.say}, this);
|
if (this.say) this.sayTask = makeTask(this.logger, {say: this.say}, this);
|
||||||
if (this.play) this.playTask = makeTask(this.logger, {play: this.play}, this);
|
if (this.play) this.playTask = makeTask(this.logger, {play: this.play}, this);
|
||||||
|
|
||||||
|
this.parentTask = parentTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() { return TaskName.Gather; }
|
get name() { return TaskName.Gather; }
|
||||||
@@ -154,7 +156,6 @@ class TaskGather extends Task {
|
|||||||
});
|
});
|
||||||
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
ep.addCustomEventListener(AwsTranscriptionEvents.Transcription, this._onTranscription.bind(this, cs, ep));
|
||||||
}
|
}
|
||||||
this.logger.debug({vars: opts}, 'setting freeswitch vars');
|
|
||||||
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'));
|
||||||
|
|
||||||
@@ -235,7 +236,11 @@ class TaskGather extends Task {
|
|||||||
await this.performAction({digits: this.digitBuffer});
|
await this.performAction({digits: this.digitBuffer});
|
||||||
}
|
}
|
||||||
else if (reason.startsWith('speech')) {
|
else if (reason.startsWith('speech')) {
|
||||||
await this.performAction({speech: evt});
|
if (this.parentTask) this.parentTask.emit('transcription', evt);
|
||||||
|
else await this.performAction({speech: evt});
|
||||||
|
}
|
||||||
|
else if (reason.startsWith('timeout') && this.parentTask) {
|
||||||
|
this.parentTask.emit('timeout', evt);
|
||||||
}
|
}
|
||||||
this.notifyTaskDone();
|
this.notifyTaskDone();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ function makeTask(logger, obj, parent) {
|
|||||||
case TaskName.Message:
|
case TaskName.Message:
|
||||||
const TaskMessage = require('./message');
|
const TaskMessage = require('./message');
|
||||||
return new TaskMessage(logger, data, parent);
|
return new TaskMessage(logger, data, parent);
|
||||||
|
case TaskName.Rasa:
|
||||||
|
const TaskRasa = require('./rasa');
|
||||||
|
return new TaskRasa(logger, data, parent);
|
||||||
case TaskName.Say:
|
case TaskName.Say:
|
||||||
const TaskSay = require('./say');
|
const TaskSay = require('./say');
|
||||||
return new TaskSay(logger, data, parent);
|
return new TaskSay(logger, data, parent);
|
||||||
|
|||||||
109
lib/tasks/rasa.js
Normal file
109
lib/tasks/rasa.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
const Task = require('./task');
|
||||||
|
const {TaskName, TaskPreconditions} = require('../utils/constants');
|
||||||
|
const makeTask = require('./make_task');
|
||||||
|
const bent = require('bent');
|
||||||
|
|
||||||
|
class Rasa extends Task {
|
||||||
|
constructor(logger, opts) {
|
||||||
|
super(logger, opts);
|
||||||
|
this.preconditions = TaskPreconditions.Endpoint;
|
||||||
|
|
||||||
|
this.prompt = this.data.prompt;
|
||||||
|
this.eventHook = this.data?.eventHook;
|
||||||
|
this.actionHook = this.data?.actionHook;
|
||||||
|
this.post = bent('POST', 'json', 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() { return TaskName.Rasa; }
|
||||||
|
|
||||||
|
async exec(cs, ep) {
|
||||||
|
await super.exec(cs);
|
||||||
|
|
||||||
|
this.ep = ep;
|
||||||
|
try {
|
||||||
|
/* set event handlers */
|
||||||
|
this.on('transcription', this._onTranscription.bind(this, cs, ep));
|
||||||
|
this.on('timeout', this._onTimeout.bind(this, cs, ep));
|
||||||
|
|
||||||
|
/* start the first gather */
|
||||||
|
this.gatherTask = this._makeGatherTask(this.prompt);
|
||||||
|
this.gatherTask.exec(cs, ep, this)
|
||||||
|
.catch((err) => this.logger.info({err}, 'Rasa gather task returned error'));
|
||||||
|
|
||||||
|
await this.awaitTaskDone();
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error({err}, 'Rasa error');
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async kill(cs) {
|
||||||
|
super.kill(cs);
|
||||||
|
if (this.ep.connected) {
|
||||||
|
this.logger.debug('Rasa:kill');
|
||||||
|
|
||||||
|
this.performAction({rasaResult: 'caller hungup'})
|
||||||
|
.catch((err) => this.logger.error({err}, 'rasa - error w/ action webook'));
|
||||||
|
|
||||||
|
await this.ep.api('uuid_break', this.ep.uuid).catch((err) => this.logger.info(err, 'Error killing audio'));
|
||||||
|
}
|
||||||
|
this.removeAllListeners();
|
||||||
|
this.notifyTaskDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
_makeGatherTask(prompt) {
|
||||||
|
let opts = {
|
||||||
|
input: ['speech'],
|
||||||
|
timeout: this.data.timeout || 10,
|
||||||
|
recognizer: this.data.recognizer || {
|
||||||
|
vendor: 'default',
|
||||||
|
language: 'default'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (prompt) {
|
||||||
|
const sayOpts = this.data.tts ?
|
||||||
|
{text: prompt, synthesizer: this.data.tts} :
|
||||||
|
{text: prompt};
|
||||||
|
|
||||||
|
opts = {
|
||||||
|
...opts,
|
||||||
|
say: sayOpts
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.logger.debug({opts}, 'constructing a nested gather object');
|
||||||
|
const gather = makeTask(this.logger, {gather: opts}, this);
|
||||||
|
return gather;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _onTranscription(cs, ep, evt) {
|
||||||
|
this.logger.debug({evt}, `Rasa: got transcription for callSid ${cs.callSid}`);
|
||||||
|
const utterance = evt.alternatives[0].transcript;
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
sender: cs.callSid,
|
||||||
|
message: utterance
|
||||||
|
};
|
||||||
|
this.logger.debug({payload}, 'Rasa:_onTranscription - sending payload to Rasa');
|
||||||
|
const response = await this.post(this.data.url, payload);
|
||||||
|
this.logger.debug({response}, 'Rasa:_onTranscription - got response from Rasa');
|
||||||
|
const botUtterance = Array.isArray(response) ?
|
||||||
|
response.reduce((prev, current) => `${prev} ${current.text}`, '') :
|
||||||
|
null;
|
||||||
|
if (botUtterance) {
|
||||||
|
this.logger.debug({botUtterance}, 'playing out bot utterance');
|
||||||
|
this.gatherTask = this._makeGatherTask(botUtterance);
|
||||||
|
this.gatherTask.exec(cs, ep, this)
|
||||||
|
.catch((err) => this.logger.info({err}, 'Rasa gather task returned error'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error({err}, 'Error sending');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_onTimeout(cs, ep, evt) {
|
||||||
|
this.logger.debug({evt}, 'Rasa: got timeout');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Rasa;
|
||||||
@@ -78,7 +78,6 @@
|
|||||||
"say": "#say"
|
"say": "#say"
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"actionHook"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"conference": {
|
"conference": {
|
||||||
@@ -228,6 +227,19 @@
|
|||||||
"length"
|
"length"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"rasa": {
|
||||||
|
"properties": {
|
||||||
|
"url": "string",
|
||||||
|
"recognizer": "#recognizer",
|
||||||
|
"tts": "#synthesizer",
|
||||||
|
"prompt": "string",
|
||||||
|
"actionHook": "object|string",
|
||||||
|
"eventHook": "object|string"
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
]
|
||||||
|
},
|
||||||
"redirect": {
|
"redirect": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"actionHook": "object|string"
|
"actionHook": "object|string"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"Message": "message",
|
"Message": "message",
|
||||||
"Pause": "pause",
|
"Pause": "pause",
|
||||||
"Play": "play",
|
"Play": "play",
|
||||||
|
"Rasa": "rasa",
|
||||||
"Redirect": "redirect",
|
"Redirect": "redirect",
|
||||||
"RestDial": "rest:dial",
|
"RestDial": "rest:dial",
|
||||||
"SipDecline": "sip:decline",
|
"SipDecline": "sip:decline",
|
||||||
|
|||||||
Reference in New Issue
Block a user