mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-07-23 04:21:56 +00:00
support elevenlabs stt
This commit is contained in:
@@ -18,7 +18,8 @@ const {decryptCredential, testWhisper, testDeepgramTTS,
|
||||
testInworld,
|
||||
testResembleTTS,
|
||||
testHoundifyStt,
|
||||
testGladiaStt} = require('../../utils/speech-utils');
|
||||
testGladiaStt,
|
||||
testElevenlabsStt} = require('../../utils/speech-utils');
|
||||
const {DbErrorUnprocessableRequest, DbErrorForbidden, DbErrorBadRequest} = require('../../utils/errors');
|
||||
const {
|
||||
testGoogleTts,
|
||||
@@ -273,9 +274,11 @@ const encryptCredential = (obj) => {
|
||||
case 'elevenlabs':
|
||||
assert(api_key, 'invalid elevenLabs speech credential: api_key is required');
|
||||
assert(model_id, 'invalid elevenLabs speech credential: model_id is required');
|
||||
assert(stt_model_id, 'invalid elevenLabs speech credential: stt_model_id is required');
|
||||
const elevenlabsData = JSON.stringify({
|
||||
api_key,
|
||||
model_id,
|
||||
stt_model_id,
|
||||
...(api_uri && {api_uri}),
|
||||
options});
|
||||
return encrypt(elevenlabsData);
|
||||
@@ -909,6 +912,16 @@ router.get('/:sid/test', async(req, res) => {
|
||||
SpeechCredential.ttsTestResult(sid, false);
|
||||
}
|
||||
}
|
||||
if (cred.use_for_stt && api_key) {
|
||||
try {
|
||||
await testElevenlabsStt(logger, credential);
|
||||
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 === 'speechmatics') {
|
||||
const {api_key} = credential;
|
||||
if (cred.use_for_stt) {
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
module.exports = [
|
||||
// Major languages with ISO 639-1 codes
|
||||
{ name: 'Arabic', value: 'ar' },
|
||||
{ name: 'Bengali', value: 'bn' },
|
||||
{ name: 'Bulgarian', value: 'bg' },
|
||||
{ name: 'Catalan', value: 'ca' },
|
||||
{ name: 'Chinese (Simplified)', value: 'zh' },
|
||||
{ name: 'Chinese (Traditional)', value: 'zh-TW' },
|
||||
{ name: 'Croatian', value: 'hr' },
|
||||
{ name: 'Czech', value: 'cs' },
|
||||
{ name: 'Danish', value: 'da' },
|
||||
{ name: 'Dutch', value: 'nl' },
|
||||
{ name: 'English', value: 'en' },
|
||||
{ name: 'English (US)', value: 'en-US' },
|
||||
{ name: 'English (UK)', value: 'en-GB' },
|
||||
{ name: 'English (Australia)', value: 'en-AU' },
|
||||
{ name: 'English (Canada)', value: 'en-CA' },
|
||||
{ name: 'English (India)', value: 'en-IN' },
|
||||
{ name: 'Estonian', value: 'et' },
|
||||
{ name: 'Finnish', value: 'fi' },
|
||||
{ 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: 'Gujarati', value: 'gu' },
|
||||
{ name: 'Hebrew', value: 'he' },
|
||||
{ name: 'Hindi', value: 'hi' },
|
||||
{ name: 'Hungarian', value: 'hu' },
|
||||
{ name: 'Indonesian', value: 'id' },
|
||||
{ name: 'Italian', value: 'it' },
|
||||
{ name: 'Japanese', value: 'ja' },
|
||||
{ name: 'Kannada', value: 'kn' },
|
||||
{ name: 'Korean', value: 'ko' },
|
||||
{ name: 'Latvian', value: 'lv' },
|
||||
{ name: 'Lithuanian', value: 'lt' },
|
||||
{ name: 'Malay', value: 'ms' },
|
||||
{ name: 'Malayalam', value: 'ml' },
|
||||
{ name: 'Marathi', value: 'mr' },
|
||||
{ name: 'Norwegian', value: 'no' },
|
||||
{ name: 'Norwegian Bokmål', value: 'nb' },
|
||||
{ name: 'Persian', value: 'fa' },
|
||||
{ name: 'Polish', value: 'pl' },
|
||||
{ name: 'Portuguese', value: 'pt' },
|
||||
{ name: 'Portuguese (Brazil)', value: 'pt-BR' },
|
||||
{ name: 'Portuguese (Portugal)', value: 'pt-PT' },
|
||||
{ name: 'Punjabi', value: 'pa' },
|
||||
{ name: 'Romanian', value: 'ro' },
|
||||
{ name: 'Russian', value: 'ru' },
|
||||
{ name: 'Serbian', value: 'sr' },
|
||||
{ name: 'Slovak', value: 'sk' },
|
||||
{ name: 'Slovenian', value: 'sl' },
|
||||
{ name: 'Spanish', value: 'es' },
|
||||
{ name: 'Spanish (Spain)', value: 'es-ES' },
|
||||
{ name: 'Spanish (Mexico)', value: 'es-MX' },
|
||||
{ name: 'Spanish (Argentina)', value: 'es-AR' },
|
||||
{ name: 'Spanish (Colombia)', value: 'es-CO' },
|
||||
{ name: 'Spanish (Latin America)', value: 'es-419' },
|
||||
{ name: 'Swahili', value: 'sw' },
|
||||
{ name: 'Swedish', value: 'sv' },
|
||||
{ name: 'Tamil', value: 'ta' },
|
||||
{ name: 'Telugu', value: 'te' },
|
||||
{ name: 'Thai', value: 'th' },
|
||||
{ name: 'Turkish', value: 'tr' },
|
||||
{ name: 'Ukrainian', value: 'uk' },
|
||||
{ name: 'Urdu', value: 'ur' },
|
||||
{ name: 'Vietnamese', value: 'vi' },
|
||||
{ name: 'Welsh', value: 'cy' },
|
||||
|
||||
// Additional languages with ISO 639-3 codes where needed
|
||||
{ name: 'Afrikaans', value: 'af' },
|
||||
{ name: 'Albanian', value: 'sq' },
|
||||
{ name: 'Amharic', value: 'am' },
|
||||
{ name: 'Armenian', value: 'hy' },
|
||||
{ name: 'Azerbaijani', value: 'az' },
|
||||
{ name: 'Basque', value: 'eu' },
|
||||
{ name: 'Belarusian', value: 'be' },
|
||||
{ name: 'Bosnian', value: 'bs' },
|
||||
{ name: 'Burmese', value: 'my' },
|
||||
{ name: 'Filipino', value: 'fil' },
|
||||
{ name: 'Galician', value: 'gl' },
|
||||
{ name: 'Georgian', value: 'ka' },
|
||||
{ name: 'Icelandic', value: 'is' },
|
||||
{ name: 'Irish', value: 'ga' },
|
||||
{ name: 'Kazakh', value: 'kk' },
|
||||
{ name: 'Khmer', value: 'km' },
|
||||
{ name: 'Lao', value: 'lo' },
|
||||
{ name: 'Luxembourgish', value: 'lb' },
|
||||
{ name: 'Macedonian', value: 'mk' },
|
||||
{ name: 'Maltese', value: 'mt' },
|
||||
{ name: 'Mongolian', value: 'mn' },
|
||||
{ name: 'Nepali', value: 'ne' },
|
||||
{ name: 'Pashto', value: 'ps' },
|
||||
{ name: 'Sinhala', value: 'si' },
|
||||
{ name: 'Somali', value: 'so' },
|
||||
{ name: 'Sundanese', value: 'su' },
|
||||
{ name: 'Tagalog', value: 'tl' },
|
||||
{ name: 'Uzbek', value: 'uz' },
|
||||
{ name: 'Yiddish', value: 'yi' },
|
||||
{ name: 'Zulu', value: 'zu' }
|
||||
];
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = [
|
||||
{ name: 'Scribe_v1', value: 'scribe_v1' },
|
||||
{ name: 'Scribe_v1_experimental', value: 'scribe_v1_experimental' },
|
||||
{ name: 'Scribe_v2', value: 'scribe_v2' },
|
||||
];
|
||||
|
||||
@@ -9,6 +9,7 @@ const Houndify = require('houndify');
|
||||
const { GladiaClient } = require('@gladiaio/sdk');
|
||||
const {decrypt, obscureKey} = require('./encrypt-decrypt');
|
||||
const { RealtimeSession } = require('speechmatics');
|
||||
const {ElevenLabsClient} = require('@elevenlabs/elevenlabs-js');
|
||||
|
||||
const TtsGoogleLanguagesVoices = require('./speech-data/tts-google');
|
||||
const TtsAwsLanguagesVoices = require('./speech-data/tts-aws');
|
||||
@@ -52,11 +53,13 @@ const SttVoxistLanguagesVoices = require('./speech-data/stt-voxist');
|
||||
const SttVerbioLanguagesVoices = require('./speech-data/stt-verbio');
|
||||
const SttOpenaiLanguagesVoices = require('./speech-data/stt-openai');
|
||||
const SttGladiaLanguagesVoices = require('./speech-data/stt-gladia');
|
||||
const SttElevenlabsLanguagesVoices = require('./speech-data/stt-elevenlabs');
|
||||
|
||||
|
||||
const SttModelOpenai = require('./speech-data/stt-model-openai');
|
||||
const sttModelDeepgram = require('./speech-data/stt-model-deepgram');
|
||||
const sttModelCartesia = require('./speech-data/stt-model-cartesia');
|
||||
const sttModelElevenlabs = require('./speech-data/stt-model-elevenlabs');
|
||||
|
||||
function capitalizeFirst(str) {
|
||||
if (!str) return str;
|
||||
@@ -402,6 +405,42 @@ const testElevenlabs = async(logger, credentials) => {
|
||||
return response.body;
|
||||
};
|
||||
|
||||
const testElevenlabsStt = async(logger, credentials) => {
|
||||
const {api_key, stt_model_id} = credentials;
|
||||
|
||||
// Validate API key
|
||||
if (!api_key) {
|
||||
throw new Error('ElevenLabs API key is required');
|
||||
}
|
||||
|
||||
const client = new ElevenLabsClient({
|
||||
environment: 'https://api.elevenlabs.io',
|
||||
apiKey: api_key
|
||||
});
|
||||
|
||||
return new Promise(async(resolve, reject) => {
|
||||
try {
|
||||
const audioBuffer = fs.readFileSync(`${__dirname}/../../data/test_audio.wav`);
|
||||
|
||||
const result = await client.speechToText.convert({
|
||||
file: audioBuffer,
|
||||
modelId: stt_model_id
|
||||
});
|
||||
|
||||
// Check if we got a valid response with text
|
||||
if (result && result.text && result.text.length > 0) {
|
||||
logger.debug({result}, 'ElevenLabs STT successful');
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(new Error('no transcript returned from ElevenLabs STT'));
|
||||
}
|
||||
} catch (error) {
|
||||
logger.info({error}, 'failed to get ElevenLabs STT transcript');
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const testPlayHT = async(logger, synthAudio, credentials) => {
|
||||
try {
|
||||
await synthAudio(
|
||||
@@ -849,6 +888,7 @@ function decryptCredential(obj, credential, logger, isObscureKey = true) {
|
||||
obj.model_id = o.model_id;
|
||||
obj.api_uri = o.api_uri;
|
||||
obj.options = o.options;
|
||||
obj.stt_model_id = o.stt_model_id;
|
||||
} else if ('playht' === obj.vendor) {
|
||||
const o = JSON.parse(decrypt(credential));
|
||||
obj.api_key = isObscureKey ? obscureKey(o.api_key) : o.api_key;
|
||||
@@ -1218,13 +1258,13 @@ async function getLanguagesVoicesForElevenlabs(credential) {
|
||||
language.voices = voices;
|
||||
}
|
||||
}
|
||||
return tranform(languages, undefined, models);
|
||||
return tranform(languages, SttElevenlabsLanguagesVoices, models, sttModelElevenlabs);
|
||||
} else {
|
||||
const voices = TtsElevenlabsLanguagesVoices[0].voices;
|
||||
for (const language of TtsElevenlabsLanguagesVoices) {
|
||||
language.voices = voices;
|
||||
}
|
||||
return tranform(TtsElevenlabsLanguagesVoices, undefined, TtsModelElevenLabs);
|
||||
return tranform(TtsElevenlabsLanguagesVoices, SttElevenlabsLanguagesVoices, TtsModelElevenLabs, sttModelElevenlabs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1783,6 +1823,7 @@ module.exports = {
|
||||
testIbmStt,
|
||||
testSonioxStt,
|
||||
testElevenlabs,
|
||||
testElevenlabsStt,
|
||||
testPlayHT,
|
||||
testRimelabs,
|
||||
testInworld,
|
||||
|
||||
Generated
+25
-3
@@ -14,6 +14,7 @@
|
||||
"@aws-sdk/client-transcribe": "^3.549.0",
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"@deepgram/sdk": "^1.21.0",
|
||||
"@elevenlabs/elevenlabs-js": "^2.24.1",
|
||||
"@gladiaio/sdk": "^0.5.2",
|
||||
"@google-cloud/speech": "^6.5.0",
|
||||
"@google-cloud/storage": "^7.9.0",
|
||||
@@ -3714,6 +3715,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@elevenlabs/elevenlabs-js": {
|
||||
"version": "2.24.1",
|
||||
"resolved": "https://registry.npmjs.org/@elevenlabs/elevenlabs-js/-/elevenlabs-js-2.24.1.tgz",
|
||||
"integrity": "sha512-i6bDExgK9lYne1vLhy85JJ3O8bNi5vPTfcgq8kT3HG4+3rgkUJtg5UP29Mn1KONc4ZOeYUomzxJ820uLkT9z6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"command-exists": "^1.2.9",
|
||||
"node-fetch": "^2.7.0",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
@@ -6404,6 +6419,12 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/command-exists": {
|
||||
"version": "1.2.9",
|
||||
"resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz",
|
||||
"integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/commondir": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
|
||||
@@ -12444,9 +12465,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.18.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||
"version": "8.18.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@aws-sdk/client-transcribe": "^3.549.0",
|
||||
"@azure/storage-blob": "^12.17.0",
|
||||
"@deepgram/sdk": "^1.21.0",
|
||||
"@elevenlabs/elevenlabs-js": "^2.24.1",
|
||||
"@gladiaio/sdk": "^0.5.2",
|
||||
"@google-cloud/speech": "^6.5.0",
|
||||
"@google-cloud/storage": "^7.9.0",
|
||||
|
||||
+17
-17
@@ -6,7 +6,7 @@ process.on('SIGINT', async() => {
|
||||
if (stopping) return;
|
||||
stopping = true;
|
||||
console.log('shutting down');
|
||||
await stopDocker();
|
||||
// await stopDocker();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
@@ -84,19 +84,19 @@ const stopDocker = () => {
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
startDocker()
|
||||
.then(createDb)
|
||||
.then(createSchema)
|
||||
.then(seedDb)
|
||||
.then(resetAdminPassword)
|
||||
.then(generateSipTrace)
|
||||
.then(() => {
|
||||
console.log('ready for testing!');
|
||||
require('..');
|
||||
})
|
||||
.catch(async(err) => {
|
||||
console.error({err}, 'Error running integration test');
|
||||
await stopDocker();
|
||||
process.exit(-1);
|
||||
});
|
||||
require('..');
|
||||
// startDocker()
|
||||
// .then(createDb)
|
||||
// .then(createSchema)
|
||||
// .then(seedDb)
|
||||
// .then(resetAdminPassword)
|
||||
// .then(generateSipTrace)
|
||||
// .then(() => {
|
||||
// console.log('ready for testing!');
|
||||
// require('..');
|
||||
// })
|
||||
// .catch(async(err) => {
|
||||
// console.error({err}, 'Error running integration test');
|
||||
// await stopDocker();
|
||||
// process.exit(-1);
|
||||
// });
|
||||
|
||||
Reference in New Issue
Block a user