@jambonz/speech-utils 0.0.51 → 0.1.0

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.
package/index.js CHANGED
@@ -13,10 +13,10 @@ module.exports = (opts, logger) => {
13
13
  getTtsSize: require('./lib/get-tts-size').bind(null, client, logger),
14
14
  purgeTtsCache: require('./lib/purge-tts-cache').bind(null, client, logger),
15
15
  addFileToCache: require('./lib/add-file-to-cache').bind(null, client, logger),
16
- synthAudio: require('./lib/synth-audio').bind(null, client, logger),
16
+ synthAudio: require('./lib/synth-audio').bind(null, client, createHash, retrieveHash, logger),
17
17
  getNuanceAccessToken: require('./lib/get-nuance-access-token').bind(null, client, logger),
18
18
  getIbmAccessToken: require('./lib/get-ibm-access-token').bind(null, client, logger),
19
19
  getAwsAuthToken: require('./lib/get-aws-sts-token').bind(null, logger, createHash, retrieveHash),
20
- getTtsVoices: require('./lib/get-tts-voices').bind(null, client, logger),
20
+ getTtsVoices: require('./lib/get-tts-voices').bind(null, client, createHash, retrieveHash, logger),
21
21
  };
22
22
  };
@@ -1,32 +1,41 @@
1
- const { STSClient, GetSessionTokenCommand } = require('@aws-sdk/client-sts');
1
+ const { STSClient, GetSessionTokenCommand, AssumeRoleCommand } = require('@aws-sdk/client-sts');
2
2
  const {makeAwsKey, noopLogger} = require('./utils');
3
3
  const debug = require('debug')('jambonz:speech-utils');
4
4
  const EXPIRY = 3600;
5
5
 
