mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2025-12-18 21:37:43 +00:00
add support for deepgram STT
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
run-tests.sh
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
|
||||
@@ -11,7 +11,7 @@ const sql = `
|
||||
function makeStrategy(logger, retrieveKey) {
|
||||
return new Strategy(
|
||||
async function(token, done) {
|
||||
logger.debug(`validating with token ${token}`);
|
||||
//logger.debug(`validating with token ${token}`);
|
||||
jwt.verify(token, process.env.JWT_SECRET, async(err, decoded) => {
|
||||
if (err) {
|
||||
if (err.name === 'TokenExpiredError') {
|
||||
|
||||
@@ -14,7 +14,8 @@ const {
|
||||
testMicrosoftTts,
|
||||
testWellSaidTts,
|
||||
testNuanceStt,
|
||||
testNuanceTts
|
||||
testNuanceTts,
|
||||
testDeepgramStt
|
||||
} = require('../../utils/speech-utils');
|
||||
|
||||
const obscureKey = (key) => {
|
||||
@@ -88,6 +89,11 @@ const encryptCredential = (obj) => {
|
||||
const nuanceData = JSON.stringify({client_id, secret});
|
||||
return encrypt(nuanceData);
|
||||
|
||||
case 'deepgram':
|
||||
assert(api_key, 'invalid deepgram speech credential: api_key is required');
|
||||
const deepgramData = JSON.stringify({api_key});
|
||||
return encrypt(deepgramData);
|
||||
|
||||
default:
|
||||
assert(false, `invalid or missing vendor: ${vendor}`);
|
||||
}
|
||||
@@ -175,6 +181,10 @@ router.get('/', async(req, res) => {
|
||||
obj.client_id = o.client_id;
|
||||
obj.secret = obscureKey(o.secret);
|
||||
}
|
||||
else if ('deepgram' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = obscureKey(o.api_key);
|
||||
}
|
||||
return obj;
|
||||
}));
|
||||
} catch (err) {
|
||||
@@ -225,6 +235,10 @@ router.get('/:sid', async(req, res) => {
|
||||
obj.client_id = o.client_id;
|
||||
obj.secret = obscureKey(o.secret);
|
||||
}
|
||||
else if ('deepgram' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = obscureKey(o.api_key);
|
||||
}
|
||||
res.status(200).json(obj);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
@@ -474,6 +488,19 @@ router.get('/:sid/test', async(req, res) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cred.vendor === 'deepgram') {
|
||||
const {api_key} = credential;
|
||||
if (cred.use_for_stt) {
|
||||
try {
|
||||
await testDeepgramStt(logger, {api_key});
|
||||
results.stt.status = 'ok';
|
||||
SpeechCredential.sttTestResult(sid, true);
|
||||
} catch (err) {
|
||||
results.stt = {status: 'fail', reason: err.message};
|
||||
SpeechCredential.sttTestResult(sid, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.status(200).json(results);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
|
||||
@@ -2,6 +2,7 @@ const ttsGoogle = require('@google-cloud/text-to-speech');
|
||||
const sttGoogle = require('@google-cloud/speech').v1p1beta1;
|
||||
const Polly = require('aws-sdk/clients/polly');
|
||||
const AWS = require('aws-sdk');
|
||||
const { Deepgram } = require('@deepgram/sdk');
|
||||
const bent = require('bent');
|
||||
const fs = require('fs');
|
||||
|
||||
@@ -42,6 +43,33 @@ const testGoogleStt = async(logger, credentials) => {
|
||||
}
|
||||
};
|
||||
|
||||
const testDeepgramStt = async(logger, credentials) => {
|
||||
const {api_key} = credentials;
|
||||
const deepgram = new Deepgram(api_key);
|
||||
|
||||
const mimetype = 'audio/wav';
|
||||
const source = {
|
||||
buffer: fs.readFileSync(`${__dirname}/../../data/test_audio.wav`),
|
||||
mimetype: mimetype
|
||||
};
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Send the audio to Deepgram and get the response
|
||||
deepgram.transcription
|
||||
.preRecorded(source, {punctuate: true})
|
||||
.then((response) => {
|
||||
//logger.debug({response}, 'got transcript');
|
||||
if (response?.results?.channels[0]?.alternatives?.length > 0) resolve(response);
|
||||
else reject(new Error('no transcript returned'));
|
||||
return;
|
||||
})
|
||||
.catch((err) => {
|
||||
logger.info({err}, 'failed to get deepgram transcript');
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const testAwsTts = (logger, credentials) => {
|
||||
const polly = new Polly(credentials);
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -138,5 +166,6 @@ module.exports = {
|
||||
testMicrosoftStt,
|
||||
testWellSaidStt,
|
||||
testNuanceTts,
|
||||
testNuanceStt
|
||||
testNuanceStt,
|
||||
testDeepgramStt
|
||||
};
|
||||
|
||||
87
package-lock.json
generated
87
package-lock.json
generated
@@ -9,10 +9,11 @@
|
||||
"version": "v0.7.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@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.5.7",
|
||||
"@jambonz/realtimedb-helpers": "^0.5.9",
|
||||
"@jambonz/time-series": "^0.2.5",
|
||||
"argon2-ffi": "^2.0.0",
|
||||
"aws-sdk": "^2.1152.0",
|
||||
@@ -478,6 +479,16 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@deepgram/sdk": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@deepgram/sdk/-/sdk-1.10.2.tgz",
|
||||
"integrity": "sha512-7f/Uya1Tu0NBcxYSTbbmDnvAQ+YvzuzipVNa1uwfcMyiQZgBVZv+E7ToJhhC7KRr/tmQjniW29RsPqhOMBN99Q==",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.6",
|
||||
"utf-8-validate": "^5.0.9",
|
||||
"ws": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/eslintrc": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
||||
@@ -676,9 +687,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jambonz/realtimedb-helpers": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.7.tgz",
|
||||
"integrity": "sha512-TOTnFWSa4ronCdQTWfB8c5VI6DXcBEyDA4vbZnzkVAzSP90NpRPOPrvo2tEZxcGSlVIjBZew7rWgWyqkSwUT/Q==",
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.9.tgz",
|
||||
"integrity": "sha512-1DdEL+Zy3vcgJNXeGaiAdIe5k+3NdRdtTikJMiACrKUF1GVpnEjv/NKapr5u9FdOODblJ2bgFjktLpmSsVK/9Q==",
|
||||
"dependencies": {
|
||||
"@google-cloud/text-to-speech": "^4.0.3",
|
||||
"@grpc/grpc-js": "^1.7.3",
|
||||
@@ -1320,6 +1331,18 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
|
||||
},
|
||||
"node_modules/bufferutil": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
|
||||
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@@ -4497,9 +4520,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
|
||||
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
|
||||
"integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
@@ -6404,6 +6427,18 @@
|
||||
"resolved": "https://registry.npmjs.org/username-sync/-/username-sync-1.0.3.tgz",
|
||||
"integrity": "sha512-m/7/FSqjJNAzF2La448c/aEom0gJy7HY7Y509h6l0ePvEkFictAGptwWaj1msWJ38JbfEDOUoE8kqFee9EHKdA=="
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
@@ -7046,6 +7081,16 @@
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"@deepgram/sdk": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@deepgram/sdk/-/sdk-1.10.2.tgz",
|
||||
"integrity": "sha512-7f/Uya1Tu0NBcxYSTbbmDnvAQ+YvzuzipVNa1uwfcMyiQZgBVZv+E7ToJhhC7KRr/tmQjniW29RsPqhOMBN99Q==",
|
||||
"requires": {
|
||||
"bufferutil": "^4.0.6",
|
||||
"utf-8-validate": "^5.0.9",
|
||||
"ws": "^7.5.5"
|
||||
}
|
||||
},
|
||||
"@eslint/eslintrc": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz",
|
||||
@@ -7203,9 +7248,9 @@
|
||||
}
|
||||
},
|
||||
"@jambonz/realtimedb-helpers": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.7.tgz",
|
||||
"integrity": "sha512-TOTnFWSa4ronCdQTWfB8c5VI6DXcBEyDA4vbZnzkVAzSP90NpRPOPrvo2tEZxcGSlVIjBZew7rWgWyqkSwUT/Q==",
|
||||
"version": "0.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.5.9.tgz",
|
||||
"integrity": "sha512-1DdEL+Zy3vcgJNXeGaiAdIe5k+3NdRdtTikJMiACrKUF1GVpnEjv/NKapr5u9FdOODblJ2bgFjktLpmSsVK/9Q==",
|
||||
"requires": {
|
||||
"@google-cloud/text-to-speech": "^4.0.3",
|
||||
"@grpc/grpc-js": "^1.7.3",
|
||||
@@ -7728,6 +7773,14 @@
|
||||
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
|
||||
"integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk="
|
||||
},
|
||||
"bufferutil": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
|
||||
"integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@@ -10136,9 +10189,9 @@
|
||||
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA=="
|
||||
},
|
||||
"node-gyp-build": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz",
|
||||
"integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg=="
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
|
||||
"integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg=="
|
||||
},
|
||||
"node-object-hash": {
|
||||
"version": "2.3.10",
|
||||
@@ -11600,6 +11653,14 @@
|
||||
"resolved": "https://registry.npmjs.org/username-sync/-/username-sync-1.0.3.tgz",
|
||||
"integrity": "sha512-m/7/FSqjJNAzF2La448c/aEom0gJy7HY7Y509h6l0ePvEkFictAGptwWaj1msWJ38JbfEDOUoE8kqFee9EHKdA=="
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||
"requires": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
}
|
||||
},
|
||||
"util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
"url": "https://github.com/jambonz/jambonz-api-server.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@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.5.7",
|
||||
"@jambonz/realtimedb-helpers": "^0.5.9",
|
||||
"@jambonz/time-series": "^0.2.5",
|
||||
"argon2-ffi": "^2.0.0",
|
||||
"aws-sdk": "^2.1152.0",
|
||||
|
||||
@@ -135,6 +135,7 @@ test('speech credentials tests', async(t) => {
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200 && result.body.tts.status === 'ok', 'successfully tested speech credential for deepgram');
|
||||
}
|
||||
|
||||
/* add a credential for wellsaid */
|
||||
@@ -159,6 +160,7 @@ test('speech credentials tests', async(t) => {
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200 && result.body.tts.status === 'ok', 'successfully tested speech credential for wellsaid');
|
||||
|
||||
/* delete the credential */
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}`, {
|
||||
@@ -168,6 +170,39 @@ test('speech credentials tests', async(t) => {
|
||||
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
|
||||
}
|
||||
|
||||
/* add a credential for deepgram */
|
||||
if (process.env.DEEPGRAM_API_KEY) {
|
||||
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
body: {
|
||||
vendor: 'deepgram',
|
||||
use_for_stt: true,
|
||||
api_key: process.env.DEEPGRAM_API_KEY
|
||||
}
|
||||
});
|
||||
t.ok(result.statusCode === 201, 'successfully added speech credential for deepgram');
|
||||
const ms_sid = result.body.sid;
|
||||
|
||||
/* test the speech credential */
|
||||
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${ms_sid}/test`, {
|
||||
resolveWithFullResponse: true,
|
||||
auth: authUser,
|
||||
json: true,
|
||||
});
|
||||
console.log(JSON.stringify(result));
|
||||
t.ok(result.statusCode === 200 && result.body.stt.status === 'ok', 'successfully tested speech credential for deepgram');
|
||||
|
||||
/* delete the credential */
|
||||
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${ms_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();
|
||||
|
||||
Reference in New Issue
Block a user