Compare commits

..

1 Commits

Author SHA1 Message Date
Quan HL
ae62244904 fix jambones-sql.sql missing FOREIGN_KEY_CHECKS 2023-09-26 06:56:48 +07:00
12 changed files with 444 additions and 702 deletions

3
app.js
View File

@@ -8,7 +8,6 @@ const rateLimit = require('express-rate-limit');
const cors = require('cors');
const passport = require('passport');
const routes = require('./lib/routes');
const Registrar = require('@jambonz/mw-registrar');
assert.ok(process.env.JAMBONES_MYSQL_HOST &&
process.env.JAMBONES_MYSQL_USER &&
@@ -36,7 +35,6 @@ const {
logger, process.env.JAMBONES_TIME_SERIES_HOST
);
const {
client,
retrieveCall,
deleteCall,
listCalls,
@@ -83,7 +81,6 @@ passport.use(authStrategy);
app.locals = app.locals || {};
app.locals = {
...app.locals,
registrar: new Registrar(logger, client),
logger,
retrieveCall,
deleteCall,

View File

@@ -18,14 +18,10 @@ const JAMBONES_REDIS_SENTINELS = process.env.JAMBONES_REDIS_SENTINELS ? {
}),
...(process.env.JAMBONES_REDIS_SENTINEL_USERNAME && {
username: process.env.JAMBONES_REDIS_SENTINEL_USERNAME
}),
...(process.env.JAMBONES_REDIS_SENTINEL_SENTINAL_PASSWORD && {
sentinelPassword: process.env.JAMBONES_REDIS_SENTINEL_SENTINAL_PASSWORD
}),
})
} : null;
const {
client,
retrieveCall,
deleteCall,
listCalls,
@@ -43,7 +39,6 @@ const {
}, logger);
module.exports = {
client,
retrieveCall,
deleteCall,
listCalls,

View File

@@ -1,7 +1,6 @@
const Model = require('./model');
const {promisePool} = require('../db');
const sqlRetrieveAll = 'SELECT * from phone_numbers WHERE account_sid = ? ORDER BY number';
const sqlRetrieveOne = 'SELECT * from phone_numbers WHERE phone_number_sid = ? AND account_sid = ? ORDER BY number';
const sql = 'SELECT * from phone_numbers WHERE account_sid = ? ORDER BY number';
const sqlSP = `SELECT *
FROM phone_numbers
WHERE account_sid IN
@@ -18,7 +17,7 @@ class PhoneNumber extends Model {
static async retrieveAll(account_sid) {
if (!account_sid) return await super.retrieveAll();
const [rows] = await promisePool.query(sqlRetrieveAll, account_sid);
const [rows] = await promisePool.query(sql, account_sid);
return rows;
}
static async retrieveAllForSP(service_provider_sid) {
@@ -31,7 +30,7 @@ class PhoneNumber extends Model {
*/
static async retrieve(sid, account_sid) {
if (!account_sid) return super.retrieve(sid);
const [rows] = await promisePool.query(sqlRetrieveOne, [sid, account_sid]);
const [rows] = await promisePool.query(`${sql} AND phone_number_sid = ?`, [account_sid, sid]);
return rows;
}
}

View File

@@ -20,13 +20,12 @@ class SpeechCredential extends Model {
return rows;
}
static async getSpeechCredentialsByVendorAndLabel(service_provider_sid, account_sid, vendor, label) {
static async isAvailableVendorAndLabel(service_provider_sid, account_sid, vendor, label) {
let sql;
if (account_sid) {
sql = `SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ? ${label ? 'AND label = ?' : ''}`;
sql = 'SELECT * FROM speech_credentials WHERE account_sid = ? AND vendor = ? AND label = ?';
} else {
sql = `SELECT * FROM speech_credentials WHERE service_provider_sid = ? AND vendor = ?
${label ? 'AND label = ?' : ''}`;
sql = 'SELECT * FROM speech_credentials WHERE service_provider_sid = ? AND vendor = ? AND label = ?';
}
const [rows] = await promisePool.query(sql, [account_sid ? account_sid : service_provider_sid, vendor, label]);
return rows;

View File

@@ -145,21 +145,6 @@ router.post('/:sid/VoipCarriers', async(req, res) => {
sysError(logger, res, err);
}
});
router.get('/:sid/RegisteredSipUsers', async(req, res) => {
const {logger, registrar} = req.app.locals;
try {
const account_sid = parseAccountSid(req);
await validateRequest(req, account_sid);
const result = await Account.retrieve(account_sid);
if (!result || result.length === 0) {
throw new DbErrorBadRequest(`account not found for sid ${account_sid}`);
}
const users = await registrar.getRegisteredUsersForRealm(result[0].sip_realm);
res.status(200).json(users.map((u) => `${u}@${result[0].sip_realm}`));
} catch (err) {
sysError(logger, res, err);
}
});
function coerceNumbers(callInfo) {
if (Array.isArray(callInfo)) {

View File

@@ -19,10 +19,8 @@ const {
testDeepgramStt,
testSonioxStt,
testIbmTts,
testIbmStt,
testElevenlabs
testIbmStt
} = require('../../utils/speech-utils');
const bent = require('bent');
const {promisePool} = require('../../db');
const validateAdd = async(req) => {
@@ -128,9 +126,7 @@ const encryptCredential = (obj) => {
instance_id,
custom_stt_url,
custom_tts_url,
auth_token = '',
cobalt_server_uri,
model_id
auth_token = ''
} = obj;
switch (vendor) {
@@ -200,17 +196,6 @@ const encryptCredential = (obj) => {
const sonioxData = JSON.stringify({api_key});
return encrypt(sonioxData);
case 'cobalt':
assert(cobalt_server_uri, 'invalid cobalt speech credential: cobalt_server_uri is required');
const cobaltData = JSON.stringify({cobalt_server_uri});
return encrypt(cobaltData);
case 'elevenlabs':
assert(api_key, 'invalid elevenLabs speech credential: api_key is required');
assert(model_id, 'invalid elevenLabs speech credential: model_id is required');
const elevenlabsData = JSON.stringify({api_key, model_id});
return encrypt(elevenlabsData);
default:
if (vendor.startsWith('custom:')) {
const customData = JSON.stringify({auth_token, custom_stt_url, custom_tts_url});
@@ -245,7 +230,7 @@ router.post('/', async(req, res) => {
// Check if vendor and label is already used for account or SP
if (label) {
const existingSpeech = await SpeechCredential.getSpeechCredentialsByVendorAndLabel(
const existingSpeech = await SpeechCredential.isAvailableVendorAndLabel(
service_provider_sid, account_sid, vendor, label);
if (existingSpeech.length > 0) {
throw new DbErrorUnprocessableRequest(`Label ${label} is already in use for another speech credential`);
@@ -268,76 +253,6 @@ router.post('/', async(req, res) => {
}
});
function decryptCredential(obj, credential, logger) {
if ('google' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
const key_header = '-----BEGIN PRIVATE KEY-----\n';
const obscured = {
...o,
private_key: `${key_header}${obscureKey(o.private_key.slice(key_header.length, o.private_key.length))}`
};
obj.service_key = obscured;
}
else if ('aws' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.access_key_id = o.access_key_id;
obj.secret_access_key = obscureKey(o.secret_access_key);
obj.aws_region = o.aws_region;
logger.info({obj, o}, 'retrieving aws speech credential');
}
else if ('microsoft' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
obj.region = o.region;
obj.use_custom_tts = o.use_custom_tts;
obj.custom_tts_endpoint = o.custom_tts_endpoint;
obj.custom_tts_endpoint_url = o.custom_tts_endpoint_url;
obj.use_custom_stt = o.use_custom_stt;
obj.custom_stt_endpoint = o.custom_stt_endpoint;
obj.custom_stt_endpoint_url = o.custom_stt_endpoint_url;
logger.info({obj, o}, 'retrieving azure speech credential');
}
else if ('wellsaid' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if ('nuance' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.client_id = o.client_id;
obj.secret = o.secret ? obscureKey(o.secret) : null;
}
else if ('deepgram' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if ('ibm' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.tts_api_key = obscureKey(o.tts_api_key);
obj.tts_region = o.tts_region;
obj.stt_api_key = obscureKey(o.stt_api_key);
obj.stt_region = o.stt_region;
obj.instance_id = o.instance_id;
} else if ('nvidia' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.riva_server_uri = o.riva_server_uri;
} else if ('cobalt' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.cobalt_server_uri = o.cobalt_server_uri;
} else if ('soniox' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
} else if ('elevenlabs' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
obj.model_id = o.model_id;
} else if (obj.vendor.startsWith('custom:')) {
const o = JSON.parse(decrypt(credential));
obj.auth_token = obscureKey(o.auth_token);
obj.custom_stt_url = o.custom_stt_url;
obj.custom_tts_url = o.custom_tts_url;
}
}
/**
* retrieve all speech credentials for an account
*/
@@ -364,7 +279,68 @@ router.get('/', async(req, res) => {
res.status(200).json(creds.map((c) => {
const {credential, ...obj} = c;
decryptCredential(obj, credential, logger);
if ('google' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
const key_header = '-----BEGIN PRIVATE KEY-----\n';
const obscured = {
...o,
private_key: `${key_header}${obscureKey(o.private_key.slice(key_header.length, o.private_key.length))}`
};
obj.service_key = obscured;
}
else if ('aws' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.access_key_id = o.access_key_id;
obj.secret_access_key = obscureKey(o.secret_access_key);
obj.aws_region = o.aws_region;
logger.info({obj, o}, 'retrieving aws speech credential');
}
else if ('microsoft' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
obj.region = o.region;
obj.use_custom_tts = o.use_custom_tts;
obj.custom_tts_endpoint = o.custom_tts_endpoint;
obj.custom_tts_endpoint_url = o.custom_tts_endpoint_url;
obj.use_custom_stt = o.use_custom_stt;
obj.custom_stt_endpoint = o.custom_stt_endpoint;
obj.custom_stt_endpoint_url = o.custom_stt_endpoint_url;
logger.info({obj, o}, 'retrieving azure speech credential');
}
else if ('wellsaid' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if ('nuance' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.client_id = o.client_id;
obj.secret = o.secret ? obscureKey(o.secret) : null;
}
else if ('deepgram' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if ('ibm' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.tts_api_key = obscureKey(o.tts_api_key);
obj.tts_region = o.tts_region;
obj.stt_api_key = obscureKey(o.stt_api_key);
obj.stt_region = o.stt_region;
obj.instance_id = o.instance_id;
} else if ('nvidia' == obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.riva_server_uri = o.riva_server_uri;
}
else if ('soniox' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if (obj.vendor.startsWith('custom:')) {
const o = JSON.parse(decrypt(credential));
obj.auth_token = obscureKey(o.auth_token);
obj.custom_stt_url = o.custom_stt_url;
obj.custom_tts_url = o.custom_tts_url;
}
if (req.user.hasAccountAuth && obj.account_sid === null) {
delete obj.api_key;
@@ -394,7 +370,68 @@ router.get('/:sid', async(req, res) => {
await validateRetrieveUpdateDelete(req, cred);
const {credential, ...obj} = cred[0];
decryptCredential(obj, credential, logger);
if ('google' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
const key_header = '-----BEGIN PRIVATE KEY-----\n';
const obscured = {
...o,
private_key: `${key_header}${obscureKey(o.private_key.slice(key_header.length, o.private_key.length))}`
};
obj.service_key = JSON.stringify(obscured);
}
else if ('aws' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.access_key_id = o.access_key_id;
obj.secret_access_key = obscureKey(o.secret_access_key);
obj.aws_region = o.aws_region;
}
else if ('microsoft' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
obj.region = o.region;
obj.use_custom_tts = o.use_custom_tts;
obj.custom_tts_endpoint = o.custom_tts_endpoint;
obj.custom_tts_endpoint_url = o.custom_tts_endpoint_url;
obj.use_custom_stt = o.use_custom_stt;
obj.custom_stt_endpoint = o.custom_stt_endpoint;
obj.custom_stt_endpoint_url = o.custom_stt_endpoint_url;
}
else if ('wellsaid' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if ('nuance' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.client_id = o.client_id;
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));
obj.api_key = obscureKey(o.api_key);
}
else if ('ibm' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.tts_api_key = obscureKey(o.tts_api_key);
obj.tts_region = o.tts_region;
obj.stt_api_key = obscureKey(o.stt_api_key);
obj.stt_region = o.stt_region;
obj.instance_id = o.instance_id;
} else if ('nvidia' == obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.riva_server_uri = o.riva_server_uri;
}
else if ('soniox' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = obscureKey(o.api_key);
}
else if (obj.vendor.startsWith('custom:')) {
const o = JSON.parse(decrypt(credential));
obj.auth_token = obscureKey(o.auth_token);
obj.custom_stt_url = o.custom_stt_url;
obj.custom_tts_url = o.custom_tts_url;
}
if (req.user.hasAccountAuth && obj.account_sid === null) {
delete obj.api_key;
@@ -466,8 +503,7 @@ router.put('/:sid', async(req, res) => {
custom_stt_endpoint,
custom_stt_endpoint_url,
custom_stt_url,
custom_tts_url,
cobalt_server_uri,
custom_tts_url
} = req.body;
const newCred = {
@@ -487,8 +523,7 @@ router.put('/:sid', async(req, res) => {
nuance_stt_uri,
nuance_tts_uri,
custom_stt_url,
custom_tts_url,
cobalt_server_uri
custom_tts_url
};
logger.info({o, newCred}, 'updating speech credential with this new credential');
obj.credential = encryptCredential(newCred);
@@ -745,18 +780,6 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.sttTestResult(sid, false);
}
}
} else if (cred.vendor === 'elevenlabs') {
const {api_key, model_id} = credential;
if (cred.use_for_tts) {
try {
await testElevenlabs(logger, {api_key, model_id});
results.tts.status = 'ok';
SpeechCredential.ttsTestResult(sid, true);
} catch (err) {
results.tts = {status: 'fail', reason: err.message};
SpeechCredential.ttsTestResult(sid, false);
}
}
}
res.status(200).json(results);
@@ -766,93 +789,4 @@ router.get('/:sid/test', async(req, res) => {
}
});
/**
* Fetch speech voices and languages
*/
router.post('/voices', async(req, res) => {
const logger = req.app.locals.logger;
const {vendor, label} = req.body;
const account_sid = req.user.account_sid || req.body.account_sid;
const service_provider_sid = req.user.service_provider_sid ||
req.body.service_provider_sid || parseServiceProviderSid(req);
try {
res.status(200).json(await getTtsVoices(vendor, label, service_provider_sid, account_sid));
} catch (err) {
sysError(logger, res, err);
}
});
router.post('/languages', async(req, res) => {
const logger = req.app.locals.logger;
const {vendor, label} = req.body;
const account_sid = req.user.account_sid || req.body.account_sid;
const service_provider_sid = req.user.service_provider_sid ||
req.body.service_provider_sid || parseServiceProviderSid(req);
try {
res.status(200).json(await getTtsLanguages(vendor, label, service_provider_sid, account_sid));
} catch (err) {
sysError(logger, res, err);
}
});
const getTtsVoices = async(vendor, label, service_provider_sid, account_sid) => {
const credentials = await SpeechCredential.getSpeechCredentialsByVendorAndLabel(
service_provider_sid, account_sid, vendor, label);
const tmp = credentials && credentials.length > 0 ? credentials[0] : null;
const cred = tmp ? JSON.parse(decrypt(tmp.credential)) : null;
if (vendor === 'elevenlabs') {
const get = bent('https://api.elevenlabs.io', 'GET', 'json', {
...(cred && {
'xi-api-key' : cred.api_key
})
});
const resp = await get('/v1/voices');
return resp ? resp.voices.map((v) => {
let name = `${v.name}${v.category !== 'premade' ? ` (${v.category})` : ''} -
${v.labels.accent ? ` ${v.labels.accent},` : ''}
${v.labels.description ? ` ${v.labels.description},` : ''}
${v.labels.age ? ` ${v.labels.age},` : ''}
${v.labels.gender ? ` ${v.labels.gender},` : ''}
${v.labels['use case'] ? ` ${v.labels['use case']},` : ''}
`;
const lastIndex = name.lastIndexOf(',');
if (lastIndex !== -1) {
name = name.substring(0, lastIndex);
}
return {
value: v.voice_id,
name
};
}).sort((a, b) => a.name.localeCompare(b.name)) : [];
}
return [];
};
const getTtsLanguages = async(vendor, label, service_provider_sid, account_sid) => {
const credentials = await SpeechCredential.getSpeechCredentialsByVendorAndLabel(
service_provider_sid, account_sid, vendor, label);
const tmp = credentials && credentials.length > 0 ? credentials[0] : null;
const cred = tmp ? JSON.parse(decrypt(tmp.credential)) : null;
if (vendor === 'elevenlabs') {
if (!cred) {
return [];
}
const get = bent('https://api.elevenlabs.io', 'GET', 'json', {
'xi-api-key' : cred.api_key
});
const resp = await get('/v1/models');
if (!resp || resp.length === 0) {
return [];
}
const model = resp.find((m) => m.model_id === cred.model_id);
return model ? model.languages.map((l) => {
return {
value: l.language_id,
name: l.name
};
}).sort((a, b) => a.name.localeCompare(b.name)) : [];
}
};
module.exports = router;

View File

@@ -4164,28 +4164,6 @@ paths:
type: string
length:
type: string
/Accounts/{AccountSid}/RegisteredSipUsers:
parameters:
- name: AccountSid
in: path
required: true
schema:
type: string
format: uuid
get:
tags:
- Accounts
summary: retrieve online sip users for an account
operationId: listQueues
responses:
200:
description: retrieve online sip users for an account
content:
application/json:
schema:
type: array
items:
type: string
/Lcrs:
post:
tags:

View File

@@ -203,29 +203,6 @@ const testWellSaidTts = async(logger, credentials) => {
}
};
const testElevenlabs = async(logger, credentials) => {
const {api_key, model_id} = credentials;
try {
const post = bent('https://api.elevenlabs.io', 'POST', 'buffer', {
'xi-api-key': api_key,
'Accept': 'audio/mpeg',
'Content-Type': 'application/json'
});
const mp3 = await post('/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM', {
text: 'Hello',
model_id,
voice_settings: {
stability: 0.5,
similarity_boost: 0.5
}
});
return mp3;
} catch (err) {
logger.info({err}, 'synthEvenlabs returned error');
throw err;
}
};
const testIbmTts = async(logger, getTtsVoices, credentials) => {
const {tts_api_key, tts_region} = credentials;
const voices = await getTtsVoices({vendor: 'ibm', credentials: {tts_api_key, tts_region}});
@@ -274,6 +251,5 @@ module.exports = {
testDeepgramStt,
testIbmTts,
testIbmStt,
testSonioxStt,
testElevenlabs
testSonioxStt
};

653
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,19 +19,16 @@
"url": "https://github.com/jambonz/jambonz-api-server.git"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.363.0",
"@aws-sdk/client-transcribe": "^3.363.0",
"@azure/storage-blob": "^12.15.0",
"@aws-sdk/client-s3": "^3.363.0",
"@deepgram/sdk": "^1.21.0",
"@google-cloud/speech": "^5.2.0",
"@google-cloud/storage": "^6.12.0",
"@jambonz/db-helpers": "^0.9.0",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.5",
"@jambonz/realtimedb-helpers": "^0.8.7",
"@jambonz/realtimedb-helpers": "^0.8.6",
"@jambonz/speech-utils": "^0.0.15",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.29",
"@jambonz/lamejs": "^1.2.2",
"@soniox/soniox-node": "^1.1.1",
"argon2": "^0.30.3",
"bent": "^7.3.12",
@@ -54,9 +51,11 @@
"stripe": "^8.222.0",
"swagger-ui-express": "^4.4.0",
"uuid": "^8.3.2",
"wav": "^1.0.2",
"yamljs": "^0.3.0",
"ws": "^8.12.1",
"yamljs": "^0.3.0"
"wav": "^1.0.2",
"@google-cloud/storage": "^6.12.0",
"@azure/storage-blob": "^12.15.0"
},
"devDependencies": {
"eslint": "^8.39.0",

View File

@@ -12,7 +12,6 @@ process.on('unhandledRejection', (reason, p) => {
test('client test', async(t) => {
const app = require('../app');
const {registrar} = app.locals;
try {
let result;
@@ -36,7 +35,6 @@ test('client test', async(t) => {
body: {
name: 'sample_account',
service_provider_sid: sp_sid,
sip_realm: 'drachtio.org',
registration_hook: {
url: 'http://example.com/reg',
method: 'get'
@@ -62,26 +60,6 @@ test('client test', async(t) => {
t.ok(result.statusCode === 201, 'successfully created Client');
const sid = result.body.sid;
/* register the client */
const r = await registrar.add(
"dhorton@drachtio.org",
{
contact: "10.10.1.1",
sbcAddress: "192.168.1.1",
protocol: "udp",
},
5
);
t.ok(r, 'successfully registered Client');
/* query all registered clients */
result = await request.get(`/Accounts/${account_sid}/RegisteredSipUsers`, {
auth: authAdmin,
json: true,
});
t.ok(result.length === 1 && result[0] === 'dhorton@drachtio.org',
'successfully queried all registered clients');
/* query all entity */
result = await request.get('/Clients', {
auth: authAdmin,

View File

@@ -447,51 +447,6 @@ test('speech credentials tests', async(t) => {
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
/* add a credential for cobalt */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'cobalt',
use_for_stt: true,
use_for_tts: false,
cobalt_server_uri: '32.32.32.32:2727',
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Cobalt');
const cobalt_sid = result.body.sid;
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${cobalt_sid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for Cobalt');
/* add a credential for elevenlabs */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'elevenlabs',
use_for_stt: true,
use_for_tts: false,
api_key: 'asdasdasdasddsadasda',
model_id: 'eleven_multilingual_v2'
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Cobalt');
const elevenlabs_sid = result.body.sid;
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${elevenlabs_sid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for Cobalt');
await deleteObjectBySid(request, '/Accounts', account_sid);
await deleteObjectBySid(request, '/ServiceProviders', service_provider_sid);
t.end();