mirror of
https://github.com/jambonz/jambonz-feature-server.git
synced 2026-02-14 18:30:59 +00:00
Compare commits
8 Commits
feat/dialo
...
v0.9.2-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4a6057fc6 | ||
|
|
174438bb01 | ||
|
|
4348615b75 | ||
|
|
d365883bfe | ||
|
|
c0ab936b76 | ||
|
|
600ff763fa | ||
|
|
4d077e990f | ||
|
|
eccef54b04 |
@@ -21,6 +21,7 @@ Configuration is provided via environment variables:
|
|||||||
|ENCRYPTION_SECRET| secret for credential encryption(JWT_SECRET is deprecated) |yes|
|
|ENCRYPTION_SECRET| secret for credential encryption(JWT_SECRET is deprecated) |yes|
|
||||||
|GOOGLE_APPLICATION_CREDENTIALS| path to gcp service key file|yes|
|
|GOOGLE_APPLICATION_CREDENTIALS| path to gcp service key file|yes|
|
||||||
|HTTP_PORT| tcp port to listen on for API requests from jambonz-api-server|yes|
|
|HTTP_PORT| tcp port to listen on for API requests from jambonz-api-server|yes|
|
||||||
|
|HTTP_IP| IP Address for API requests from jambonz-api-server |no|
|
||||||
|JAMBONES_GATHER_EARLY_HINTS_MATCH| if true and hints are provided, gather will opportunistically review interim transcripts if possible to reduce ASR latency |no|
|
|JAMBONES_GATHER_EARLY_HINTS_MATCH| if true and hints are provided, gather will opportunistically review interim transcripts if possible to reduce ASR latency |no|
|
||||||
|JAMBONES_FREESWITCH| IP:port:secret for Freeswitch server (e.g. '127.0.0.1:8021:JambonzR0ck$'|yes|
|
|JAMBONES_FREESWITCH| IP:port:secret for Freeswitch server (e.g. '127.0.0.1:8021:JambonzR0ck$'|yes|
|
||||||
|JAMBONES_LOGLEVEL| log level for application, 'info' or 'debug'|no|
|
|JAMBONES_LOGLEVEL| log level for application, 'info' or 'debug'|no|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ const JAMBONES_LOGLEVEL = process.env.JAMBONES_LOGLEVEL || 'info';
|
|||||||
const JAMBONES_INJECT_CONTENT = process.env.JAMBONES_INJECT_CONTENT;
|
const JAMBONES_INJECT_CONTENT = process.env.JAMBONES_INJECT_CONTENT;
|
||||||
|
|
||||||
const PORT = parseInt(process.env.HTTP_PORT, 10) || 3000;
|
const PORT = parseInt(process.env.HTTP_PORT, 10) || 3000;
|
||||||
|
const HTTP_IP = process.env.HTTP_IP;
|
||||||
const HTTP_PORT_MAX = parseInt(process.env.HTTP_PORT_MAX, 10);
|
const HTTP_PORT_MAX = parseInt(process.env.HTTP_PORT_MAX, 10);
|
||||||
|
|
||||||
const K8S = process.env.K8S;
|
const K8S = process.env.K8S;
|
||||||
@@ -170,6 +171,7 @@ module.exports = {
|
|||||||
JAMBONES_CLUSTER_ID,
|
JAMBONES_CLUSTER_ID,
|
||||||
PORT,
|
PORT,
|
||||||
HTTP_PORT_MAX,
|
HTTP_PORT_MAX,
|
||||||
|
HTTP_IP,
|
||||||
K8S,
|
K8S,
|
||||||
K8S_SBC_SIP_SERVICE_NAME,
|
K8S_SBC_SIP_SERVICE_NAME,
|
||||||
JAMBONES_SUBNET,
|
JAMBONES_SUBNET,
|
||||||
|
|||||||
@@ -97,7 +97,8 @@ router.post('/',
|
|||||||
'X-Trace-ID': rootSpan.traceId,
|
'X-Trace-ID': rootSpan.traceId,
|
||||||
...(req.body?.application_sid && {'X-Application-Sid': req.body.application_sid}),
|
...(req.body?.application_sid && {'X-Application-Sid': req.body.application_sid}),
|
||||||
...(restDial.fromHost && {'X-Preferred-From-Host': restDial.fromHost}),
|
...(restDial.fromHost && {'X-Preferred-From-Host': restDial.fromHost}),
|
||||||
...(record_all_calls && {'X-Record-All-Calls': recordOutputFormat})
|
...(record_all_calls && {'X-Record-All-Calls': recordOutputFormat}),
|
||||||
|
...target.headers
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (target.type) {
|
switch (target.type) {
|
||||||
|
|||||||
@@ -566,9 +566,7 @@ class CallSession extends Emitter {
|
|||||||
//this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - ahd settings');
|
//this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - ahd settings');
|
||||||
//await this.clearActionHookDelayProcessor();
|
//await this.clearActionHookDelayProcessor();
|
||||||
}
|
}
|
||||||
else {
|
this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - say or play action completed');
|
||||||
this.logger.debug('CallSession:clearOrRestoreActionHookDelayProcessor - restore ahd after gather override');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1069,7 +1067,7 @@ class CallSession extends Emitter {
|
|||||||
try {
|
try {
|
||||||
await this._awaitCommandsOrHangup();
|
await this._awaitCommandsOrHangup();
|
||||||
|
|
||||||
await this.clearOrRestoreActionHookDelayProcessor();
|
//await this.clearOrRestoreActionHookDelayProcessor();
|
||||||
|
|
||||||
//TODO: remove filler noise code and simply create as action hook delay
|
//TODO: remove filler noise code and simply create as action hook delay
|
||||||
if (this._isPlayingFillerNoise) {
|
if (this._isPlayingFillerNoise) {
|
||||||
@@ -2074,6 +2072,9 @@ Duration=${duration} `
|
|||||||
// When this call kicked out from conference, session need to replace endpoint
|
// When this call kicked out from conference, session need to replace endpoint
|
||||||
// but this.ms might be undefined/null at this case.
|
// but this.ms might be undefined/null at this case.
|
||||||
this.ms = this.ms || this.getMS();
|
this.ms = this.ms || this.getMS();
|
||||||
|
// Destroy previous ep if it's still running.
|
||||||
|
if (this.ep?.connected) this.ep.destroy();
|
||||||
|
|
||||||
this.ep = await this.ms.createEndpoint({remoteSdp: this.dlg.remote.sdp});
|
this.ep = await this.ms.createEndpoint({remoteSdp: this.dlg.remote.sdp});
|
||||||
this._configMsEndpoint();
|
this._configMsEndpoint();
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,9 @@ class Conference extends Task {
|
|||||||
this.emitter.emit('kill');
|
this.emitter.emit('kill');
|
||||||
await this._doFinalMemberCheck(cs);
|
await this._doFinalMemberCheck(cs);
|
||||||
if (this.ep && this.ep.connected) {
|
if (this.ep && this.ep.connected) {
|
||||||
this.ep.conn.removeAllListeners('esl::event::CUSTOM::*');
|
// drachtio-fsmrf override esl::event::CUSTOM to conference join listerner, After finish the conference
|
||||||
|
// the application need to reset the esl::event::CUSTOM for another use on the same endpoint
|
||||||
|
this.ep.resetEslCustomEvent();
|
||||||
this.ep.api(`conference ${this.confName} kick ${this.memberId}`)
|
this.ep.api(`conference ${this.confName} kick ${this.memberId}`)
|
||||||
.catch((err) => this.logger.info({err}, 'Error kicking participant'));
|
.catch((err) => this.logger.info({err}, 'Error kicking participant'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
const assert = require('assert');
|
|
||||||
const Task = require('../task');
|
const Task = require('../task');
|
||||||
const {TaskName, TaskPreconditions} = require('../../utils/constants');
|
const {TaskName, TaskPreconditions} = require('../../utils/constants');
|
||||||
const Intent = require('./intent');
|
const Intent = require('./intent');
|
||||||
@@ -11,29 +10,19 @@ class Dialogflow extends Task {
|
|||||||
super(logger, opts);
|
super(logger, opts);
|
||||||
this.preconditions = TaskPreconditions.Endpoint;
|
this.preconditions = TaskPreconditions.Endpoint;
|
||||||
this.credentials = this.data.credentials;
|
this.credentials = this.data.credentials;
|
||||||
this.project = this.data.project;
|
|
||||||
this.agent = this.data.agent;
|
|
||||||
this.region = this.data.region || 'default';
|
|
||||||
this.model = this.data.model || 'es';
|
|
||||||
this.queryInput = this.data.queryInput || {};
|
|
||||||
|
|
||||||
assert(this.agent || !this.isCX, 'agent is required for dialogflow cx');
|
/* set project id with environment and region (optionally) */
|
||||||
assert(this.credentials, 'dialogflow credentials are required');
|
if (this.data.environment && this.data.region) {
|
||||||
|
this.project = `${this.data.project}:${this.data.environment}:${this.data.region}`;
|
||||||
if (this.isCX) {
|
}
|
||||||
this.environment = this.data.environment || 'default';
|
else if (this.data.environment) {
|
||||||
|
this.project = `${this.data.project}:${this.data.environment}`;
|
||||||
|
}
|
||||||
|
else if (this.data.region) {
|
||||||
|
this.project = `${this.data.project}::${this.data.region}`;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* ES: set project id with environment and region (optionally) */
|
this.project = this.data.project;
|
||||||
if (this.data.environment && this.data.region) {
|
|
||||||
this.project = `${this.data.project}:${this.data.environment}:${this.data.region}`;
|
|
||||||
}
|
|
||||||
else if (this.data.environment) {
|
|
||||||
this.project = `${this.data.project}:${this.data.environment}`;
|
|
||||||
}
|
|
||||||
else if (this.data.region) {
|
|
||||||
this.project = `${this.data.project}::${this.data.region}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lang = this.data.lang || 'en-US';
|
this.lang = this.data.lang || 'en-US';
|
||||||
@@ -78,23 +67,31 @@ class Dialogflow extends Task {
|
|||||||
this.fallbackLabel = this.data.tts.fallbackLabel;
|
this.fallbackLabel = this.data.tts.fallbackLabel;
|
||||||
}
|
}
|
||||||
this.bargein = this.data.bargein;
|
this.bargein = this.data.bargein;
|
||||||
|
|
||||||
this.cmd = this.model === 'cx' ? 'dialogflow_cx_start' : 'dialogflow_start';
|
|
||||||
this.cmdStop = this.model === 'cx' ? 'dialogflow_cx_stop' : 'dialogflow_stop';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() { return TaskName.Dialogflow; }
|
get name() { return TaskName.Dialogflow; }
|
||||||
|
|
||||||
get isCX() { return this.model === 'cx'; }
|
|
||||||
|
|
||||||
get isES() { return !this.isCX; }
|
|
||||||
|
|
||||||
async exec(cs, {ep}) {
|
async exec(cs, {ep}) {
|
||||||
await super.exec(cs);
|
await super.exec(cs);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.init(cs, ep);
|
await this.init(cs, ep);
|
||||||
await this.startBot('default');
|
|
||||||
|
this.logger.debug(`starting dialogflow bot ${this.project}`);
|
||||||
|
|
||||||
|
// kick it off
|
||||||
|
const baseArgs = `${this.ep.uuid} ${this.project} ${this.lang} ${this.welcomeEvent}`;
|
||||||
|
if (this.welcomeEventParams) {
|
||||||
|
this.ep.api('dialogflow_start', `${baseArgs} '${JSON.stringify(this.welcomeEventParams)}'`);
|
||||||
|
}
|
||||||
|
else if (this.welcomeEvent.length) {
|
||||||
|
this.ep.api('dialogflow_start', baseArgs);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.ep.api('dialogflow_start', `${this.ep.uuid} ${this.project} ${this.lang}`);
|
||||||
|
}
|
||||||
|
this.logger.debug(`started dialogflow bot ${this.project}`);
|
||||||
|
|
||||||
await this.awaitTaskDone();
|
await this.awaitTaskDone();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.logger.error({err}, 'Dialogflow:exec error');
|
this.logger.error({err}, 'Dialogflow:exec error');
|
||||||
@@ -111,12 +108,6 @@ class Dialogflow extends Task {
|
|||||||
this.ep.removeCustomEventListener('dialogflow::end_of_utterance');
|
this.ep.removeCustomEventListener('dialogflow::end_of_utterance');
|
||||||
this.ep.removeCustomEventListener('dialogflow::error');
|
this.ep.removeCustomEventListener('dialogflow::error');
|
||||||
|
|
||||||
this.ep.removeCustomEventListener('dialogflow_cx::intent');
|
|
||||||
this.ep.removeCustomEventListener('dialogflow_cx::transcription');
|
|
||||||
this.ep.removeCustomEventListener('dialogflow_cx::audio_provided');
|
|
||||||
this.ep.removeCustomEventListener('dialogflow_cx::end_of_utterance');
|
|
||||||
this.ep.removeCustomEventListener('dialogflow_cx::error');
|
|
||||||
|
|
||||||
this._clearNoinputTimer();
|
this._clearNoinputTimer();
|
||||||
|
|
||||||
if (!this.reportedFinalAction) this.performAction({dialogflowResult: 'caller hungup'})
|
if (!this.reportedFinalAction) this.performAction({dialogflowResult: 'caller hungup'})
|
||||||
@@ -150,12 +141,6 @@ class Dialogflow extends Task {
|
|||||||
this.ep.addCustomEventListener('dialogflow::end_of_utterance', this._onEndOfUtterance.bind(this, ep, cs));
|
this.ep.addCustomEventListener('dialogflow::end_of_utterance', this._onEndOfUtterance.bind(this, ep, cs));
|
||||||
this.ep.addCustomEventListener('dialogflow::error', this._onError.bind(this, ep, cs));
|
this.ep.addCustomEventListener('dialogflow::error', this._onError.bind(this, ep, cs));
|
||||||
|
|
||||||
this.ep.addCustomEventListener('dialogflow_cx::intent', this._onIntent.bind(this, ep, cs));
|
|
||||||
this.ep.addCustomEventListener('dialogflow_cx::transcription', this._onTranscription.bind(this, ep, cs));
|
|
||||||
this.ep.addCustomEventListener('dialogflow_cx::audio_provided', this._onAudioProvided.bind(this, ep, cs));
|
|
||||||
this.ep.addCustomEventListener('dialogflow_cx::end_of_utterance', this._onEndOfUtterance.bind(this, ep, cs));
|
|
||||||
this.ep.addCustomEventListener('dialogflow_cx::error', this._onError.bind(this, ep, cs));
|
|
||||||
|
|
||||||
const obj = typeof this.credentials === 'string' ? JSON.parse(this.credentials) : this.credentials;
|
const obj = typeof this.credentials === 'string' ? JSON.parse(this.credentials) : this.credentials;
|
||||||
const creds = JSON.stringify(obj);
|
const creds = JSON.stringify(obj);
|
||||||
await this.ep.set('GOOGLE_APPLICATION_CREDENTIALS', creds);
|
await this.ep.set('GOOGLE_APPLICATION_CREDENTIALS', creds);
|
||||||
@@ -166,51 +151,6 @@ class Dialogflow extends Task {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async startBot(intent) {
|
|
||||||
if (this.isCX) {
|
|
||||||
await this.startBotCX(intent);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await this.startBotES(intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async startBotES() {
|
|
||||||
this.logger.info('starting dialogflow ES bot');
|
|
||||||
const baseArgs = `${this.ep.uuid} ${this.project} ${this.lang} ${this.welcomeEvent}`;
|
|
||||||
if (this.welcomeEventParams) {
|
|
||||||
await this.ep.api(this.cmd, `${baseArgs} '${JSON.stringify(this.welcomeEventParams)}'`);
|
|
||||||
}
|
|
||||||
else if (this.welcomeEvent.length) {
|
|
||||||
await this.ep.api(this.cmd, baseArgs);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
await this.ep.api(this.cmd, `${this.ep.uuid} ${this.project} ${this.lang}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async startBotCX(intent) {
|
|
||||||
const baseArgs = [
|
|
||||||
this.ep.uuid,
|
|
||||||
this.region,
|
|
||||||
this.project,
|
|
||||||
this.agent,
|
|
||||||
this.environment,
|
|
||||||
this.lang,
|
|
||||||
];
|
|
||||||
if (intent) {
|
|
||||||
baseArgs.push(intent);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
if (Object.keys(this.queryInput).length > 0) {
|
|
||||||
baseArgs.push(`'${JSON.stringify(this.queryInput)}'`);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
this.logger.info({args: baseArgs}, 'starting dialogflow CX bot');
|
|
||||||
|
|
||||||
await this.ep.api(this.cmd, `${baseArgs.join(' ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An intent has been returned. Since we are using SINGLE_UTTERANCE on the dialogflow side,
|
* An intent has been returned. Since we are using SINGLE_UTTERANCE on the dialogflow side,
|
||||||
* we may get an empty intent, signified by the lack of a 'response_id' attribute.
|
* we may get an empty intent, signified by the lack of a 'response_id' attribute.
|
||||||
@@ -231,20 +171,20 @@ class Dialogflow extends Task {
|
|||||||
if (this.noinput && this.greetingPlayed) {
|
if (this.noinput && this.greetingPlayed) {
|
||||||
this.logger.info('no input timer fired, reprompting..');
|
this.logger.info('no input timer fired, reprompting..');
|
||||||
this.noinput = false;
|
this.noinput = false;
|
||||||
ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang} ${this.noInputEvent}`);
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang} ${this.noInputEvent}`);
|
||||||
}
|
}
|
||||||
else if (this.dtmfEntry && this.greetingPlayed) {
|
else if (this.dtmfEntry && this.greetingPlayed) {
|
||||||
this.logger.info('dtmf detected, reprompting..');
|
this.logger.info('dtmf detected, reprompting..');
|
||||||
ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang} none \'${this.dtmfEntry}\'`);
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang} none \'${this.dtmfEntry}\'`);
|
||||||
this.dtmfEntry = null;
|
this.dtmfEntry = null;
|
||||||
}
|
}
|
||||||
else if (this.greetingPlayed) {
|
else if (this.greetingPlayed) {
|
||||||
this.logger.info('starting another intent');
|
this.logger.info('starting another intent');
|
||||||
ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang}`);
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.logger.info('got empty intent');
|
this.logger.info('got empty intent');
|
||||||
ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang}`);
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -291,7 +231,7 @@ class Dialogflow extends Task {
|
|||||||
// start a new intent, (we want to continue to listen during the audio playback)
|
// start a new intent, (we want to continue to listen during the audio playback)
|
||||||
// _unless_ we are transferring or ending the session
|
// _unless_ we are transferring or ending the session
|
||||||
if (!this.hangupAfterPlayDone) {
|
if (!this.hangupAfterPlayDone) {
|
||||||
ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang}`);
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -446,12 +386,10 @@ class Dialogflow extends Task {
|
|||||||
// kill filler audio
|
// kill filler audio
|
||||||
await ep.api('uuid_break', ep.uuid);
|
await ep.api('uuid_break', ep.uuid);
|
||||||
|
|
||||||
// if ES start a new intent (for CX we do not set single_utterance on),
|
// start a new intent, (we want to continue to listen during the audio playback)
|
||||||
// (we want to continue to listen during the audio playback)
|
|
||||||
// _unless_ we are transferring or ending the session
|
// _unless_ we are transferring or ending the session
|
||||||
if (!this.hangupAfterPlayDone) {
|
if (/*this.greetingPlayed &&*/ !this.hangupAfterPlayDone) {
|
||||||
this.startBot();
|
ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`);
|
||||||
//ep.api(this.cmd, `${ep.uuid} ${this.project} ${this.lang}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.playInProgress = true;
|
this.playInProgress = true;
|
||||||
@@ -476,7 +414,12 @@ class Dialogflow extends Task {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
if (!this.inbound && !this.greetingPlayed) {
|
||||||
|
this.logger.info('finished greeting on outbound call, starting new intent');
|
||||||
|
this.ep.api('dialogflow_start', `${ep.uuid} ${this.project} ${this.lang}`);
|
||||||
|
}
|
||||||
|
*/
|
||||||
this.greetingPlayed = true;
|
this.greetingPlayed = true;
|
||||||
|
|
||||||
if (this.hangupAfterPlayDone) {
|
if (this.hangupAfterPlayDone) {
|
||||||
@@ -511,7 +454,7 @@ class Dialogflow extends Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kill the current dialogflow, which will result in us getting an immediate intent
|
// kill the current dialogflow, which will result in us getting an immediate intent
|
||||||
ep.api(this.cmdStop, `${ep.uuid}`)
|
ep.api('dialogflow_stop', `${ep.uuid}`)
|
||||||
.catch((err) => this.logger.info(`dialogflow_stop failed: ${err.message}`));
|
.catch((err) => this.logger.info(`dialogflow_stop failed: ${err.message}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +472,7 @@ class Dialogflow extends Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// kill the current dialogflow, which will result in us getting an immediate intent
|
// kill the current dialogflow, which will result in us getting an immediate intent
|
||||||
ep.api(this.cmdStop, `${ep.uuid}`)
|
ep.api('dialogflow_stop', `${ep.uuid}`)
|
||||||
.catch((err) => this.logger.info(`dialogflow_stop failed: ${err.message}`));
|
.catch((err) => this.logger.info(`dialogflow_stop failed: ${err.message}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,31 +4,19 @@ class Intent {
|
|||||||
this.evt = evt;
|
this.evt = evt;
|
||||||
|
|
||||||
this.logger.debug({evt}, 'intent');
|
this.logger.debug({evt}, 'intent');
|
||||||
this.qr = this.isCX ? evt.detect_intent_response.query_result : evt.query_result;
|
this.dtmfRequest = checkIntentForDtmfEntry(logger, evt);
|
||||||
this.dtmfRequest = this._checkIntentForDtmfEntry(logger, evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
get response_id() {
|
|
||||||
return this.isCX ? this.evt.detect_intent_response.response_id : this.evt.response_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isEmpty() {
|
get isEmpty() {
|
||||||
return !(this.response_id?.length > 0);
|
return this.evt.response_id.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fulfillmentText() {
|
get fulfillmentText() {
|
||||||
return this.qr.fulfillment_text;
|
return this.evt.query_result.fulfillment_text;
|
||||||
}
|
}
|
||||||
|
|
||||||
get saysEndInteraction() {
|
get saysEndInteraction() {
|
||||||
if (this.isCX) {
|
return this.evt.query_result.intent.end_interaction ;
|
||||||
const end_interaction = this.qr.response_messages
|
|
||||||
.find((m) => typeof m === 'object' && 'end_interaction' in m)?.end_interaction;
|
|
||||||
|
|
||||||
//TODO: need to do more checking on the actual contents
|
|
||||||
return end_interaction && Object.keys(end_interaction).length > 0;
|
|
||||||
}
|
|
||||||
else return this.qr.intent.end_interaction ;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get saysCollectDtmf() {
|
get saysCollectDtmf() {
|
||||||
@@ -40,22 +28,7 @@ class Intent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
if (!this.isEmpty) {
|
if (!this.isEmpty) return this.evt.query_result.intent.display_name;
|
||||||
if (this.isCX) {
|
|
||||||
return this.qr.match?.intent?.display_name;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return this.qr.intent.display_name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get isCX() {
|
|
||||||
return typeof this.evt.detect_intent_response === 'object';
|
|
||||||
}
|
|
||||||
|
|
||||||
get isES() {
|
|
||||||
return !this.isCX;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON() {
|
||||||
@@ -65,7 +38,11 @@ class Intent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
}
|
||||||
|
|
||||||
|
module.exports = Intent;
|
||||||
|
|
||||||
|
/**
|
||||||
* Parse a returned intent for DTMF entry information
|
* Parse a returned intent for DTMF entry information
|
||||||
* i.e.
|
* i.e.
|
||||||
* allow-dtmf-x-y-z
|
* allow-dtmf-x-y-z
|
||||||
@@ -78,39 +55,35 @@ class Intent {
|
|||||||
* allow-dtmf-1-4-# : collect 1-4 digits, terminating if '#' is entered
|
* allow-dtmf-1-4-# : collect 1-4 digits, terminating if '#' is entered
|
||||||
* @param {*} intent - dialogflow intent
|
* @param {*} intent - dialogflow intent
|
||||||
*/
|
*/
|
||||||
_checkIntentForDtmfEntry(logger, intent) {
|
const checkIntentForDtmfEntry = (logger, intent) => {
|
||||||
const qr = this.isCX ? intent.detect_intent_response.query_result : intent.query_result;
|
const qr = intent.query_result;
|
||||||
|
if (!qr || !qr.fulfillment_messages || !qr.output_contexts) {
|
||||||
|
logger.info({f: qr.fulfillment_messages, o: qr.output_contexts}, 'no dtmfs');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!qr || !qr.fulfillment_messages || !qr.output_contexts) {
|
// check for custom payloads with a gather verb
|
||||||
logger.info({f: qr.fulfillment_messages, o: qr.output_contexts}, 'no dtmfs');
|
const custom = qr.fulfillment_messages.find((f) => f.payload && f.payload.verb === 'gather');
|
||||||
return;
|
if (custom && custom.payload && custom.payload.verb === 'gather') {
|
||||||
}
|
logger.info({custom}, 'found dtmf custom payload');
|
||||||
|
return {
|
||||||
|
max: custom.payload.numDigits,
|
||||||
|
term: custom.payload.finishOnKey,
|
||||||
|
template: custom.payload.responseTemplate
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// check for custom payloads with a gather verb
|
// check for an output context with a specific naming convention
|
||||||
const custom = qr.fulfillment_messages.find((f) => f.payload && f.payload.verb === 'gather');
|
const context = qr.output_contexts.find((oc) => oc.name.includes('/contexts/allow-dtmf-'));
|
||||||
if (custom && custom.payload && custom.payload.verb === 'gather') {
|
if (context) {
|
||||||
logger.info({custom}, 'found dtmf custom payload');
|
const arr = /allow-dtmf-(\d+)(?:-(\d+))?(?:-(.*))?/.exec(context.name);
|
||||||
|
if (arr) {
|
||||||
|
logger.info({custom}, 'found dtmf output context');
|
||||||
return {
|
return {
|
||||||
max: custom.payload.numDigits,
|
min: parseInt(arr[1]),
|
||||||
term: custom.payload.finishOnKey,
|
max: arr.length > 2 ? parseInt(arr[2]) : null,
|
||||||
template: custom.payload.responseTemplate
|
term: arr.length > 3 ? arr[3] : null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for an output context with a specific naming convention
|
|
||||||
const context = qr.output_contexts.find((oc) => oc.name.includes('/contexts/allow-dtmf-'));
|
|
||||||
if (context) {
|
|
||||||
const arr = /allow-dtmf-(\d+)(?:-(\d+))?(?:-(.*))?/.exec(context.name);
|
|
||||||
if (arr) {
|
|
||||||
logger.info({custom}, 'found dtmf output context');
|
|
||||||
return {
|
|
||||||
min: parseInt(arr[1]),
|
|
||||||
max: arr.length > 2 ? parseInt(arr[2]) : null,
|
|
||||||
term: arr.length > 3 ? arr[3] : null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
module.exports = Intent;
|
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ class TaskRestDial extends Task {
|
|||||||
|
|
||||||
if (this.data.amd) {
|
if (this.data.amd) {
|
||||||
this.startAmd = cs.startAmd;
|
this.startAmd = cs.startAmd;
|
||||||
this.stopAmd = cs.stopAmd;
|
|
||||||
this.on('amd', this._onAmdEvent.bind(this, cs));
|
this.on('amd', this._onAmdEvent.bind(this, cs));
|
||||||
}
|
}
|
||||||
|
this.stopAmd = cs.stopAmd;
|
||||||
|
|
||||||
this._setCallTimer();
|
this._setCallTimer();
|
||||||
await this.awaitTaskDone();
|
await this.awaitTaskDone();
|
||||||
|
|||||||
@@ -141,12 +141,12 @@ class TaskSay extends TtsTask {
|
|||||||
await this.playToConfMember(ep, memberId, confName, confUuid, filepath[segment]);
|
await this.playToConfMember(ep, memberId, confName, confUuid, filepath[segment]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let tts_cache_filename;
|
const isStreaming = filepath[segment].startsWith('say:{');
|
||||||
if (filepath[segment].startsWith('say:{')) {
|
if (isStreaming) {
|
||||||
const arr = /^say:\{.*\}\s*(.*)$/.exec(filepath[segment]);
|
const arr = /^say:\{.*\}\s*(.*)$/.exec(filepath[segment]);
|
||||||
if (arr) this.logger.debug(`Say:exec sending streaming tts request: ${arr[1].substring(0, 64)}..`);
|
if (arr) this.logger.debug(`Say:exec sending streaming tts request: ${arr[1].substring(0, 64)}..`);
|
||||||
}
|
}
|
||||||
else this.logger.debug(`Say:exec sending ${filepath[segment].substring(0, 64)}`);
|
else this.logger.debug(`Say:exec sending ${filepath[segment].substring(0, 64)}`);
|
||||||
ep.once('playback-start', (evt) => {
|
ep.once('playback-start', (evt) => {
|
||||||
this.logger.debug({evt}, 'Say got playback-start');
|
this.logger.debug({evt}, 'Say got playback-start');
|
||||||
if (this.otelSpan) {
|
if (this.otelSpan) {
|
||||||
@@ -154,17 +154,19 @@ class TaskSay extends TtsTask {
|
|||||||
this.otelSpan.end();
|
this.otelSpan.end();
|
||||||
this.otelSpan = null;
|
this.otelSpan = null;
|
||||||
if (evt.variable_tts_cache_filename) {
|
if (evt.variable_tts_cache_filename) {
|
||||||
tts_cache_filename = evt.variable_tts_cache_filename;
|
|
||||||
cs.trackTmpFile(evt.variable_tts_cache_filename);
|
cs.trackTmpFile(evt.variable_tts_cache_filename);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.logger.info('No tts_cache_filename in playback-start event');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ep.once('playback-stop', (evt) => {
|
ep.once('playback-stop', (evt) => {
|
||||||
if (!tts_cache_filename || evt.variable_tts_cache_filename !== tts_cache_filename) {
|
this.logger.debug({evt}, 'Say got playback-stop');
|
||||||
this.logger.info({evt}, 'Say: discarding playback-stop from other say verb');
|
if (evt.variable_tts_error) {
|
||||||
|
writeAlerts({
|
||||||
|
account_sid,
|
||||||
|
alert_type: AlertType.TTS_FAILURE,
|
||||||
|
vendor,
|
||||||
|
detail: evt.variable_tts_error
|
||||||
|
}).catch((err) => this.logger.info({err}, 'Error generating alert for no tts'));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.logger.debug({evt}, 'Say got playback-stop');
|
this.logger.debug({evt}, 'Say got playback-stop');
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ class ActionHookDelayProcessor extends Emitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async stop() {
|
async stop() {
|
||||||
this.logger.debug('ActionHookDelayProcessor#stop');
|
|
||||||
this._active = false;
|
this._active = false;
|
||||||
|
|
||||||
if (this._noResponseTimer) {
|
if (this._noResponseTimer) {
|
||||||
@@ -91,25 +90,19 @@ class ActionHookDelayProcessor extends Emitter {
|
|||||||
this._noResponseGiveUpTimer = null;
|
this._noResponseGiveUpTimer = null;
|
||||||
}
|
}
|
||||||
if (this._taskInProgress) {
|
if (this._taskInProgress) {
|
||||||
this.logger.debug(`ActionHookDelayProcessor#stop: killing task in progress: ${this._taskInProgress.name}`);
|
this.logger.debug(`ActionHookDelayProcessor#stop: stopping ${this._taskInProgress.name}`);
|
||||||
|
|
||||||
/** if we are doing a play, kill it immediately
|
this._sayResolver = () => {
|
||||||
* if we are doing a say, wait for it to finish
|
this.logger.debug('ActionHookDelayProcessor#stop: play/say is done, continue on..');
|
||||||
*/
|
//this._taskInProgress.kill(this.cs);
|
||||||
if (TaskName.Say === this._taskInProgress.name) {
|
|
||||||
this._sayResolver = () => {
|
|
||||||
this.logger.debug('ActionHookDelayProcessor#stop: say is done, continue on..');
|
|
||||||
this._taskInProgress.kill(this.cs);
|
|
||||||
this._taskInProgress = null;
|
|
||||||
};
|
|
||||||
this.logger.debug('ActionHookDelayProcessor#stop returning promise');
|
|
||||||
return new Promise((resolve) => this._sayResolver = resolve);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* play */
|
|
||||||
this._taskInProgress.kill(this.cs);
|
|
||||||
this._taskInProgress = null;
|
this._taskInProgress = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* we let Say finish, but interrupt Play */
|
||||||
|
if (TaskName.Play === this._taskInProgress.name) {
|
||||||
|
await this._taskInProgress.kill(this.cs);
|
||||||
}
|
}
|
||||||
|
return new Promise((resolve) => this._sayResolver = resolve);
|
||||||
}
|
}
|
||||||
this.logger.debug('ActionHookDelayProcessor#stop returning');
|
this.logger.debug('ActionHookDelayProcessor#stop returning');
|
||||||
}
|
}
|
||||||
@@ -147,7 +140,7 @@ class ActionHookDelayProcessor extends Emitter {
|
|||||||
this._taskInProgress = null;
|
this._taskInProgress = null;
|
||||||
if (this._sayResolver) {
|
if (this._sayResolver) {
|
||||||
/* we were waiting for the play to finish before continuing to next task */
|
/* we were waiting for the play to finish before continuing to next task */
|
||||||
this.logger.debug({evt}, 'got playback-stop');
|
this.logger.debug({evt}, 'ActionHookDelayProcessor#_onNoResponseTimer got playback-stop');
|
||||||
this._sayResolver();
|
this._sayResolver();
|
||||||
this._sayResolver = null;
|
this._sayResolver = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const {
|
|||||||
JAMBONES_TIME_SERIES_HOST,
|
JAMBONES_TIME_SERIES_HOST,
|
||||||
JAMBONES_ESL_LISTEN_ADDRESS,
|
JAMBONES_ESL_LISTEN_ADDRESS,
|
||||||
PORT,
|
PORT,
|
||||||
|
HTTP_IP,
|
||||||
NODE_ENV,
|
NODE_ENV,
|
||||||
} = require('../config');
|
} = require('../config');
|
||||||
const Registrar = require('@jambonz/mw-registrar');
|
const Registrar = require('@jambonz/mw-registrar');
|
||||||
@@ -193,7 +194,8 @@ function installSrfLocals(srf, logger) {
|
|||||||
|
|
||||||
let localIp;
|
let localIp;
|
||||||
try {
|
try {
|
||||||
localIp = ip.address();
|
// Either use the configured IP address or call ip.address() to find it
|
||||||
|
localIp = HTTP_IP || ip.address();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error({err}, 'installSrfLocals - error detecting local ipv4 address');
|
logger.error({err}, 'installSrfLocals - error detecting local ipv4 address');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ const optimalDeepramModels = {
|
|||||||
tr: ['nova-2', 'nova-2'],
|
tr: ['nova-2', 'nova-2'],
|
||||||
uk: ['nova-2', 'nova-2']
|
uk: ['nova-2', 'nova-2']
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectDefaultDeepgramModel = (task, language) => {
|
const selectDefaultDeepgramModel = (task, language) => {
|
||||||
if (language in optimalDeepramModels) {
|
if (language in optimalDeepramModels) {
|
||||||
const [gather, transcribe] = optimalDeepramModels[language];
|
const [gather, transcribe] = optimalDeepramModels[language];
|
||||||
@@ -151,6 +150,24 @@ const selectDefaultDeepgramModel = (task, language) => {
|
|||||||
return 'base';
|
return 'base';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const optimalGoogleModels = {
|
||||||
|
'v1' : {
|
||||||
|
'en-IN':['telephony', 'latest_long']
|
||||||
|
},
|
||||||
|
'v2' : {
|
||||||
|
'en-IN':['telephony', 'long']
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const selectDefaultGoogleModel = (task, language, version) => {
|
||||||
|
const useV2 = version === 'v2';
|
||||||
|
if (language in optimalGoogleModels[version]) {
|
||||||
|
const [gather, transcribe] = optimalGoogleModels[version][language];
|
||||||
|
return task.name === TaskName.Gather ? gather : transcribe;
|
||||||
|
}
|
||||||
|
return task.name === TaskName.Gather ?
|
||||||
|
(useV2 ? 'telephony_short' : 'command_and_search') :
|
||||||
|
(useV2 ? 'long' : 'latest_long');
|
||||||
|
};
|
||||||
const consolidateTranscripts = (bufferedTranscripts, channel, language, vendor) => {
|
const consolidateTranscripts = (bufferedTranscripts, channel, language, vendor) => {
|
||||||
if (bufferedTranscripts.length === 1) return bufferedTranscripts[0];
|
if (bufferedTranscripts.length === 1) return bufferedTranscripts[0];
|
||||||
let totalConfidence = 0;
|
let totalConfidence = 0;
|
||||||
@@ -497,9 +514,9 @@ module.exports = (logger) => {
|
|||||||
|
|
||||||
if ('google' === vendor) {
|
if ('google' === vendor) {
|
||||||
const useV2 = rOpts.googleOptions?.serviceVersion === 'v2';
|
const useV2 = rOpts.googleOptions?.serviceVersion === 'v2';
|
||||||
const model = task.name === TaskName.Gather ?
|
const version = useV2 ? 'v2' : 'v1';
|
||||||
(useV2 ? 'telephony_short' : 'command_and_search') :
|
let {model} = rOpts;
|
||||||
(useV2 ? 'long' : 'latest_long');
|
model = model || selectDefaultGoogleModel(task, language, version);
|
||||||
opts = {
|
opts = {
|
||||||
...opts,
|
...opts,
|
||||||
...(sttCredentials && {GOOGLE_APPLICATION_CREDENTIALS: JSON.stringify(sttCredentials.credentials)}),
|
...(sttCredentials && {GOOGLE_APPLICATION_CREDENTIALS: JSON.stringify(sttCredentials.credentials)}),
|
||||||
|
|||||||
63
package-lock.json
generated
63
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "jambonz-feature-server",
|
"name": "jambonz-feature-server",
|
||||||
"version": "0.9.0",
|
"version": "0.9.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "jambonz-feature-server",
|
"name": "jambonz-feature-server",
|
||||||
"version": "0.9.0",
|
"version": "0.9.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-auto-scaling": "^3.549.0",
|
"@aws-sdk/client-auto-scaling": "^3.549.0",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"@jambonz/speech-utils": "^0.1.15",
|
"@jambonz/speech-utils": "^0.1.15",
|
||||||
"@jambonz/stats-collector": "^0.1.10",
|
"@jambonz/stats-collector": "^0.1.10",
|
||||||
"@jambonz/time-series": "^0.2.9",
|
"@jambonz/time-series": "^0.2.9",
|
||||||
"@jambonz/verb-specifications": "^0.0.79",
|
"@jambonz/verb-specifications": "^0.0.76",
|
||||||
"@opentelemetry/api": "^1.8.0",
|
"@opentelemetry/api": "^1.8.0",
|
||||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"bent": "^7.3.12",
|
"bent": "^7.3.12",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"deepcopy": "^2.1.0",
|
"deepcopy": "^2.1.0",
|
||||||
"drachtio-fsmrf": "^3.0.43",
|
"drachtio-fsmrf": "^3.0.45",
|
||||||
"drachtio-srf": "^4.5.35",
|
"drachtio-srf": "^4.5.35",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"express-validator": "^7.0.1",
|
"express-validator": "^7.0.1",
|
||||||
@@ -45,10 +45,10 @@
|
|||||||
"short-uuid": "^5.1.0",
|
"short-uuid": "^5.1.0",
|
||||||
"sinon": "^17.0.1",
|
"sinon": "^17.0.1",
|
||||||
"to-snake-case": "^1.0.0",
|
"to-snake-case": "^1.0.0",
|
||||||
"undici": "^6.19.2",
|
"undici": "^6.19.4",
|
||||||
"uuid-random": "^1.3.2",
|
"uuid-random": "^1.3.2",
|
||||||
"verify-aws-sns-signature": "^0.1.0",
|
"verify-aws-sns-signature": "^0.1.0",
|
||||||
"ws": "^8.17.1",
|
"ws": "^8.18.0",
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -1575,9 +1575,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@jambonz/verb-specifications": {
|
"node_modules/@jambonz/verb-specifications": {
|
||||||
"version": "0.0.79",
|
"version": "0.0.76",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.79.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.76.tgz",
|
||||||
"integrity": "sha512-SJpUfRivPaBBF16sUhkKPuXC4KFf2vE03LuSNYGhtjzZ03PnIGXbsuz16cK+XeQow5tkof+ptmxwFgfv6TM5RQ==",
|
"integrity": "sha512-7s61qAsG07xLLaEAHW236rSYzEoh9Qg0aRWHPbTfxCsuTKDNeq+5EwGAShDU5R5ZpjgweZJLhArQm8Ym+4xJ2A==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"pino": "^8.8.0"
|
"pino": "^8.8.0"
|
||||||
@@ -3831,9 +3831,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/drachtio-fsmrf": {
|
"node_modules/drachtio-fsmrf": {
|
||||||
"version": "3.0.43",
|
"version": "3.0.45",
|
||||||
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.43.tgz",
|
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.45.tgz",
|
||||||
"integrity": "sha512-ZDgO4WCzZssmOpf+ng20P8r5pu7demWFiECqwRivbWadXqAm3LNabZwWOred2MdCRDvzgHWUwBZoJPzCqKBL5g==",
|
"integrity": "sha512-YsrevTwGvg9v0OQXB4gZedlmzegB83fyd3NX3X2x7NXsKa5jO6TZCvZVHtBf/jR/ELE3B0aVLpxHw2YviRMIuQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"camel-case": "^4.1.2",
|
"camel-case": "^4.1.2",
|
||||||
"debug": "^2.6.9",
|
"debug": "^2.6.9",
|
||||||
@@ -8831,10 +8832,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/undici": {
|
"node_modules/undici": {
|
||||||
"version": "6.19.2",
|
"version": "6.19.4",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.4.tgz",
|
||||||
"integrity": "sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==",
|
"integrity": "sha512-i3uaEUwNdkRq2qtTRRJb13moW5HWqviu7Vl7oYRYz++uPtGHJj+x7TGjcEuwS5Mt2P4nA0U9dhIX3DdB6JGY0g==",
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.17"
|
"node": ">=18.17"
|
||||||
}
|
}
|
||||||
@@ -9193,10 +9193,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.17.1",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
},
|
},
|
||||||
@@ -10539,9 +10538,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@jambonz/verb-specifications": {
|
"@jambonz/verb-specifications": {
|
||||||
"version": "0.0.79",
|
"version": "0.0.76",
|
||||||
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.79.tgz",
|
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.76.tgz",
|
||||||
"integrity": "sha512-SJpUfRivPaBBF16sUhkKPuXC4KFf2vE03LuSNYGhtjzZ03PnIGXbsuz16cK+XeQow5tkof+ptmxwFgfv6TM5RQ==",
|
"integrity": "sha512-7s61qAsG07xLLaEAHW236rSYzEoh9Qg0aRWHPbTfxCsuTKDNeq+5EwGAShDU5R5ZpjgweZJLhArQm8Ym+4xJ2A==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"pino": "^8.8.0"
|
"pino": "^8.8.0"
|
||||||
@@ -12255,9 +12254,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"drachtio-fsmrf": {
|
"drachtio-fsmrf": {
|
||||||
"version": "3.0.43",
|
"version": "3.0.45",
|
||||||
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.43.tgz",
|
"resolved": "https://registry.npmjs.org/drachtio-fsmrf/-/drachtio-fsmrf-3.0.45.tgz",
|
||||||
"integrity": "sha512-ZDgO4WCzZssmOpf+ng20P8r5pu7demWFiECqwRivbWadXqAm3LNabZwWOred2MdCRDvzgHWUwBZoJPzCqKBL5g==",
|
"integrity": "sha512-YsrevTwGvg9v0OQXB4gZedlmzegB83fyd3NX3X2x7NXsKa5jO6TZCvZVHtBf/jR/ELE3B0aVLpxHw2YviRMIuQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"camel-case": "^4.1.2",
|
"camel-case": "^4.1.2",
|
||||||
"debug": "^2.6.9",
|
"debug": "^2.6.9",
|
||||||
@@ -16056,9 +16055,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"undici": {
|
"undici": {
|
||||||
"version": "6.19.2",
|
"version": "6.19.4",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-6.19.4.tgz",
|
||||||
"integrity": "sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA=="
|
"integrity": "sha512-i3uaEUwNdkRq2qtTRRJb13moW5HWqviu7Vl7oYRYz++uPtGHJj+x7TGjcEuwS5Mt2P4nA0U9dhIX3DdB6JGY0g=="
|
||||||
},
|
},
|
||||||
"undici-types": {
|
"undici-types": {
|
||||||
"version": "5.26.5",
|
"version": "5.26.5",
|
||||||
@@ -16329,9 +16328,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "8.17.1",
|
"version": "8.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"xml2js": {
|
"xml2js": {
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jambonz-feature-server",
|
"name": "jambonz-feature-server",
|
||||||
"version": "0.9.0",
|
"version": "0.9.2",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 18.x"
|
"node": ">= 18.x"
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
"@jambonz/speech-utils": "^0.1.15",
|
"@jambonz/speech-utils": "^0.1.15",
|
||||||
"@jambonz/stats-collector": "^0.1.10",
|
"@jambonz/stats-collector": "^0.1.10",
|
||||||
"@jambonz/time-series": "^0.2.9",
|
"@jambonz/time-series": "^0.2.9",
|
||||||
"@jambonz/verb-specifications": "^0.0.79",
|
"@jambonz/verb-specifications": "^0.0.76",
|
||||||
"@opentelemetry/api": "^1.8.0",
|
"@opentelemetry/api": "^1.8.0",
|
||||||
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
"@opentelemetry/exporter-jaeger": "^1.23.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.50.0",
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
"bent": "^7.3.12",
|
"bent": "^7.3.12",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"deepcopy": "^2.1.0",
|
"deepcopy": "^2.1.0",
|
||||||
"drachtio-fsmrf": "^3.0.43",
|
"drachtio-fsmrf": "^3.0.45",
|
||||||
"drachtio-srf": "^4.5.35",
|
"drachtio-srf": "^4.5.35",
|
||||||
"express": "^4.19.2",
|
"express": "^4.19.2",
|
||||||
"express-validator": "^7.0.1",
|
"express-validator": "^7.0.1",
|
||||||
@@ -61,10 +61,10 @@
|
|||||||
"short-uuid": "^5.1.0",
|
"short-uuid": "^5.1.0",
|
||||||
"sinon": "^17.0.1",
|
"sinon": "^17.0.1",
|
||||||
"to-snake-case": "^1.0.0",
|
"to-snake-case": "^1.0.0",
|
||||||
"undici": "^6.19.2",
|
"undici": "^6.19.4",
|
||||||
"uuid-random": "^1.3.2",
|
"uuid-random": "^1.3.2",
|
||||||
"verify-aws-sns-signature": "^0.1.0",
|
"verify-aws-sns-signature": "^0.1.0",
|
||||||
"ws": "^8.17.1",
|
"ws": "^8.18.0",
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
Reference in New Issue
Block a user