mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-07-23 12:32:06 +00:00
feat: add nuance on-premise (#131)
* feat: add nuance on-premise * feat: update fetch nuance credential * fix: update * fix nuance tts test against on-prem, refactor aws/google tts testing to use speech-utils package --------- Co-authored-by: Quan HL <quanluuhoang8@gmail.com> Co-authored-by: Dave Horton <daveh@beachdognet.com>
This commit is contained in:
@@ -44,6 +44,8 @@ const encryptCredential = (obj) => {
|
||||
region,
|
||||
client_id,
|
||||
secret,
|
||||
nuance_tts_uri,
|
||||
nuance_stt_uri,
|
||||
use_custom_tts,
|
||||
custom_tts_endpoint,
|
||||
use_custom_stt,
|
||||
@@ -97,9 +99,10 @@ const encryptCredential = (obj) => {
|
||||
return encrypt(wsData);
|
||||
|
||||
case 'nuance':
|
||||
assert(client_id, 'invalid nuance speech credential: client_id is required');
|
||||
assert(secret, 'invalid nuance speech credential: secret is required');
|
||||
const nuanceData = JSON.stringify({client_id, secret});
|
||||
const checked = (client_id && secret) || (nuance_tts_uri || nuance_stt_uri);
|
||||
assert(checked, 'invalid nuance speech credential: either entered client id and\
|
||||
secret or entered a nuance_tts_uri or nuance_stt_uri');
|
||||
const nuanceData = JSON.stringify({client_id, secret, nuance_tts_uri, nuance_stt_uri});
|
||||
return encrypt(nuanceData);
|
||||
|
||||
case 'deepgram':
|
||||
@@ -216,7 +219,7 @@ router.get('/', async(req, res) => {
|
||||
else if ('nuance' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.client_id = o.client_id;
|
||||
obj.secret = obscureKey(o.secret);
|
||||
obj.secret = o.secret ? obscureKey(o.secret) : null;
|
||||
}
|
||||
else if ('deepgram' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
@@ -291,7 +294,9 @@ router.get('/:sid', async(req, res) => {
|
||||
else if ('nuance' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.client_id = o.client_id;
|
||||
obj.secret = obscureKey(o.secret);
|
||||
obj.secret = o.secret ? obscureKey(o.secret) : null;
|
||||
obj.nuance_tts_uri = o.nuance_tts_uri;
|
||||
obj.nuance_stt_uri = o.nuance_stt_uri;
|
||||
}
|
||||
else if ('deepgram' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
@@ -347,7 +352,8 @@ router.put('/:sid', async(req, res) => {
|
||||
const sid = req.params.sid;
|
||||
const logger = req.app.locals.logger;
|
||||
try {
|
||||
const {use_for_tts, use_for_stt, region, aws_region, stt_region, tts_region, riva_server_uri} = req.body;
|
||||
const {use_for_tts, use_for_stt, region, aws_region, stt_region, tts_region,
|
||||
riva_server_uri, nuance_tts_uri, nuance_stt_uri} = req.body;
|
||||
if (typeof use_for_tts === 'undefined' && typeof use_for_stt === 'undefined') {
|
||||
throw new DbErrorUnprocessableRequest('use_for_tts and use_for_stt are the only updateable fields');
|
||||
}
|
||||
@@ -383,7 +389,9 @@ router.put('/:sid', async(req, res) => {
|
||||
custom_stt_endpoint,
|
||||
stt_region,
|
||||
tts_region,
|
||||
riva_server_uri
|
||||
riva_server_uri,
|
||||
nuance_stt_uri,
|
||||
nuance_tts_uri
|
||||
};
|
||||
logger.info({o, newCred}, 'updating speech credential with this new credential');
|
||||
obj.credential = encryptCredential(newCred);
|
||||
@@ -435,7 +443,8 @@ router.get('/:sid/test', async(req, res) => {
|
||||
|
||||
if (cred.use_for_tts) {
|
||||
try {
|
||||
await testGoogleTts(logger, credential);
|
||||
const {getTtsVoices} = req.app.locals;
|
||||
await testGoogleTts(logger, getTtsVoices, credential);
|
||||
results.tts.status = 'ok';
|
||||
SpeechCredential.ttsTestResult(sid, true);
|
||||
} catch (err) {
|
||||
@@ -457,8 +466,9 @@ router.get('/:sid/test', async(req, res) => {
|
||||
}
|
||||
else if (cred.vendor === 'aws') {
|
||||
if (cred.use_for_tts) {
|
||||
const {getTtsVoices} = req.app.locals;
|
||||
try {
|
||||
await testAwsTts(logger, {
|
||||
await testAwsTts(logger, getTtsVoices, {
|
||||
accessKeyId: credential.access_key_id,
|
||||
secretAccessKey: credential.secret_access_key,
|
||||
region: credential.aws_region || process.env.AWS_REGION
|
||||
@@ -540,13 +550,16 @@ router.get('/:sid/test', async(req, res) => {
|
||||
|
||||
const {
|
||||
client_id,
|
||||
secret
|
||||
secret,
|
||||
nuance_tts_uri,
|
||||
nuance_stt_uri
|
||||
} = credential;
|
||||
if (cred.use_for_tts) {
|
||||
try {
|
||||
await testNuanceTts(logger, getTtsVoices, {
|
||||
client_id,
|
||||
secret
|
||||
secret,
|
||||
nuance_tts_uri
|
||||
});
|
||||
results.tts.status = 'ok';
|
||||
SpeechCredential.ttsTestResult(sid, true);
|
||||
@@ -561,7 +574,7 @@ router.get('/:sid/test', async(req, res) => {
|
||||
}
|
||||
if (cred.use_for_stt) {
|
||||
try {
|
||||
await testNuanceStt(logger, {client_id, secret});
|
||||
await testNuanceStt(logger, {client_id, secret, nuance_stt_uri});
|
||||
results.stt.status = 'ok';
|
||||
SpeechCredential.sttTestResult(sid, true);
|
||||
} catch (err) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const ttsGoogle = require('@google-cloud/text-to-speech');
|
||||
const sttGoogle = require('@google-cloud/speech').v1p1beta1;
|
||||
const { PollyClient, DescribeVoicesCommand } = require('@aws-sdk/client-polly');
|
||||
const { TranscribeClient, ListVocabulariesCommand } = require('@aws-sdk/client-transcribe');
|
||||
const { Deepgram } = require('@deepgram/sdk');
|
||||
const sdk = require('microsoft-cognitiveservices-speech-sdk');
|
||||
@@ -34,9 +32,10 @@ const testNuanceStt = async(logger, credentials) => {
|
||||
return true;
|
||||
};
|
||||
|
||||
const testGoogleTts = async(logger, credentials) => {
|
||||
const client = new ttsGoogle.TextToSpeechClient({credentials});
|
||||
await client.listVoices();
|
||||
const testGoogleTts = async(logger, getTtsVoices, credentials) => {
|
||||
const voices = await getTtsVoices({vendor: 'google', credentials});
|
||||
return voices;
|
||||
|
||||
};
|
||||
|
||||
const testGoogleStt = async(logger, credentials) => {
|
||||
@@ -120,19 +119,10 @@ const testMicrosoftStt = async(logger, credentials) => {
|
||||
});
|
||||
};
|
||||
|
||||
const testAwsTts = async(logger, credentials) => {
|
||||
const testAwsTts = async(logger, getTtsVoices, credentials) => {
|
||||
try {
|
||||
const {region, accessKeyId, secretAccessKey} = credentials;
|
||||
const client = new PollyClient({
|
||||
region,
|
||||
credentials: {
|
||||
accessKeyId,
|
||||
secretAccessKey
|
||||
}
|
||||
});
|
||||
const command = new DescribeVoicesCommand({LanguageCode: 'en-US'});
|
||||
const response = await client.send(command);
|
||||
return response;
|
||||
const voices = await getTtsVoices({vendor: 'aws', credentials});
|
||||
return voices;
|
||||
} catch (err) {
|
||||
logger.info({err}, 'testMicrosoftTts - failed to list voices for region ${region}');
|
||||
throw err;
|
||||
|
||||
Generated
+7
-7
@@ -16,7 +16,7 @@
|
||||
"@google-cloud/text-to-speech": "^4.0.3",
|
||||
"@jambonz/db-helpers": "^0.7.3",
|
||||
"@jambonz/realtimedb-helpers": "^0.7.0",
|
||||
"@jambonz/speech-utils": "^0.0.6",
|
||||
"@jambonz/speech-utils": "^0.0.8",
|
||||
"@jambonz/time-series": "^0.2.5",
|
||||
"@jambonz/verb-specifications": "^0.0.3",
|
||||
"@soniox/soniox-node": "^1.1.0",
|
||||
@@ -1721,9 +1721,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/speech-utils": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.0.6.tgz",
|
||||
"integrity": "sha512-cZckiUe2tKUB93aXjvZAXSeyuMbLp/fvT4GQqRh5u7BriA/CS2r+hTFMxzSpboNlnni30M/pM0/Jjta6gzFPgw==",
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.0.8.tgz",
|
||||
"integrity": "sha512-OuJUk2D1JT1oZlWS7mCFWY3VbNS34MpIRRjwqm/wKTSuzfpvKezXHp4/9eddmuP+mjAC2OwFxphTn08yiApvHQ==",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-polly": "^3.276.0",
|
||||
"@google-cloud/text-to-speech": "^4.2.0",
|
||||
@@ -10227,9 +10227,9 @@
|
||||
}
|
||||
},
|
||||
"@jambonz/speech-utils": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.0.6.tgz",
|
||||
"integrity": "sha512-cZckiUe2tKUB93aXjvZAXSeyuMbLp/fvT4GQqRh5u7BriA/CS2r+hTFMxzSpboNlnni30M/pM0/Jjta6gzFPgw==",
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.0.8.tgz",
|
||||
"integrity": "sha512-OuJUk2D1JT1oZlWS7mCFWY3VbNS34MpIRRjwqm/wKTSuzfpvKezXHp4/9eddmuP+mjAC2OwFxphTn08yiApvHQ==",
|
||||
"requires": {
|
||||
"@aws-sdk/client-polly": "^3.276.0",
|
||||
"@google-cloud/text-to-speech": "^4.2.0",
|
||||
|
||||
+1
-3
@@ -18,14 +18,12 @@
|
||||
"url": "https://github.com/jambonz/jambonz-api-server.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-polly": "^3.290.0",
|
||||
"@aws-sdk/client-transcribe": "^3.290.0",
|
||||
"@deepgram/sdk": "^1.10.2",
|
||||
"@google-cloud/speech": "^5.1.0",
|
||||
"@google-cloud/text-to-speech": "^4.0.3",
|
||||
"@jambonz/db-helpers": "^0.7.3",
|
||||
"@jambonz/realtimedb-helpers": "^0.7.0",
|
||||
"@jambonz/speech-utils": "^0.0.6",
|
||||
"@jambonz/speech-utils": "^0.0.8",
|
||||
"@jambonz/time-series": "^0.2.5",
|
||||
"@jambonz/verb-specifications": "^0.0.3",
|
||||
"@soniox/soniox-node": "^1.1.0",
|
||||
|
||||
@@ -392,9 +392,34 @@ test('speech credentials tests', async(t) => {
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
|
||||
/* add a credential for nuance */
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'nuance',
|
||||
use_for_stt: true,
|
||||
use_for_tts: true,
|
||||
client_id: 'client_id',
|
||||
secret: 'secret',
|
||||
nuance_tts_uri: "192.168.1.2:5060",
|
||||
nuance_stt_uri: "192.168.1.2:5061"
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully added speech credential for nuance');
|
||||
const nuance_sid = result.body.sid;
|
||||
|
||||
/* delete the credential */
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${nuance_sid}`, {
|
||||
auth: authUser,
|
||||
resolveWithFullResponse: true,
|
||||
});
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
|
||||
await deleteObjectBySid(request, '/Accounts', account_sid);
|
||||
await deleteObjectBySid(request, '/ServiceProviders', service_provider_sid);
|
||||
//t.end();
|
||||
t.end();
|
||||
}
|
||||
catch (err) {
|
||||
console.error(err);
|
||||
|
||||
Reference in New Issue
Block a user