Compare commits

...

19 Commits

Author SHA1 Message Date
Hoan Luu Huu
e2c1383723 support mod_playht_tts (#304)
* support mod_playht_tts

* wip

* wip

* wip

* wip

* wip

* update speech utils version
2024-04-08 10:21:29 -04:00
Dave Horton
40de2c5945 option_ping was incorrectly removed, adding back (#305) 2024-04-08 08:56:31 -04:00
Dave Horton
3a299bc3ca update to speech utils with azure 1.36.0 (#303) 2024-04-07 17:45:33 -04:00
Dave Horton
70c9407742 update to speech utils with azure 1.36.0 2024-04-07 12:16:55 -04:00
Dave Horton
dba66d58fc back out column addition of -register_use_tls 2024-04-06 13:48:26 -04:00
Dave Horton
0ff3d22faf Revert "feat send options ping for sip gateway (#273)"
This reverts commit a4792a521f.
2024-04-06 13:27:32 -04:00
Hoan Luu Huu
187a428a75 register use tls (#302) 2024-04-04 08:02:29 -04:00
Hoan Luu Huu
a4792a521f feat send options ping for sip gateway (#273)
* feat send options ping for sip gateway

* update upgrade db script to have 8006
2024-03-30 09:14:29 -04:00
Dave Horton
3ac9693735 update speech-utils with fixes for deepgram production api and tts streaming 2024-03-24 08:15:00 -04:00
Dave Horton
3ad54a0e72 update to released deepgram tts voices (#299) 2024-03-13 09:16:12 -04:00
Hoan Luu Huu
bd8fb2f9db remove use_streaming from speech credential (#294)
* remove use_streaming from speech credential

* wip
2024-02-20 08:01:33 -05:00
Dave Horton
32b317ae68 update to latest speech-utils 2024-02-12 21:11:49 -05:00
Hoan Luu Huu
40e8d08727 support deepgram tts onprem (#292)
* support deepgram tts onprem

* wip

* wip

* deepgram disable speech test if api_key is missng
2024-02-12 09:27:13 -05:00
Hoan Luu Huu
256ca440a0 add use_streaming flag for elevenlabs and whisper (#290)
* add use_streaming flag for elevenlabs (not for whisper yet)
---------

Co-authored-by: Dave Horton <daveh@beachdognet.com>
2024-02-12 09:18:49 -05:00
Markus Frindt
68d73345ef Improve Swagger file, add login route, fix swagger linting (#291)
Co-authored-by: Markus Frindt <m.frindt@cognigy.com>
2024-02-06 12:10:31 -05:00
Hoan Luu Huu
54dd72ff66 fetch list of tts voices from provider (#289)
* fetch list of tts voices from provider

* revert serve-integration

* fix

* fix for aws

* fix for aws

* fix for aws

* update speech-utils version
2024-01-25 12:03:02 -05:00
Dave Horton
832a4e8032 update db-helpers 2024-01-17 13:24:51 -05:00
Hoan Luu Huu
33c3b99e2e update paid account to active if it's in deactivated (#287)
* update paid account to active if it's in deactivated

* fix review comment
2024-01-17 09:20:38 -05:00
Hoan Luu Huu
8b2a2e196e Feat/record upload buffer (#285)
* uploader with buffer for google and azure

* wip

* wip

* wip
2024-01-15 09:51:10 -05:00
17 changed files with 3538 additions and 13140 deletions

View File

@@ -162,7 +162,7 @@ regex VARCHAR(32) NOT NULL COMMENT 'regex-based pattern match against dialed num
description VARCHAR(1024),
priority INTEGER NOT NULL COMMENT 'lower priority routes are attempted first',
PRIMARY KEY (lcr_route_sid)
) COMMENT='An ordered list of digit patterns in an LCR table. The pat';
) COMMENT='An ordered list of digit patterns in an LCR table. The patterns are tested in sequence until one matches';
CREATE TABLE lcr
(
@@ -173,7 +173,7 @@ default_carrier_set_entry_sid CHAR(36) COMMENT 'default carrier/route to use whe
service_provider_sid CHAR(36),
account_sid CHAR(36),
PRIMARY KEY (lcr_sid)
) COMMENT='An LCR (least cost routing) table that is used by a service ';
) COMMENT='An LCR (least cost routing) table that is used by a service provider or account to make decisions about routing outbound calls when multiple carriers are available.';
CREATE TABLE password_settings
(
@@ -458,6 +458,7 @@ inbound BOOLEAN NOT NULL COMMENT 'if true, whitelist this IP to allow inbound ca
outbound BOOLEAN NOT NULL COMMENT 'if true, include in least-cost routing when placing calls to the PSTN',
voip_carrier_sid CHAR(36) NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT 1,
send_options_ping BOOLEAN NOT NULL DEFAULT 0,
pad_crypto BOOLEAN NOT NULL DEFAULT 0,
protocol ENUM('udp','tcp','tls', 'tls/srtp') DEFAULT 'udp' COMMENT 'Outbound call protocol',
PRIMARY KEY (sip_gateway_sid)
@@ -495,7 +496,7 @@ messaging_hook_sid CHAR(36) COMMENT 'webhook to call for inbound SMS/MMS ',
app_json TEXT,
speech_synthesis_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
speech_synthesis_language VARCHAR(12) NOT NULL DEFAULT 'en-US',
speech_synthesis_voice VARCHAR(64),
speech_synthesis_voice VARCHAR(256),
speech_synthesis_label VARCHAR(64),
speech_recognizer_vendor VARCHAR(64) NOT NULL DEFAULT 'google',
speech_recognizer_language VARCHAR(64) NOT NULL DEFAULT 'en-US',
@@ -503,7 +504,7 @@ speech_recognizer_label VARCHAR(64),
use_for_fallback_speech BOOLEAN DEFAULT false,
fallback_speech_synthesis_vendor VARCHAR(64),
fallback_speech_synthesis_language VARCHAR(12),
fallback_speech_synthesis_voice VARCHAR(64),
fallback_speech_synthesis_voice VARCHAR(256),
fallback_speech_synthesis_label VARCHAR(64),
fallback_speech_recognizer_vendor VARCHAR(64),
fallback_speech_recognizer_language VARCHAR(64),

View File

@@ -551,7 +551,7 @@
</location>
<size>
<width>293.00</width>
<height>540.00</height>
<height>560.00</height>
</size>
<zorder>6</zorder>
<SQLField>
@@ -2332,7 +2332,7 @@
</location>
<size>
<width>281.00</width>
<height>240.00</height>
<height>260.00</height>
</size>
<zorder>7</zorder>
<SQLField>
@@ -2399,10 +2399,18 @@
<notNull><![CDATA[1]]></notNull>
<uid><![CDATA[27D4A5BD-8093-4ADD-B5B5-D546844206F9]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[send_options_ping]]></name>
<type><![CDATA[BOOLEAN]]></type>
<defaultValue><![CDATA[0]]></defaultValue>
<notNull><![CDATA[1]]></notNull>
<uid><![CDATA[E04C19A2-12BF-443F-AB61-96990224A18D]]></uid>
</SQLField>
<SQLField>
<name><![CDATA[pad_crypto]]></name>
<type><![CDATA[BOOLEAN]]></type>
<defaultValue><![CDATA[0]]></defaultValue>
<forcedUnique><![CDATA[0]]></forcedUnique>
<notNull><![CDATA[1]]></notNull>
<uid><![CDATA[C5C0043B-100A-4476-BF01-BE0777AE27C0]]></uid>
</SQLField>
@@ -2560,7 +2568,7 @@
</SQLField>
<SQLField>
<name><![CDATA[speech_synthesis_voice]]></name>
<type><![CDATA[VARCHAR(64)]]></type>
<type><![CDATA[VARCHAR(256)]]></type>
<notNull><![CDATA[0]]></notNull>
<uid><![CDATA[929D66F0-64B9-4D7C-AB4B-24F131E1178F]]></uid>
</SQLField>
@@ -2610,7 +2618,7 @@
</SQLField>
<SQLField>
<name><![CDATA[fallback_speech_synthesis_voice]]></name>
<type><![CDATA[VARCHAR(64)]]></type>
<type><![CDATA[VARCHAR(256)]]></type>
<notNull><![CDATA[0]]></notNull>
<uid><![CDATA[6A0E92C9-32B9-4179-A893-3DADF5DD7728]]></uid>
</SQLField>
@@ -3097,17 +3105,17 @@
<overviewPanelHidden><![CDATA[0]]></overviewPanelHidden>
<pageBoundariesVisible><![CDATA[0]]></pageBoundariesVisible>
<PageGridVisible><![CDATA[0]]></PageGridVisible>
<RightSidebarWidth><![CDATA[1924.000000]]></RightSidebarWidth>
<RightSidebarWidth><![CDATA[1235.000000]]></RightSidebarWidth>
<sidebarIndex><![CDATA[2]]></sidebarIndex>
<snapToGrid><![CDATA[0]]></snapToGrid>
<SourceSidebarWidth><![CDATA[0.000000]]></SourceSidebarWidth>
<SourceSidebarWidth><![CDATA[312.000000]]></SourceSidebarWidth>
<SQLEditorFileFormatVersion><![CDATA[4]]></SQLEditorFileFormatVersion>
<uid><![CDATA[58C99A00-06C9-478C-A667-C63842E088F3]]></uid>
<windowHeight><![CDATA[985.000000]]></windowHeight>
<windowLocationX><![CDATA[-1307.000000]]></windowLocationX>
<windowLocationY><![CDATA[1008.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{1.5, 786}]]></windowScrollOrigin>
<windowWidth><![CDATA[2201.000000]]></windowWidth>
<windowHeight><![CDATA[870.000000]]></windowHeight>
<windowLocationX><![CDATA[-1164.000000]]></windowLocationX>
<windowLocationY><![CDATA[1131.000000]]></windowLocationY>
<windowScrollOrigin><![CDATA[{0.5, 0}]]></windowScrollOrigin>
<windowWidth><![CDATA[1512.000000]]></windowWidth>
</SQLDocumentInfo>
<AllowsIndexRenamingOnInsert><![CDATA[1]]></AllowsIndexRenamingOnInsert>
<defaultLabelExpanded><![CDATA[1]]></defaultLabelExpanded>

View File

@@ -190,7 +190,12 @@ const sql = {
'ALTER TABLE google_custom_voices ADD FOREIGN KEY speech_credential_sid_idxfk (speech_credential_sid) REFERENCES speech_credentials (speech_credential_sid) ON DELETE CASCADE',
'ALTER TABLE clients ADD COLUMN allow_direct_queue_calling BOOLEAN NOT NULL DEFAULT 1',
'ALTER TABLE clients ADD COLUMN allow_direct_user_calling BOOLEAN NOT NULL DEFAULT 1',
'ALTER TABLE clients ADD COLUMN allow_direct_app_calling BOOLEAN NOT NULL DEFAULT 1'
'ALTER TABLE clients ADD COLUMN allow_direct_app_calling BOOLEAN NOT NULL DEFAULT 1',
],
9000: [
'ALTER TABLE sip_gateways ADD COLUMN send_options_ping BOOLEAN NOT NULL DEFAULT 0',
'ALTER TABLE applications MODIFY COLUMN speech_synthesis_voice VARCHAR(256)',
'ALTER TABLE applications MODIFY COLUMN fallback_speech_synthesis_voice VARCHAR(256)',
]
};
@@ -223,6 +228,7 @@ const doIt = async() => {
if (val < 8003) upgrades.push(...sql['8003']);
if (val < 8004) upgrades.push(...sql['8004']);
if (val < 8005) upgrades.push(...sql['8005']);
if (val < 9000) upgrades.push(...sql['9000']);
// perform all upgrades
logger.info({upgrades}, 'applying schema upgrades..');

View File

@@ -199,8 +199,9 @@ class Account extends Model {
debug(r3, 'Account.activateSubscription - replaced old subscription');
/* update account.plan to paid, if it isnt already */
/* update account.is_active to 1, if account is deactivated */
await promisePool.execute(
'UPDATE accounts SET plan_type = \'paid\' WHERE account_sid = ?',
'UPDATE accounts SET plan_type = \'paid\', is_active = 1 WHERE account_sid = ?',
[account_sid]);
return true;
}

View File

@@ -61,6 +61,10 @@ VoipCarrier.fields = [
name: 'requires_register',
type: 'number'
},
{
name: 'register_use_tls',
type: 'number'
},
{
name: 'register_username',
type: 'string'

View File

@@ -1,28 +1,58 @@
const { Writable } = require('stream');
const { BlobServiceClient } = require('@azure/storage-blob');
const { v4: uuidv4 } = require('uuid');
const streamBuffers = require('stream-buffers');
class AzureStorageUploadStream extends Writable {
constructor(logger, opts) {
super(opts);
const blobServiceClient = BlobServiceClient.fromConnectionString(opts.connection_string);
this.blockBlobClient = blobServiceClient.getContainerClient(opts.bucketName).getBlockBlobClient(opts.Key);
this.metadata = opts.metadata;
this.blocks = [];
this.bufferSize = 2 * 1024 * 1024; // Buffer size set to 2MB
this.buffer = new streamBuffers.WritableStreamBuffer({
initialSize: this.bufferSize,
incrementAmount: this.bufferSize
});
}
async _write(chunk, encoding, callback) {
const blockID = uuidv4().replace(/-/g, '');
this.blocks.push(blockID);
try {
await this.blockBlobClient.stageBlock(blockID, chunk, chunk.length);
this.buffer.write(chunk, encoding);
if (this.buffer.size() >= this.bufferSize) {
const blockID = uuidv4().replace(/-/g, '');
this.blocks.push(blockID);
try {
const dataToWrite = this.buffer.getContents();
await this.blockBlobClient.stageBlock(blockID, dataToWrite, dataToWrite.length);
callback();
} catch (error) {
callback(error);
}
} else {
callback();
} catch (error) {
callback(error);
}
}
async _final(callback) {
// Write any remaining data in buffer
if (this.buffer.size() > 0) {
const remainingData = this.buffer.getContents();
const blockID = uuidv4().replace(/-/g, '');
this.blocks.push(blockID);
try {
await this.blockBlobClient.stageBlock(blockID, remainingData, remainingData.length);
} catch (error) {
callback(error);
return;
}
}
try {
await this.blockBlobClient.commitBlockList(this.blocks);
// remove all null/undefined props

View File

@@ -1,5 +1,6 @@
const { Storage } = require('@google-cloud/storage');
const { Writable } = require('stream');
const streamBuffers = require('stream-buffers');
class GoogleStorageUploadStream extends Writable {
@@ -12,18 +13,38 @@ class GoogleStorageUploadStream extends Writable {
this.gcsFile = storage.bucket(opts.bucketName).file(opts.Key);
this.writeStream = this.gcsFile.createWriteStream();
this.bufferSize = 2 * 1024 * 1024; // Buffer size set to 2MB
this.buffer = new streamBuffers.WritableStreamBuffer({
initialSize: this.bufferSize,
incrementAmount: this.bufferSize
});
this.writeStream.on('error', (err) => this.logger.error(err));
this.writeStream.on('finish', () => {
this.logger.info('google storage Upload completed.');
this.logger.info('Google storage Upload completed.');
this._addMetadata();
});
}
_write(chunk, encoding, callback) {
this.writeStream.write(chunk, encoding, callback);
this.buffer.write(chunk, encoding);
// Write to GCS when buffer reaches desired size
if (this.buffer.size() >= this.bufferSize) {
const dataToWrite = this.buffer.getContents();
this.writeStream.write(dataToWrite, callback);
} else {
callback();
}
}
_final(callback) {
// Write any remaining data in the buffer to GCS
if (this.buffer.size() > 0) {
const remainingData = this.buffer.getContents();
this.writeStream.write(remainingData);
}
this.writeStream.end();
this.writeStream.once('finish', callback);
}
@@ -33,7 +54,7 @@ class GoogleStorageUploadStream extends Writable {
await this.gcsFile.setMetadata({metadata: this.metadata});
this.logger.info('Google storage Upload and metadata setting completed.');
} catch (err) {
this.logger.error(err, 'Google storage An error occurred while setting metadata');
this.logger.error(err, 'Google storage An error occurred while setting metadata');
}
}
}

View File

@@ -16,7 +16,7 @@ class S3MultipartUploadStream extends Writable {
this.partNumber = 1;
this.multipartETags = [];
this.buffer = Buffer.alloc(0);
this.minPartSize = 5 * 1024 * 1024; // 5 MB
this.minPartSize = 2 * 1024 * 1024; // 5 MB
this.s3 = new S3Client(opts.bucketCredential);
this.metadata = opts.metadata;
}

View File

@@ -6,7 +6,8 @@ const sysError = require('../error');
const {decrypt, encrypt} = require('../../utils/encrypt-decrypt');
const {parseAccountSid, parseServiceProviderSid, parseSpeechCredentialSid} = require('./utils');
const {decryptCredential, testWhisper, testDeepgramTTS,
getLanguagesAndVoicesForVendor} = require('../../utils/speech-utils');
getLanguagesAndVoicesForVendor,
testPlayHT} = require('../../utils/speech-utils');
const {DbErrorUnprocessableRequest, DbErrorForbidden, DbErrorBadRequest} = require('../../utils/errors');
const {
testGoogleTts,
@@ -116,6 +117,8 @@ const encryptCredential = (obj) => {
secret,
nuance_tts_uri,
nuance_stt_uri,
deepgram_stt_uri,
deepgram_stt_use_tls,
use_custom_tts,
custom_tts_endpoint,
custom_tts_endpoint_url,
@@ -133,6 +136,8 @@ const encryptCredential = (obj) => {
auth_token = '',
cobalt_server_uri,
model_id,
user_id,
voice_engine,
options
} = obj;
@@ -185,8 +190,11 @@ const encryptCredential = (obj) => {
return encrypt(nuanceData);
case 'deepgram':
assert(api_key, 'invalid deepgram speech credential: api_key is required');
const deepgramData = JSON.stringify({api_key});
// API key is optional if onprem
if (!deepgram_stt_uri) {
assert(api_key, 'invalid deepgram speech credential: api_key is required');
}
const deepgramData = JSON.stringify({api_key, deepgram_stt_uri, deepgram_stt_use_tls});
return encrypt(deepgramData);
case 'ibm':
@@ -214,6 +222,13 @@ const encryptCredential = (obj) => {
const elevenlabsData = JSON.stringify({api_key, model_id, options});
return encrypt(elevenlabsData);
case 'playht':
assert(api_key, 'invalid playht speech credential: api_key is required');
assert(user_id, 'invalid playht speech credential: user_id is required');
assert(voice_engine, 'invalid voice_engine speech credential: voice_engine is required');
const playhtData = JSON.stringify({api_key, user_id, voice_engine, options});
return encrypt(playhtData);
case 'assemblyai':
assert(api_key, 'invalid assemblyai speech credential: api_key is required');
const assemblyaiData = JSON.stringify({api_key});
@@ -413,7 +428,10 @@ router.put('/:sid', async(req, res) => {
custom_tts_url,
cobalt_server_uri,
model_id,
options
voice_engine,
options,
deepgram_stt_uri,
deepgram_stt_use_tls,
} = req.body;
const newCred = {
@@ -436,7 +454,10 @@ router.put('/:sid', async(req, res) => {
custom_tts_url,
cobalt_server_uri,
model_id,
options
voice_engine,
options,
deepgram_stt_uri,
deepgram_stt_use_tls,
};
logger.info({o, newCred}, 'updating speech credential with this new credential');
obj.credential = encryptCredential(newCred);
@@ -647,7 +668,7 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.ttsTestResult(sid, false);
}
}
if (cred.use_for_stt) {
if (cred.use_for_stt && api_key) {
try {
await testDeepgramStt(logger, {api_key});
results.stt.status = 'ok';
@@ -715,6 +736,17 @@ router.get('/:sid/test', async(req, res) => {
SpeechCredential.ttsTestResult(sid, false);
}
}
} else if (cred.vendor === 'playht') {
if (cred.use_for_tts) {
try {
await testPlayHT(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 === 'assemblyai') {
const {api_key} = credential;
if (cred.use_for_stt) {
@@ -752,7 +784,7 @@ router.get('/:sid/test', async(req, res) => {
*/
router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
const logger = req.app.locals.logger;
const {logger, getTtsVoices} = req.app.locals;
try {
const {vendor, label} = req.query;
if (!vendor) {
@@ -767,7 +799,7 @@ router.get('/speech/supportedLanguagesAndVoices', async(req, res) => {
const tmp = credentials && credentials.length > 0 ? credentials[0] : null;
const cred = tmp ? JSON.parse(decrypt(tmp.credential)) : null;
try {
const data = await getLanguagesAndVoicesForVendor(logger, vendor, cred);
const data = await getLanguagesAndVoicesForVendor(logger, vendor, cred, getTtsVoices);
res.status(200).json(data);
} catch (err) {
throw new DbErrorUnprocessableRequest(err.message);

View File

@@ -382,11 +382,35 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
/login:
post:
tags:
- Authentication
summary: login and retrieve a JWT
operationId: login
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/Login'
responses:
200:
description: user logged in
content:
application/json:
schema:
$ref: '#/components/schemas/SuccessfulLogin'
500:
description: system error
content:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
/logout:
post:
tags:
- Authentication
summary: log out and deactivate jwt
summary: log out and deactivate the JWT
operationId: logoutUser
responses:
204:
@@ -584,10 +608,9 @@ paths:
content:
application/json:
schema:
type:
array
type: array
items:
$ref: '#/components/schemas/Users'
$ref: '#/components/schemas/UserList'
403:
description: unauthorized
500:
@@ -610,27 +633,13 @@ paths:
- Users
summary: retrieve user information
operationId: getUser
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
email:
type: string
is_active:
type: boolean
force_change:
type: boolean
scope:
type: string
permissions:
type: array
responses:
204:
200:
description: user information
content:
application/json:
schema:
$ref: '#/components/schemas/UserProfile'
403:
description: user information
content:
@@ -674,6 +683,8 @@ paths:
type: string
permissions:
type: array
items:
type: string
responses:
204:
description: user updated
@@ -712,6 +723,8 @@ paths:
type: string
permissions:
type: array
items:
type: string
old_password:
type: string
description: existing password, which is to be replaced
@@ -998,7 +1011,7 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/GeneralError'
/AccountTest/:ServiceProviderSid:
/AccountTest/{ServiceProviderSid}:
parameters:
- name: ServiceProviderSid
in: path
@@ -1095,6 +1108,9 @@ paths:
requires_register:
type: boolean
description: wehther this provider requires us to send a REGISTER to them in order to receive calls
register_use_tls:
type: boolean
description: wehther this provider requires us to send a REGISTER use TLS protocol
register_username:
type: string
description: sip username to authenticate with, if registration is required
@@ -1971,7 +1987,7 @@ paths:
tags:
- Service Providers
summary: add a VoiPCarrier to a service provider based on PredefinedCarrier template
operationId: createVoipCarrierFromTemplate
operationId: createVoipCarrierFromTemplateBySP
responses:
201:
description: voip carrier successfully created
@@ -2079,6 +2095,12 @@ paths:
summary: get supported languages, voices and models
operationId: supportedLanguagesAndVoices
parameters:
- name: ServiceProviderSid
in: path
required: true
schema:
type: string
format: uuid
- name: vendor
in: query
required: true
@@ -2920,7 +2942,7 @@ paths:
tags:
- Accounts
summary: get a specific speech credential
operationId: getSpeechCredential
operationId: getSpeechCredentialByAccount
responses:
200:
description: retrieve speech credentials for a specified account
@@ -2934,7 +2956,7 @@ paths:
tags:
- Accounts
summary: update a speech credential
operationId: updateSpeechCredential
operationId: updateSpeechCredentialByAccount
requestBody:
content:
application/json:
@@ -2955,7 +2977,7 @@ paths:
tags:
- Accounts
summary: delete a speech credential
operationId: deleteSpeechCredential
operationId: deleteSpeechCredentialByAccount
responses:
204:
description: credential successfully deleted
@@ -2966,8 +2988,14 @@ paths:
tags:
- Accounts
summary: get supported languages, voices and models
operationId: supportedLanguagesAndVoices
operationId: supportedLanguagesAndVoicesByAccount
parameters:
- name: AccountSid
in: path
required: true
schema:
type: string
format: uuid
- name: vendor
in: query
required: true
@@ -2995,7 +3023,7 @@ paths:
tags:
- Accounts
summary: test a speech credential
operationId: testSpeechCredential
operationId: testSpeechCredentialByAccount
parameters:
- name: AccountSid
in: path
@@ -3241,7 +3269,7 @@ paths:
tags:
- Service Providers
summary: retrieve pcap for a call
operationId: getRecentCallTrace
operationId: getRecentCallTraceBySP
responses:
200:
description: retrieve sip trace data
@@ -3327,7 +3355,7 @@ paths:
tags:
- Service Providers
summary: retrieve recent calls for an account
operationId: listRecentCalls
operationId: listRecentCallsBySP
responses:
200:
description: retrieve recent call records for a specified account
@@ -3428,7 +3456,7 @@ paths:
tags:
- Service Providers
summary: retrieve sip trace detail for a call
operationId: getRecentCallTrace
operationId: getRecentCallTraceByCallId
responses:
200:
description: retrieve sip trace data
@@ -3455,7 +3483,7 @@ paths:
tags:
- Accounts
summary: retrieve pcap for a call
operationId: getRecentCallTrace
operationId: getRecentCallTraceByAccount
responses:
200:
description: retrieve sip trace data
@@ -3641,7 +3669,7 @@ paths:
tags:
- Accounts
summary: retrieve alerts for an account
operationId: listAlerts
operationId: listAlertsByAccount
responses:
200:
description: retrieve alerts for a specified account
@@ -4240,7 +4268,7 @@ paths:
tags:
- Accounts
summary: retrieve online sip users for an account
operationId: listQueues
operationId: listRegisteredSipUsers
responses:
200:
description: retrieve online sip users for an account
@@ -4254,7 +4282,7 @@ paths:
tags:
- Accounts
summary: retrieve online sip users for an account by list of sip username
operationId: listRegisteredSipUsers
operationId: listRegisteredSipUsersByUsername
requestBody:
content:
application/json:
@@ -4273,6 +4301,12 @@ paths:
$ref: '#/components/schemas/RegisteredClient'
/Accounts/{AccountSid}/RegisteredSipUsers/{Client}:
parameters:
- name: AccountSid
in: path
required: true
schema:
type: string
format: uuid
- name: Client
in: path
required: true
@@ -4293,6 +4327,13 @@ paths:
schema:
$ref: '#/components/schemas/RegisteredClient'
/Accounts/{AccountSid}/TtsCache/Synthesize:
parameters:
- name: AccountSid
in: path
required: true
schema:
type: string
format: uuid
post:
tags:
- Accounts
@@ -5031,17 +5072,32 @@ components:
scheme: bearer
bearerFormat: token
schemas:
SuccessfulLogin:
type: object
required:
- username
- password
properties:
token:
type: string
user_sid:
type: string
scope:
type: string
force_change:
type: boolean
Login:
type: object
properties:
user_sid:
username:
type: string
api_token:
type: string
change_password:
type: boolean
password:
type: string
required:
- user_sid
- username
- password
SuccessfulApiKeyAdd:
type: object
required:
@@ -6096,8 +6152,23 @@ components:
type: array
items:
$ref: '#/components/schemas/TtsModel'
UserList:
type: object
properties:
name:
type: string
email:
type: string
is_active:
type: boolean
force_change:
type: boolean
scope:
type: string
permissions:
type: array
items:
type: string
security:
- bearerAuth: []

View File

@@ -1,9 +1,14 @@
module.exports = [
{ name: 'Aurora English (US) Female', value: 'alpha-aurora-en-v2' },
{ name: 'Asteria English (US) Female', value: 'alpha-asteria-en-v2' },
{ name: 'Artemis English (UK) Female', value: 'alpha-artemis-en-v3' },
{ name: 'Andromeda English (US) Female', value: 'alpha-andromeda-en-v3' },
{ name: 'Stella English (UK) Female', value: 'alpha-stella-en-v2' },
{ name: 'Orion English (US) Male', value: 'alpha-orion-en-v2' },
{ name: 'Atlas English (US) Male', value: 'alpha-atlas-en-v3' },
{ name: 'Asteria English (US) Female', value: 'aura-asteria-en' },
{ name: 'Luna English (US) Female', value: 'aura-luna-en' },
{ name: 'Stella English (US) Female', value: 'aura-stella-en' },
{ name: 'Stella English (UK) Female', value: 'aura-athena-en' },
{ name: 'Hera English (US) Female', value: 'aura-hera-en' },
{ name: 'Orion English (US) Male', value: 'aura-orion-en' },
{ name: 'Arcas English (US) Male', value: 'aura-arcas-en' },
{ name: 'Perseus English (US) Male', value: 'aura-perseus-en' },
{ name: 'Angus English (Ireland) Male', value: 'aura-angus-en' },
{ name: 'Orpheus English (US) Male', value: 'aura-orpheus-en' },
{ name: 'Helios English (UK) Male', value: 'aura-helios-en' },
{ name: 'Zeus English (US) Male', value: 'aura-zeus-en' },
];

View File

@@ -0,0 +1,6 @@
module.exports = [
{ name: 'PlayHT2.0-turbo', value: 'PlayHT2.0-turbo' },
{ name: 'PlayHT2.0', value: 'PlayHT2.0' },
{ name: 'PlayHT1.0', value: 'PlayHT1.0' },
];

View File

@@ -21,6 +21,7 @@ const TtsWhisperLanguagesVoices = require('./speech-data/tts-whisper');
const TtsModelDeepgram = require('./speech-data/tts-model-deepgram');
const TtsModelElevenLabs = require('./speech-data/tts-model-elevenlabs');
const TtsModelWhisper = require('./speech-data/tts-model-whisper');
const TtsModelPlayHT = require('./speech-data/tts-model-playht');
const SttGoogleLanguagesVoices = require('./speech-data/stt-google');
const SttAwsLanguagesVoices = require('./speech-data/stt-aws');
@@ -240,6 +241,27 @@ const testElevenlabs = async(logger, credentials) => {
}
};
const testPlayHT = async(logger, synthAudio, credentials) => {
try {
await synthAudio(
{
increment: () => {},
histogram: () => {}
},
{
vendor: 'playht',
credentials,
language: 'en-US',
voice: 's3://voice-cloning-zero-shot/d9ff78ba-d016-47f6-b0ef-dd630f59414e/female-cs/manifest.json',
text: 'Hi there and welcome to jambones!'
}
);
} catch (err) {
logger.info({err}, 'synth Playht returned error');
throw err;
}
};
const testWhisper = async(logger, synthAudio, credentials) => {
try {
await synthAudio({increment: () => {}, histogram: () => {}},
@@ -263,9 +285,7 @@ const testDeepgramTTS = async(logger, synthAudio, credentials) => {
{
vendor: 'deepgram',
credentials,
language: 'en-US',
voice: 'alpha-aurora-en-v2',
model: 'alpha-aurora-en-v2',
model: 'aura-asteria-en',
text: 'Hi there and welcome to jambones!'
}
);
@@ -406,6 +426,8 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
else if ('deepgram' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.deepgram_stt_uri = o.deepgram_stt_uri;
obj.deepgram_stt_use_tls = o.deepgram_stt_use_tls;
}
else if ('ibm' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
@@ -428,6 +450,12 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.model_id = o.model_id;
obj.options = o.options;
} else if ('playht' === obj.vendor) {
const o = JSON.parse(decrypt(credential));
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
obj.user_id = o.user_id;
obj.voice_engine = o.voice_engine;
obj.options = o.options;
} else if (obj.vendor.startsWith('custom:')) {
const o = JSON.parse(decrypt(credential));
obj.auth_token = isObscureKey ? obscureKey(o.auth_token) : o.auth_token;
@@ -464,49 +492,92 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
]
}
*/
async function getLanguagesAndVoicesForVendor(logger, vendor, credential) {
async function getLanguagesAndVoicesForVendor(logger, vendor, credential, getTtsVoices) {
switch (vendor) {
case 'google':
return await getLanguagesVoicesForGoogle(credential);
return await getLanguagesVoicesForGoogle(credential, getTtsVoices, logger);
case 'aws':
return await getLanguagesVoicesForAws(credential);
return await getLanguagesVoicesForAws(credential, getTtsVoices, logger);
case 'microsoft':
return await getLanguagesVoicesForMicrosoft(credential);
return await getLanguagesVoicesForMicrosoft(credential, getTtsVoices, logger);
case 'wellsaid':
return await getLanguagesVoicesForWellsaid(credential);
return await getLanguagesVoicesForWellsaid(credential, getTtsVoices, logger);
case 'nuance':
return await getLanguagesVoicesForNuane(credential);
return await getLanguagesVoicesForNuane(credential, getTtsVoices, logger);
case 'deepgram':
return await getLanguagesVoicesForDeepgram(credential);
return await getLanguagesVoicesForDeepgram(credential, getTtsVoices, logger);
case 'ibm':
return await getLanguagesVoicesForIbm(credential);
return await getLanguagesVoicesForIbm(credential, getTtsVoices, logger);
case 'nvidia':
return await getLanguagesVoicesForNvida(credential);
return await getLanguagesVoicesForNvida(credential, getTtsVoices, logger);
case 'cobalt':
return await getLanguagesVoicesForCobalt(credential);
return await getLanguagesVoicesForCobalt(credential, getTtsVoices, logger);
case 'soniox':
return await getLanguagesVoicesForSoniox(credential);
return await getLanguagesVoicesForSoniox(credential, getTtsVoices, logger);
case 'elevenlabs':
return await getLanguagesVoicesForElevenlabs(credential);
return await getLanguagesVoicesForElevenlabs(credential, getTtsVoices, logger);
case 'playht':
return await getLanguagesVoicesForPlayHT(credential, getTtsVoices, logger);
case 'assemblyai':
return await getLanguagesVoicesForAssemblyAI(credential);
return await getLanguagesVoicesForAssemblyAI(credential, getTtsVoices, logger);
case 'whisper':
return await getLanguagesVoicesForWhisper(credential);
return await getLanguagesVoicesForWhisper(credential, getTtsVoices, logger);
default:
logger.info(`invalid vendor ${vendor}, return empty result`);
throw new Error(`Invalid vendor ${vendor}`);
}
}
async function getLanguagesVoicesForGoogle(credential) {
async function getLanguagesVoicesForGoogle(credential, getTtsVoices, logger) {
if (credential) {
try {
const [result] = await getTtsVoices({
vendor: 'google',
credentials: credential
});
const tts = parseGooglelanguagesVoices(result.voices);
return tranform(tts, SttGoogleLanguagesVoices);
} catch (err) {
logger.info('Error while fetching google languages, voices, return predefined values', err);
}
}
return tranform(TtsGoogleLanguagesVoices, SttGoogleLanguagesVoices);
}
async function getLanguagesVoicesForAws(credential) {
async function getLanguagesVoicesForAws(credential, getTtsVoices, logger) {
if (credential) {
try {
const result = await getTtsVoices({
vendor: 'aws',
credentials: {
accessKeyId: credential.access_key_id,
secretAccessKey: credential.secret_access_key,
region: credential.aws_region || process.env.AWS_REGION
}
});
const tts = parseAwsLanguagesVoices(result.Voices);
return tranform(tts, SttAwsLanguagesVoices);
} catch (err) {
logger.info('Error while fetching AWS languages, voices, return predefined values', err);
}
}
return tranform(TtsAwsLanguagesVoices, SttAwsLanguagesVoices);
}
async function getLanguagesVoicesForMicrosoft(credential) {
async function getLanguagesVoicesForMicrosoft(credential, getTtsVoices, logger) {
if (credential) {
try {
const get = bent('https://westus.tts.speech.microsoft.com', 'GET', 'json', {
'Ocp-Apim-Subscription-Key' : credential.api_key
});
const voices = await get('/cognitiveservices/voices/list');
const tts = parseMicrosoftLanguagesVoices(voices);
return tranform(tts, SttMicrosoftLanguagesVoices);
} catch (err) {
logger.info('Error while fetching Microsoft languages, voices, return predefined values', err);
}
}
return tranform(TtsMicrosoftLanguagesVoices, SttMicrosoftLanguagesVoices);
}
@@ -514,7 +585,19 @@ async function getLanguagesVoicesForWellsaid(credential) {
return tranform(TtsWellsaidLanguagesVoices);
}
async function getLanguagesVoicesForNuane(credential) {
async function getLanguagesVoicesForNuane(credential, getTtsVoices, logger) {
if (credential) {
try {
const result = await getTtsVoices({
vendor: 'nuance',
credentials: credential
});
const tts = parseNuanceLanguagesVoices(result.result.voices);
return tranform(tts, SttNuanceLanguagesVoices);
} catch (err) {
logger.info('Error while fetching IBM languages, voices, return predefined values', err);
}
}
return tranform(TtsNuanceLanguagesVoices, SttNuanceLanguagesVoices);
}
@@ -522,7 +605,19 @@ async function getLanguagesVoicesForDeepgram(credential) {
return tranform(undefined, SttDeepgramLanguagesVoices, TtsModelDeepgram);
}
async function getLanguagesVoicesForIbm(credential) {
async function getLanguagesVoicesForIbm(credential, getTtsVoices, logger) {
if (credential) {
try {
const result = await getTtsVoices({
vendor: 'ibm',
credentials: credential
});
const tts = parseIBMLanguagesVoices(result.result.voices);
return tranform(tts, SttIbmLanguagesVoices);
} catch (err) {
logger.info('Error while fetching IBM languages, voices, return predefined values', err);
}
}
return tranform(TtsIbmLanguagesVoices, SttIbmLanguagesVoices);
}
@@ -580,6 +675,49 @@ async function getLanguagesVoicesForElevenlabs(credential) {
}
}
const concat = (a) => {
return a ? ` ${a},` : '';
};
async function getLanguagesVoicesForPlayHT(credential) {
if (credential) {
const get = bent('https://api.play.ht', 'GET', 'json', {
'AUTHORIZATION' : credential.api_key,
'X-USER-ID': credential.user_id,
'Accept': 'application/json'
});
const voices = await get('/api/v2/voices');
const buildVoice = (d) => {
let name = `${d.name} -${concat(d.accent)}${concat(d.age)}${concat(d.gender)}
${concat(d.loudness)}${concat(d.style)}${concat(d.tempo)}${concat(d.texture)}` ;
name = name.endsWith(',') ? name.slice(0, -1) : name;
return {
value: `${d.id}`,
name
};
};
const ttsVoices = voices.reduce((acc, voice) => {
const languageCode = voice.language_code;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push(buildVoice(voice));
} else {
acc.push({
value: voice.language_code,
name: voice.language,
voices: [buildVoice(voice)]
});
}
return acc;
}, []);
return tranform(ttsVoices, undefined, TtsModelPlayHT);
}
return tranform(undefined, undefined, TtsModelPlayHT);
}
async function getLanguagesVoicesForAssemblyAI(credential) {
return tranform(undefined, SttAssemblyaiLanguagesVoices);
}
@@ -596,6 +734,125 @@ function tranform(tts, stt, models) {
};
}
function parseGooglelanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.languageCodes[0];
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.name,
name: `${voice.name.substring(languageCode.length + 1, voice.name.length)} (${voice.ssmlGender})`
});
} else {
acc.push({
value: languageCode,
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
voices: [{
value: voice.name,
name: `${voice.name.substring(languageCode.length + 1, voice.name.length)} (${voice.ssmlGender})`
}]
});
}
return acc;
}, []);
}
function parseIBMLanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.language;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.name,
name: `(${voice.gender}) ${voice.description}`
});
} else {
acc.push({
value: languageCode,
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
voices: [{
value: voice.name,
name: `(${voice.gender}) ${voice.description}`
}]
});
}
return acc;
}, []);
}
function parseAwsLanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.LanguageCode;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.Id,
name: `(${voice.Gender}) ${voice.Name}`
});
} else {
acc.push({
value: languageCode,
name: voice.LanguageName,
voices: [{
value: voice.Id,
name: `(${voice.Gender}) ${voice.Name}`
}]
});
}
return acc;
}, []);
}
function parseNuanceLanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.language;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.name,
name: voice.name,
model: voice.model
});
} else {
acc.push({
value: languageCode,
name: SttGoogleLanguagesVoices.find((lang) => lang.value === languageCode)?.name || languageCode,
voices: [{
value: voice.name,
name: voice.name,
model: voice.model
}]
});
}
return acc;
}, []);
}
function parseMicrosoftLanguagesVoices(data) {
return data.reduce((acc, voice) => {
const languageCode = voice.Locale;
const existingLanguage = acc.find((lang) => lang.value === languageCode);
if (existingLanguage) {
existingLanguage.voices.push({
value: voice.ShortName,
name: `${voice.DisplayName} (${voice.Gender})`,
});
} else {
acc.push({
value: voice.Locale,
name: voice.LocaleName,
voices: [{
value: voice.ShortName,
name: `${voice.DisplayName} (${voice.Gender})`,
}]
});
}
return acc;
}, []);
}
module.exports = {
testGoogleTts,
testGoogleStt,
@@ -612,6 +869,7 @@ module.exports = {
testIbmStt,
testSonioxStt,
testElevenlabs,
testPlayHT,
testAssemblyStt,
testDeepgramTTS,
getSpeechCredential,

15866
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "jambonz-api-server",
"version": "0.8.5",
"version": "0.9.0",
"description": "",
"main": "app.js",
"scripts": {
@@ -19,53 +19,54 @@
"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.550.0",
"@aws-sdk/client-transcribe": "^3.549.0",
"@azure/storage-blob": "^12.17.0",
"@deepgram/sdk": "^1.21.0",
"@google-cloud/speech": "^5.2.0",
"@google-cloud/storage": "^6.12.0",
"@jambonz/db-helpers": "^0.9.1",
"@google-cloud/speech": "^6.5.0",
"@google-cloud/storage": "^7.9.0",
"@jambonz/db-helpers": "^0.9.3",
"@jambonz/lamejs": "^1.2.2",
"@jambonz/mw-registrar": "^0.2.7",
"@jambonz/realtimedb-helpers": "^0.8.7",
"@jambonz/speech-utils": "^0.0.33",
"@jambonz/realtimedb-helpers": "^0.8.8",
"@jambonz/speech-utils": "^0.0.50",
"@jambonz/time-series": "^0.2.8",
"@jambonz/verb-specifications": "^0.0.45",
"@soniox/soniox-node": "^1.1.1",
"argon2": "^0.30.3",
"assemblyai": "^3.0.1",
"@jambonz/verb-specifications": "^0.0.69",
"@soniox/soniox-node": "^1.2.2",
"argon2": "^0.40.1",
"assemblyai": "^4.3.4",
"bent": "^7.3.12",
"cors": "^2.8.5",
"debug": "^4.3.4",
"express": "^4.18.1",
"express-rate-limit": "^6.4.0",
"form-data": "^2.5.1",
"helmet": "^5.1.0",
"ibm-watson": "^7.1.2",
"jsonwebtoken": "^9.0.0",
"mailgun.js": "^9.1.2",
"microsoft-cognitiveservices-speech-sdk": "1.31.0",
"mysql2": "^2.3.3",
"nocache": "3.0.4",
"passport": "^0.6.0",
"express": "^4.19.2",
"express-rate-limit": "^7.2.0",
"form-data": "^4.0.0",
"helmet": "^7.1.0",
"ibm-watson": "^9.0.1",
"jsonwebtoken": "^9.0.2",
"mailgun.js": "^10.2.1",
"microsoft-cognitiveservices-speech-sdk": "1.36.0",
"mysql2": "^3.9.3",
"nocache": "4.0.0",
"passport": "^0.7.0",
"passport-http-bearer": "^1.0.1",
"pino": "^5.17.0",
"short-uuid": "^4.1.0",
"stripe": "^8.222.0",
"swagger-ui-express": "^4.4.0",
"uuid": "^8.3.2",
"pino": "^8.20.0",
"short-uuid": "^4.2.2",
"stream-buffers": "^3.0.2",
"stripe": "^14.24.0",
"swagger-ui-express": "^5.0.0",
"uuid": "^9.0.1",
"wav": "^1.0.2",
"ws": "^8.12.1",
"ws": "^8.16.0",
"yamljs": "^0.3.0"
},
"devDependencies": {
"eslint": "^8.39.0",
"eslint-plugin-promise": "^6.1.1",
"husky": "7.0.4",
"husky": "9.0.11",
"nyc": "^15.1.0",
"request": "^2.88.2",
"request-promise-native": "^1.0.9",
"tape": "^5.5.3"
"tape": "^5.7.5"
}
}

View File

@@ -10,7 +10,7 @@ networks:
services:
mysql:
platform: linux/x86_64
# platform: linux/x86_64
image: mysql:5.7
ports:
- "3360:3306"
@@ -36,7 +36,7 @@ services:
ipv4_address: 172.58.0.3
influxdb:
platform: linux/x86_64
# platform: linux/x86_64
image: influxdb:1.8
ports:
- "8086:8086"

View File

@@ -292,6 +292,60 @@ test('speech credentials tests', async(t) => {
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential');
}
// test create deepgram onprem
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'deepgram',
use_for_stt: true,
deepgram_stt_uri: "127.0.0.1:50002",
deepgram_stt_use_tls: true
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for deepgram');
const dg_sid = result.body.sid;
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
});
//console.log(JSON.stringify(result));
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram');
t.ok(result.body.deepgram_stt_uri === '127.0.0.1:50002', "deepgram_stt_uri is correct for deepgram");
t.ok(result.body.deepgram_stt_use_tls === true, "deepgram_stt_use_tls is correct for deepgram");
result = await request.put(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'deepgram',
use_for_stt: true,
deepgram_stt_uri: "127.0.0.2:50002",
deepgram_stt_use_tls: false
}
});
t.ok(result.statusCode === 204, 'successfully updated speech credential for deepgram onprem');
result = await request.get(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
});
//console.log(JSON.stringify(result));
t.ok(result.statusCode === 200, 'successfully get speech credential for deepgram onprem');
t.ok(result.body.deepgram_stt_uri === '127.0.0.2:50002', "deepgram_stt_uri is correct for deepgram onprem");
t.ok(result.body.deepgram_stt_use_tls === false, "deepgram_stt_use_tls is correct for deepgram onprem");
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${dg_sid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for deepgram onprem');
/* add a credential for ibm tts */
if (process.env.IBM_TTS_API_KEY && process.env.IBM_TTS_REGION) {
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
@@ -482,7 +536,7 @@ test('speech credentials tests', async(t) => {
model_id: 'eleven_multilingual_v2'
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for Cobalt');
t.ok(result.statusCode === 201, 'successfully added speech credential for elevenlabs');
const elevenlabs_sid = result.body.sid;
/* delete the credential */
@@ -490,7 +544,31 @@ test('speech credentials tests', async(t) => {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for Cobalt');
t.ok(result.statusCode === 204, 'successfully deleted speech credential for elevenlabs');
/* add a credential for playht */
result = await request.post(`/Accounts/${account_sid}/SpeechCredentials`, {
resolveWithFullResponse: true,
auth: authUser,
json: true,
body: {
vendor: 'playht',
use_for_stt: false,
use_for_tts: true,
api_key: 'asdasdasdasddsadasda',
user_id: 'user_id',
voice_engine: 'PlayHT2.0-turbo'
}
});
t.ok(result.statusCode === 201, 'successfully added speech credential for playht');
const playht_sid = result.body.sid;
/* delete the credential */
result = await request.delete(`/Accounts/${account_sid}/SpeechCredentials/${playht_sid}`, {
auth: authUser,
resolveWithFullResponse: true,
});
t.ok(result.statusCode === 204, 'successfully deleted speech credential for playht');
/* add a credential for custom voices google */