@jambonz/speech-utils 0.2.30 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,11 +13,6 @@ jobs:
13
13
  with:
14
14
  node-version: '20'
15
15
  - run: npm install
16
- - name: Install Docker Compose
17
- run: |
18
- sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
19
- sudo chmod +x /usr/local/bin/docker-compose
20
- docker-compose --version
21
16
  - run: npm run jslint
22
17
  - run: sudo apt update && sudo apt install -y squid
23
18
  - run: sudo cp test/squid.conf /etc/squid/squid.conf
@@ -28,9 +23,7 @@ jobs:
28
23
  AWS_REGION: ${{ secrets.AWS_REGION }}
29
24
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
30
25
  GCP_JSON_KEY: ${{ secrets.GCP_JSON_KEY }}
31
- IBM_API_KEY: ${{ secrets.IBM_API_KEY }}
32
- IBM_TTS_API_KEY: ${{ secrets.IBM_TTS_API_KEY }}
33
- IBM_TTS_REGION: ${{ secrets.IBM_TTS_REGION }}
26
+
34
27
  MICROSOFT_API_KEY: ${{ secrets.MICROSOFT_API_KEY }}
35
28
  MICROSOFT_REGION: ${{ secrets.MICROSOFT_REGION }}
36
29
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
package/index.js CHANGED
@@ -16,7 +16,7 @@ module.exports = (opts, logger) => {
16
16
  synthAudio: require('./lib/synth-audio').bind(null, client, createHash, retrieveHash, logger),
17
17
  getVerbioAccessToken: require('./lib/get-verbio-token').bind(null, client, logger),
18
18
  getNuanceAccessToken: require('./lib/get-nuance-access-token').bind(null, client, logger),
19
- getIbmAccessToken: require('./lib/get-ibm-access-token').bind(null, client, logger),
19
+
20
20
  getAwsAuthToken: require('./lib/get-aws-sts-token').bind(null, logger, createHash, retrieveHash),
21
21
  getTtsVoices: require('./lib/get-tts-voices').bind(null, client, createHash, retrieveHash, logger),
22
22
  };
@@ -3,8 +3,6 @@ const {noopLogger, createNuanceClient, createKryptonClient} = require('./utils')
3
3
  const getNuanceAccessToken = require('./get-nuance-access-token');
4
4
  const getVerbioAccessToken = require('./get-verbio-token');
5
5
  const {GetVoicesRequest, Voice} = require('../stubs/nuance/synthesizer_pb');
6
- const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
7
- const { IamAuthenticator } = require('ibm-watson/auth');
8
6
  const ttsGoogle = require('@google-cloud/text-to-speech');
9
7
  const { PollyClient, DescribeVoicesCommand } = require('@aws-sdk/client-polly');
10
8
  const getAwsAuthToken = require('./get-aws-sts-token');
@@ -12,21 +10,6 @@ const {Pool} = require('undici');
12
10
  const { HTTP_TIMEOUT } = require('./config');
13
11
  const verbioVoicePool = new Pool('https://us.rest.speechcenter.verbio.com');
14
12
 
