Compare commits

..

1 Commits

Author SHA1 Message Date
RJ Burnham
e53852f056 Add support for configuring the IP address that is advertised to the API server.
This is needed when running in fargate as ip.address() will return the wrong ip address.
2024-08-21 22:58:55 +01:00
10 changed files with 84 additions and 132 deletions

View File

@@ -21,6 +21,7 @@ Configuration is provided via environment variables:
|ENCRYPTION_SECRET| secret for credential encryption(JWT_SECRET is deprecated) |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_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_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|

View File

@@ -73,6 +73,7 @@ const JAMBONES_LOGLEVEL = process.env.JAMBONES_LOGLEVEL || 'info';
const JAMBONES_INJECT_CONTENT = process.env.JAMBONES_INJECT_CONTENT;
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 K8S = process.env.K8S;
@@ -170,6 +171,7 @@ module.exports = {
JAMBONES_CLUSTER_ID,
PORT,
HTTP_PORT_MAX,
HTTP_IP,
K8S,
K8S_SBC_SIP_SERVICE_NAME,
JAMBONES_SUBNET,

View File

@@ -2005,11 +2005,6 @@ Duration=${duration} `
if (this.direction === CallDirection.Inbound) {
if (task.earlyMedia && !this.req.finalResponseSent) {
this.res.send(183, {body: ep.local.sdp});
this._notifyCallStatusChange({
callStatus: CallStatus.EarlyMedia,
sipStatus: 183,
sipReason: 'Early Media'
});
return {ep};
}
this.logger.debug('propogating answer');

View File

@@ -35,21 +35,11 @@ class InboundCallSession extends CallSession {
_onCancel() {
this.rootSpan.setAttributes({'call.termination': 'caller abandoned'});
this.callInfo.callTerminationBy = 'caller';
const wasEarlyMedia = this.callInfo.callStatus === 'early-media';
this._notifyCallStatusChange({
callStatus: CallStatus.NoAnswer,
sipStatus: 487,
sipReason: 'Request Terminated'
});
if (wasEarlyMedia) {
const duration = 0; // Set duration to 0 for early media termination, required param
this._notifyCallStatusChange({
callStatus: CallStatus.Completed,
sipStatus: 487,
sipReason: 'Call Terminated During Early Media',
duration: duration
});
}
this._callReleased();
}

View File

@@ -736,16 +736,6 @@ class TaskGather extends SttTask {
this.logger.info({evt}, 'TaskGather:_onTranscription - got empty transcript, continue listening');
return;
}
const confidence = evt.alternatives[0].confidence;
const minConfidence = this.data.recognizer?.minConfidence;
this.logger.debug({evt},
`TaskGather:_onTranscription - confidence (${confidence}), minConfidence (${minConfidence})`);
if (confidence && minConfidence && confidence < minConfidence) {
this.logger.info({evt},
'TaskGather:_onTranscription - Transcript confidence ' +
`(${confidence}) < minConfidence (${minConfidence})`);
return this._resolve('stt-low-confidence', evt);
}
/* fast path: our first partial transcript exactly matches an early hint */
if (this.earlyHintsMatch && evt.is_final === false && this.partialTranscriptsCount++ === 0) {
@@ -1101,12 +1091,6 @@ class TaskGather extends SttTask {
this.emit('stt-error', evt);
returnedVerbs = await this.performAction({reason: 'error', details: evt.error});
}
} else if (reason.startsWith('stt-low-confidence')) {
if (this.parentTask) this.parentTask.emit('stt-low-confidence', evt);
else {
this.emit('stt-low-confidence', evt);
returnedVerbs = await this.performAction({reason: 'stt-low-confidence'});
}
}
} catch (err) { /*already logged error*/ }

View File

@@ -151,7 +151,7 @@ class TaskSay extends TtsTask {
preCache
});
if (!filePath.startsWith('say:')) {
this.logger.debug(`Say: file ${filePath}, served from cache ${servedFromCache}`);
this.logger.debug(`file ${filePath}, served from cache ${servedFromCache}`);
if (filePath) cs.trackTmpFile(filePath);
if (this.otelSpan) {
this.otelSpan.setAttributes({'tts.cached': servedFromCache});
@@ -173,7 +173,7 @@ class TaskSay extends TtsTask {
}
}
else {
this.logger.debug('Say: a streaming tts api will be used');
this.logger.debug('a streaming tts api will be used');
const modifiedPath = filePath.replace('say:{', `say:{session-uuid=${ep.uuid},`);
return modifiedPath;
}
@@ -280,52 +280,40 @@ class TaskSay extends TtsTask {
await this.playToConfMember(ep, memberId, confName, confUuid, filepath[segment]);
}
else {
let tts_cache_filename;
if (filepath[segment].startsWith('say:{')) {
const arr = /^say:\{.*\}\s*(.*)$/.exec(filepath[segment]);
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)}`);
ep.once('playback-start', (evt) => {
this.logger.debug({evt}, 'Say got playback-start');
this.logger.debug({evt}, 'got playback-start');
if (this.otelSpan) {
this._addStreamingTtsAttributes(this.otelSpan, evt);
this.otelSpan.end();
this.otelSpan = null;
if (evt.variable_tts_cache_filename) {
tts_cache_filename = evt.variable_tts_cache_filename;
cs.trackTmpFile(evt.variable_tts_cache_filename);
}
else {
this.logger.info('No tts_cache_filename in playback-start event');
}
if (evt.variable_tts_cache_filename) cs.trackTmpFile(evt.variable_tts_cache_filename);
}
});
ep.once('playback-stop', (evt) => {
if (!tts_cache_filename || evt.variable_tts_cache_filename !== tts_cache_filename) {
this.logger.info({evt}, 'Say: discarding playback-stop from other say verb');
this.logger.debug({evt}, 'got playback-stop');
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 {
this.logger.debug({evt}, 'Say got playback-stop');
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'));
}
if (evt.variable_tts_cache_filename && !this.killed) {
const text = parseTextFromSayString(this.text[segment]);
addFileToCache(evt.variable_tts_cache_filename, {
account_sid,
vendor,
language,
voice,
engine,
text
}).catch((err) => this.logger.info({err}, 'Error adding file to cache'));
}
if (evt.variable_tts_cache_filename) {
const text = parseTextFromSayString(this.text[segment]);
addFileToCache(evt.variable_tts_cache_filename, {
account_sid,
vendor,
language,
voice,
engine,
text
}).catch((err) => this.logger.info({err}, 'Error adding file to cache'));
}
if (this._playResolve) {
evt.variable_tts_error ? this._playReject(new Error(evt.variable_tts_error)) : this._playResolve();
@@ -336,8 +324,7 @@ class TaskSay extends TtsTask {
this._playResolve = resolve;
this._playReject = reject;
});
const r = await ep.play(filepath[segment]);
this.logger.debug({r}, 'Say:exec play result');
await ep.play(filepath[segment]);
try {
// wait for playback-stop event received to confirm if the playback is successful
await this._playPromise;
@@ -382,10 +369,7 @@ class TaskSay extends TtsTask {
this.ep.removeAllListeners('playback-start');
this.ep.removeAllListeners('playback-stop');
// if we are waiting for playback-stop event, resolve the promise
if (this._playResolve) {
this._playResolve();
this._playResolve = null;
}
if (this._playResolve) this._playResolve();
}
}

View File

@@ -12,6 +12,7 @@ const {
JAMBONES_TIME_SERIES_HOST,
JAMBONES_ESL_LISTEN_ADDRESS,
PORT,
HTTP_IP,
NODE_ENV,
} = require('../config');
const Registrar = require('@jambonz/mw-registrar');
@@ -193,7 +194,8 @@ function installSrfLocals(srf, logger) {
let localIp;
try {
localIp = ip.address();
// Either use the configured IP address or call ip.address() to find it
localIp = HTTP_IP || ip.address();
} catch (err) {
logger.error({err}, 'installSrfLocals - error detecting local ipv4 address');
}

View File

@@ -590,8 +590,6 @@ module.exports = (logger) => {
{AZURE_SPEECH_SEGMENTATION_SILENCE_TIMEOUT_MS: azureOptions.speechSegmentationSilenceTimeoutMs}),
...(azureOptions.languageIdMode &&
{AZURE_LANGUAGE_ID_MODE: azureOptions.languageIdMode}),
...(azureOptions.postProcessing &&
{AZURE_POST_PROCESSING_OPTION: azureOptions.postProcessing}),
...(sttCredentials && {
...(sttCredentials.api_key && {AZURE_SUBSCRIPTION_KEY: sttCredentials.api_key}),
...(sttCredentials.region && {AZURE_REGION: sttCredentials.region}),

110
package-lock.json generated
View File

@@ -11,7 +11,7 @@
"dependencies": {
"@aws-sdk/client-auto-scaling": "^3.549.0",
"@aws-sdk/client-sns": "^3.549.0",
"@jambonz/db-helpers": "^0.9.6",
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.8",
@@ -2217,6 +2217,11 @@
"node": ">=6"
}
},
"node_modules/@grpc/proto-loader/node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz",
@@ -2277,13 +2282,13 @@
}
},
"node_modules/@jambonz/db-helpers": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.6.tgz",
"integrity": "sha512-uvtkqdP6GP6KSaM7eo2jcU1k0R6Ymodhl0RH/XiNESgIhZ7sx7ZnMQtrZQVVmzrJ429H7q8dQqgcB2YhAyEK+A==",
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.3.tgz",
"integrity": "sha512-3XFs7NC7J7Q/eb1CwG1YJHa6N4elh8IP/4hMMDgoM9U5Loplx61XI4nZ58FIrY3C/F6gEF4UdjqKvbusEVw7cQ==",
"dependencies": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
"mysql2": "^3.10.3",
"mysql2": "^2.3.3",
"node-object-hash": "^2.3.10",
"uuid": "^8.3.2"
}
@@ -3794,14 +3799,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/aws-ssl-profiles": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.1.tgz",
"integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ==",
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/axios": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
@@ -7201,9 +7198,9 @@
"dev": true
},
"node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"node_modules/lower-case": {
"version": "2.0.2",
@@ -7475,17 +7472,16 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mysql2": {
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.10.3.tgz",
"integrity": "sha512-k43gmH9i79rZD4hGPdj7pDuT0UBiFjs4UzXEy1cJrV0QqcSABomoLwvejqdbcXN+Vd7gi999CVM6o9vCPKq29g==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
"dependencies": {
"aws-ssl-profiles": "^1.1.1",
"denque": "^2.1.0",
"denque": "^2.0.1",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru-cache": "^8.0.0",
"named-placeholders": "^1.1.3",
"long": "^4.0.0",
"lru-cache": "^6.0.0",
"named-placeholders": "^1.1.2",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
@@ -7504,14 +7500,6 @@
"node": ">=0.10.0"
}
},
"node_modules/mysql2/node_modules/lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==",
"engines": {
"node": ">=16.14"
}
},
"node_modules/named-placeholders": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz",
@@ -8282,6 +8270,11 @@
"node": ">=12.0.0"
}
},
"node_modules/protobufjs/node_modules/long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
},
"node_modules/protocols": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/protocols/-/protocols-2.0.1.tgz",
@@ -11867,6 +11860,13 @@
"long": "^5.0.0",
"protobufjs": "^7.2.4",
"yargs": "^17.7.2"
},
"dependencies": {
"long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
}
}
},
"@humanwhocodes/config-array": {
@@ -11919,13 +11919,13 @@
"dev": true
},
"@jambonz/db-helpers": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.6.tgz",
"integrity": "sha512-uvtkqdP6GP6KSaM7eo2jcU1k0R6Ymodhl0RH/XiNESgIhZ7sx7ZnMQtrZQVVmzrJ429H7q8dQqgcB2YhAyEK+A==",
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/@jambonz/db-helpers/-/db-helpers-0.9.3.tgz",
"integrity": "sha512-3XFs7NC7J7Q/eb1CwG1YJHa6N4elh8IP/4hMMDgoM9U5Loplx61XI4nZ58FIrY3C/F6gEF4UdjqKvbusEVw7cQ==",
"requires": {
"cidr-matcher": "^2.1.1",
"debug": "^4.3.4",
"mysql2": "^3.10.3",
"mysql2": "^2.3.3",
"node-object-hash": "^2.3.10",
"uuid": "^8.3.2"
}
@@ -13125,11 +13125,6 @@
"possible-typed-array-names": "^1.0.0"
}
},
"aws-ssl-profiles": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.1.tgz",
"integrity": "sha512-+H+kuK34PfMaI9PNU/NSjBKL5hh/KDM9J72kwYeYEm0A8B1AC4fuCy3qsjnA7lxklgyXsB68yn8Z2xoZEjgwCQ=="
},
"axios": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.0.tgz",
@@ -15688,9 +15683,9 @@
"dev": true
},
"long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
},
"lower-case": {
"version": "2.0.2",
@@ -15886,17 +15881,16 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mysql2": {
"version": "3.10.3",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.10.3.tgz",
"integrity": "sha512-k43gmH9i79rZD4hGPdj7pDuT0UBiFjs4UzXEy1cJrV0QqcSABomoLwvejqdbcXN+Vd7gi999CVM6o9vCPKq29g==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-2.3.3.tgz",
"integrity": "sha512-wxJUev6LgMSgACDkb/InIFxDprRa6T95+VEoR+xPvtngtccNH2dGjEB/fVZ8yg1gWv1510c9CvXuJHi5zUm0ZA==",
"requires": {
"aws-ssl-profiles": "^1.1.1",
"denque": "^2.1.0",
"denque": "^2.0.1",
"generate-function": "^2.3.1",
"iconv-lite": "^0.6.3",
"long": "^5.2.1",
"lru-cache": "^8.0.0",
"named-placeholders": "^1.1.3",
"long": "^4.0.0",
"lru-cache": "^6.0.0",
"named-placeholders": "^1.1.2",
"seq-queue": "^0.0.5",
"sqlstring": "^2.3.2"
},
@@ -15908,11 +15902,6 @@
"requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
}
},
"lru-cache": {
"version": "8.0.5",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz",
"integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA=="
}
}
},
@@ -16513,6 +16502,13 @@
"@protobufjs/utf8": "^1.1.0",
"@types/node": ">=13.7.0",
"long": "^5.0.0"
},
"dependencies": {
"long": {
"version": "5.2.3",
"resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
"integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
}
}
},
"protocols": {

View File

@@ -27,7 +27,7 @@
"dependencies": {
"@aws-sdk/client-auto-scaling": "^3.549.0",
"@aws-sdk/client-sns": "^3.549.0",
"@jambonz/db-helpers": "^0.9.6",
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/http-health-check": "^0.0.1",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.8",