6
6
  async function getAwsAuthToken(
7
- logger,
8
- createHash, retrieveHash,
9
- awsAccessKeyId, awsSecretAccessKey, awsRegion) {
7
+ logger, createHash, retrieveHash,
8
+ awsAccessKeyId, awsSecretAccessKey, awsRegion, roleArn = null) {
10
9
  logger = logger || noopLogger;
11
10
  try {
12
- const key = makeAwsKey(awsAccessKeyId);
11
+ const key = makeAwsKey(roleArn || awsAccessKeyId);
13
12
  const obj = await retrieveHash(key);
14
13
  if (obj) return {...obj, servedFromCache: true};
15
14
 
16
- /* access token not found in cache, so generate it using STS */
17
- const stsClient = new STSClient({
18
- region: awsRegion,
19
- credentials: {
20
- accessKeyId: awsAccessKeyId,
21
- secretAccessKey: awsSecretAccessKey,
22
- }
23
- });
24
- const command = new GetSessionTokenCommand({DurationSeconds: EXPIRY});
25
- const data = await stsClient.send(command);
15
+ let data;
16
+ if (roleArn) {
17
+ const stsClient = new STSClient({ region: awsRegion});
18
+ const roleToAssume = { RoleArn: roleArn, RoleSessionName: 'Jambonz_Speech', DurationSeconds: EXPIRY};
19
+ const command = new AssumeRoleCommand(roleToAssume);
20
+
21
+ data = await stsClient.send(command);
22
+ } else {
23
+ /* access token not found in cache, so generate it using STS */
24
+ const stsClient = new STSClient({
25
+ region: awsRegion,
26
+ credentials: {
27
+ accessKeyId: awsAccessKeyId,
28
+ secretAccessKey: awsSecretAccessKey,
29
+ }
30
+ });
31
+ const command = new GetSessionTokenCommand({DurationSeconds: EXPIRY});
32
+ data = await stsClient.send(command);
33
+ }
26
34
 
27
35
  const credentials = {
28
36
  accessKeyId: data.Credentials.AccessKeyId,
29
37
  secretAccessKey: data.Credentials.SecretAccessKey,
38
+ sessionToken: data.Credentials.SessionToken,
30
39
  securityToken: data.Credentials.SessionToken
31
40
  };
32
41
 
@@ -6,6 +6,7 @@ const TextToSpeechV1 = require('ibm-watson/text-to-speech/v1');
6
6
  const { IamAuthenticator } = require('ibm-watson/auth');
7
7
  const ttsGoogle = require('@google-cloud/text-to-speech');
8
8
  const { PollyClient, DescribeVoicesCommand } = require('@aws-sdk/client-polly');
9
+ const getAwsAuthToken = require('./get-aws-sts-token');
9
10
 
10
11
  const getIbmVoices = async(client, logger, credentials) => {
11
12
  const {tts_region, tts_api_key} = credentials;
@@ -87,16 +88,26 @@ const getGoogleVoices = async(_client, logger, credentials) => {
87
88
  return await client.listVoices();
88
89
  };
89
90
 
90
- const getAwsVoices = async(_client, logger, credentials) => {
91
+ const getAwsVoices = async(_client, createHash, retrieveHash, logger, credentials) => {
91
92
  try {
92
- const {region, accessKeyId, secretAccessKey} = credentials;
93
- const client = new PollyClient({
94
- region,
95
- credentials: {
96
- accessKeyId,
97
- secretAccessKey
98
- }
99
- });
93
+ const {region, accessKeyId, secretAccessKey, roleArn} = credentials;
94
+ let client = null;
95
+ if (accessKeyId && secretAccessKey) {
96
+ client = new PollyClient({
97
+ region,
98
+ credentials: {
99
+ accessKeyId,
100
+ secretAccessKey
101
+ }
102
+ });
103
+ } else if (roleArn) {
104
+ client = new PollyClient({
105
+ region,
106
+ credentials: await getAwsAuthToken(logger, createHash, retrieveHash, null, null, region, roleArn),
107
+ });
108
+ } else {
109
+ client = new PollyClient({region});
110
+ }
100
111
  const command = new DescribeVoicesCommand({});
101
112
  const response = await client.send(command);
102
113
  return response;
@@ -122,7 +133,7 @@ const getAwsVoices = async(_client, logger, credentials) => {
122
133
  * @returns object containing filepath to an mp3 file in the /tmp folder containing
123
134
  * the synthesized audio, and a variable indicating whether it was served from cache
124
135
  */
125
- async function getTtsVoices(client, logger, {vendor, credentials}) {
136
+ async function getTtsVoices(client, createHash, retrieveHash, logger, {vendor, credentials}) {
126
137
  logger = logger || noopLogger;
127
138
 
128
139
  assert.ok(['nuance', 'ibm', 'google', 'aws', 'polly'].includes(vendor),
@@ -137,7 +148,7 @@ async function getTtsVoices(client, logger, {vendor, credentials}) {
137
148
  return getGoogleVoices(client, logger, credentials);
138
149
  case 'aws':
139
150
  case 'polly':
140
- return getAwsVoices(client, logger, credentials);
151
+ return getAwsVoices(client, createHash, retrieveHash, logger, credentials);
141
152
  default:
142
153
  break;
143
154
  }
@@ -39,6 +39,7 @@ const debug = require('debug')('jambonz:realtimedb-helpers');
39
39
  const EXPIRES = (process.env.JAMBONES_TTS_CACHE_DURATION_MINS || 4 * 60) * 60; // cache tts for 4 hours
40
40
  const TMP_FOLDER = '/tmp';
41
41
  const OpenAI = require('openai');
42
+ const getAwsAuthToken = require('./get-aws-sts-token');
42
43
 
43
44
 
44
45
  const trimTrailingSilence = (buffer) => {
@@ -75,7 +76,7 @@ const trimTrailingSilence = (buffer) => {
75
76
  * @returns object containing filepath to an mp3 file in the /tmp folder containing
76
77
  * the synthesized audio, and a variable indicating whether it was served from cache
77
78
  */
78
- async function synthAudio(client, logger, stats, { account_sid,
79
+ async function synthAudio(client, createHash, retrieveHash, logger, stats, { account_sid,
79
80
  vendor, language, voice, gender, text, engine, salt, model, credentials, deploymentId,
80
81
  disableTtsCache, renderForCaching, disableTtsStreaming, options
81
82
  }) {
@@ -187,7 +188,8 @@ async function synthAudio(client, logger, stats, { account_sid,
187
188
  case 'aws':
188
189
  case 'polly':
189
190
  vendorLabel = 'aws';
190
- audioBuffer = await synthPolly(logger, {credentials, stats, language, voice, text, engine});
191
+ audioBuffer = await synthPolly(createHash, retrieveHash, logger,
192
+ {credentials, stats, language, voice, text, engine});
191
193
  break;
192
194
  case 'azure':
193
195
  case 'microsoft':
@@ -263,16 +265,28 @@ async function synthAudio(client, logger, stats, { account_sid,
263
265
  });
264
266
  }
265
267
 
266
- const synthPolly = async(logger, {credentials, stats, language, voice, engine, text}) => {
268
+ const synthPolly = async(createHash, retrieveHash, logger,
269
+ {credentials, stats, language, voice, engine, text}) => {
267
270
  try {
268
- const {region, accessKeyId, secretAccessKey} = credentials;
269
- const polly = new PollyClient({
270
- region,
271
- credentials: {
272
- accessKeyId,
273
- secretAccessKey
274
- }
275
- });
271
+ const {region, accessKeyId, secretAccessKey, roleArn} = credentials;
272
+ let polly;
273
+ if (accessKeyId && secretAccessKey) {
274
+ polly = new PollyClient({
275
+ region,
276
+ credentials: {
277
+ accessKeyId,
278
+ secretAccessKey
279
+ }
280
+ });
281
+ } else if (roleArn) {
282
+ polly = new PollyClient({
283
+ region,
284
+ credentials: await getAwsAuthToken(logger, createHash, retrieveHash, null, null, region, roleArn),
285
+ });
286
+ } else {
287
+ // AWS RoleArn assigned to Instance profile
288
+ polly = new PollyClient({region});
289
+ }
276
290
  const opts = {
277
291
  Engine: engine,
278
292
  OutputFormat: 'mp3',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jambonz/speech-utils",
3
- "version": "0.0.51",
3
+ "version": "0.1.0",
4
4
  "description": "TTS-related speech utilities for jambonz",
5
5
  "main": "index.js",
6
6
  "author": "Dave Horton",
package/test/synth.js CHANGED
@@ -162,6 +162,33 @@ test('AWS speech synth tests', async(t) => {
162
162
  client.quit();
163
163
  });
164
164
 
165
+ test('AWS speech synth tests by RoleArn', async(t) => {
166
+ const fn = require('..');
167
+ const {synthAudio, client} = fn(opts, logger);
168
+
169
+ if (!process.env.AWS_ROLE_ARN || !process.env.AWS_REGION) {
170
+ t.pass('skipping AWS speech synth tests by RoleArn since AWS_ROLE_ARN or AWS_REGION not provided');
171
+ return t.end();
172
+ }
173
+ try {
174
+ let opts = await synthAudio(stats, {
175
+ vendor: 'aws',
176
+ credentials: {
177
+ roleArn: process.env.AWS_ROLE_ARN,
178
+ region: process.env.AWS_REGION,
179
+ },
180
+ language: 'en-US',
181
+ voice: 'Joey',
182
+ text: 'This is a test. This is only a test',
183
+ });
184
+ t.ok(!opts.servedFromCache, `successfully synthesized aws by roleArn audio to ${opts.filePath}`);
185
+ } catch (err) {
186
+ console.error(err);
187
+ t.end(err);
188
+ }
189
+ client.quit();
190
+ });
191
+
165
192
  test('Azure speech synth tests', async(t) => {
166
193
  const fn = require('..');
167
194
  const {synthAudio, client} = fn(opts, logger);