15
- const getIbmVoices = async(client, logger, credentials) => {
16
- const {tts_region, tts_api_key} = credentials;
17
- console.log(`region: ${tts_region}, api_key: ${tts_api_key}`);
18
-
19
- const textToSpeech = new TextToSpeechV1({
20
- authenticator: new IamAuthenticator({
21
- apikey: tts_api_key,
22
- }),
23
- serviceUrl: `https://api.${tts_region}.text-to-speech.watson.cloud.ibm.com`
24
- });
25
-
26
- const voices = await textToSpeech.listVoices();
27
- return voices;
28
- };
29
-
30
13
  const getNuanceVoices = async(client, logger, credentials) => {
31
14
  const {client_id: clientId, secret: secret, nuance_tts_uri} = credentials;
32
15
 
@@ -165,14 +148,12 @@ const getVerbioVoices = async(client, logger, credentials) => {
165
148
  async function getTtsVoices(client, createHash, retrieveHash, logger, {vendor, credentials}) {
166
149
  logger = logger || noopLogger;
167
150
 
168
- assert.ok(['nuance', 'ibm', 'google', 'aws', 'polly', 'verbio'].includes(vendor),
151
+ assert.ok(['nuance', 'google', 'aws', 'polly', 'verbio'].includes(vendor),
169
152
  `getTtsVoices not supported for vendor ${vendor}`);
170
153
 
171
154
  switch (vendor) {
172
155
  case 'nuance':
173
156
  return getNuanceVoices(client, logger, credentials);
174
- case 'ibm':
175
- return getIbmVoices(client, logger, credentials);
176
157
  case 'google':
177
158
  return getGoogleVoices(client, logger, credentials);
178
159
  case 'aws':
@@ -6,8 +6,7 @@ const { PollyClient, SynthesizeSpeechCommand } = require('@aws-sdk/client-polly'
6
6
  const { CartesiaClient } = require('@cartesia/cartesia-js');
7
7
 
8
8
  const sdk = require('microsoft-cognitiveservices-speech-sdk');
9
- const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
10
- const { IamAuthenticator } = require('ibm-watson/auth');
9
+
11
10
  const {
12
11
  ResultReason,
13
12
  SpeechConfig,
@@ -96,7 +95,7 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
96
95
  let rtt;
97
96
  logger = logger || noopLogger;
98
97
 
99
- assert.ok(['google', 'aws', 'polly', 'microsoft', 'wellsaid', 'nuance', 'nvidia', 'ibm', 'elevenlabs',
98
+ assert.ok(['google', 'aws', 'polly', 'microsoft', 'wellsaid', 'nuance', 'nvidia', 'elevenlabs',
100
99
  'whisper', 'deepgram', 'playht', 'rimelabs', 'verbio', 'cartesia', 'inworld', 'resemble'].includes(vendor) ||
101
100
  vendor.startsWith('custom'),
102
101
  `synthAudio supported vendors are google, aws, microsoft, nuance, nvidia and wellsaid ..etc, not ${vendor}`);
@@ -122,11 +121,6 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
122
121
  assert.ok(language, 'synthAudio requires language when nvidia is used');
123
122
  assert.ok(credentials.riva_server_uri, 'synthAudio requires riva_server_uri in credentials when nvidia is used');
124
123
  }
125
- else if ('ibm' === vendor) {
126
- assert.ok(voice, 'synthAudio requires voice when ibm is used');
127
- assert.ok(credentials.tts_region, 'synthAudio requires tts_region in credentials when ibm watson is used');
128
- assert.ok(credentials.tts_api_key, 'synthAudio requires tts_api_key in credentials when nuance is used');
129
- }
130
124
  else if ('wellsaid' === vendor) {
131
125
  language = 'en-US'; // WellSaid only supports English atm
132
126
  assert.ok(voice, 'synthAudio requires voice when wellsaid is used');
@@ -230,9 +224,6 @@ async function synthAudio(client, createHash, retrieveHash, logger, stats, { acc
230
224
  audioData = await synthNvidia(client, logger, {credentials, stats, language, voice, model, key, text,
231
225
  renderForCaching, disableTtsStreaming, disableTtsCache});
232
226
  break;
233
- case 'ibm':
234
- audioData = await synthIbm(logger, {credentials, stats, voice, key, text});
235
- break;
236
227
  case 'wellsaid':
237
228
  audioData = await synthWellSaid(logger, {credentials, stats, language, voice, key, text});
238
229
  break;
@@ -535,39 +526,6 @@ const synthGoogle = async(logger, {
535
526
  }
536
527
  };
537
528
 
538
- const synthIbm = async(logger, {credentials, stats, voice, text}) => {
539
- const {tts_api_key, tts_region} = credentials;
540
- const params = {
541
- text,
542
- voice,
543
- accept: 'audio/mp3'
544
- };
545
-
546
- try {
547
- const textToSpeech = new TextToSpeechV1({
548
- authenticator: new IamAuthenticator({
549
- apikey: tts_api_key,
550
- }),
551
- serviceUrl: `https://api.${tts_region}.text-to-speech.watson.cloud.ibm.com`
552
- });
553
-
554
- const r = await textToSpeech.synthesize(params);
555
- const chunks = [];
556
- for await (const chunk of r.result) {
557
- chunks.push(chunk);
558
- }
559
- return {
560
- audioContent: Buffer.concat(chunks),
561
- extension: 'mp3',
562
- sampleRate: 8000
563
- };
564
- } catch (err) {
565
- logger.info({err, params}, 'synthAudio: Error synthesizing speech using ibm');
566
- stats.increment('tts.count', ['vendor:ibm', 'accepted:no']);
567
- throw new Error(err.statusText || err.message);
568
- }
569
- };
570
-
571
529
  async function _synthOnPremMicrosoft(logger, {
572
530
  credentials,
573
531
  language,
@@ -969,7 +927,7 @@ const synthElevenlabs = async(logger, {
969
927
  const optimize_streaming_latency = opts.optimize_streaming_latency ?
970
928
  `?optimize_streaming_latency=${opts.optimize_streaming_latency}` : '';
971
929
  try {
972
- const post = bent(`https://${api_uri}`, 'POST', 'buffer', {
930
+ const post = bent(`https://${api_uri || 'api.elevenlabs.io'}`, 'POST', 'buffer', {
973
931
  'xi-api-key': api_key,
974
932
  'Accept': 'audio/mpeg',
975
933
  'Content-Type': 'application/json'
@@ -1178,6 +1136,8 @@ const synthRimelabs = async(logger, {
1178
1136
  if (opts.repetition_penalty) params += `,repetition_penalty=${opts.repetition_penalty}`;
1179
1137
  if (opts.top_p) params += `,top_p=${opts.top_p}`;
1180
1138
  if (opts.max_tokens) params += `,max_tokens=${opts.max_tokens}`;
1139
+ // Coda model parameters
1140
+ if (opts.timeScaleFactor) params += `,time_scale_factor=${opts.timeScaleFactor}`;
1181
1141
  params += '}';
1182
1142
 
1183
1143
  return {
package/lib/utils.js CHANGED
@@ -54,12 +54,6 @@ function makeBasicAuthHeader(username, password) {
54
54
  return {Authorization: header};
55
55
  }
56
56
 
57
- function makeIbmKey(apiKey) {
58
- const hash = crypto.createHash('sha1');
59
- hash.update(apiKey);
60
- return `ibm:${hash.digest('hex')}`;
61
- }
62
-
63
57
  function makeAwsKey(awsAccessKeyId) {
64
58
  const hash = crypto.createHash('sha1');
65
59
  hash.update(awsAccessKeyId);
@@ -143,7 +137,7 @@ const createRivaClient = async(rivaUri) => {
143
137
  module.exports = {
144
138
  makeSynthKey,
145
139
  makeNuanceKey,
146
- makeIbmKey,
140
+
147
141
  makePlayhtKey,
148
142
  makeAwsKey,
149
143
  makeVerbioKey,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/speech-utils",
3
- "version": "0.2.30",
3
+ "version": "1.0.3",
4
4
  "description": "TTS-related speech utilities for jambonz",
5
5
  "main": "index.js",
6
6
  "author": "Dave Horton",
@@ -37,7 +37,6 @@
37
37
  "debug": "^4.3.4",
38
38
  "form-urlencoded": "^6.1.4",
39
39
  "google-protobuf": "^3.21.2",
40
- "ibm-watson": "^11.0.0",
41
40
  "microsoft-cognitiveservices-speech-sdk": "1.38.0",
42
41
  "openai": "^4.98.0",
43
42
  "undici": "^7.5.0"
@@ -2,7 +2,7 @@ const test = require('tape').test ;
2
2
  const exec = require('child_process').exec ;
3
3
 
4
4
  test('starting docker network..', (t) => {
5
- exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml up -d`, (err, stdout, stderr) => {
5
+ exec(`docker compose -f ${__dirname}/docker-compose-testbed.yaml up -d`, (err, stdout, stderr) => {
6
6
  setTimeout(() => {
7
7
  t.end(err);
8
8
  }, 2000);
@@ -3,7 +3,7 @@ const exec = require('child_process').exec ;
3
3
 
4
4
  test('stopping docker network..', (t) => {
5
5
  t.timeoutAfter(10000);
6
- exec(`docker-compose -f ${__dirname}/docker-compose-testbed.yaml down`, (err, stdout, stderr) => {
6
+ exec(`docker compose -f ${__dirname}/docker-compose-testbed.yaml down`, (err, stdout, stderr) => {
7
7
  //console.log(`stderr: ${stderr}`);
8
8
  process.exit(0);
9
9
  });
package/test/index.js CHANGED
@@ -2,6 +2,6 @@ require('./docker_start');
2
2
  require('./synth');
3
3
  require('./list-voices');
4
4
  require('./aws');
5
- require('./ibm');
5
+
6
6
  require('./nuance');
7
7
  require('./docker_stop');
@@ -38,71 +38,6 @@ test('Verbio - get Access key and voices', async(t) => {
38
38
  client.quit();
39
39
  });
40
40
 
41
- test('IBM - create access key', async(t) => {
42
- const fn = require('..');
43
- const {client, getIbmAccessToken} = fn(opts, logger);
44
-
45
- if (!process.env.IBM_API_KEY ) {
46
- t.pass('skipping IBM test since no IBM api_key provided');
47
- t.end();
48
- client.quit();
49
- return;
50
- }
51
- try {
52
- let obj = await getIbmAccessToken(process.env.IBM_API_KEY);
53
- //console.log({obj}, 'received access token from IBM');
54
- t.ok(obj.access_token && !obj.servedFromCache, 'successfull received access token from IBM');
55
-
56
- obj = await getIbmAccessToken(process.env.IBM_API_KEY);
57
- //console.log({obj}, 'received access token from IBM - second request');
58
- t.ok(obj.access_token && obj.servedFromCache, 'successfully received access token from cache');
59
-
60
- await client.flushall();
61
- t.end();
62
- }
63
- catch (err) {
64
- console.error(err);
65
- t.end(err);
66
- }
67
- client.quit();
68
- });
69
-
70
- test('IBM - retrieve tts voices test', async(t) => {
71
- const fn = require('..');
72
- const {client, getTtsVoices} = fn(opts, logger);
73
-
74
- if (!process.env.IBM_TTS_API_KEY || !process.env.IBM_TTS_REGION) {
75
- t.pass('skipping IBM test since no IBM api_key and/or region provided');
76
- t.end();
77
- client.quit();
78
- return;
79
- }
80
- try {
81
- const opts = {
82
- vendor: 'ibm',
83
- credentials: {
84
- tts_api_key: process.env.IBM_TTS_API_KEY,
85
- tts_region: process.env.IBM_TTS_REGION
86
- }
87
- };
88
- const obj = await getTtsVoices(opts);
89
- const {voices} = obj.result;
90
- //console.log(JSON.stringify(voices));
91
- t.ok(voices.length > 0 && voices[0].language,
92
- `GetVoices: successfully retrieved ${voices.length} voices from IBM`);
93
-
94
- await client.flushall();
95
-
96
- t.end();
97
-
98
- }
99
- catch (err) {
100
- console.error(err);
101
- t.end(err);
102
- }
103
- client.quit();
104
- });
105
-
106
41
  test('Nuance hosted tests', async(t) => {
107
42
  const fn = require('..');
108
43
  const {client, getTtsVoices} = fn(opts, logger);
package/test/synth.js CHANGED
@@ -872,46 +872,6 @@ test('Nvidia speech synth tests', async(t) => {
872
872
  client.quit();
873
873
  });
874
874
 
875
- test('IBM watson speech synth tests', async(t) => {
876
- const fn = require('..');
877
- const {synthAudio, client} = fn(opts, logger);
878
-
879
- if (!process.env.IBM_TTS_API_KEY || !process.env.IBM_TTS_REGION) {
880
- t.pass('skipping IBM Watson speech synth tests since IBM_TTS_API_KEY or IBM_TTS_API_KEY not provided');
881
- return t.end();
882
- }
883
- const text = `<speak> Hi there and welcome to jambones! jambones is the <sub alias="seapass">CPaaS</sub> designed with the needs of communication service providers in mind. This is an example of simple text-to-speech, but there is so much more you can do. Try us out!</speak>`;
884
- try {
885
- let opts = await synthAudio(stats, {
886
- vendor: 'ibm',
887
- credentials: {
888
- tts_api_key: process.env.IBM_TTS_API_KEY,
889
- tts_region: process.env.IBM_TTS_REGION,
890
- },
891
- language: 'en-US',
892
- voice: 'en-US_AllisonV2Voice',
893
- text,
894
- });
895
- t.ok(!opts.servedFromCache, `successfully synthesized ibm audio to ${opts.filePath}`);
896
-
897
- opts = await synthAudio(stats, {
898
- vendor: 'ibm',
899
- credentials: {
900
- tts_api_key: process.env.IBM_TTS_API_KEY,
901
- tts_region: process.env.IBM_TTS_REGION,
902
- },
903
- language: 'en-US',
904
- voice: 'en-US_AllisonV2Voice',
905
- text,
906
- });
907
- t.ok(opts.servedFromCache, `successfully retrieved ibm audio from cache ${opts.filePath}`);
908
- } catch (err) {
909
- console.error(JSON.stringify(err));
910
- t.end(err);
911
- }
912
- client.quit();
913
- });
914
-
915
875
  test('Custom Vendor speech synth tests', async(t) => {
916
876
  const fn = require('..');
917
877
  const {synthAudio, client} = fn(opts, logger);
@@ -1,48 +0,0 @@
1
- const formurlencoded = require('form-urlencoded');
2
- const {Pool} = require('undici');
3
- const pool = new Pool('https://iam.cloud.ibm.com');
4
- const {makeIbmKey, noopLogger} = require('./utils');
5
- const { HTTP_TIMEOUT } = require('./config');
6
- const debug = require('debug')('jambonz:realtimedb-helpers');
7
-
8
- async function getIbmAccessToken(client, logger, apiKey) {
9
- logger = logger || noopLogger;
10
- try {
11
- const key = makeIbmKey(apiKey);
12
- const access_token = await client.get(key);
13
- if (access_token) return {access_token, servedFromCache: true};
14
-
15
- /* access token not found in cache, so fetch it from Ibm */
16
- const payload = {
17
- grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
18
- apikey: apiKey
19
- };
20
- const {statusCode, headers, body} = await pool.request({
21
- path: '/identity/token',
22
- method: 'POST',
23
- headers: {
24
- 'Content-Type': 'application/x-www-form-urlencoded'
25
- },
26
- body: formurlencoded(payload),
27
- timeout: HTTP_TIMEOUT,
28
- followRedirects: false
29
- });
30
-
31
- if (200 !== statusCode) {
32
- const json = await body.json();
33
- logger.debug({statusCode, headers, body: json}, 'error fetching access token from Ibm');
34
- const err = new Error();
35
- err.statusCode = statusCode;
36
- throw err;
37
- }
38
- const json = await body.json();
39
- await client.set(key, json.access_token, 'EX', json.expires_in - 30);
40
- return {...json, servedFromCache: false};
41
- } catch (err) {
42
- debug(err, 'getIbmAccessToken: Error retrieving Ibm access token');
43
- logger.error(err, 'getIbmAccessToken: Error retrieving Ibm access token for client_id ${clientId}');
44
- throw err;
45
- }
46
- }
47
-
48
- module.exports = getIbmAccessToken;
package/test/ibm.js DELETED
@@ -1,78 +0,0 @@
1
- const test = require('tape').test ;
2
- const config = require('config');
3
- const opts = config.get('redis');
4
- const fs = require('fs');
5
- const logger = require('pino')({level: 'error'});
6
- process.on('unhandledRejection', (reason, p) => {
7
- console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
8
- });
9
-
10
- const stats = {
11
- increment: () => {},
12
- histogram: () => {}
13
- };
14
-
15
- test('IBM - create access key', async(t) => {
16
- const fn = require('..');
17
- const {client, getIbmAccessToken} = fn(opts, logger);
18
-
19
- if (!process.env.IBM_API_KEY ) {
20
- t.pass('skipping IBM test since no IBM api_key provided');
21
- t.end();
22
- client.quit();
23
- return;
24
- }
25
- try {
26
- let obj = await getIbmAccessToken(process.env.IBM_API_KEY);
27
- //console.log({obj}, 'received access token from IBM');
28
- t.ok(obj.access_token && !obj.servedFromCache, 'successfull received access token from IBM');
29
-
30
- obj = await getIbmAccessToken(process.env.IBM_API_KEY);
31
- //console.log({obj}, 'received access token from IBM - second request');
32
- t.ok(obj.access_token && obj.servedFromCache, 'successfully received access token from cache');
33
-
34
- await client.flushall();
35
- t.end();
36
- }
37
- catch (err) {
38
- console.error(err);
39
- t.end(err);
40
- }
41
- client.quit();
42
- });
43
-
44
- test('IBM - retrieve tts voices test', async(t) => {
45
- const fn = require('..');
46
- const {client, getTtsVoices} = fn(opts, logger);
47
-
48
- if (!process.env.IBM_TTS_API_KEY || !process.env.IBM_TTS_REGION) {
49
- t.pass('skipping IBM test since no IBM api_key and/or region provided');
50
- t.end();
51
- client.quit();
52
- return;
53
- }
54
- try {
55
- const opts = {
56
- vendor: 'ibm',
57
- credentials: {
58
- tts_api_key: process.env.IBM_TTS_API_KEY,
59
- tts_region: process.env.IBM_TTS_REGION
60
- }
61
- };
62
- const obj = await getTtsVoices(opts);
63
- const {voices} = obj.result;
64
- //console.log(JSON.stringify(voices));
65
- t.ok(voices.length > 0 && voices[0].language,
66
- `GetVoices: successfully retrieved ${voices.length} voices from IBM`);
67
-
68
- await client.flushall();
69
-
70
- t.end();
71
-
72
- }
73
- catch (err) {
74
- console.error(err);
75
- t.end(err);
76
- }
77
- client.quit();
78
- });