@loonylabs/tts-middleware 0.1.1
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/LICENSE +21 -0
- package/README.md +201 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/services/tts/index.d.ts +30 -0
- package/dist/middleware/services/tts/index.d.ts.map +1 -0
- package/dist/middleware/services/tts/index.js +69 -0
- package/dist/middleware/services/tts/index.js.map +1 -0
- package/dist/middleware/services/tts/providers/azure-provider.d.ts +131 -0
- package/dist/middleware/services/tts/providers/azure-provider.d.ts.map +1 -0
- package/dist/middleware/services/tts/providers/azure-provider.js +375 -0
- package/dist/middleware/services/tts/providers/azure-provider.js.map +1 -0
- package/dist/middleware/services/tts/providers/base-tts-provider.d.ts +204 -0
- package/dist/middleware/services/tts/providers/base-tts-provider.d.ts.map +1 -0
- package/dist/middleware/services/tts/providers/base-tts-provider.js +267 -0
- package/dist/middleware/services/tts/providers/base-tts-provider.js.map +1 -0
- package/dist/middleware/services/tts/providers/edenai-provider.d.ts +112 -0
- package/dist/middleware/services/tts/providers/edenai-provider.d.ts.map +1 -0
- package/dist/middleware/services/tts/providers/edenai-provider.js +289 -0
- package/dist/middleware/services/tts/providers/edenai-provider.js.map +1 -0
- package/dist/middleware/services/tts/providers/index.d.ts +9 -0
- package/dist/middleware/services/tts/providers/index.d.ts.map +1 -0
- package/dist/middleware/services/tts/providers/index.js +29 -0
- package/dist/middleware/services/tts/providers/index.js.map +1 -0
- package/dist/middleware/services/tts/tts.service.d.ts +175 -0
- package/dist/middleware/services/tts/tts.service.d.ts.map +1 -0
- package/dist/middleware/services/tts/tts.service.js +287 -0
- package/dist/middleware/services/tts/tts.service.js.map +1 -0
- package/dist/middleware/services/tts/types/common.types.d.ts +303 -0
- package/dist/middleware/services/tts/types/common.types.d.ts.map +1 -0
- package/dist/middleware/services/tts/types/common.types.js +42 -0
- package/dist/middleware/services/tts/types/common.types.js.map +1 -0
- package/dist/middleware/services/tts/types/index.d.ts +22 -0
- package/dist/middleware/services/tts/types/index.d.ts.map +1 -0
- package/dist/middleware/services/tts/types/index.js +46 -0
- package/dist/middleware/services/tts/types/index.js.map +1 -0
- package/dist/middleware/services/tts/types/provider-options.types.d.ts +414 -0
- package/dist/middleware/services/tts/types/provider-options.types.d.ts.map +1 -0
- package/dist/middleware/services/tts/types/provider-options.types.js +71 -0
- package/dist/middleware/services/tts/types/provider-options.types.js.map +1 -0
- package/dist/middleware/services/tts/utils/character-counter.utils.d.ts +160 -0
- package/dist/middleware/services/tts/utils/character-counter.utils.d.ts.map +1 -0
- package/dist/middleware/services/tts/utils/character-counter.utils.js +205 -0
- package/dist/middleware/services/tts/utils/character-counter.utils.js.map +1 -0
- package/dist/middleware/services/tts/utils/index.d.ts +9 -0
- package/dist/middleware/services/tts/utils/index.d.ts.map +1 -0
- package/dist/middleware/services/tts/utils/index.js +25 -0
- package/dist/middleware/services/tts/utils/index.js.map +1 -0
- package/dist/middleware/services/tts/utils/logger.utils.d.ts +116 -0
- package/dist/middleware/services/tts/utils/logger.utils.d.ts.map +1 -0
- package/dist/middleware/services/tts/utils/logger.utils.js +186 -0
- package/dist/middleware/services/tts/utils/logger.utils.js.map +1 -0
- package/dist/middleware/shared/config/tts.config.d.ts +147 -0
- package/dist/middleware/shared/config/tts.config.d.ts.map +1 -0
- package/dist/middleware/shared/config/tts.config.js +162 -0
- package/dist/middleware/shared/config/tts.config.js.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Base TTS Provider Abstract Class
|
|
4
|
+
*
|
|
5
|
+
* All TTS providers must extend this class and implement the abstract methods.
|
|
6
|
+
* This ensures consistency across all providers.
|
|
7
|
+
*
|
|
8
|
+
* @abstract
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.BaseTTSProvider = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = void 0;
|
|
12
|
+
const logger_utils_1 = require("../utils/logger.utils");
|
|
13
|
+
/**
|
|
14
|
+
* Base error class for all TTS errors
|
|
15
|
+
*/
|
|
16
|
+
class TTSError extends Error {
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new TTS error
|
|
19
|
+
*
|
|
20
|
+
* @param provider - The provider that threw the error
|
|
21
|
+
* @param code - Error code for categorization
|
|
22
|
+
* @param message - Human-readable error message
|
|
23
|
+
* @param cause - Optional underlying error that caused this error
|
|
24
|
+
*/
|
|
25
|
+
constructor(provider, code, message, cause) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.provider = provider;
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.cause = cause;
|
|
30
|
+
this.name = 'TTSError';
|
|
31
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
32
|
+
if (Error.captureStackTrace) {
|
|
33
|
+
Error.captureStackTrace(this, TTSError);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns a formatted error message with context
|
|
38
|
+
*/
|
|
39
|
+
toString() {
|
|
40
|
+
return `[${this.provider}] ${this.code}: ${this.message}${this.cause ? ` (caused by: ${this.cause.message})` : ''}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.TTSError = TTSError;
|
|
44
|
+
/**
|
|
45
|
+
* Error thrown when TTS configuration is invalid or missing
|
|
46
|
+
*/
|
|
47
|
+
class InvalidConfigError extends TTSError {
|
|
48
|
+
constructor(provider, message, cause) {
|
|
49
|
+
super(provider, 'INVALID_CONFIG', message, cause);
|
|
50
|
+
this.name = 'InvalidConfigError';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.InvalidConfigError = InvalidConfigError;
|
|
54
|
+
/**
|
|
55
|
+
* Error thrown when voice ID is invalid or not found
|
|
56
|
+
*/
|
|
57
|
+
class InvalidVoiceError extends TTSError {
|
|
58
|
+
constructor(provider, voiceId, message, cause) {
|
|
59
|
+
super(provider, 'INVALID_VOICE', message || `Voice not found: ${voiceId}`, cause);
|
|
60
|
+
this.name = 'InvalidVoiceError';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.InvalidVoiceError = InvalidVoiceError;
|
|
64
|
+
/**
|
|
65
|
+
* Error thrown when provider quota/rate limit is exceeded
|
|
66
|
+
*/
|
|
67
|
+
class QuotaExceededError extends TTSError {
|
|
68
|
+
constructor(provider, message, cause) {
|
|
69
|
+
super(provider, 'QUOTA_EXCEEDED', message || 'Provider quota or rate limit exceeded', cause);
|
|
70
|
+
this.name = 'QuotaExceededError';
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.QuotaExceededError = QuotaExceededError;
|
|
74
|
+
/**
|
|
75
|
+
* Error thrown when provider service is unavailable
|
|
76
|
+
*/
|
|
77
|
+
class ProviderUnavailableError extends TTSError {
|
|
78
|
+
constructor(provider, message, cause) {
|
|
79
|
+
super(provider, 'PROVIDER_UNAVAILABLE', message || 'Provider service is temporarily unavailable', cause);
|
|
80
|
+
this.name = 'ProviderUnavailableError';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.ProviderUnavailableError = ProviderUnavailableError;
|
|
84
|
+
/**
|
|
85
|
+
* Error thrown when synthesis fails for unknown reasons
|
|
86
|
+
*/
|
|
87
|
+
class SynthesisFailedError extends TTSError {
|
|
88
|
+
constructor(provider, message, cause) {
|
|
89
|
+
super(provider, 'SYNTHESIS_FAILED', message, cause);
|
|
90
|
+
this.name = 'SynthesisFailedError';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.SynthesisFailedError = SynthesisFailedError;
|
|
94
|
+
/**
|
|
95
|
+
* Error thrown when network operation fails
|
|
96
|
+
*/
|
|
97
|
+
class NetworkError extends TTSError {
|
|
98
|
+
constructor(provider, message, cause) {
|
|
99
|
+
super(provider, 'NETWORK_ERROR', message, cause);
|
|
100
|
+
this.name = 'NetworkError';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
exports.NetworkError = NetworkError;
|
|
104
|
+
/**
|
|
105
|
+
* Abstract base class for all TTS providers
|
|
106
|
+
*
|
|
107
|
+
* @abstract
|
|
108
|
+
*
|
|
109
|
+
* @description All provider implementations must:
|
|
110
|
+
* - Extend this class
|
|
111
|
+
* - Implement the abstract synthesize() method
|
|
112
|
+
* - Call super() in constructor with the provider name
|
|
113
|
+
* - Use the provided error classes for consistent error handling
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* class AzureProvider extends BaseTTSProvider {
|
|
118
|
+
* constructor() {
|
|
119
|
+
* super(TTSProvider.AZURE);
|
|
120
|
+
* }
|
|
121
|
+
*
|
|
122
|
+
* async synthesize(text: string, voiceId: string, request: TTSSynthesizeRequest): Promise<TTSResponse> {
|
|
123
|
+
* this.validateConfig(request);
|
|
124
|
+
* // ... implementation
|
|
125
|
+
* }
|
|
126
|
+
* }
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
class BaseTTSProvider {
|
|
130
|
+
/**
|
|
131
|
+
* Creates a new TTS provider
|
|
132
|
+
*
|
|
133
|
+
* @param providerName - The provider identifier
|
|
134
|
+
*/
|
|
135
|
+
constructor(providerName) {
|
|
136
|
+
this.providerName = providerName;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the provider name
|
|
140
|
+
*
|
|
141
|
+
* @returns The provider identifier
|
|
142
|
+
*/
|
|
143
|
+
getProviderName() {
|
|
144
|
+
return this.providerName;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Validate provider configuration
|
|
148
|
+
*
|
|
149
|
+
* @protected
|
|
150
|
+
* @param request - The synthesis request to validate
|
|
151
|
+
* @throws {InvalidConfigError} If configuration is invalid
|
|
152
|
+
*
|
|
153
|
+
* @description Subclasses should override this to validate provider-specific
|
|
154
|
+
* configuration (e.g., API keys, region settings)
|
|
155
|
+
*/
|
|
156
|
+
validateConfig(request) {
|
|
157
|
+
if (!request.text || request.text.trim().length === 0) {
|
|
158
|
+
throw new InvalidConfigError(this.providerName, 'Text cannot be empty');
|
|
159
|
+
}
|
|
160
|
+
if (!request.voice || !request.voice.id) {
|
|
161
|
+
throw new InvalidConfigError(this.providerName, 'Voice ID is required');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Count characters in text for billing purposes
|
|
166
|
+
*
|
|
167
|
+
* @protected
|
|
168
|
+
* @param text - The input text to count
|
|
169
|
+
* @returns The number of characters (including spaces and punctuation)
|
|
170
|
+
*
|
|
171
|
+
* @description This is the base implementation that counts all characters.
|
|
172
|
+
* Providers that use SSML or have special character counting rules
|
|
173
|
+
* should override this method.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* const count = this.countCharacters("Hello World!"); // returns 12
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
countCharacters(text) {
|
|
181
|
+
return text.length;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Validate that a voice exists
|
|
185
|
+
*
|
|
186
|
+
* @protected
|
|
187
|
+
* @param _voiceId - The voice identifier to validate (unused in base implementation)
|
|
188
|
+
* @returns Promise resolving to true if voice exists
|
|
189
|
+
* @throws {InvalidVoiceError} If voice does not exist
|
|
190
|
+
*
|
|
191
|
+
* @description Base implementation always returns true. Providers should
|
|
192
|
+
* override this to validate voice IDs against their voice catalogs.
|
|
193
|
+
*/
|
|
194
|
+
async validateVoiceExists(_voiceId) {
|
|
195
|
+
// Base implementation: assume voice exists
|
|
196
|
+
// Subclasses should override to validate against provider's voice catalog
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Create a standardized error from a provider-specific error
|
|
201
|
+
*
|
|
202
|
+
* @protected
|
|
203
|
+
* @param error - The original error
|
|
204
|
+
* @param context - Additional context for debugging
|
|
205
|
+
* @returns A standardized TTS error
|
|
206
|
+
*
|
|
207
|
+
* @description Wraps provider-specific errors in our error classes
|
|
208
|
+
* for consistent error handling across providers.
|
|
209
|
+
*/
|
|
210
|
+
handleError(error, context) {
|
|
211
|
+
// If it's already a TTSError, return it
|
|
212
|
+
if (error instanceof TTSError) {
|
|
213
|
+
return error;
|
|
214
|
+
}
|
|
215
|
+
// Check for common HTTP error codes
|
|
216
|
+
const errorMessage = error.message.toLowerCase();
|
|
217
|
+
if (errorMessage.includes('401') || errorMessage.includes('403')) {
|
|
218
|
+
return new InvalidConfigError(this.providerName, `Authentication failed${context ? `: ${context}` : ''}`, error);
|
|
219
|
+
}
|
|
220
|
+
if (errorMessage.includes('429')) {
|
|
221
|
+
return new QuotaExceededError(this.providerName, `Rate limit exceeded${context ? `: ${context}` : ''}`, error);
|
|
222
|
+
}
|
|
223
|
+
if (errorMessage.includes('503') ||
|
|
224
|
+
errorMessage.includes('504') ||
|
|
225
|
+
errorMessage.includes('502')) {
|
|
226
|
+
return new ProviderUnavailableError(this.providerName, `Service temporarily unavailable${context ? `: ${context}` : ''}`, error);
|
|
227
|
+
}
|
|
228
|
+
if (errorMessage.includes('timeout') ||
|
|
229
|
+
errorMessage.includes('econnrefused') ||
|
|
230
|
+
errorMessage.includes('enotfound')) {
|
|
231
|
+
return new NetworkError(this.providerName, `Network error${context ? `: ${context}` : ''}`, error);
|
|
232
|
+
}
|
|
233
|
+
// Default to SynthesisFailedError for unknown errors
|
|
234
|
+
return new SynthesisFailedError(this.providerName, `Synthesis failed${context ? `: ${context}` : ''}: ${error.message}`, error);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Log a message using the pluggable logger
|
|
238
|
+
*
|
|
239
|
+
* @protected
|
|
240
|
+
* @param level - Log level
|
|
241
|
+
* @param message - Log message
|
|
242
|
+
* @param meta - Optional metadata
|
|
243
|
+
*
|
|
244
|
+
* @description Uses the global TTS logger which can be customized via setLogger().
|
|
245
|
+
* By default uses console logging, but can be replaced with any logger (Winston, Pino, etc.)
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* // In application code:
|
|
250
|
+
* import { setLogger, silentLogger } from '@loonylabs/tts-middleware';
|
|
251
|
+
*
|
|
252
|
+
* // Disable all logging
|
|
253
|
+
* setLogger(silentLogger);
|
|
254
|
+
*
|
|
255
|
+
* // Use custom logger
|
|
256
|
+
* setLogger({
|
|
257
|
+
* info: (msg, meta) => myLogger.info(msg, meta),
|
|
258
|
+
* // ...
|
|
259
|
+
* });
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
log(level, message, meta) {
|
|
263
|
+
(0, logger_utils_1.log)(this.providerName, level, message, meta);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
exports.BaseTTSProvider = BaseTTSProvider;
|
|
267
|
+
//# sourceMappingURL=base-tts-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-tts-provider.js","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/base-tts-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EdenAI TTS Provider
|
|
3
|
+
*
|
|
4
|
+
* @description Provider for EdenAI TTS API (multi-provider aggregator)
|
|
5
|
+
* EdenAI acts as an aggregator, providing access to multiple TTS providers
|
|
6
|
+
* (Amazon, Google, IBM, Microsoft, OpenAI, ElevenLabs) through a single API.
|
|
7
|
+
*
|
|
8
|
+
* @see https://docs.edenai.co/reference/text_to_speech_create
|
|
9
|
+
*/
|
|
10
|
+
import type { TTSSynthesizeRequest, TTSResponse } from '../types';
|
|
11
|
+
import { BaseTTSProvider } from './base-tts-provider';
|
|
12
|
+
/**
|
|
13
|
+
* EdenAI-specific configuration
|
|
14
|
+
*/
|
|
15
|
+
interface EdenAIConfig {
|
|
16
|
+
apiKey: string;
|
|
17
|
+
apiUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* EdenAI provider implementation
|
|
21
|
+
*
|
|
22
|
+
* @description Provides TTS synthesis using EdenAI's multi-provider aggregator API.
|
|
23
|
+
* EdenAI routes requests to underlying providers (Amazon, Google, IBM, Microsoft, OpenAI, ElevenLabs).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const provider = new EdenAIProvider();
|
|
28
|
+
* const response = await provider.synthesize(
|
|
29
|
+
* "Hello World",
|
|
30
|
+
* "en-US",
|
|
31
|
+
* {
|
|
32
|
+
* text: "Hello World",
|
|
33
|
+
* voice: { id: "en-US-JennyNeural" },
|
|
34
|
+
* audio: { speed: 1.0, format: "mp3" },
|
|
35
|
+
* providerOptions: { provider: "google" }
|
|
36
|
+
* }
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare class EdenAIProvider extends BaseTTSProvider {
|
|
41
|
+
private config;
|
|
42
|
+
private readonly apiUrl;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new EdenAI TTS provider
|
|
45
|
+
*
|
|
46
|
+
* @param config - Optional EdenAI configuration (uses env vars if not provided)
|
|
47
|
+
* @throws {InvalidConfigError} If API key is missing
|
|
48
|
+
*/
|
|
49
|
+
constructor(config?: Partial<EdenAIConfig>);
|
|
50
|
+
/**
|
|
51
|
+
* Validate EdenAI-specific configuration
|
|
52
|
+
*
|
|
53
|
+
* @private
|
|
54
|
+
* @throws {InvalidConfigError} If configuration is invalid
|
|
55
|
+
*/
|
|
56
|
+
private validateEdenAIConfig;
|
|
57
|
+
/**
|
|
58
|
+
* Synthesize text to speech using EdenAI
|
|
59
|
+
*
|
|
60
|
+
* @param text - The input text to synthesize
|
|
61
|
+
* @param voiceId - The voice identifier (language code or voice name)
|
|
62
|
+
* @param request - The full synthesis request with options
|
|
63
|
+
* @returns Promise resolving to the synthesis response
|
|
64
|
+
* @throws {InvalidConfigError} If configuration is invalid
|
|
65
|
+
* @throws {SynthesisFailedError} If synthesis fails
|
|
66
|
+
*/
|
|
67
|
+
synthesize(text: string, voiceId: string, request: TTSSynthesizeRequest): Promise<TTSResponse>;
|
|
68
|
+
/**
|
|
69
|
+
* Build EdenAI API request payload
|
|
70
|
+
*
|
|
71
|
+
* @private
|
|
72
|
+
* @param text - The input text
|
|
73
|
+
* @param voiceId - The voice identifier
|
|
74
|
+
* @param request - The synthesis request
|
|
75
|
+
* @param options - EdenAI-specific options
|
|
76
|
+
* @returns Request payload for EdenAI API
|
|
77
|
+
*/
|
|
78
|
+
private buildEdenAIRequest;
|
|
79
|
+
/**
|
|
80
|
+
* Call EdenAI API
|
|
81
|
+
*
|
|
82
|
+
* @private
|
|
83
|
+
* @param requestBody - The request payload
|
|
84
|
+
* @returns Promise resolving to audio buffer
|
|
85
|
+
* @throws {SynthesisFailedError} If API call fails
|
|
86
|
+
*/
|
|
87
|
+
private callEdenAIAPI;
|
|
88
|
+
/**
|
|
89
|
+
* Extract audio buffer from EdenAI response
|
|
90
|
+
*
|
|
91
|
+
* @private
|
|
92
|
+
* @param data - The EdenAI API response
|
|
93
|
+
* @returns Audio buffer or null
|
|
94
|
+
*/
|
|
95
|
+
private extractAudioFromResponse;
|
|
96
|
+
/**
|
|
97
|
+
* Extract language code from voice ID
|
|
98
|
+
*
|
|
99
|
+
* @private
|
|
100
|
+
* @param voiceId - The voice identifier
|
|
101
|
+
* @returns Language code (e.g., 'en-US', 'de-DE')
|
|
102
|
+
*
|
|
103
|
+
* @description Extracts language code from voice IDs like:
|
|
104
|
+
* - 'en-US-JennyNeural' -> 'en-US'
|
|
105
|
+
* - 'de-DE-KatjaNeural' -> 'de-DE'
|
|
106
|
+
* - 'en-US' -> 'en-US'
|
|
107
|
+
* - 'en' -> 'en'
|
|
108
|
+
*/
|
|
109
|
+
private extractLanguage;
|
|
110
|
+
}
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=edenai-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edenai-provider.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/edenai-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAElE,OAAO,EACL,eAAe,EAEhB,MAAM,qBAAqB,CAAC;AAG7B;;GAEG;AACH,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAeD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,cAAe,SAAQ,eAAe;IACjD,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,YAAY,CAAC;IAqB1C;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAS5B;;;;;;;;;OASG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC;IA2DvB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IAgE1B;;;;;;;OAOG;YACW,aAAa;IAmC3B;;;;;;OAMG;YACW,wBAAwB;IA2CtC;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,eAAe;CAWxB"}
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* EdenAI TTS Provider
|
|
4
|
+
*
|
|
5
|
+
* @description Provider for EdenAI TTS API (multi-provider aggregator)
|
|
6
|
+
* EdenAI acts as an aggregator, providing access to multiple TTS providers
|
|
7
|
+
* (Amazon, Google, IBM, Microsoft, OpenAI, ElevenLabs) through a single API.
|
|
8
|
+
*
|
|
9
|
+
* @see https://docs.edenai.co/reference/text_to_speech_create
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.EdenAIProvider = void 0;
|
|
13
|
+
const types_1 = require("../types");
|
|
14
|
+
const base_tts_provider_1 = require("./base-tts-provider");
|
|
15
|
+
/**
|
|
16
|
+
* EdenAI provider implementation
|
|
17
|
+
*
|
|
18
|
+
* @description Provides TTS synthesis using EdenAI's multi-provider aggregator API.
|
|
19
|
+
* EdenAI routes requests to underlying providers (Amazon, Google, IBM, Microsoft, OpenAI, ElevenLabs).
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const provider = new EdenAIProvider();
|
|
24
|
+
* const response = await provider.synthesize(
|
|
25
|
+
* "Hello World",
|
|
26
|
+
* "en-US",
|
|
27
|
+
* {
|
|
28
|
+
* text: "Hello World",
|
|
29
|
+
* voice: { id: "en-US-JennyNeural" },
|
|
30
|
+
* audio: { speed: 1.0, format: "mp3" },
|
|
31
|
+
* providerOptions: { provider: "google" }
|
|
32
|
+
* }
|
|
33
|
+
* );
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
class EdenAIProvider extends base_tts_provider_1.BaseTTSProvider {
|
|
37
|
+
/**
|
|
38
|
+
* Creates a new EdenAI TTS provider
|
|
39
|
+
*
|
|
40
|
+
* @param config - Optional EdenAI configuration (uses env vars if not provided)
|
|
41
|
+
* @throws {InvalidConfigError} If API key is missing
|
|
42
|
+
*/
|
|
43
|
+
constructor(config) {
|
|
44
|
+
super(types_1.TTSProvider.EDENAI);
|
|
45
|
+
// Load configuration from environment or provided config
|
|
46
|
+
this.config = {
|
|
47
|
+
apiKey: config?.apiKey || process.env.EDENAI_API_KEY || '',
|
|
48
|
+
apiUrl: config?.apiUrl,
|
|
49
|
+
};
|
|
50
|
+
this.apiUrl =
|
|
51
|
+
this.config.apiUrl || 'https://api.edenai.run/v2/audio/text_to_speech';
|
|
52
|
+
// Validate configuration
|
|
53
|
+
this.validateEdenAIConfig();
|
|
54
|
+
this.log('info', 'EdenAI provider initialized', {
|
|
55
|
+
hasApiKey: !!this.config.apiKey,
|
|
56
|
+
apiUrl: this.apiUrl,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Validate EdenAI-specific configuration
|
|
61
|
+
*
|
|
62
|
+
* @private
|
|
63
|
+
* @throws {InvalidConfigError} If configuration is invalid
|
|
64
|
+
*/
|
|
65
|
+
validateEdenAIConfig() {
|
|
66
|
+
if (!this.config.apiKey) {
|
|
67
|
+
throw new base_tts_provider_1.InvalidConfigError(this.providerName, 'EdenAI API key is required (EDENAI_API_KEY)');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Synthesize text to speech using EdenAI
|
|
72
|
+
*
|
|
73
|
+
* @param text - The input text to synthesize
|
|
74
|
+
* @param voiceId - The voice identifier (language code or voice name)
|
|
75
|
+
* @param request - The full synthesis request with options
|
|
76
|
+
* @returns Promise resolving to the synthesis response
|
|
77
|
+
* @throws {InvalidConfigError} If configuration is invalid
|
|
78
|
+
* @throws {SynthesisFailedError} If synthesis fails
|
|
79
|
+
*/
|
|
80
|
+
async synthesize(text, voiceId, request) {
|
|
81
|
+
// Validate configuration
|
|
82
|
+
this.validateConfig(request);
|
|
83
|
+
const startTime = Date.now();
|
|
84
|
+
// Extract options (use as EdenAIProviderOptions if it looks like it, otherwise empty)
|
|
85
|
+
const options = (request.providerOptions || {});
|
|
86
|
+
// Build EdenAI request
|
|
87
|
+
const edenaiRequest = this.buildEdenAIRequest(text, voiceId, request, options);
|
|
88
|
+
this.log('debug', 'Synthesizing with EdenAI', {
|
|
89
|
+
voiceId,
|
|
90
|
+
provider: options.provider || 'auto',
|
|
91
|
+
textLength: text.length,
|
|
92
|
+
});
|
|
93
|
+
try {
|
|
94
|
+
// Call EdenAI API
|
|
95
|
+
const audioBuffer = await this.callEdenAIAPI(edenaiRequest);
|
|
96
|
+
// Calculate duration
|
|
97
|
+
const duration = Date.now() - startTime;
|
|
98
|
+
// Count billable characters (original text)
|
|
99
|
+
const characters = this.countCharacters(text);
|
|
100
|
+
this.log('info', 'Synthesis successful', {
|
|
101
|
+
voiceId,
|
|
102
|
+
characters,
|
|
103
|
+
duration,
|
|
104
|
+
audioSize: audioBuffer.length,
|
|
105
|
+
});
|
|
106
|
+
// Return response
|
|
107
|
+
return {
|
|
108
|
+
audio: audioBuffer,
|
|
109
|
+
metadata: {
|
|
110
|
+
provider: this.providerName,
|
|
111
|
+
voice: voiceId,
|
|
112
|
+
duration,
|
|
113
|
+
audioFormat: request.audio?.format || options.audio_format || 'mp3',
|
|
114
|
+
sampleRate: request.audio?.sampleRate || options.sampling_rate || 24000,
|
|
115
|
+
},
|
|
116
|
+
billing: {
|
|
117
|
+
characters,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
this.log('error', 'Synthesis failed', {
|
|
123
|
+
voiceId,
|
|
124
|
+
error: error.message,
|
|
125
|
+
});
|
|
126
|
+
throw this.handleError(error, 'during EdenAI API call');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Build EdenAI API request payload
|
|
131
|
+
*
|
|
132
|
+
* @private
|
|
133
|
+
* @param text - The input text
|
|
134
|
+
* @param voiceId - The voice identifier
|
|
135
|
+
* @param request - The synthesis request
|
|
136
|
+
* @param options - EdenAI-specific options
|
|
137
|
+
* @returns Request payload for EdenAI API
|
|
138
|
+
*/
|
|
139
|
+
buildEdenAIRequest(text, voiceId, request, options) {
|
|
140
|
+
// Extract language from voice ID (e.g., 'en-US-JennyNeural' -> 'en-US', 'en-US' -> 'en-US')
|
|
141
|
+
const language = this.extractLanguage(voiceId);
|
|
142
|
+
// Base request
|
|
143
|
+
const edenaiRequest = {
|
|
144
|
+
text,
|
|
145
|
+
language,
|
|
146
|
+
option: voiceId, // Use voiceId as option for voice selection
|
|
147
|
+
providers: options.provider || 'google', // Top-level providers field
|
|
148
|
+
};
|
|
149
|
+
// Build settings object for provider-specific options
|
|
150
|
+
const settings = {};
|
|
151
|
+
// Speaking rate (maps to speed)
|
|
152
|
+
const speakingRate = options.speaking_rate ?? request.audio?.speed;
|
|
153
|
+
if (speakingRate !== undefined) {
|
|
154
|
+
settings.speaking_rate = speakingRate;
|
|
155
|
+
}
|
|
156
|
+
// Speaking pitch
|
|
157
|
+
if (options.speaking_pitch !== undefined) {
|
|
158
|
+
settings.speaking_pitch = options.speaking_pitch;
|
|
159
|
+
}
|
|
160
|
+
// Speaking volume
|
|
161
|
+
if (options.speaking_volume !== undefined) {
|
|
162
|
+
settings.speaking_volume = options.speaking_volume;
|
|
163
|
+
}
|
|
164
|
+
// Audio format
|
|
165
|
+
if (options.audio_format !== undefined) {
|
|
166
|
+
settings.audio_format = options.audio_format;
|
|
167
|
+
}
|
|
168
|
+
// Sampling rate
|
|
169
|
+
if (options.sampling_rate !== undefined) {
|
|
170
|
+
settings.sampling_rate = options.sampling_rate;
|
|
171
|
+
}
|
|
172
|
+
// Add providers to settings if provider is explicitly specified
|
|
173
|
+
if (options.provider) {
|
|
174
|
+
settings.providers = options.provider;
|
|
175
|
+
}
|
|
176
|
+
// Add settings to request if any settings were specified
|
|
177
|
+
if (Object.keys(settings).length > 0) {
|
|
178
|
+
edenaiRequest.settings = settings;
|
|
179
|
+
}
|
|
180
|
+
// Fallback providers (top-level)
|
|
181
|
+
if (options.fallback_providers && options.fallback_providers.length > 0) {
|
|
182
|
+
edenaiRequest.fallback_providers = options.fallback_providers;
|
|
183
|
+
}
|
|
184
|
+
return edenaiRequest;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Call EdenAI API
|
|
188
|
+
*
|
|
189
|
+
* @private
|
|
190
|
+
* @param requestBody - The request payload
|
|
191
|
+
* @returns Promise resolving to audio buffer
|
|
192
|
+
* @throws {SynthesisFailedError} If API call fails
|
|
193
|
+
*/
|
|
194
|
+
async callEdenAIAPI(requestBody) {
|
|
195
|
+
try {
|
|
196
|
+
const response = await fetch(this.apiUrl, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: {
|
|
199
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
200
|
+
'Content-Type': 'application/json',
|
|
201
|
+
},
|
|
202
|
+
body: JSON.stringify(requestBody),
|
|
203
|
+
});
|
|
204
|
+
if (!response.ok) {
|
|
205
|
+
const errorText = await response.text();
|
|
206
|
+
throw new Error(`EdenAI API error (${response.status}): ${errorText}`);
|
|
207
|
+
}
|
|
208
|
+
const data = (await response.json());
|
|
209
|
+
// Extract audio from response
|
|
210
|
+
// EdenAI returns provider-specific results
|
|
211
|
+
const audioBuffer = await this.extractAudioFromResponse(data);
|
|
212
|
+
if (!audioBuffer) {
|
|
213
|
+
throw new Error('No audio data in EdenAI response');
|
|
214
|
+
}
|
|
215
|
+
return audioBuffer;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (error instanceof Error) {
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
throw new Error(`EdenAI API call failed: ${error}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Extract audio buffer from EdenAI response
|
|
226
|
+
*
|
|
227
|
+
* @private
|
|
228
|
+
* @param data - The EdenAI API response
|
|
229
|
+
* @returns Audio buffer or null
|
|
230
|
+
*/
|
|
231
|
+
async extractAudioFromResponse(data) {
|
|
232
|
+
// EdenAI returns results keyed by provider name
|
|
233
|
+
// Find the first successful provider response
|
|
234
|
+
for (const providerName of Object.keys(data)) {
|
|
235
|
+
const providerResult = data[providerName];
|
|
236
|
+
// Check for errors
|
|
237
|
+
if (providerResult.error || providerResult.status === 'fail') {
|
|
238
|
+
this.log('warn', `Provider ${providerName} failed`, {
|
|
239
|
+
error: providerResult.error,
|
|
240
|
+
});
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
// Check for base64-encoded audio
|
|
244
|
+
if (providerResult.audio) {
|
|
245
|
+
this.log('debug', `Using audio from provider: ${providerName}`);
|
|
246
|
+
return Buffer.from(providerResult.audio, 'base64');
|
|
247
|
+
}
|
|
248
|
+
// Check for audio URL
|
|
249
|
+
if (providerResult.audio_resource_url) {
|
|
250
|
+
this.log('debug', `Downloading audio from URL (provider: ${providerName})`);
|
|
251
|
+
const audioResponse = await fetch(providerResult.audio_resource_url);
|
|
252
|
+
if (!audioResponse.ok) {
|
|
253
|
+
this.log('warn', `Failed to download audio from URL`, {
|
|
254
|
+
provider: providerName,
|
|
255
|
+
url: providerResult.audio_resource_url,
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
const arrayBuffer = await audioResponse.arrayBuffer();
|
|
260
|
+
return Buffer.from(arrayBuffer);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Extract language code from voice ID
|
|
267
|
+
*
|
|
268
|
+
* @private
|
|
269
|
+
* @param voiceId - The voice identifier
|
|
270
|
+
* @returns Language code (e.g., 'en-US', 'de-DE')
|
|
271
|
+
*
|
|
272
|
+
* @description Extracts language code from voice IDs like:
|
|
273
|
+
* - 'en-US-JennyNeural' -> 'en-US'
|
|
274
|
+
* - 'de-DE-KatjaNeural' -> 'de-DE'
|
|
275
|
+
* - 'en-US' -> 'en-US'
|
|
276
|
+
* - 'en' -> 'en'
|
|
277
|
+
*/
|
|
278
|
+
extractLanguage(voiceId) {
|
|
279
|
+
// Match language code patterns: en-US, de-DE, etc.
|
|
280
|
+
const match = voiceId.match(/^([a-z]{2}(-[A-Z]{2})?)/);
|
|
281
|
+
if (match) {
|
|
282
|
+
return match[1];
|
|
283
|
+
}
|
|
284
|
+
// If no match, assume voice ID is already a language code
|
|
285
|
+
return voiceId;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
exports.EdenAIProvider = EdenAIProvider;
|
|
289
|
+
//# sourceMappingURL=edenai-provider.js.map
|