Compare commits

...

13 Commits

Author SHA1 Message Date
Hoan Luu Huu
9421bb8aa1 fixed deepgram cannot fetch list of available voices for model (#500) 2025-09-27 10:13:06 -04:00
Hoan Luu Huu
a15c5cd267 speech utils v0.2.23 (#497) 2025-09-11 01:30:12 -04:00
Dave Horton
4de66789ef fix db upgrade script (#495) 2025-09-07 10:58:14 -04:00
RJ Burnham
a297d2038f Refactor S3MultipartUploadStream to optimize buffer handling and improve upload efficiency (#494)
- Replaced Buffer.concat with chunk accumulation to reduce time complexity during writes.
- Introduced bufferedBytes to track total size of accumulated chunks.
- Updated upload logic to handle parts more efficiently, minimizing memory overhead.
- Enhanced logging in upload function to include selected encoder format for better traceability.

(cherry picked from commit ce8bba2f18d807d4872b168e451e4501b1acb824)
2025-09-04 07:34:19 -04:00
Sam Machin
2e0ea56925 Fix API for Carriers & SIP Gateways (#492)
* allow account api keys to get/post sip gateways

* require sp sid when creating carriers

* allow account level api keys to query carriers

* lookup and set the service_provider_sid on account create carrier
2025-08-28 08:46:42 -04:00
Dave Horton
9c8bfebd53 update to latest speech-utils 2025-08-17 09:53:33 -04:00
Dave Horton
035458ad3c logging 2025-08-13 20:33:40 -04:00
Hoan Luu Huu
fd9dc77a58 support resemble TTS (#488)
* support resemble TTS

* wip

* wip

* update speech utils version

* update resemble voice list
2025-08-13 08:18:08 -04:00
Hoan Luu Huu
2b66a121a0 fixed deepgram river does not return api_key (#486) 2025-07-30 08:29:57 -04:00
Hoan Luu Huu
3a6d10e725 support deepgram river (#481)
* support deepgram river

* update verb specification version
2025-07-29 13:51:36 -04:00
Dave Horton
6f87204d88 bump version 2025-07-15 11:44:19 -04:00
Sam Machin
9854666d4f add new /Callcount endpoint (#480)
* add new /Callcount endpoint

* update db-helpers

* update endpoint
2025-07-03 11:49:27 -04:00
Hoan Luu Huu
0d4b7e88ad update verb specification 0.0.107 (#479) 2025-07-03 07:15:59 -04:00
16 changed files with 1096 additions and 250 deletions

6
app.js
View File

@@ -48,7 +48,8 @@ const {
retrieveKey,
deleteKey,
incrKey,
listConferences
listConferences,
getCallCount
} = require('./lib/helpers/realtimedb-helpers');
const {
getTtsVoices,
@@ -118,7 +119,8 @@ app.locals = {
queryAlertsSP,
writeCdrs,
writeAlerts,
AlertType
AlertType,
getCallCount
};
const unless = (paths, middleware) => {

View File

@@ -267,6 +267,8 @@ const doIt = async() => {
if (val < 9000) upgrades.push(...sql['9000']);
if (val < 9002) upgrades.push(...sql['9002']);
if (val < 9003) upgrades.push(...sql['9003']);
if (val < 9004) upgrades.push(...sql['9004']);
if (val < 9005) upgrades.push(...sql['9005']);
// perform all upgrades
logger.info({upgrades}, 'applying schema upgrades..');

View File

@@ -13,7 +13,8 @@ const {
deleteKey,
incrKey,
client: redisClient,
listConferences
listConferences,
getCallCount
} = require('@jambonz/realtimedb-helpers')({}, logger);
module.exports = {
@@ -29,5 +30,6 @@ module.exports = {
deleteKey,
redisClient,
incrKey,
listConferences
listConferences,
getCallCount
};

View File

@@ -15,7 +15,9 @@ class S3MultipartUploadStream extends Writable {
this.uploadId = null;
this.partNumber = 1;
this.multipartETags = [];
this.buffer = Buffer.alloc(0);
// accumulate incoming chunks to avoid O(n^2) Buffer.concat on every write
this.chunks = [];
this.bufferedBytes = 0;
this.minPartSize = 5 * 1024 * 1024; // 5 MB
this.s3 = new S3Client(opts.bucketCredential);
this.metadata = opts.metadata;
@@ -31,13 +33,13 @@ class S3MultipartUploadStream extends Writable {
return response.UploadId;
}
async _uploadBuffer() {
async _uploadPart(bodyBuffer) {
const uploadPartCommand = new UploadPartCommand({
Bucket: this.bucketName,
Key: this.objectKey,
PartNumber: this.partNumber,
UploadId: this.uploadId,
Body: this.buffer,
Body: bodyBuffer,
});
const uploadPartResponse = await this.s3.send(uploadPartCommand);
@@ -54,11 +56,16 @@ class S3MultipartUploadStream extends Writable {
this.uploadId = await this._initMultipartUpload();
}
this.buffer = Buffer.concat([this.buffer, chunk]);
// accumulate without concatenating on every write
this.chunks.push(chunk);
this.bufferedBytes += chunk.length;
if (this.buffer.length >= this.minPartSize) {
await this._uploadBuffer();
this.buffer = Buffer.alloc(0);
if (this.bufferedBytes >= this.minPartSize) {
const partBuffer = Buffer.concat(this.chunks, this.bufferedBytes);
// reset accumulators before awaiting upload to allow GC
this.chunks = [];
this.bufferedBytes = 0;
await this._uploadPart(partBuffer);
}
callback(null);
@@ -69,8 +76,11 @@ class S3MultipartUploadStream extends Writable {
async _finalize(err) {
try {
if (this.buffer.length > 0) {
await this._uploadBuffer();
if (this.bufferedBytes > 0) {
const finalBuffer = Buffer.concat(this.chunks, this.bufferedBytes);
this.chunks = [];
this.bufferedBytes = 0;
await this._uploadPart(finalBuffer);
}
const completeMultipartUploadCommand = new CompleteMultipartUploadCommand({

View File

@@ -51,8 +51,10 @@ async function upload(logger, socket) {
/**encoder */
let encoder;
let recordFormat;
if (account[0].record_format === 'wav') {
encoder = new wav.Writer({ channels: 2, sampleRate, bitDepth: 16 });
recordFormat = 'wav';
} else {
// default is mp3
encoder = new PCMToMP3Encoder({
@@ -60,7 +62,9 @@ async function upload(logger, socket) {
sampleRate: sampleRate,
bitrate: 128
}, logger);
recordFormat = 'mp3';
}
logger.info({ record_format: recordFormat, channels: 2, sampleRate }, 'record upload: selected encoder');
/* start streaming data */
pipeline(

View File

@@ -161,6 +161,9 @@ router.post('/:sid/VoipCarriers', async(req, res) => {
try {
const account_sid = parseAccountSid(req);
await validateRequest(req, account_sid);
// Set the service_provder_sid to the relevent value for the account
const account = await Account.retrieve(req.user.account_sid);
payload.service_provider_sid = account[0].service_provider_sid;
logger.debug({payload}, 'POST /:sid/VoipCarriers');
const uuid = await VoipCarrier.make({
@@ -1220,4 +1223,23 @@ router.get('/:sid/Conferences', async(req, res) => {
}
});
/**
* retrieve counts of calls under an account
*/
router.get('/:sid/CallCount', async(req, res) => {
const {logger, getCallCount} = req.app.locals;
try {
const accountSid = parseAccountSid(req);
await validateRequest(req, accountSid);
const count = await getCallCount(accountSid);
count.outbound = Number(count.outbound);
count.inbound = Number(count.inbound);
logger.debug(`retrieved, outbound: ${count.outbound}, inbound: ${count.inbound}, for account sid ${accountSid}`);
res.status(200).json(count);
updateLastUsed(logger, accountSid, req).catch((err) => {});
} catch (err) {
sysError(logger, res, err);
}
});
module.exports = router;

View File

@@ -52,20 +52,21 @@ router.post('/', async(req, res) => {
let obj;
try {
if (!email || !validateEmail(email)) {
logger.info({email}, 'Bad POST to /forgot-password is missing email or invalid email');
return res.status(400).json({error: 'invalid or missing email'});
}
const [r] = await promisePool.query({sql, nestTables: true}, email);
if (0 === r.length) {
logger.info('user not found');
logger.info(`user not found: ${email}`);
return res.status(400).json({error: 'failed to reset your password'});
}
obj = r[0];
if (!obj.user.is_active) {
logger.info(obj.user.name, 'user is inactive');
logger.info({user: obj.user.name, obj}, 'user is inactive');
return res.status(400).json({error: 'failed to reset your password'});
} else if (obj.acc.account_sid !== null && !obj.acc.is_active) {
logger.info(obj.acc.account_sid, 'account is inactive');
logger.info({account_sid: obj.acc.account_sid, obj}, 'account is inactive');
return res.status(400).json({error: 'failed to reset your password'});
}
res.sendStatus(204);

View File

@@ -46,10 +46,16 @@ async function validateRetrieve(req) {
return;
}
if (req.user.hasScope('service_provider') || req.user.hasScope('account')) {
if (req.user.hasScope('service_provider')) {
if (service_provider_sid === req.user.service_provider_sid) return;
}
if (req.user.hasScope('account')) {
const results = await Account.retrieve(req.user.account_sid);
if (service_provider_sid === results[0].service_provider_sid) return;
}
throw new DbErrorForbidden('insufficient permissions');
} catch (error) {
throw error;

View File

@@ -18,8 +18,7 @@ const checkUserScope = async(req, voip_carrier_sid) => {
const carrier = await lookupCarrierBySid(voip_carrier_sid);
if (!carrier) throw new DbErrorBadRequest('invalid voip_carrier_sid');
if ((!carrier.service_provider_sid || carrier.service_provider_sid === req.user.service_provider_sid) &&
(!carrier.account_sid || carrier.account_sid === req.user.account_sid)) {
if (!carrier.account_sid || carrier.account_sid === req.user.account_sid) {
if (req.method !== 'GET' && !carrier.account_sid) {
throw new DbErrorForbidden('insufficient privileges');

View File

@@ -15,7 +15,8 @@ const {decryptCredential, testWhisper, testDeepgramTTS,
testCartesia,
testVoxistStt,
testOpenAiStt,
testInworld} = require('../../utils/speech-utils');
testInworld,
testResembleTTS} = require('../../utils/speech-utils');
const {DbErrorUnprocessableRequest, DbErrorForbidden, DbErrorBadRequest} = require('../../utils/errors');
const {
testGoogleTts,
@@ -132,6 +133,8 @@ const encryptCredential = (obj) => {
deepgram_stt_use_tls,
deepgram_tts_uri,
playht_tts_uri,
resemble_tts_uri,
resemble_tts_use_tls,
use_custom_tts,
custom_tts_endpoint,
custom_tts_endpoint_url,
@@ -226,6 +229,20 @@ const encryptCredential = (obj) => {
deepgram_stt_use_tls, deepgram_tts_uri, model_id});
return encrypt(deepgramData);
case 'resemble':
assert(api_key, 'invalid resemble speech credential: api_key is required');
const resembleData = JSON.stringify({
api_key,
...(resemble_tts_uri && {resemble_tts_uri}),
...(resemble_tts_use_tls && {resemble_tts_use_tls})
});
return encrypt(resembleData);
case 'deepgramriver':
assert(api_key, 'invalid deepgram river speech credential: api_key is required');
const deepgramriverData = JSON.stringify({api_key});
return encrypt(deepgramriverData);
case 'ibm':
const ibmData = JSON.stringify({tts_api_key, tts_region, stt_api_key, stt_region, instance_id});
return encrypt(ibmData);
@@ -518,7 +535,9 @@ router.put('/:sid', async(req, res) => {
playht_tts_uri,
engine_version,
service_version,
speechmatics_stt_uri
speechmatics_stt_uri,
resemble_tts_use_tls,
resemble_tts_uri
} = req.body;
const newCred = {
@@ -551,7 +570,9 @@ router.put('/:sid', async(req, res) => {
playht_tts_uri,
engine_version,
service_version,
speechmatics_stt_uri
speechmatics_stt_uri,
resemble_tts_uri,
resemble_tts_use_tls
};
logger.info({o, newCred}, 'updating speech credential with this new credential');
obj.credential = encryptCredential(newCred);
@@ -751,6 +772,17 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.sttTestResult(sid, false);
}
}
} else if (cred.vendor === 'resemble') {
if (cred.use_for_tts) {
try {
await testResembleTTS(logger, synthAudio, credential);
results.tts.status = 'ok';
SpeechCredential.ttsTestResult(sid, true);
} catch (err) {
results.tts = {status: 'fail', reason: err.message};
SpeechCredential.ttsTestResult(sid, false);
}
}
} else if (cred.vendor === 'deepgram') {
const {api_key} = credential;
if (cred.use_for_tts) {
@@ -774,6 +806,19 @@ router.get('/:sid/test', async(req, res) => {
}
}
}
else if (cred.vendor === 'deepgramriver') {
const {api_key} = credential;
if (cred.use_for_stt && api_key) {
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);
}
}
}
else if (cred.vendor === 'ibm') {
const {getTtsVoices} = req.app.locals;

View File

@@ -1,56 +1,153 @@
module.exports = [
{ name: 'Multilingual', value: 'multi' },
{ name: 'Afrikaans', value: 'af' },
{ name: 'Amharic', value: 'am' },
{ name: 'Arabic', value: 'ar' },
{ name: 'Assamese', value: 'as' },
{ name: 'Azerbaijani', value: 'az' },
{ name: 'Bashkir', value: 'ba' },
{ name: 'Belarusian', value: 'be' },
{ name: 'Bulgarian', value: 'bg' },
{ name: 'Bengali', value: 'bn' },
{ name: 'Tibetan', value: 'bo' },
{ name: 'Breton', value: 'br' },
{ name: 'Bosnian', value: 'bs' },
{ name: 'Catalan', value: 'ca' },
{ name: 'Chinese (Mandarin, Simplified)', value: 'zh' },
{ name: 'Chinese (Mandarin, Simplified - China)', value: 'zh-CN' },
{ name: 'Chinese (Mandarin, Simplified - Hans)', value: 'zh-Hans' },
{ name: 'Chinese (Mandarin, Traditional)', value: 'zh-TW' },
{ name: 'Chinese (Mandarin, Traditional - Hant)', value: 'zh-Hant' },
{ name: 'Chinese (Cantonese, Traditional - Hong Kong)', value: 'zh-HK' },
{ name: 'Czech', value: 'cs' },
{ name: 'Welsh', value: 'cy' },
{ name: 'Danish', value: 'da' },
{ name: 'Danish (Denmark)', value: 'da-DK' },
{ name: 'Dutch', value: 'nl' },
{ name: 'English', value: 'en' },
{ name: 'English (United States)', value: 'en-US' },
{ name: 'English (Australia)', value: 'en-AU' },
{ name: 'English (United Kingdom)', value: 'en-GB' },
{ name: 'English (New Zealand)', value: 'en-NZ' },
{ name: 'English (India)', value: 'en-IN' },
{ name: 'Estonian', value: 'et' },
{ name: 'Finnish', value: 'fi' },
{ name: 'Flemish', value: 'nl-BE' },
{ name: 'French', value: 'fr' },
{ name: 'French (Canada)', value: 'fr-CA' },
{ name: 'German', value: 'de' },
{ name: 'German (Austria)', value: 'de-AT' },
{ name: 'German (Switzerland)', value: 'de-CH' },
{ name: 'Greek', value: 'el' },
{ name: 'German (Germany)', value: 'de-DE' },
{ name: 'Modern Greek', value: 'el' },
{ name: 'English', value: 'en' },
{ name: 'English (Australia)', value: 'en-AU' },
{ name: 'English (Canada)', value: 'en-CA' },
{ name: 'English (United Kingdom)', value: 'en-GB' },
{ name: 'English (Ireland)', value: 'en-IE' },
{ name: 'English (India)', value: 'en-IN' },
{ name: 'English (Malaysia)', value: 'en-MY' },
{ name: 'English (New Zealand)', value: 'en-NZ' },
{ name: 'English (Philippines)', value: 'en-PH' },
{ name: 'English (United States)', value: 'en-US' },
{ name: 'English (South Africa)', value: 'en-ZA' },
{ name: 'Spanish', value: 'es' },
{ name: 'Spanish (Latin America and the Caribbean)', value: 'es-419' },
{ name: 'Spanish (Argentina)', value: 'es-AR' },
{ name: 'Spanish (Colombia)', value: 'es-CO' },
{ name: 'Spanish (Spain)', value: 'es-ES' },
{ name: 'Spanish (Latin America)', value: 'es-LATAM' },
{ name: 'Spanish (Mexico)', value: 'es-MX' },
{ name: 'Spanish (United States)', value: 'es-US' },
{ name: 'Estonian', value: 'et' },
{ name: 'Basque', value: 'eu' },
{ name: 'Persian', value: 'fa' },
{ name: 'Finnish', value: 'fi' },
{ name: 'Faroese', value: 'fo' },
{ name: 'French', value: 'fr' },
{ name: 'French (Belgium)', value: 'fr-BE' },
{ name: 'French (Canada)', value: 'fr-CA' },
{ name: 'French (Switzerland)', value: 'fr-CH' },
{ name: 'French (France)', value: 'fr-FR' },
{ name: 'French (Canada)', value: 'fr-ca' },
{ name: 'Galician', value: 'gl' },
{ name: 'Gujarati', value: 'gu' },
{ name: 'Hausa', value: 'ha' },
{ name: 'Hawaiian', value: 'haw' },
{ name: 'Hebrew', value: 'he' },
{ name: 'Hindi', value: 'hi' },
{ name: 'Hindi (Latin)', value: 'hi-Latn' },
{ name: 'Croatian', value: 'hr' },
{ name: 'Haitian', value: 'ht' },
{ name: 'Hungarian', value: 'hu' },
{ name: 'Armenian', value: 'hy' },
{ name: 'Indonesian', value: 'id' },
{ name: 'Indonesian (Indonesia)', value: 'id-ID' },
{ name: 'Icelandic', value: 'is' },
{ name: 'Italian', value: 'it' },
{ name: 'Italian (Italy)', value: 'it-IT' },
{ name: 'Japanese', value: 'ja' },
{ name: 'Japanese (Japan)', value: 'ja-JP' },
{ name: 'Javanese', value: 'jw' },
{ name: 'Georgian', value: 'ka' },
{ name: 'Kazakh', value: 'kk' },
{ name: 'Khmer', value: 'km' },
{ name: 'Kannada', value: 'kn' },
{ name: 'Korean', value: 'ko' },
{ name: 'Korean (South Korea)', value: 'ko-KR' },
{ name: 'Latvian', value: 'lv' },
{ name: 'Korean (Republic of Korea)', value: 'ko-KR' },
{ name: 'Latin', value: 'la' },
{ name: 'Luxembourgish', value: 'lb' },
{ name: 'Lingala', value: 'ln' },
{ name: 'Lao', value: 'lo' },
{ name: 'Lithuanian', value: 'lt' },
{ name: 'Latvian', value: 'lv' },
{ name: 'Malagasy', value: 'mg' },
{ name: 'Maori', value: 'mi' },
{ name: 'Macedonian', value: 'mk' },
{ name: 'Malayalam', value: 'ml' },
{ name: 'Mongolian', value: 'mn' },
{ name: 'Marathi', value: 'mr' },
{ name: 'Malay', value: 'ms' },
{ name: 'Malay (Malaysia)', value: 'ms-MY' },
{ name: 'Malay (Singapore)', value: 'ms-SG' },
{ name: 'Maltese', value: 'mt' },
{ name: 'Burmese', value: 'my' },
{ name: 'Nepali', value: 'ne' },
{ name: 'Dutch', value: 'nl' },
{ name: 'Dutch (Belgium)', value: 'nl-BE' },
{ name: 'Dutch (Netherlands)', value: 'nl-NL' },
{ name: 'Norwegian Nynorsk', value: 'nn' },
{ name: 'Norwegian', value: 'no' },
{ name: 'Norwegian (Norway)', value: 'no-NO' },
{ name: 'Occitan', value: 'oc' },
{ name: 'Panjabi', value: 'pa' },
{ name: 'Polish', value: 'pl' },
{ name: 'Polish (Poland)', value: 'pl-PL' },
{ name: 'Pushto', value: 'ps' },
{ name: 'Portuguese', value: 'pt' },
{ name: 'Portuguese (Brazil)', value: 'pt-BR' },
{ name: 'Portuguese (Portugal)', value: 'pt-PT' },
{ name: 'Romanian', value: 'ro' },
{ name: 'Romanian (Moldova)', value: 'ro-MD' },
{ name: 'Russian', value: 'ru' },
{ name: 'Russian (Latin)', value: 'ru-Latn' },
{ name: 'Russian (Russian Federation)', value: 'ru-RU' },
{ name: 'Sanskrit', value: 'sa' },
{ name: 'Sindhi', value: 'sd' },
{ name: 'Sinhala', value: 'si' },
{ name: 'Slovak', value: 'sk' },
{ name: 'Spanish', value: 'es' },
{ name: 'Spanish (Latin America)', value: 'es-419' },
{ name: 'Slovenian', value: 'sl' },
{ name: 'Shona', value: 'sn' },
{ name: 'Somali', value: 'so' },
{ name: 'Albanian', value: 'sq' },
{ name: 'Serbian', value: 'sr' },
{ name: 'Sundanese', value: 'su' },
{ name: 'Swedish', value: 'sv' },
{ name: 'Swedish (Sweden)', value: 'sv-SE' },
{ name: 'Swahili', value: 'sw' },
{ name: 'Tamil', value: 'ta' },
{ name: 'Tamasheq', value: 'taq' },
{ name: 'Telugu', value: 'te' },
{ name: 'Tajik', value: 'tg' },
{ name: 'Thai', value: 'th' },
{ name: 'Thai (Thailand)', value: 'th-TH' },
{ name: 'Turkmen', value: 'tk' },
{ name: 'Tagalog', value: 'tl' },
{ name: 'Turkish', value: 'tr' },
{ name: 'Turkish (Türkiye)', value: 'tr-TR' },
{ name: 'Tatar', value: 'tt' },
{ name: 'Ukrainian', value: 'uk' },
{ name: 'Vietnamese', value: 'vi' }
{ name: 'Urdu', value: 'ur' },
{ name: 'Uzbek', value: 'uz' },
{ name: 'Vietnamese', value: 'vi' },
{ name: 'Yiddish', value: 'yi' },
{ name: 'Yoruba', value: 'yo' },
{ name: 'Chinese (Mandarin, Mainland)', value: 'zh' },
{ name: 'Chinese (China)', value: 'zh-CN' },
{ name: 'Chinese (Cantonese, Hong Kong)', value: 'zh-HK' },
{ name: 'Chinese (Han (Simplified variant))', value: 'zh-Hans' },
{ name: 'Chinese (Han (Traditional variant))', value: 'zh-Hant' },
{ name: 'Chinese (Traditional, Taiwan)', value: 'zh-TW' }
];

View File

@@ -0,0 +1,438 @@
module.exports = [
{
value: 'en-gb',
name: 'En-gb',
voices: [
{
name: 'Seth (Legacy) (professional) - Resemble Voice',
value: 'a52c4efc',
},
{
name: 'Seth (professional) - Resemble Voice',
value: 'd3e61caf',
},
],
},
{
value: 'en-GB',
name: 'En-GB',
voices: [
{
name: 'Beatrice Pendergast (professional) - Resemble Voice',
value: '00b1fd4e',
},
{
name: 'Ed Smart (professional) - Resemble Voice',
value: '0c755526',
},
{
name: 'Paula J (professional) - Resemble Voice',
value: '33e64cd2',
},
],
},
{
value: 'en-us',
name: 'En-us',
voices: [
{
name: 'David (professional) - Resemble Voice',
value: '5bb13f03',
},
],
},
{
value: 'en-US',
name: 'En-US',
voices: [
{
name: 'Adam Lofbomm (professional) - Resemble Voice',
value: '4e228dba',
},
{
name: 'Alex (professional) - Resemble Voice',
value: '41b99669',
},
{
name: 'Amelia (professional) - Resemble Voice',
value: 'ecbe5d97',
},
{
name: 'Andrew (rapid) - Resemble Marketplace',
value: 'd2f26a3e',
},
{
name: 'Annika (professional) - Resemble Voice',
value: 'b27f3cc0',
},
{
name: 'Arthur (professional) - Resemble Voice',
value: '9de11312',
},
{
name: 'Ash (professional) - Resemble Voice',
value: 'ee322483',
},
{
name: 'Aurora (professional) - Resemble Voice',
value: 'a72d9fca',
},
{
name: 'Austin (professional) - Resemble Voice',
value: '82a67e58',
},
{
name: 'Beth (Legacy) (professional) - Resemble Voice',
value: '25c7823f',
},
{
name: 'Beth (professional) - Resemble Voice',
value: 'fa66d263',
},
{
name: 'Blade (professional) - Resemble Voice',
value: '8bedd793',
},
{
name: 'Brandy Sky (professional) - Resemble Voice',
value: '79e2f1dc',
},
{
name: 'Brenley (professional) - Resemble Voice',
value: 'e6ec3ca4',
},
{
name: 'Britney (professional) - Resemble Voice',
value: 'e57e23ff',
},
{
name: 'Broadcast Joe (professional) - Resemble Voice',
value: '21e49584',
},
{
name: 'Carl Bishop (Angry) (professional) - Resemble Voice',
value: 'f06cd770',
},
{
name: 'Carl Bishop (Conversational) (professional) - Resemble Voice',
value: '7f40ff35',
},
{
name: 'Carl Bishop (Happy) (professional) - Resemble Voice',
value: '99751e42',
},
{
name: 'Carl Bishop (professional) - Resemble Voice',
value: '01bcc102',
},
{
name: 'Carl Bishop (Scared) (Legacy) (professional) - Resemble Voice',
value: '1dcf0222',
},
{
name: 'Carl Bishop (Scared) (professional) - Resemble Voice',
value: 'eacbc44f',
},
{
name: 'Charles (Legacy) (professional) - Resemble Voice',
value: '4c6d3da5',
},
{
name: 'Charles (professional) - Resemble Voice',
value: 'd79a5198',
},
{
name: 'Charlotte (professional) - Resemble Voice',
value: '96b91cf9',
},
{
name: 'Chris Whiting (professional) - Resemble Voice',
value: '95b7560a',
},
{
name: 'Cliff (professional) - Resemble Voice',
value: 'fcf8490c',
},
{
name: 'Connor (professional) - Resemble Voice',
value: 'a6131acf',
},
{
name: 'Deanna (professional) - Resemble Voice',
value: '0842fdf9',
},
{
name: 'Ember (professional) - Resemble Voice',
value: '55592656',
},
{
name: 'Gene Amore (professional) - Resemble Voice',
value: 'f2ea7aa0',
},
{
name: 'Harry Robinson (professional) - Resemble Voice',
value: '3c36d67d',
},
{
name: 'Helena (professional) - Resemble Voice',
value: 'ac948df2',
},
{
name: 'Hem (professional) - Resemble Voice',
value: 'b6edbe5f',
},
{
name: 'John (professional) - Resemble Voice',
value: 'ac48daeb',
},
{
name: 'Josh (professional) - Resemble Voice',
value: '987c99e9',
},
{
name: 'Julie Hoverson (professional) - Resemble Voice',
value: 'b119524c',
},
{
name: 'Justin (Legacy) (professional) - Resemble Voice',
value: 'b2d1bb75',
},
{
name: 'Justin (Meditative) (Legacy) (professional) - Resemble Voice',
value: '93ce0920',
},
{
name: 'Justin (Meditative) (professional) - Resemble Voice',
value: '2570000e',
},
{
name: 'Justin (professional) - Resemble Voice',
value: '9d513c17',
},
{
name: 'Karl Nordman (professional) - Resemble Voice',
value: 'da67f17e',
},
{
name: 'Kate (professional) - Resemble Voice',
value: '28b4cc5a',
},
{
name: 'Katya (professional) - Resemble Voice',
value: 'c9ee13b4',
},
{
name: 'Ken (professional) - Resemble Voice',
value: '3dbfbf3d',
},
{
name: 'Kessi (professional) - Resemble Voice',
value: '2211cb8c',
},
{
name: 'Little Ari (professional) - Resemble Voice',
value: '805adead',
},
{
name: 'Little Brittle (professional) - Resemble Voice',
value: '8a73f115',
},
{
name: 'Liz (professional) - Resemble Voice',
value: '4884d94a',
},
{
name: 'Lothar (professional) - Resemble Voice',
value: '78671217',
},
{
name: 'Luna (professional) - Resemble Voice',
value: 'ae8223ca',
},
{
name: 'Matt Weller (professional) - Resemble Voice',
value: 'f4da4639',
},
{
name: 'Maureen (Angry) (professional) - Resemble Voice',
value: '482babfc',
},
{
name: 'Maureen (Caring) (professional) - Resemble Voice',
value: 'b15e550f',
},
{
name: 'Maureen (Happy) (professional) - Resemble Voice',
value: '91947e5c',
},
{
name: 'Maureen (professional) - Resemble Voice',
value: '7d94218f',
},
{
name: 'Maureen (Sad) (professional) - Resemble Voice',
value: 'bca7481c',
},
{
name: 'Maureen (Scared) (professional) - Resemble Voice',
value: '251c9439',
},
{
name: 'Mauren (Announcer) (professional) - Resemble Voice',
value: 'e984fb89',
},
{
name: 'Melody (Legacy) (professional) - Resemble Voice',
value: '15be93bd',
},
{
name: 'Melody (professional) - Resemble Voice',
value: '1c49e774',
},
{
name: 'Mike (professional) - Resemble Voice',
value: '3a02dc40',
},
{
name: 'Niki (professional) - Resemble Voice',
value: 'db37643c',
},
{
name: 'Olga (professional) - Resemble Voice',
value: '07c1d6b5',
},
{
name: 'Olivia (Legacy) (professional) - Resemble Voice',
value: '405b58e3',
},
{
name: 'Olivia (professional) - Resemble Voice',
value: 'ef49f972',
},
{
name: 'Orion (professional) - Resemble Voice',
value: 'aa8053cc',
},
{
name: 'Pete (professional) - Resemble Voice',
value: '1864fd63',
},
{
name: 'Primrose (Legacy) (professional) - Resemble Voice',
value: '7c8e47ca',
},
{
name: 'Primrose (professional) - Resemble Voice',
value: '33eecc17',
},
{
name: 'Primrose (Whispering) (Legacy) (professional) - Resemble Voice',
value: 'a56c5c6f',
},
{
name: 'Primrose (Whispering) (professional) - Resemble Voice',
value: '28fcdf76',
},
{
name: 'Primrose (Winded) (Legacy) (professional) - Resemble Voice',
value: '6f9a77a4',
},
{
name: 'Primrose (Winded) (professional) - Resemble Voice',
value: '0097f246',
},
{
name: 'Professor Shaposhnikov (professional) - Resemble Voice',
value: '3f5fb9f1',
},
{
name: 'Radio Nikole (professional) - Resemble Voice',
value: '19eae884',
},
{
name: 'Richard Garifo (professional) - Resemble Voice',
value: '85ba84f2',
},
{
name: 'Rico (professional) - Resemble Voice',
value: '14ca34b3',
},
{
name: 'Robert (professional) - Resemble Voice',
value: '3e907bcc',
},
{
name: 'Rupert (rapid) - Resemble Voice',
value: '28f1626c',
},
{
name: 'Sam (professional) - Resemble Voice',
value: '0f2f9a7e',
},
{
name: 'Samantha (Legacy) (professional) - Resemble Voice',
value: '266bfae9',
},
{
name: 'Samantha (professional) - Resemble Voice',
value: 'e28236ee',
},
{
name: 'Siobhan (professional) - Resemble Voice',
value: 'af72c1ac',
},
{
name: 'Steve (Scared) (professional) - Resemble Voice',
value: 'aaa56e79',
},
{
name: 'Tanja (professional) - Resemble Voice',
value: 'adb84c77',
},
{
name: 'Tanja (Telephonic) (professional) - Resemble Voice',
value: '4f5a470b',
},
{
name: 'Tanja (Warm Word Weaver) (professional) - Resemble Voice',
value: 'abbbc383',
},
{
name: 'Tarkos (professional) - Resemble Voice',
value: '779842bf',
},
{
name: 'Tyler (professional) - Resemble Voice',
value: 'ff225977',
},
{
name: 'Vicky (professional) - Resemble Voice',
value: 'f453b918',
},
{
name: 'Vivian (Legacy) (professional) - Resemble Voice',
value: 'bed1044d',
},
{
name: 'Vivian (professional) - Resemble Voice',
value: '1ff0045f',
},
{
name: 'William (Whispering) (Legacy) (professional) - Resemble Voice',
value: '79eb7953',
},
{
name: 'William (Whispering) (professional) - Resemble Voice',
value: 'e2180df0',
},
{
name: 'Willow (Whispering) (professional) - Resemble Voice',
value: 'f2906c4a',
},
{
name: 'Willow II (Whispering) (professional) - Resemble Voice',
value: 'c815cd7a',
},
],
},
];

View File

@@ -21,6 +21,7 @@ const TtsPlayHtLanguagesVoices = require('./speech-data/tts-playht');
const TtsVerbioLanguagesVoices = require('./speech-data/tts-verbio');
const TtsInworldLanguagesVoices = require('./speech-data/tts-inworld');
const ttsCartesia = require('./speech-data/tts-cartesia');
const TtsResembleLanguagesVoices = require('./speech-data/tts-resemble');
const TtsModelDeepgram = require('./speech-data/tts-model-deepgram');
const TtsLanguagesDeepgram = require('./speech-data/tts-deepgram');
@@ -424,6 +425,24 @@ const testWhisper = async(logger, synthAudio, credentials) => {
}
};
const testResembleTTS = async(logger, synthAudio, credentials) => {
try {
await synthAudio({increment: () => {}, histogram: () => {}},
{
vendor: 'resemble',
credentials,
language: 'en-US',
voice: '3f5fb9f1',
text: 'Hi there and welcome to jambones!',
renderForCaching: true
}
);
} catch (err) {
logger.info({err}, 'synth resemble returned error');
throw err;
}
};
const testDeepgramTTS = async(logger, synthAudio, credentials) => {
try {
await synthAudio({increment: () => {}, histogram: () => {}},
@@ -667,6 +686,10 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
obj.deepgram_tts_uri = o.deepgram_tts_uri;
obj.model_id = o.model_id;
}
else if ('deepgramriver' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
}
else if ('ibm' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.tts_api_key = isObscureKey ? obscureKey(o.tts_api_key) : o.tts_api_key;
@@ -725,6 +748,11 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.service_version = o.service_version;
} else if ('resemble' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.resemble_tts_uri = o.resemble_tts_uri;
obj.resemble_tts_use_tls = o.resemble_tts_use_tls;
} else if ('voxist' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
@@ -795,6 +823,8 @@ async function getLanguagesAndVoicesForVendor(logger, vendor, credential, getTts
return await getLanguagesVoicesForRimelabs(credential, getTtsVoices, logger);
case 'inworld':
return await getLanguagesVoicesForInworld(credential, getTtsVoices, logger);
case 'resemble':
return await getLanguagesAndVoicesForResemble(credential, getTtsVoices, logger);
case 'assemblyai':
return await getLanguagesVoicesForAssemblyAI(credential, getTtsVoices, logger);
case 'voxist':
@@ -905,8 +935,30 @@ async function getLanguagesVoicesForDeepgram(credential, getTtsVoices, logger) {
logger.error({response}, 'Error fetching Deepgram voices');
throw new Error('failed to list voices');
}
const {stt, tts} = await response.json();
let sttLangs = SttDeepgramLanguagesVoices;
const {stt, tts, languages} = await response.json();
// Helper function to get language name
const getLanguageName = (langCode) => {
if (languages && languages[langCode]) {
return languages[langCode];
}
const existingLang = SttDeepgramLanguagesVoices.find((l) => l.value === langCode);
return existingLang ? existingLang.name : capitalizeFirst(langCode);
};
// Collect unique languages from selected models
const allSttLanguages = new Set();
const modelsToProcess = model_id ?
stt.filter((m) => m.canonical_name === model_id) :
stt;
modelsToProcess.forEach((model) => {
if (model.languages && Array.isArray(model.languages)) {
model.languages.forEach((lang) => allSttLanguages.add(lang));
}
});
// Convert to expected format
const sttLangs = Array.from(allSttLanguages).map((langCode) => ({
name: getLanguageName(langCode),
value: langCode
})).sort((a, b) => a.name.localeCompare(b.name));
const sttModels = Array.from(
new Map(
stt.map((m) => [m.canonical_name, { name: capitalizeFirst(m.canonical_name), value: m.canonical_name }])
@@ -917,16 +969,6 @@ async function getLanguagesVoicesForDeepgram(credential, getTtsVoices, logger) {
tts.map((m) => [m.canonical_name, { name: capitalizeFirst(m.canonical_name), value: m.canonical_name }])
).values()
).sort((a, b) => a.name.localeCompare(b.name));
// if model_id is not provided, return all models, all voices, all languages
if (!model_id) {
return tranform(TtsLanguagesDeepgram, sttLangs, ttsModels, sttModels);
}
const selectedSttModel = stt.find((m) => m.canonical_name === model_id);
const selectedSttLangs = selectedSttModel ? selectedSttModel.languages : [];
sttLangs = SttDeepgramLanguagesVoices.filter((l) => {
return selectedSttLangs.includes(l.value);
});
return tranform(TtsLanguagesDeepgram, sttLangs, ttsModels, sttModels);
}
}
@@ -1236,6 +1278,82 @@ async function getLanguagesVoicesForVerbio(credentials, getTtsVoices, logger) {
}
}
async function getLanguagesAndVoicesForResemble(credential, getTtsVoices, logger) {
if (credential) {
try {
const {api_key} = credential;
let allVoices = [];
let page = 1;
let hasMorePages = true;
// Fetch all pages of voices
while (hasMorePages) {
const response = await fetch(`https://app.resemble.ai/api/v2/voices?page=${page}&page_size=100`, {
headers: {
'Authorization': `Token token=${api_key}`,
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('failed to list voices');
}
const data = await response.json();
if (!data.success) {
throw new Error('API returned unsuccessful response');
}
allVoices = allVoices.concat(data.items);
// Check if there are more pages
hasMorePages = page < data.num_pages;
page++;
}
// Filter only finished voices that support text_to_speech
const availableVoices = allVoices.filter((voice) =>
voice.status === 'finished' &&
voice.component_status?.text_to_speech?.status === 'ready'
);
// Group voices by language
const ttsVoices = availableVoices.reduce((acc, voice) => {
const languageCode = voice.default_language || 'en-US';
const existingLanguage = acc.find((lang) => lang.value === languageCode);
const voiceEntry = {
name: `${voice.name} (${voice.voice_type}) - ${voice.source}`,
value: voice.uuid
};
if (existingLanguage) {
existingLanguage.voices.push(voiceEntry);
} else {
acc.push({
value: languageCode,
name: capitalizeFirst(languageCode),
voices: [voiceEntry]
});
}
return acc;
}, []);
// Sort languages and voices
ttsVoices.sort((a, b) => a.name.localeCompare(b.name));
ttsVoices.forEach((lang) => {
lang.voices.sort((a, b) => a.name.localeCompare(b.name));
});
return tranform(ttsVoices);
} catch (err) {
logger.info('Error while fetching Resemble languages, voices, return predefined values', err);
}
}
return tranform(TtsResembleLanguagesVoices);
}
function tranform(tts, stt, models, sttModels) {
return {
...(tts && {tts}),
@@ -1524,5 +1642,6 @@ module.exports = {
testSpeechmaticsStt,
testCartesia,
testVoxistStt,
testOpenAiStt
testOpenAiStt,
testResembleTTS
};

429
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "jambonz-api-server",
"version": "0.9.4",
"version": "0.9.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jambonz-api-server",
"version": "0.9.4",
"version": "0.9.5",
"license": "MIT",
"dependencies": {
"@aws-sdk/client-cloudwatch-logs": "^3.750.0",
@@ -19,10 +19,10 @@
"@jambonz/db-helpers": "^0.9.12",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.14",
"@jambonz/speech-utils": "^0.2.13",
"@jambonz/realtimedb-helpers": "^0.8.15",
"@jambonz/speech-utils": "^0.2.23",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.106",
"@jambonz/verb-specifications": "^0.0.111",
"@soniox/soniox-node": "^1.2.2",
"ajv": "^8.17.1",
"argon2": "^0.40.1",
@@ -3219,12 +3219,14 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz",
"integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"license": "MIT",
"dependencies": {
"@babel/highlight": "^7.24.2",
"picocolors": "^1.0.0"
"@babel/helper-validator-identifier": "^7.27.1",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
@@ -3429,18 +3431,20 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.24.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz",
"integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz",
"integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==",
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
@@ -3455,102 +3459,28 @@
}
},
"node_modules/@babel/helpers": {
"version": "7.24.4",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.4.tgz",
"integrity": "sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==",
"version": "7.27.6",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz",
"integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.24.0",
"@babel/traverse": "^7.24.1",
"@babel/types": "^7.24.0"
"@babel/template": "^7.27.2",
"@babel/types": "^7.27.6"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.24.2",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz",
"integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==",
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0",
"picocolors": "^1.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/@babel/highlight/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@babel/highlight/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/parser": {
"version": "7.24.4",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz",
"integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==",
"version": "7.28.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz",
"integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/types": "^7.28.0"
},
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -3559,14 +3489,15 @@
}
},
"node_modules/@babel/template": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz",
"integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==",
"version": "7.27.2",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
"integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.23.5",
"@babel/parser": "^7.24.0",
"@babel/types": "^7.24.0"
"@babel/code-frame": "^7.27.1",
"@babel/parser": "^7.27.2",
"@babel/types": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -3603,14 +3534,14 @@
}
},
"node_modules/@babel/types": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz",
"integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==",
"version": "7.28.1",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz",
"integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-string-parser": "^7.23.4",
"@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
"@babel/helper-string-parser": "^7.27.1",
"@babel/helper-validator-identifier": "^7.27.1"
},
"engines": {
"node": ">=6.9.0"
@@ -4145,9 +4076,9 @@
}
},
"node_modules/@jambonz/realtimedb-helpers": {
"version": "0.8.14",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.8.14.tgz",
"integrity": "sha512-KW+5f7Tlmx6WQjD2m1TBD3dEOPGhAhVespgy4OlevoXuLK1XGFSpArFePWmMfrOfrQCnFvpQY8A1njmK9TezSg==",
"version": "0.8.15",
"resolved": "https://registry.npmjs.org/@jambonz/realtimedb-helpers/-/realtimedb-helpers-0.8.15.tgz",
"integrity": "sha512-gGQrUL9eHO+Gx2nSphl6/GwCo1Vui3GfKvjUYrxxicGPFDV+m8UMvWPKfgL9xHa3GIwGGDGeGDj5jJf77/RT9A==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.4",
@@ -4155,9 +4086,9 @@
}
},
"node_modules/@jambonz/speech-utils": {
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.2.13.tgz",
"integrity": "sha512-8ISTWTfz3fWtPmzPDsZG8zgnf6pTjLA1WasMAF/d/ktGswqVsbhoPcDh5ZyZ7BsEqOMLMIv2Hn0ESmrBuMn5kw==",
"version": "0.2.23",
"resolved": "https://registry.npmjs.org/@jambonz/speech-utils/-/speech-utils-0.2.23.tgz",
"integrity": "sha512-o28IBoKzdnQoUUSC1XljHVkDPWhkTH+rFnI9OWYC6p1/f8px++4Y23/JMIAJVbxqKB1CIf531JhTwy4tCnQP7g==",
"license": "MIT",
"dependencies": {
"23": "^0.0.0",
@@ -4171,7 +4102,7 @@
"debug": "^4.3.4",
"form-urlencoded": "^6.1.4",
"google-protobuf": "^3.21.2",
"ibm-watson": "^8.0.0",
"ibm-watson": "^11.0.0",
"microsoft-cognitiveservices-speech-sdk": "1.38.0",
"openai": "^4.98.0",
"undici": "^7.5.0"
@@ -4180,7 +4111,24 @@
"node_modules/@jambonz/speech-utils/node_modules/@types/node": {
"version": "13.13.52",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.52.tgz",
"integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ=="
"integrity": "sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==",
"license": "MIT"
},
"node_modules/@jambonz/speech-utils/node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@jambonz/speech-utils/node_modules/https-proxy-agent": {
"version": "4.0.0",
@@ -4204,10 +4152,46 @@
"node": ">= 6.0.0"
}
},
"node_modules/@jambonz/speech-utils/node_modules/ibm-cloud-sdk-core": {
"version": "5.4.2",
"resolved": "https://registry.npmjs.org/ibm-cloud-sdk-core/-/ibm-cloud-sdk-core-5.4.2.tgz",
"integrity": "sha512-5VFkKYU/vSIWFJTVt392XEdPmiEwUJqhxjn1MRO3lfELyU2FB+yYi8brbmXUgq+D1acHR1fpS7tIJ6IlnrR9Cg==",
"license": "Apache-2.0",
"dependencies": {
"@types/debug": "^4.1.12",
"@types/node": "^18.19.80",
"@types/tough-cookie": "^4.0.0",
"axios": "^1.11.0",
"camelcase": "^6.3.0",
"debug": "^4.3.4",
"dotenv": "^16.4.5",
"extend": "3.0.2",
"file-type": "16.5.4",
"form-data": "^4.0.4",
"isstream": "0.1.2",
"jsonwebtoken": "^9.0.2",
"mime-types": "2.1.35",
"retry-axios": "^2.6.0",
"tough-cookie": "^4.1.3"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@jambonz/speech-utils/node_modules/ibm-cloud-sdk-core/node_modules/@types/node": {
"version": "18.19.122",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.122.tgz",
"integrity": "sha512-yzegtT82dwTNEe/9y+CM8cgb42WrUfMMCg2QqSddzO1J6uPmBD7qKCZ7dOHZP2Yrpm/kb0eqdNMn2MUyEiqBmA==",
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/@jambonz/speech-utils/node_modules/ibm-watson": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/ibm-watson/-/ibm-watson-8.0.0.tgz",
"integrity": "sha512-0vhB1bMZbF6H6AR18LDziaO5+7l8tkTeSeum2/z1gYEy25EatNWh3tHWiFgra923/HST1ZFJWcxloeZTohP9oA==",
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/ibm-watson/-/ibm-watson-11.0.0.tgz",
"integrity": "sha512-4b0v217rdOhMGNYE0vDYrgGt66DuwkCADxVolllrqeB/WNSmF1YBKC5NQAEhGw8b/tNwDBA3MUAOmhay+P0c4g==",
"license": "Apache-2.0",
"dependencies": {
"@types/async": "^3.2.5",
"@types/extend": "^3.0.1",
@@ -4217,12 +4201,12 @@
"async": "^3.2.0",
"camelcase": "^6.2.0",
"extend": "~3.0.2",
"ibm-cloud-sdk-core": "^4.0.3",
"ibm-cloud-sdk-core": "^5.4.0",
"isstream": "~0.1.2",
"websocket": "^1.0.33"
},
"engines": {
"node": ">=16.0.0"
"node": ">=20.0.0"
}
},
"node_modules/@jambonz/speech-utils/node_modules/microsoft-cognitiveservices-speech-sdk": {
@@ -4270,9 +4254,9 @@
}
},
"node_modules/@jambonz/verb-specifications": {
"version": "0.0.106",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.106.tgz",
"integrity": "sha512-xBCGKKW5QC7ItZyeF22esytpG2yIhkGWIvBgTaf97CilQmUdLGo3rWG3i7qnRvU9MPXFCtVCMt/aaMt1Ep6V2g==",
"version": "0.0.111",
"resolved": "https://registry.npmjs.org/@jambonz/verb-specifications/-/verb-specifications-0.0.111.tgz",
"integrity": "sha512-P2lDki5wCHGPPXpPTeMdht4NfGujF6NrLyrI8o/4U6l+2elfc/1mKl/Lz/PWGo9rQM4mTOVIhkk9ECo0NvOVoA==",
"license": "MIT",
"dependencies": {
"debug": "^4.3.4",
@@ -5493,13 +5477,17 @@
}
},
"node_modules/@types/request/node_modules/form-data": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"version": "2.5.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
"integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.35",
"safe-buffer": "^5.2.1"
},
"engines": {
"node": ">= 0.12"
@@ -5874,15 +5862,32 @@
}
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz",
"integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axios/node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -5972,9 +5977,10 @@
"integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="
},
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
"integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"license": "MIT",
"dependencies": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -6144,6 +6150,19 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -6672,6 +6691,20 @@
"ignored": "bin/ignored"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/duplexify": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
@@ -6801,12 +6834,10 @@
}
},
"node_modules/es-define-property": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
"dependencies": {
"get-intrinsic": "^1.2.4"
},
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
@@ -6840,10 +6871,10 @@
}
},
"node_modules/es-object-atoms": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz",
"integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==",
"dev": true,
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
@@ -6852,14 +6883,15 @@
}
},
"node_modules/es-set-tostringtag": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz",
"integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==",
"dev": true,
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"get-intrinsic": "^1.2.4",
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.1"
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
@@ -7697,15 +7729,21 @@
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
"has-symbols": "^1.0.3",
"hasown": "^2.0.0"
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
@@ -7723,6 +7761,19 @@
"node": ">=8.0.0"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/get-symbol-description": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz",
@@ -7845,11 +7896,12 @@
"integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA=="
},
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
"dependencies": {
"get-intrinsic": "^1.1.3"
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
@@ -7926,6 +7978,7 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
"integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
"dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -7934,9 +7987,10 @@
}
},
"node_modules/has-symbols": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
@@ -7948,7 +8002,6 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"dev": true,
"dependencies": {
"has-symbols": "^1.0.3"
},
@@ -8861,7 +8914,8 @@
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "4.1.0",
@@ -9155,6 +9209,15 @@
"semver": "bin/semver.js"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -10020,9 +10083,10 @@
}
},
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
@@ -11325,15 +11389,6 @@
"real-require": "^0.2.0"
}
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "jambonz-api-server",
"version": "0.9.4",
"version": "0.9.5",
"description": "",
"main": "app.js",
"scripts": {
@@ -30,10 +30,10 @@
"@jambonz/db-helpers": "^0.9.12",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.14",
"@jambonz/speech-utils": "^0.2.13",
"@jambonz/realtimedb-helpers": "^0.8.15",
"@jambonz/speech-utils": "^0.2.23",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.106",
"@jambonz/verb-specifications": "^0.0.111",
"@soniox/soniox-node": "^1.2.2",
"ajv": "^8.17.1",
"argon2": "^0.40.1",

View File

@@ -902,6 +902,50 @@ test('speech credentials tests', async(t) => {
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
/* add a credential for resemble */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'resemble',
use_for_tts: true,
use_for_stt: false,
api_key: 'api_key',
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Resemble');
const resembleSid = result.body.sid;
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${resembleSid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for Resemble');
/* add a credential for deepgram river */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'deepgramriver',
use_for_tts: false,
use_for_stt: true,
api_key: 'api_key',
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Verbio');
const deepgramriverSid = result.body.sid;
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${deepgramriverSid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential deepgramriver');
/* Check google supportedLanguagesAndVoices */
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/speech/supportedLanguagesAndVoices?vendor=google`, {
resolveWithFullResponse: true,