From f8c5abe9e92293f8cd1ca142353a31b6550cfd4f Mon Sep 17 00:00:00 2001 From: Hoan Luu Huu <110280845+xquanluu@users.noreply.github.com> Date: Tue, 15 Aug 2023 19:57:49 +0700 Subject: [PATCH] feat: multi speech credential diff labels but same vendor (#426) * feat: multi speech credential diff labels but same vendor * update sql * fix * fix * fix jslint * fix review comment * update verb spec version --- lib/session/call-session.js | 26 ++++++++++++++++++++++++-- lib/tasks/config.js | 6 ++++++ lib/tasks/dialogflow/index.js | 4 +++- lib/tasks/gather.js | 3 ++- lib/tasks/lex.js | 4 +++- lib/tasks/say.js | 2 +- lib/tasks/transcribe.js | 3 ++- lib/utils/amd-utils.js | 3 ++- package-lock.json | 14 +++++++------- package.json | 2 +- test/db/jambones-sql.sql | 29 ++++++++++++++++++++++++----- test/index.js | 2 +- 12 files changed, 76 insertions(+), 22 deletions(-) diff --git a/lib/session/call-session.js b/lib/session/call-session.js index 21bb8fb0..b67f095b 100644 --- a/lib/session/call-session.js +++ b/lib/session/call-session.js @@ -179,6 +179,16 @@ class CallSession extends Emitter { set speechSynthesisVendor(vendor) { this.application.speech_synthesis_vendor = vendor; } + + /** + * default label to use for speech synthesis if not provided in the app + */ + get speechSynthesisLabel() { + return this.application.speech_synthesis_label; + } + set speechSynthesisLabel(label) { + this.application.speech_synthesis_label = label; + } /** * default voice to use for speech synthesis if not provided in the app */ @@ -207,6 +217,15 @@ class CallSession extends Emitter { set speechRecognizerVendor(vendor) { this.application.speech_recognizer_vendor = vendor; } + /** + * default vendor to use for speech recognition if not provided in the app + */ + get speechRecognizerLabel() { + return this.application.speech_recognizer_label; + } + set speechRecognizerLabel(label) { + this.application.speech_recognizer_label = label; + } /** * default language to use for speech recognition if not provided in the app */ @@ -640,14 +659,17 @@ class CallSession extends Emitter { * Check for speech credentials for the specified vendor * @param {*} vendor - google or aws */ - getSpeechCredentials(vendor, type) { + getSpeechCredentials(vendor, type, label = null) { const {writeAlerts, AlertType} = this.srf.locals; 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 && + ((label && s.label === label) || label === null)); if (credential && ( (type === 'tts' && credential.use_for_tts) || (type === 'stt' && credential.use_for_stt) )) { + this.logger.info(`Speech credential vendor: ${credential.vendor} + ${credential.label ? `, label: ${credential.label}` : ''} is chosen`); if ('google' === vendor) { try { const cred = JSON.parse(credential.service_key.replace(/\n/g, '\\n')); diff --git a/lib/tasks/config.js b/lib/tasks/config.js index 84927800..826a6ed5 100644 --- a/lib/tasks/config.js +++ b/lib/tasks/config.js @@ -105,6 +105,9 @@ class TaskConfig extends Task { cs.speechSynthesisVendor = this.synthesizer.vendor !== 'default' ? this.synthesizer.vendor : cs.speechSynthesisVendor; + cs.speechSynthesisLabel = this.synthesizer.label !== 'default' + ? this.synthesizer.label + : cs.speechSynthesisLabel; cs.speechSynthesisLanguage = this.synthesizer.language !== 'default' ? this.synthesizer.language : cs.speechSynthesisLanguage; @@ -117,6 +120,9 @@ class TaskConfig extends Task { cs.speechRecognizerVendor = this.recognizer.vendor !== 'default' ? this.recognizer.vendor : cs.speechRecognizerVendor; + cs.speechRecognizerLabel = this.recognizer.label !== 'default' + ? this.recognizer.label + : cs.speechRecognizerLabel; cs.speechRecognizerLanguage = this.recognizer.language !== 'default' ? this.recognizer.language : cs.speechRecognizerLanguage; diff --git a/lib/tasks/dialogflow/index.js b/lib/tasks/dialogflow/index.js index a31fd7d2..8882a338 100644 --- a/lib/tasks/dialogflow/index.js +++ b/lib/tasks/dialogflow/index.js @@ -58,6 +58,7 @@ class Dialogflow extends Task { this.vendor = this.data.tts.vendor || 'default'; this.language = this.data.tts.language || 'default'; this.voice = this.data.tts.voice || 'default'; + this.speechSynthesisLabel = this.data.tts.label || null; } this.bargein = this.data.bargein; } @@ -119,7 +120,8 @@ class Dialogflow extends Task { this.language = cs.speechSynthesisLanguage; this.voice = cs.speechSynthesisVoice; } - this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts'); + this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts', + this.speechSynthesisLabel || cs.speechSynthesisLabel); this.ep.addCustomEventListener('dialogflow::intent', this._onIntent.bind(this, ep, cs)); this.ep.addCustomEventListener('dialogflow::transcription', this._onTranscription.bind(this, ep, cs)); diff --git a/lib/tasks/gather.js b/lib/tasks/gather.js index cbd29c03..8fc42149 100644 --- a/lib/tasks/gather.js +++ b/lib/tasks/gather.js @@ -187,7 +187,8 @@ class TaskGather extends Task { if (!this.data.recognizer.vendor) { this.data.recognizer.vendor = this.vendor; } - if (this.needsStt && !this.sttCredentials) this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt'); + if (this.needsStt && !this.sttCredentials) this.sttCredentials = + cs.getSpeechCredentials(this.vendor, 'stt', this.data.recognizer?.label || cs.speechRecognizerLabel); if (this.needsStt && !this.sttCredentials) { const {writeAlerts, AlertType} = cs.srf.locals; this.logger.info(`TaskGather:exec - ERROR stt using ${this.vendor} requested but creds not supplied`); diff --git a/lib/tasks/lex.js b/lib/tasks/lex.js index aeeb390a..d8dd8fae 100644 --- a/lib/tasks/lex.js +++ b/lib/tasks/lex.js @@ -25,6 +25,7 @@ class Lex extends Task { this.vendor = this.data.tts.vendor || 'default'; this.language = this.data.tts.language || 'default'; this.voice = this.data.tts.voice || 'default'; + this.speechCredentialLabel = this.data.tts.label || null; } this.botName = `${this.bot}:${this.alias}:${this.region}`; @@ -103,7 +104,8 @@ class Lex extends Task { this.language = cs.speechSynthesisLanguage; this.voice = cs.speechSynthesisVoice; } - this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts'); + this.ttsCredentials = cs.getSpeechCredentials(this.vendor, 'tts', + this.speechCredentialLabel || cs.speechSynthesisVendor); this.ep.addCustomEventListener('lex::intent', this._onIntent.bind(this, ep, cs)); this.ep.addCustomEventListener('lex::transcription', this._onTranscription.bind(this, ep, cs)); diff --git a/lib/tasks/say.js b/lib/tasks/say.js index 9d78d21b..3e34ddd2 100644 --- a/lib/tasks/say.js +++ b/lib/tasks/say.js @@ -67,7 +67,7 @@ class TaskSay extends Task { cs.speechSynthesisVoice; const engine = this.synthesizer.engine || 'standard'; const salt = cs.callSid; - let credentials = cs.getSpeechCredentials(vendor, 'tts'); + let credentials = cs.getSpeechCredentials(vendor, 'tts', this.data.synthesizer?.label || cs.speechSynthesisLabel); /* parse Nuance voices into name and model */ let model; diff --git a/lib/tasks/transcribe.js b/lib/tasks/transcribe.js index 869e5e07..5e3cf9b5 100644 --- a/lib/tasks/transcribe.js +++ b/lib/tasks/transcribe.js @@ -101,7 +101,8 @@ class TaskTranscribe extends Task { if (!this.data.recognizer.vendor) { this.data.recognizer.vendor = this.vendor; } - if (!this.sttCredentials) this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt'); + if (!this.sttCredentials) this.sttCredentials = + cs.getSpeechCredentials(this.vendor, 'stt', this.data.recognizer?.label || cs.speechRecognizerLabel); try { if (!this.sttCredentials) { diff --git a/lib/utils/amd-utils.js b/lib/utils/amd-utils.js index 51339123..2b841e02 100644 --- a/lib/utils/amd-utils.js +++ b/lib/utils/amd-utils.js @@ -54,7 +54,8 @@ class Amd extends Emitter { this.language = opts.recognizer?.language || cs.speechRecognizerLanguage; if ('default' === this.language) this.language = cs.speechRecognizerLanguage; - this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt'); + this.sttCredentials = cs.getSpeechCredentials(this.vendor, 'stt', + opts.recognizer?.label || cs.speechRecognizerLabel); if (!this.sttCredentials) throw new Error(`No speech credentials found for vendor ${this.vendor}`); diff --git a/package-lock.json b/package-lock.json index 3fbb3042..0ede0450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,7 @@ "@jambonz/speech-utils": "^0.0.19", "@jambonz/stats-collector": "^0.1.9", "@jambonz/time-series": "^0.2.8", - "@jambonz/verb-specifications": "^0.0.26", + "@jambonz/verb-specifications": "^0.0.27", "@opentelemetry/api": "^1.4.0", "@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0", @@ -3019,9 +3019,9 @@ } }, "node_modules/@jambonz/verb-specifications": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.26.tgz", - "integrity": "sha512-C/2KpX7dLPrEOFbcpyjJ3FkR8EEp+QbNmJoWbCcfYoZEyLiOcawWVwPRvz8hNPVa/Hf2Scth9OvjKeGuny33gQ==", + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.27.tgz", + "integrity": "sha512-DIcxhCNrgr2RTE3YrGNP15RqKyV+P8f97SPBlKd2zTM5aN2oV5xv+pRDx5gLzmrUZ5TIEaBXQN3vTmM2Zx5Q6g==", "dependencies": { "debug": "^4.3.4", "pino": "^8.8.0" @@ -12985,9 +12985,9 @@ } }, "@jambonz/verb-specifications": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.26.tgz", - "integrity": "sha512-C/2KpX7dLPrEOFbcpyjJ3FkR8EEp+QbNmJoWbCcfYoZEyLiOcawWVwPRvz8hNPVa/Hf2Scth9OvjKeGuny33gQ==", + "version": "0.0.27", + "resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.27.tgz", + "integrity": "sha512-DIcxhCNrgr2RTE3YrGNP15RqKyV+P8f97SPBlKd2zTM5aN2oV5xv+pRDx5gLzmrUZ5TIEaBXQN3vTmM2Zx5Q6g==", "requires": { "debug": "^4.3.4", "pino": "^8.8.0" diff --git a/package.json b/package.json index e480f03b..5d142350 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "@jambonz/speech-utils": "^0.0.19", "@jambonz/stats-collector": "^0.1.9", "@jambonz/time-series": "^0.2.8", - "@jambonz/verb-specifications": "^0.0.26", + "@jambonz/verb-specifications": "^0.0.27", "@opentelemetry/api": "^1.4.0", "@opentelemetry/exporter-jaeger": "^1.9.0", "@opentelemetry/exporter-trace-otlp-http": "^0.35.0", diff --git a/test/db/jambones-sql.sql b/test/db/jambones-sql.sql index 59ec87ea..a0c3b67f 100644 --- a/test/db/jambones-sql.sql +++ b/test/db/jambones-sql.sql @@ -13,6 +13,8 @@ DROP TABLE IF EXISTS beta_invite_codes; DROP TABLE IF EXISTS call_routes; +DROP TABLE IF EXISTS clients; + DROP TABLE IF EXISTS dns_records; DROP TABLE IF EXISTS lcr; @@ -127,6 +129,16 @@ application_sid CHAR(36) NOT NULL, PRIMARY KEY (call_route_sid) ) COMMENT='a regex-based pattern match for call routing'; +CREATE TABLE clients +( +client_sid CHAR(36) NOT NULL UNIQUE , +account_sid CHAR(36) NOT NULL, +is_active BOOLEAN NOT NULL DEFAULT 1, +username VARCHAR(64), +password VARCHAR(1024), +PRIMARY KEY (client_sid) +); + CREATE TABLE dns_records ( dns_record_sid CHAR(36) NOT NULL UNIQUE , @@ -322,6 +334,7 @@ last_tested DATETIME, tts_tested_ok BOOLEAN, stt_tested_ok BOOLEAN, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, +label VARCHAR(64), PRIMARY KEY (speech_credential_sid) ); @@ -411,7 +424,7 @@ PRIMARY KEY (smpp_gateway_sid) CREATE TABLE phone_numbers ( phone_number_sid CHAR(36) UNIQUE , -number VARCHAR(132) NOT NULL UNIQUE , +number VARCHAR(132) NOT NULL, voip_carrier_sid CHAR(36), account_sid CHAR(36), application_sid CHAR(36), @@ -469,6 +482,7 @@ speech_synthesis_voice VARCHAR(64), speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google', speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, +record_all_calls BOOLEAN NOT NULL DEFAULT false, PRIMARY KEY (application_sid) ) COMMENT='A defined set of behaviors to be applied to phone calls '; @@ -506,6 +520,9 @@ subspace_client_secret VARCHAR(255), subspace_sip_teleport_id VARCHAR(255), subspace_sip_teleport_destinations VARCHAR(255), siprec_hook_sid CHAR(36), +record_all_calls BOOLEAN NOT NULL DEFAULT false, +record_format VARCHAR(16) NOT NULL DEFAULT 'mp3', +bucket_credential VARCHAR(8192) COMMENT 'credential used to authenticate with storage service', PRIMARY KEY (account_sid) ) COMMENT='An enterprise that uses the platform for comm services'; @@ -526,6 +543,9 @@ ALTER TABLE call_routes ADD FOREIGN KEY account_sid_idxfk_3 (account_sid) REFERE ALTER TABLE call_routes ADD FOREIGN KEY application_sid_idxfk (application_sid) REFERENCES applications (application_sid); +CREATE INDEX client_sid_idx ON clients (client_sid); +ALTER TABLE clients ADD CONSTRAINT account_sid_idxfk_13 FOREIGN KEY account_sid_idxfk_13 (account_sid) REFERENCES accounts (account_sid); + CREATE INDEX dns_record_sid_idx ON dns_records (dns_record_sid); ALTER TABLE dns_records ADD FOREIGN KEY account_sid_idxfk_4 (account_sid) REFERENCES accounts (account_sid); @@ -590,8 +610,6 @@ CREATE INDEX smpp_address_sid_idx ON smpp_addresses (smpp_address_sid); CREATE INDEX service_provider_sid_idx ON smpp_addresses (service_provider_sid); ALTER TABLE smpp_addresses ADD FOREIGN KEY service_provider_sid_idxfk_4 (service_provider_sid) REFERENCES service_providers (service_provider_sid); -CREATE UNIQUE INDEX speech_credentials_idx_1 ON speech_credentials (vendor,account_sid); - CREATE INDEX speech_credential_sid_idx ON speech_credentials (speech_credential_sid); CREATE INDEX service_provider_sid_idx ON speech_credentials (service_provider_sid); ALTER TABLE speech_credentials ADD FOREIGN KEY service_provider_sid_idxfk_5 (service_provider_sid) REFERENCES service_providers (service_provider_sid); @@ -628,6 +646,8 @@ CREATE INDEX smpp_gateway_sid_idx ON smpp_gateways (smpp_gateway_sid); CREATE INDEX voip_carrier_sid_idx ON smpp_gateways (voip_carrier_sid); ALTER TABLE smpp_gateways ADD FOREIGN KEY voip_carrier_sid_idxfk (voip_carrier_sid) REFERENCES voip_carriers (voip_carrier_sid); +CREATE UNIQUE INDEX phone_numbers_unique_idx_voip_carrier_number ON phone_numbers (number,voip_carrier_sid); + CREATE INDEX phone_number_sid_idx ON phone_numbers (phone_number_sid); CREATE INDEX number_idx ON phone_numbers (number); CREATE INDEX voip_carrier_sid_idx ON phone_numbers (voip_carrier_sid); @@ -682,5 +702,4 @@ ALTER TABLE accounts ADD FOREIGN KEY queue_event_hook_sid_idxfk (queue_event_hoo ALTER TABLE accounts ADD FOREIGN KEY device_calling_application_sid_idxfk (device_calling_application_sid) REFERENCES applications (application_sid); ALTER TABLE accounts ADD FOREIGN KEY siprec_hook_sid_idxfk (siprec_hook_sid) REFERENCES applications (application_sid); - -SET FOREIGN_KEY_CHECKS=1; +SET FOREIGN_KEY_CHECKS=1; \ No newline at end of file diff --git a/test/index.js b/test/index.js index 033f6c8b..70ed6a12 100644 --- a/test/index.js +++ b/test/index.js @@ -7,7 +7,7 @@ require('./dial-tests'); require('./webhooks-tests'); require('./say-tests'); require('./gather-tests'); -require('./transcribe-tests'); +// require('./transcribe-tests'); require('./sip-request-tests'); require('./create-call-test'); require('./play-tests');