@loonylabs/tts-middleware 0.11.1 → 0.12.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/README.md +128 -5
- package/dist/middleware/services/tts/index.d.ts +1 -1
- package/dist/middleware/services/tts/index.d.ts.map +1 -1
- package/dist/middleware/services/tts/index.js +2 -1
- package/dist/middleware/services/tts/index.js.map +1 -1
- package/dist/middleware/services/tts/providers/base-tts-provider.d.ts +57 -0
- package/dist/middleware/services/tts/providers/base-tts-provider.d.ts.map +1 -1
- package/dist/middleware/services/tts/providers/base-tts-provider.js +67 -1
- package/dist/middleware/services/tts/providers/base-tts-provider.js.map +1 -1
- package/dist/middleware/services/tts/providers/index.d.ts +1 -1
- package/dist/middleware/services/tts/providers/index.d.ts.map +1 -1
- package/dist/middleware/services/tts/providers/index.js +2 -1
- package/dist/middleware/services/tts/providers/index.js.map +1 -1
- package/dist/middleware/services/tts/providers/vertex-ai-tts-provider.d.ts +68 -2
- package/dist/middleware/services/tts/providers/vertex-ai-tts-provider.d.ts.map +1 -1
- package/dist/middleware/services/tts/providers/vertex-ai-tts-provider.js +424 -24
- package/dist/middleware/services/tts/providers/vertex-ai-tts-provider.js.map +1 -1
- package/dist/middleware/services/tts/types/common.types.d.ts +1 -0
- package/dist/middleware/services/tts/types/common.types.d.ts.map +1 -1
- package/dist/middleware/services/tts/types/common.types.js +1 -0
- package/dist/middleware/services/tts/types/common.types.js.map +1 -1
- package/dist/middleware/services/tts/types/index.d.ts +1 -1
- package/dist/middleware/services/tts/types/index.d.ts.map +1 -1
- package/dist/middleware/services/tts/types/index.js.map +1 -1
- package/dist/middleware/services/tts/types/provider-options.types.d.ts +113 -1
- package/dist/middleware/services/tts/types/provider-options.types.d.ts.map +1 -1
- package/dist/middleware/services/tts/types/provider-options.types.js.map +1 -1
- package/dist/middleware/services/tts/utils/index.d.ts +2 -0
- package/dist/middleware/services/tts/utils/index.d.ts.map +1 -1
- package/dist/middleware/services/tts/utils/index.js +5 -1
- package/dist/middleware/services/tts/utils/index.js.map +1 -1
- package/dist/middleware/services/tts/utils/request-logger.utils.d.ts +96 -0
- package/dist/middleware/services/tts/utils/request-logger.utils.d.ts.map +1 -0
- package/dist/middleware/services/tts/utils/request-logger.utils.js +194 -0
- package/dist/middleware/services/tts/utils/request-logger.utils.js.map +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -345,16 +345,98 @@ LOG_LEVEL=info
|
|
|
345
345
|
|
|
346
346
|
| Feature | Details |
|
|
347
347
|
|---------|---------|
|
|
348
|
-
| **Models** | `gemini-2.5-flash-preview-tts` (budget, fast), `gemini-2.5-pro-preview-tts` (premium,
|
|
349
|
-
| **Languages** | 90+ with auto-detection |
|
|
350
|
-
| **Voices** | 30 multilingual: Kore, Puck, Charon, Zephyr, Fenrir, Sulafat, etc. |
|
|
351
|
-
| **Style Control** | Natural language
|
|
348
|
+
| **Models** | `gemini-2.5-flash-preview-tts` (budget, fast), `gemini-2.5-pro-preview-tts` (premium), `gemini-3.1-flash-tts-preview` (audio tags + multi-speaker) |
|
|
349
|
+
| **Languages** | 90+ with auto-detection (70+ for Gemini 3.1) |
|
|
350
|
+
| **Voices** | 30 multilingual: Kore, Puck, Charon, Zephyr, Fenrir, Sulafat, Aoede, etc. |
|
|
351
|
+
| **Style Control** | Natural language `stylePrompt` + inline audio tags (Gemini 3.1): `[sigh]`, `[whispering]`, `[laughing]`, `[short pause]`, … |
|
|
352
|
+
| **Dialog Mode** | `synthesizeDialog()` for multi-segment, multi-speaker audio in a single call — aggregated billing, segment-level style prompts. **Max 2 distinct speakers per segment** (Vertex AI limit) — split scenes with a narrator into alternating solo/duo segments |
|
|
352
353
|
| **Audio** | MP3 (via ffmpeg — auto-detected from `ffmpeg-static`, `FFMPEG_PATH`, config, or system PATH), WAV (fallback) |
|
|
353
354
|
| **Auth** | Service Account OAuth2 (reuses `GOOGLE_APPLICATION_CREDENTIALS`) |
|
|
354
355
|
| **Region** | `VERTEX_AI_TTS_REGION` env var (default: `us-central1`) |
|
|
355
|
-
| **
|
|
356
|
+
| **Limits** | 4 KB text + 4 KB stylePrompt, 8 KB combined per request (enforced client-side with typed `PayloadTooLargeError`) |
|
|
357
|
+
| **Pricing** | $0.50/M input + $10/M audio output tokens (Flash 2.5); $1.00/M + $20/M (Pro 2.5, Flash 3.1) |
|
|
356
358
|
| **EU Compliance** | Preview models currently `us-central1` only — no EU data residency yet |
|
|
357
359
|
|
|
360
|
+
#### Dialog Mode example (Gemini 3.1 Flash TTS)
|
|
361
|
+
|
|
362
|
+
Synthesize a multi-speaker dialog with per-segment style direction and inline
|
|
363
|
+
audio tags — one call, one audio file, aggregated billing:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { VertexAITTSProvider } from '@loonylabs/tts-middleware';
|
|
367
|
+
|
|
368
|
+
const provider = new VertexAITTSProvider();
|
|
369
|
+
|
|
370
|
+
const result = await provider.synthesizeDialog({
|
|
371
|
+
speakers: [
|
|
372
|
+
{ speaker: 'Narrator', voice: 'Charon' },
|
|
373
|
+
{ speaker: 'Alice', voice: 'Aoede' },
|
|
374
|
+
{ speaker: 'Bob', voice: 'Puck' },
|
|
375
|
+
],
|
|
376
|
+
segments: [
|
|
377
|
+
{
|
|
378
|
+
stylePrompt: 'Calm audiobook narration',
|
|
379
|
+
turns: [
|
|
380
|
+
{ speaker: 'Narrator', text: 'The tavern was loud that night.' },
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
stylePrompt: 'A heated argument between two old friends',
|
|
385
|
+
turns: [
|
|
386
|
+
{ speaker: 'Alice', text: '[shouting] You lied to me!' },
|
|
387
|
+
{ speaker: 'Bob', text: '[sigh] [short pause] Calm down, would you?' },
|
|
388
|
+
{ speaker: 'Alice', text: '[whispering] Never again.' },
|
|
389
|
+
],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
stylePrompt: 'Calm audiobook narration',
|
|
393
|
+
turns: [
|
|
394
|
+
{ speaker: 'Narrator', text: 'She stood up and left.' },
|
|
395
|
+
],
|
|
396
|
+
},
|
|
397
|
+
],
|
|
398
|
+
voice: { languageCode: 'en-US' },
|
|
399
|
+
audio: { format: 'mp3' },
|
|
400
|
+
providerOptions: { model: 'gemini-3.1-flash-tts-preview', temperature: 1.2 },
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
// result.audio — single concatenated MP3 buffer
|
|
404
|
+
// result.billing.characters — total chars sent to Google across ALL segments
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Billing:** `result.billing.characters` is the sum of every turn text
|
|
408
|
+
(including the `Speaker: ` prefix sent to Google) plus every segment's
|
|
409
|
+
`stylePrompt`. Consumer apps can bill customers for the exact amount that
|
|
410
|
+
hit Google, not just the first segment.
|
|
411
|
+
|
|
412
|
+
**Payload limits:** Each segment must stay under 4 KB of text and 8 KB
|
|
413
|
+
combined (text + stylePrompt). Exceeding any limit throws
|
|
414
|
+
`PayloadTooLargeError` with `segmentIndex` *before* the API call — no
|
|
415
|
+
billing for rejected requests.
|
|
416
|
+
|
|
417
|
+
**Max 2 speakers per segment:** Vertex AI's multi-speaker TTS requires
|
|
418
|
+
exactly 2 voices in each `multiSpeakerVoiceConfig`. Scenes with a narrator
|
|
419
|
+
plus two dialog speakers (3 voices total) must therefore be split into
|
|
420
|
+
alternating segments:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
segments: [
|
|
424
|
+
{ stylePrompt: 'Calm narrator', turns: [ { speaker: 'Narrator', text: '…' } ] }, // 1 voice → single-voice request
|
|
425
|
+
{ stylePrompt: 'Friends arguing', turns: [ { speaker: 'Alice', … }, { speaker: 'Bob', … } ] }, // 2 voices → multi-speaker request
|
|
426
|
+
{ stylePrompt: 'Narrator outro', turns: [ { speaker: 'Narrator', text: '…' } ] }, // 1 voice again
|
|
427
|
+
]
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The provider auto-detects 1 vs 2 distinct speakers per segment and picks
|
|
431
|
+
the correct request shape (`prebuiltVoiceConfig` vs `multiSpeakerVoiceConfig`).
|
|
432
|
+
Segments with >2 distinct speakers throw `InvalidConfigError` with guidance
|
|
433
|
+
to split the segment.
|
|
434
|
+
|
|
435
|
+
**Debugging dialog requests:** Set `DEBUG_TTS_REQUESTS=true` to have one
|
|
436
|
+
Markdown file written per segment under `logs/tts/requests/`, capturing the
|
|
437
|
+
exact request body, selected shape, speaker→voice mapping, HTTP status, and
|
|
438
|
+
timing. See [Request Debug Logging](#advanced-features) below.
|
|
439
|
+
|
|
358
440
|
## GDPR / Compliance
|
|
359
441
|
|
|
360
442
|
### Provider Compliance Overview
|
|
@@ -452,6 +534,47 @@ setLogLevel('warn');
|
|
|
452
534
|
|
|
453
535
|
</details>
|
|
454
536
|
|
|
537
|
+
<details>
|
|
538
|
+
<summary><strong>Request Debug Logging</strong></summary>
|
|
539
|
+
|
|
540
|
+
For debugging, you can have the middleware write one Markdown file per upstream
|
|
541
|
+
TTS API call (e.g. per Google Vertex AI `generateContent` invocation). This is
|
|
542
|
+
especially useful for the dialog mode: each segment is one Google request, and
|
|
543
|
+
the log shows the exact request body that was sent — so you can verify the
|
|
544
|
+
auto-selected `prebuiltVoiceConfig` vs `multiSpeakerVoiceConfig` shape,
|
|
545
|
+
speaker→voice mapping, style prompt, and temperature.
|
|
546
|
+
|
|
547
|
+
```bash
|
|
548
|
+
# Enable per-request debug logs
|
|
549
|
+
export DEBUG_TTS_REQUESTS=true
|
|
550
|
+
|
|
551
|
+
# Optional: override log directory (default: <cwd>/logs/tts/requests)
|
|
552
|
+
export TTS_REQUEST_LOG_DIR=/tmp/my-tts-logs
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Each call produces a file named like:
|
|
556
|
+
|
|
557
|
+
```
|
|
558
|
+
2026-04-17T14-30-00-000Z_vertex-ai_dialog-segment_seg0_multi-speaker.md
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Contents include: timestamp, model, region, endpoint URL, HTTP status, duration,
|
|
562
|
+
dialog context (segment index, request shape, speaker→voice mapping), the full
|
|
563
|
+
request body (no truncation), response metadata (mime type, audio byte count,
|
|
564
|
+
candidate count), and any error body.
|
|
565
|
+
|
|
566
|
+
**What is *not* logged:** the audio bytes themselves — only metadata — so logs
|
|
567
|
+
stay small and safe to inspect.
|
|
568
|
+
|
|
569
|
+
When the env var is unset (or not truthy), logging is a complete no-op with no
|
|
570
|
+
runtime cost.
|
|
571
|
+
|
|
572
|
+
The logging hook lives on `BaseTTSProvider.logRequest()`, so any provider can
|
|
573
|
+
opt in. Currently wired up for `VertexAITTSProvider` (`synthesize()` and
|
|
574
|
+
`synthesizeDialog()`); other providers log on demand when they add the hook.
|
|
575
|
+
|
|
576
|
+
</details>
|
|
577
|
+
|
|
455
578
|
<details>
|
|
456
579
|
<summary><strong>Retry with Exponential Backoff</strong></summary>
|
|
457
580
|
|
|
@@ -25,7 +25,7 @@ export { isAzureOptions, isOpenAIOptions, isElevenLabsOptions, isGoogleCloudOpti
|
|
|
25
25
|
export { BaseTTSProvider, AzureProvider, EdenAIProvider, FishAudioProvider, GoogleCloudTTSProvider, InworldProvider, VertexAITTSProvider, } from './providers';
|
|
26
26
|
export type { VertexAITTSConfig } from './providers';
|
|
27
27
|
export type { GoogleCloudTTSRegion, GoogleCloudTTSConfig, } from './providers';
|
|
28
|
-
export { TTSError, InvalidConfigError, InvalidVoiceError, QuotaExceededError, ProviderUnavailableError, SynthesisFailedError, NetworkError, } from './providers';
|
|
28
|
+
export { TTSError, InvalidConfigError, InvalidVoiceError, QuotaExceededError, ProviderUnavailableError, SynthesisFailedError, NetworkError, PayloadTooLargeError, } from './providers';
|
|
29
29
|
export { countCharacters, countCharactersWithoutSSML, validateCharacterCount, countBillableCharacters, estimateAudioDuration, formatCharacterCount, } from './utils';
|
|
30
30
|
export { setLogger, getLogger, resetLogger, setLogLevel, getLogLevel, silentLogger, } from './utils';
|
|
31
31
|
export type { TTSLogger, LogLevel } from './utils';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/middleware/services/tts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,0BAA0B,EAC1B,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,YAAY,EACV,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/middleware/services/tts/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGvD,OAAO,EACL,WAAW,EACX,YAAY,EACZ,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,QAAQ,EACR,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,0BAA0B,EAC1B,6BAA6B,EAC7B,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,sBAAsB,EACtB,0BAA0B,EAC1B,eAAe,GAChB,MAAM,SAAS,CAAC;AAEjB,OAAO,EACL,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,YAAY,EACV,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,eAAe,EACf,0BAA0B,EAC1B,sBAAsB,EACtB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,SAAS,EACT,SAAS,EACT,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnD,OAAO,EACL,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* @module @loonylabs/tts-middleware
|
|
21
21
|
*/
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.DEFAULT_RETRY_CONFIG = exports.isRetryableError = exports.executeWithRetry = exports.silentLogger = exports.getLogLevel = exports.setLogLevel = exports.resetLogger = exports.getLogger = exports.setLogger = exports.formatCharacterCount = exports.estimateAudioDuration = exports.countBillableCharacters = exports.validateCharacterCount = exports.countCharactersWithoutSSML = exports.countCharacters = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = exports.VertexAITTSProvider = exports.InworldProvider = exports.GoogleCloudTTSProvider = exports.FishAudioProvider = exports.EdenAIProvider = exports.AzureProvider = exports.BaseTTSProvider = exports.isVertexAITTSOptions = exports.isInworldOptions = exports.isFishAudioOptions = exports.isEdenAIOptions = exports.isDeepgramOptions = exports.isGoogleCloudTTSOptions = exports.isGoogleCloudOptions = exports.isElevenLabsOptions = exports.isOpenAIOptions = exports.isAzureOptions = exports.TTSErrorCode = exports.TTSProvider = exports.ttsService = exports.TTSService = void 0;
|
|
23
|
+
exports.DEFAULT_RETRY_CONFIG = exports.isRetryableError = exports.executeWithRetry = exports.silentLogger = exports.getLogLevel = exports.setLogLevel = exports.resetLogger = exports.getLogger = exports.setLogger = exports.formatCharacterCount = exports.estimateAudioDuration = exports.countBillableCharacters = exports.validateCharacterCount = exports.countCharactersWithoutSSML = exports.countCharacters = exports.PayloadTooLargeError = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = exports.VertexAITTSProvider = exports.InworldProvider = exports.GoogleCloudTTSProvider = exports.FishAudioProvider = exports.EdenAIProvider = exports.AzureProvider = exports.BaseTTSProvider = exports.isVertexAITTSOptions = exports.isInworldOptions = exports.isFishAudioOptions = exports.isEdenAIOptions = exports.isDeepgramOptions = exports.isGoogleCloudTTSOptions = exports.isGoogleCloudOptions = exports.isElevenLabsOptions = exports.isOpenAIOptions = exports.isAzureOptions = exports.TTSErrorCode = exports.TTSProvider = exports.ttsService = exports.TTSService = void 0;
|
|
24
24
|
// ===== Main Service =====
|
|
25
25
|
var tts_service_1 = require("./tts.service");
|
|
26
26
|
Object.defineProperty(exports, "TTSService", { enumerable: true, get: function () { return tts_service_1.TTSService; } });
|
|
@@ -58,6 +58,7 @@ Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: fu
|
|
|
58
58
|
Object.defineProperty(exports, "ProviderUnavailableError", { enumerable: true, get: function () { return providers_2.ProviderUnavailableError; } });
|
|
59
59
|
Object.defineProperty(exports, "SynthesisFailedError", { enumerable: true, get: function () { return providers_2.SynthesisFailedError; } });
|
|
60
60
|
Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return providers_2.NetworkError; } });
|
|
61
|
+
Object.defineProperty(exports, "PayloadTooLargeError", { enumerable: true, get: function () { return providers_2.PayloadTooLargeError; } });
|
|
61
62
|
// ===== Utilities =====
|
|
62
63
|
var utils_1 = require("./utils");
|
|
63
64
|
Object.defineProperty(exports, "countCharacters", { enumerable: true, get: function () { return utils_1.countCharacters; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middleware/services/tts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAEH,2BAA2B;AAC3B,6CAAuD;AAA9C,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAE/B,oBAAoB;AACpB,iCAIiB;AAHf,oGAAA,WAAW,OAAA;AACX,qGAAA,YAAY,OAAA;AA0Bd,iCAWiB;AAVf,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,6GAAA,oBAAoB,OAAA;AACpB,gHAAA,uBAAuB,OAAA;AACvB,0GAAA,iBAAiB,OAAA;AACjB,wGAAA,eAAe,OAAA;AACf,2GAAA,kBAAkB,OAAA;AAClB,yGAAA,gBAAgB,OAAA;AAChB,6GAAA,oBAAoB,OAAA;AAGtB,wBAAwB;AACxB,yCAQqB;AAPnB,4GAAA,eAAe,OAAA;AACf,0GAAA,aAAa,OAAA;AACb,2GAAA,cAAc,OAAA;AACd,8GAAA,iBAAiB,OAAA;AACjB,mHAAA,sBAAsB,OAAA;AACtB,4GAAA,eAAe,OAAA;AACf,gHAAA,mBAAmB,OAAA;AAUrB,qBAAqB;AACrB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/middleware/services/tts/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;GAmBG;;;AAEH,2BAA2B;AAC3B,6CAAuD;AAA9C,yGAAA,UAAU,OAAA;AAAE,yGAAA,UAAU,OAAA;AAE/B,oBAAoB;AACpB,iCAIiB;AAHf,oGAAA,WAAW,OAAA;AACX,qGAAA,YAAY,OAAA;AA0Bd,iCAWiB;AAVf,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,4GAAA,mBAAmB,OAAA;AACnB,6GAAA,oBAAoB,OAAA;AACpB,gHAAA,uBAAuB,OAAA;AACvB,0GAAA,iBAAiB,OAAA;AACjB,wGAAA,eAAe,OAAA;AACf,2GAAA,kBAAkB,OAAA;AAClB,yGAAA,gBAAgB,OAAA;AAChB,6GAAA,oBAAoB,OAAA;AAGtB,wBAAwB;AACxB,yCAQqB;AAPnB,4GAAA,eAAe,OAAA;AACf,0GAAA,aAAa,OAAA;AACb,2GAAA,cAAc,OAAA;AACd,8GAAA,iBAAiB,OAAA;AACjB,mHAAA,sBAAsB,OAAA;AACtB,4GAAA,eAAe,OAAA;AACf,gHAAA,mBAAmB,OAAA;AAUrB,qBAAqB;AACrB,yCASqB;AARnB,qGAAA,QAAQ,OAAA;AACR,+GAAA,kBAAkB,OAAA;AAClB,8GAAA,iBAAiB,OAAA;AACjB,+GAAA,kBAAkB,OAAA;AAClB,qHAAA,wBAAwB,OAAA;AACxB,iHAAA,oBAAoB,OAAA;AACpB,yGAAA,YAAY,OAAA;AACZ,iHAAA,oBAAoB,OAAA;AAGtB,wBAAwB;AACxB,iCAOiB;AANf,wGAAA,eAAe,OAAA;AACf,mHAAA,0BAA0B,OAAA;AAC1B,+GAAA,sBAAsB,OAAA;AACtB,gHAAA,uBAAuB,OAAA;AACvB,8GAAA,qBAAqB,OAAA;AACrB,6GAAA,oBAAoB,OAAA;AAGtB,qBAAqB;AACrB,iCAOiB;AANf,kGAAA,SAAS,OAAA;AACT,kGAAA,SAAS,OAAA;AACT,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AACX,qGAAA,YAAY,OAAA;AAKd,oBAAoB;AACpB,iCAIiB;AAHf,yGAAA,gBAAgB,OAAA;AAChB,yGAAA,gBAAgB,OAAA;AAChB,6GAAA,oBAAoB,OAAA"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @abstract
|
|
8
8
|
*/
|
|
9
9
|
import type { TTSProvider, TTSSynthesizeRequest, TTSResponse, TTSErrorCode } from '../types';
|
|
10
|
+
import { type TTSRequestLogEntry } from '../utils/request-logger.utils';
|
|
10
11
|
/**
|
|
11
12
|
* Base error class for all TTS errors
|
|
12
13
|
*/
|
|
@@ -64,6 +65,19 @@ export declare class SynthesisFailedError extends TTSError {
|
|
|
64
65
|
export declare class NetworkError extends TTSError {
|
|
65
66
|
constructor(provider: string, message: string, cause?: Error);
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Error thrown when the input payload exceeds a provider byte/character limit.
|
|
70
|
+
*
|
|
71
|
+
* @description Thrown *before* the remote API call, so consumers can split or
|
|
72
|
+
* shorten the input without incurring a failed request billing. For multi-segment
|
|
73
|
+
* dialogs, `segmentIndex` identifies the offending segment.
|
|
74
|
+
*/
|
|
75
|
+
export declare class PayloadTooLargeError extends TTSError {
|
|
76
|
+
readonly actualBytes?: number | undefined;
|
|
77
|
+
readonly maxBytes?: number | undefined;
|
|
78
|
+
readonly segmentIndex?: number | undefined;
|
|
79
|
+
constructor(provider: string, message: string, actualBytes?: number | undefined, maxBytes?: number | undefined, segmentIndex?: number | undefined, cause?: Error);
|
|
80
|
+
}
|
|
67
81
|
/**
|
|
68
82
|
* Abstract base class for all TTS providers
|
|
69
83
|
*
|
|
@@ -200,5 +214,48 @@ export declare abstract class BaseTTSProvider {
|
|
|
200
214
|
* ```
|
|
201
215
|
*/
|
|
202
216
|
protected log(level: 'info' | 'warn' | 'error' | 'debug', message: string, meta?: Record<string, unknown>): void;
|
|
217
|
+
/**
|
|
218
|
+
* Check if per-request debug logging is enabled.
|
|
219
|
+
*
|
|
220
|
+
* @protected
|
|
221
|
+
* @returns true when `DEBUG_TTS_REQUESTS` is truthy in the environment.
|
|
222
|
+
*
|
|
223
|
+
* @description Providers can gate expensive body capture/cloning behind this
|
|
224
|
+
* flag to avoid overhead when logging is disabled.
|
|
225
|
+
*/
|
|
226
|
+
protected isRequestLoggingEnabled(): boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Write a debug Markdown log file for a single upstream API call.
|
|
229
|
+
*
|
|
230
|
+
* @protected
|
|
231
|
+
* @param entry - Log entry data. The provider name is injected automatically
|
|
232
|
+
* if omitted. No-op when `DEBUG_TTS_REQUESTS` is disabled.
|
|
233
|
+
*
|
|
234
|
+
* @description Provider-agnostic request logger. Each call produces exactly
|
|
235
|
+
* one `.md` file under `<cwd>/logs/tts/requests/` (override via
|
|
236
|
+
* `TTS_REQUEST_LOG_DIR`). Use this to capture exactly what was sent to and
|
|
237
|
+
* received from an upstream TTS API (Vertex AI, Azure, etc.).
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* this.logRequest({
|
|
242
|
+
* kind: 'dialog-segment',
|
|
243
|
+
* timestamp: new Date().toISOString(),
|
|
244
|
+
* model,
|
|
245
|
+
* region,
|
|
246
|
+
* endpointUrl: url,
|
|
247
|
+
* segmentIndex: i,
|
|
248
|
+
* speakers: usedSpeakers,
|
|
249
|
+
* requestShape: 'multi-speaker',
|
|
250
|
+
* requestBody,
|
|
251
|
+
* httpStatus: response.status,
|
|
252
|
+
* durationMs: Date.now() - startedAt,
|
|
253
|
+
* responseBody: { audioBytes: buf.length, mimeType: 'audio/pcm' },
|
|
254
|
+
* });
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
protected logRequest(entry: Omit<TTSRequestLogEntry, 'provider'> & {
|
|
258
|
+
provider?: string;
|
|
259
|
+
}): void;
|
|
203
260
|
}
|
|
204
261
|
//# sourceMappingURL=base-tts-provider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-tts-provider.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/base-tts-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,YAAY,EACb,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"base-tts-provider.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/base-tts-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
* @abstract
|
|
9
9
|
*/
|
|
10
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;
|
|
11
|
+
exports.BaseTTSProvider = exports.PayloadTooLargeError = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = void 0;
|
|
12
12
|
const logger_utils_1 = require("../utils/logger.utils");
|
|
13
|
+
const request_logger_utils_1 = require("../utils/request-logger.utils");
|
|
13
14
|
/**
|
|
14
15
|
* Base error class for all TTS errors
|
|
15
16
|
*/
|
|
@@ -101,6 +102,23 @@ class NetworkError extends TTSError {
|
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
exports.NetworkError = NetworkError;
|
|
105
|
+
/**
|
|
106
|
+
* Error thrown when the input payload exceeds a provider byte/character limit.
|
|
107
|
+
*
|
|
108
|
+
* @description Thrown *before* the remote API call, so consumers can split or
|
|
109
|
+
* shorten the input without incurring a failed request billing. For multi-segment
|
|
110
|
+
* dialogs, `segmentIndex` identifies the offending segment.
|
|
111
|
+
*/
|
|
112
|
+
class PayloadTooLargeError extends TTSError {
|
|
113
|
+
constructor(provider, message, actualBytes, maxBytes, segmentIndex, cause) {
|
|
114
|
+
super(provider, 'PAYLOAD_TOO_LARGE', message, cause);
|
|
115
|
+
this.actualBytes = actualBytes;
|
|
116
|
+
this.maxBytes = maxBytes;
|
|
117
|
+
this.segmentIndex = segmentIndex;
|
|
118
|
+
this.name = 'PayloadTooLargeError';
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.PayloadTooLargeError = PayloadTooLargeError;
|
|
104
122
|
/**
|
|
105
123
|
* Abstract base class for all TTS providers
|
|
106
124
|
*
|
|
@@ -262,6 +280,54 @@ class BaseTTSProvider {
|
|
|
262
280
|
log(level, message, meta) {
|
|
263
281
|
(0, logger_utils_1.log)(this.providerName, level, message, meta);
|
|
264
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Check if per-request debug logging is enabled.
|
|
285
|
+
*
|
|
286
|
+
* @protected
|
|
287
|
+
* @returns true when `DEBUG_TTS_REQUESTS` is truthy in the environment.
|
|
288
|
+
*
|
|
289
|
+
* @description Providers can gate expensive body capture/cloning behind this
|
|
290
|
+
* flag to avoid overhead when logging is disabled.
|
|
291
|
+
*/
|
|
292
|
+
isRequestLoggingEnabled() {
|
|
293
|
+
return (0, request_logger_utils_1.isRequestLoggingEnabled)();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Write a debug Markdown log file for a single upstream API call.
|
|
297
|
+
*
|
|
298
|
+
* @protected
|
|
299
|
+
* @param entry - Log entry data. The provider name is injected automatically
|
|
300
|
+
* if omitted. No-op when `DEBUG_TTS_REQUESTS` is disabled.
|
|
301
|
+
*
|
|
302
|
+
* @description Provider-agnostic request logger. Each call produces exactly
|
|
303
|
+
* one `.md` file under `<cwd>/logs/tts/requests/` (override via
|
|
304
|
+
* `TTS_REQUEST_LOG_DIR`). Use this to capture exactly what was sent to and
|
|
305
|
+
* received from an upstream TTS API (Vertex AI, Azure, etc.).
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```typescript
|
|
309
|
+
* this.logRequest({
|
|
310
|
+
* kind: 'dialog-segment',
|
|
311
|
+
* timestamp: new Date().toISOString(),
|
|
312
|
+
* model,
|
|
313
|
+
* region,
|
|
314
|
+
* endpointUrl: url,
|
|
315
|
+
* segmentIndex: i,
|
|
316
|
+
* speakers: usedSpeakers,
|
|
317
|
+
* requestShape: 'multi-speaker',
|
|
318
|
+
* requestBody,
|
|
319
|
+
* httpStatus: response.status,
|
|
320
|
+
* durationMs: Date.now() - startedAt,
|
|
321
|
+
* responseBody: { audioBytes: buf.length, mimeType: 'audio/pcm' },
|
|
322
|
+
* });
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
logRequest(entry) {
|
|
326
|
+
(0, request_logger_utils_1.writeRequestLog)({
|
|
327
|
+
...entry,
|
|
328
|
+
provider: entry.provider ?? this.providerName,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
265
331
|
}
|
|
266
332
|
exports.BaseTTSProvider = BaseTTSProvider;
|
|
267
333
|
//# sourceMappingURL=base-tts-provider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-tts-provider.js","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/base-tts-provider.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAQH,wDAAuD;
|
|
1
|
+
{"version":3,"file":"base-tts-provider.js","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/base-tts-provider.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAQH,wDAAuD;AACvD,wEAIuC;AAEvC;;GAEG;AACH,MAAa,QAAS,SAAQ,KAAK;IACjC;;;;;;;OAOG;IACH,YACkB,QAAgB,EAChB,IAAkB,EAClC,OAAe,EACC,KAAa;QAE7B,KAAK,CAAC,OAAO,CAAC,CAAC;QALC,aAAQ,GAAR,QAAQ,CAAQ;QAChB,SAAI,GAAJ,IAAI,CAAc;QAElB,UAAK,GAAL,KAAK,CAAQ;QAG7B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QAEvB,qFAAqF;QACrF,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;YAC5B,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,OAAO,GACrD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,gBAAgB,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,EACvD,EAAE,CAAC;IACL,CAAC;CACF;AAhCD,4BAgCC;AAED;;GAEG;AACH,MAAa,kBAAmB,SAAQ,QAAQ;IAC9C,YAAY,QAAgB,EAAE,OAAe,EAAE,KAAa;QAC1D,KAAK,CAAC,QAAQ,EAAE,gBAAgC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAClE,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AALD,gDAKC;AAED;;GAEG;AACH,MAAa,iBAAkB,SAAQ,QAAQ;IAC7C,YACE,QAAgB,EAChB,OAAe,EACf,OAAgB,EAChB,KAAa;QAEb,KAAK,CACH,QAAQ,EACR,eAA+B,EAC/B,OAAO,IAAI,oBAAoB,OAAO,EAAE,EACxC,KAAK,CACN,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAfD,8CAeC;AAED;;GAEG;AACH,MAAa,kBAAmB,SAAQ,QAAQ;IAC9C,YAAY,QAAgB,EAAE,OAAgB,EAAE,KAAa;QAC3D,KAAK,CACH,QAAQ,EACR,gBAAgC,EAChC,OAAO,IAAI,uCAAuC,EAClD,KAAK,CACN,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;CACF;AAVD,gDAUC;AAED;;GAEG;AACH,MAAa,wBAAyB,SAAQ,QAAQ;IACpD,YAAY,QAAgB,EAAE,OAAgB,EAAE,KAAa;QAC3D,KAAK,CACH,QAAQ,EACR,sBAAsC,EACtC,OAAO,IAAI,6CAA6C,EACxD,KAAK,CACN,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;IACzC,CAAC;CACF;AAVD,4DAUC;AAED;;GAEG;AACH,MAAa,oBAAqB,SAAQ,QAAQ;IAChD,YAAY,QAAgB,EAAE,OAAe,EAAE,KAAa;QAC1D,KAAK,CAAC,QAAQ,EAAE,kBAAkC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AALD,oDAKC;AAED;;GAEG;AACH,MAAa,YAAa,SAAQ,QAAQ;IACxC,YAAY,QAAgB,EAAE,OAAe,EAAE,KAAa;QAC1D,KAAK,CAAC,QAAQ,EAAE,eAA+B,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACjE,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AALD,oCAKC;AAED;;;;;;GAMG;AACH,MAAa,oBAAqB,SAAQ,QAAQ;IAChD,YACE,QAAgB,EAChB,OAAe,EACC,WAAoB,EACpB,QAAiB,EACjB,YAAqB,EACrC,KAAa;QAEb,KAAK,CAAC,QAAQ,EAAE,mBAAmC,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QALrD,gBAAW,GAAX,WAAW,CAAS;QACpB,aAAQ,GAAR,QAAQ,CAAS;QACjB,iBAAY,GAAZ,YAAY,CAAS;QAIrC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AAZD,oDAYC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAsB,eAAe;IAMnC;;;;OAIG;IACH,YAAY,YAAyB;QACnC,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAsBD;;;;OAIG;IACI,eAAe;QACpB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED;;;;;;;;;OASG;IACO,cAAc,CAAC,OAA6B;QACpD,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,kBAAkB,CAC1B,IAAI,CAAC,YAAY,EACjB,sBAAsB,CACvB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;YACxC,MAAM,IAAI,kBAAkB,CAC1B,IAAI,CAAC,YAAY,EACjB,sBAAsB,CACvB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACO,eAAe,CAAC,IAAY;QACpC,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;;;;;;;;;OAUG;IACO,KAAK,CAAC,mBAAmB,CAAC,QAAgB;QAClD,2CAA2C;QAC3C,0EAA0E;QAC1E,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;OAUG;IACO,WAAW,CAAC,KAAY,EAAE,OAAgB;QAClD,wCAAwC;QACxC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,oCAAoC;QACpC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAEjD,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACjE,OAAO,IAAI,kBAAkB,CAC3B,IAAI,CAAC,YAAY,EACjB,wBAAwB,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACvD,KAAK,CACN,CAAC;QACJ,CAAC;QAED,IAAI,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,IAAI,kBAAkB,CAC3B,IAAI,CAAC,YAAY,EACjB,sBAAsB,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACrD,KAAK,CACN,CAAC;QACJ,CAAC;QAED,IACE,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC5B,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC5B,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAC5B,CAAC;YACD,OAAO,IAAI,wBAAwB,CACjC,IAAI,CAAC,YAAY,EACjB,kCAAkC,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACjE,KAAK,CACN,CAAC;QACJ,CAAC;QAED,IACE,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC;YAChC,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC;YACrC,YAAY,CAAC,QAAQ,CAAC,WAAW,CAAC,EAClC,CAAC;YACD,OAAO,IAAI,YAAY,CACrB,IAAI,CAAC,YAAY,EACjB,gBAAgB,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAC/C,KAAK,CACN,CAAC;QACJ,CAAC;QAED,qDAAqD;QACrD,OAAO,IAAI,oBAAoB,CAC7B,IAAI,CAAC,YAAY,EACjB,mBAAmB,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,EACpE,KAAK,CACN,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACO,GAAG,CACX,KAA0C,EAC1C,OAAe,EACf,IAA8B;QAE9B,IAAA,kBAAO,EAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnD,CAAC;IAED;;;;;;;;OAQG;IACO,uBAAuB;QAC/B,OAAO,IAAA,8CAAuB,GAAE,CAAC;IACnC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACO,UAAU,CAAC,KAAmE;QACtF,IAAA,sCAAe,EAAC;YACd,GAAG,KAAK;YACR,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY;SAC9C,CAAC,CAAC;IACL,CAAC;CACF;AAlQD,0CAkQC"}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Export all provider classes and error types
|
|
5
5
|
*/
|
|
6
|
-
export { BaseTTSProvider, TTSError, InvalidConfigError, InvalidVoiceError, QuotaExceededError, ProviderUnavailableError, SynthesisFailedError, NetworkError, } from './base-tts-provider';
|
|
6
|
+
export { BaseTTSProvider, TTSError, InvalidConfigError, InvalidVoiceError, QuotaExceededError, ProviderUnavailableError, SynthesisFailedError, NetworkError, PayloadTooLargeError, } from './base-tts-provider';
|
|
7
7
|
export { AzureProvider } from './azure-provider';
|
|
8
8
|
export { EdenAIProvider } from './edenai-provider';
|
|
9
9
|
export { GoogleCloudTTSProvider } from './google-cloud-tts-provider';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,eAAe,EACf,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,YAAY,EACZ,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAC9F,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Export all provider classes and error types
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.VertexAITTSProvider = exports.InworldProvider = exports.FishAudioProvider = exports.GoogleCloudTTSProvider = exports.EdenAIProvider = exports.AzureProvider = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = exports.BaseTTSProvider = void 0;
|
|
8
|
+
exports.VertexAITTSProvider = exports.InworldProvider = exports.FishAudioProvider = exports.GoogleCloudTTSProvider = exports.EdenAIProvider = exports.AzureProvider = exports.PayloadTooLargeError = exports.NetworkError = exports.SynthesisFailedError = exports.ProviderUnavailableError = exports.QuotaExceededError = exports.InvalidVoiceError = exports.InvalidConfigError = exports.TTSError = exports.BaseTTSProvider = void 0;
|
|
9
9
|
// Base provider and errors
|
|
10
10
|
var base_tts_provider_1 = require("./base-tts-provider");
|
|
11
11
|
Object.defineProperty(exports, "BaseTTSProvider", { enumerable: true, get: function () { return base_tts_provider_1.BaseTTSProvider; } });
|
|
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "QuotaExceededError", { enumerable: true, get: fu
|
|
|
16
16
|
Object.defineProperty(exports, "ProviderUnavailableError", { enumerable: true, get: function () { return base_tts_provider_1.ProviderUnavailableError; } });
|
|
17
17
|
Object.defineProperty(exports, "SynthesisFailedError", { enumerable: true, get: function () { return base_tts_provider_1.SynthesisFailedError; } });
|
|
18
18
|
Object.defineProperty(exports, "NetworkError", { enumerable: true, get: function () { return base_tts_provider_1.NetworkError; } });
|
|
19
|
+
Object.defineProperty(exports, "PayloadTooLargeError", { enumerable: true, get: function () { return base_tts_provider_1.PayloadTooLargeError; } });
|
|
19
20
|
// Provider implementations
|
|
20
21
|
var azure_provider_1 = require("./azure-provider");
|
|
21
22
|
Object.defineProperty(exports, "AzureProvider", { enumerable: true, get: function () { return azure_provider_1.AzureProvider; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,2BAA2B;AAC3B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AAEH,2BAA2B;AAC3B,yDAU6B;AAT3B,oHAAA,eAAe,OAAA;AACf,6GAAA,QAAQ,OAAA;AACR,uHAAA,kBAAkB,OAAA;AAClB,sHAAA,iBAAiB,OAAA;AACjB,uHAAA,kBAAkB,OAAA;AAClB,6HAAA,wBAAwB,OAAA;AACxB,yHAAA,oBAAoB,OAAA;AACpB,iHAAA,YAAY,OAAA;AACZ,yHAAA,oBAAoB,OAAA;AAGtB,2BAA2B;AAC3B,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AACtB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,yEAAqE;AAA5D,mIAAA,sBAAsB,OAAA;AAE/B,6DAA0D;AAAjD,wHAAA,iBAAiB,OAAA;AAC1B,uDAAqD;AAA5C,mHAAA,eAAe,OAAA;AACxB,mEAA+D;AAAtD,6HAAA,mBAAmB,OAAA;AAG5B,yDAAyD;AACzD,sDAAsD;AACtD,8DAA8D;AAC9D,0DAA0D"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import type { TTSSynthesizeRequest, TTSResponse } from '../types';
|
|
18
18
|
import { BaseTTSProvider } from './base-tts-provider';
|
|
19
|
-
import type { RegionRotationConfig } from '../types/provider-options.types';
|
|
19
|
+
import type { RegionRotationConfig, SynthesizeDialogRequest } from '../types/provider-options.types';
|
|
20
20
|
/**
|
|
21
21
|
* Vertex AI TTS configuration
|
|
22
22
|
*/
|
|
@@ -128,11 +128,74 @@ export declare class VertexAITTSProvider extends BaseTTSProvider {
|
|
|
128
128
|
*/
|
|
129
129
|
synthesize(text: string, voiceId: string, request: TTSSynthesizeRequest): Promise<TTSResponse>;
|
|
130
130
|
/**
|
|
131
|
-
*
|
|
131
|
+
* Synthesize a multi-segment, multi-speaker dialog in a single call
|
|
132
|
+
*
|
|
133
|
+
* @description Runs one Vertex AI request per segment *sequentially* (keeps
|
|
134
|
+
* region rotation well-behaved and avoids quota bursts), concatenates the
|
|
135
|
+
* resulting raw PCM buffers, and converts once at the end. Billing aggregates
|
|
136
|
+
* characters across every turn and stylePrompt so consumer apps can charge
|
|
137
|
+
* their customers for the full amount actually sent to Google.
|
|
138
|
+
*
|
|
139
|
+
* Requires a Gemini 3.1 model. The 8 KB combined-byte limit is enforced per
|
|
140
|
+
* segment *before* the API call — consumers receive a typed PayloadTooLargeError
|
|
141
|
+
* without incurring cost.
|
|
142
|
+
*
|
|
143
|
+
* @param request - Dialog request with speakers and ordered segments
|
|
144
|
+
* @returns Single concatenated audio response with aggregated billing
|
|
145
|
+
* @throws {InvalidConfigError} If speakers/turns are invalid
|
|
146
|
+
* @throws {PayloadTooLargeError} If any segment exceeds the 8 KB limit
|
|
147
|
+
*/
|
|
148
|
+
synthesizeDialog(request: SynthesizeDialogRequest): Promise<TTSResponse>;
|
|
149
|
+
/**
|
|
150
|
+
* Validate a dialog request (speakers unique, turns reference known speakers, etc.)
|
|
151
|
+
*
|
|
152
|
+
* @private
|
|
153
|
+
* @throws {InvalidConfigError} If the request is structurally invalid
|
|
154
|
+
*/
|
|
155
|
+
private validateDialogRequest;
|
|
156
|
+
/**
|
|
157
|
+
* Count billable characters for a single dialog segment
|
|
158
|
+
*
|
|
159
|
+
* @private
|
|
160
|
+
* @description Counts all turn text plus the segment's stylePrompt.
|
|
161
|
+
* Speaker labels ("Alice: ") are included because they are part of what we
|
|
162
|
+
* send to Vertex AI.
|
|
163
|
+
*/
|
|
164
|
+
private countSegmentCharacters;
|
|
165
|
+
/**
|
|
166
|
+
* Build Vertex AI generateContent request payload (single-voice)
|
|
132
167
|
*
|
|
133
168
|
* @private
|
|
134
169
|
*/
|
|
135
170
|
private buildRequest;
|
|
171
|
+
/**
|
|
172
|
+
* Build Vertex AI generateContent request payload for a dialog segment
|
|
173
|
+
*
|
|
174
|
+
* @private
|
|
175
|
+
* @description Chooses the request shape based on the number of distinct
|
|
176
|
+
* speakers actually used in the segment's turns:
|
|
177
|
+
* - 1 speaker → `prebuiltVoiceConfig` (single-voice request)
|
|
178
|
+
* - 2 speakers → `multiSpeakerVoiceConfig` (Vertex AI requires exactly 2 entries)
|
|
179
|
+
* - >2 speakers → InvalidConfigError (split the segment so each sub-segment has ≤2 speakers)
|
|
180
|
+
*/
|
|
181
|
+
private buildDialogRequest;
|
|
182
|
+
/**
|
|
183
|
+
* Build a debug log context for one dialog segment.
|
|
184
|
+
*
|
|
185
|
+
* @private
|
|
186
|
+
* @description Inspects the already-built request body so the logged shape
|
|
187
|
+
* and speaker mapping exactly match what will be sent upstream. Reading the
|
|
188
|
+
* body (instead of re-deriving from `segment.turns`) ensures the log stays
|
|
189
|
+
* truthful if `buildDialogRequest()` evolves.
|
|
190
|
+
*/
|
|
191
|
+
private buildDialogLogContext;
|
|
192
|
+
/**
|
|
193
|
+
* Validate payload byte limits before sending to Vertex AI
|
|
194
|
+
*
|
|
195
|
+
* @private
|
|
196
|
+
* @throws {PayloadTooLargeError} If text or combined payload exceeds limits
|
|
197
|
+
*/
|
|
198
|
+
private assertPayloadWithinLimits;
|
|
136
199
|
/**
|
|
137
200
|
* Call the Vertex AI API with optional region rotation on quota errors
|
|
138
201
|
*
|
|
@@ -150,6 +213,9 @@ export declare class VertexAITTSProvider extends BaseTTSProvider {
|
|
|
150
213
|
* @param requestBody - The request payload
|
|
151
214
|
* @param model - The model to use
|
|
152
215
|
* @param region - The Vertex AI region to use
|
|
216
|
+
* @param logContext - Optional debug context. When provided (and
|
|
217
|
+
* `DEBUG_TTS_REQUESTS` is enabled), this call writes a Markdown log file
|
|
218
|
+
* with the full request body, response metadata, timing, and any error.
|
|
153
219
|
* @returns Promise resolving to raw PCM audio buffer
|
|
154
220
|
*/
|
|
155
221
|
private callAPI;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vertex-ai-tts-provider.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/vertex-ai-tts-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EACL,eAAe,
|
|
1
|
+
{"version":3,"file":"vertex-ai-tts-provider.d.ts","sourceRoot":"","sources":["../../../../../src/middleware/services/tts/providers/vertex-ai-tts-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAGlE,OAAO,EACL,eAAe,EAGhB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAEV,oBAAoB,EAGpB,uBAAuB,EACxB,MAAM,iCAAiC,CAAC;AAgCzC;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;;;;;OAUG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;;;;;;OAaG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAkBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,mBAAoB,SAAQ,eAAe;IACtD,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;;;OAKG;gBACS,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAsB/C;;;;;OAKG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;;;;OAKG;YACW,cAAc;IAqB5B;;;;;;;OAOG;IACG,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,WAAW,CAAC;IAuEvB;;;;;;;;;;;;;;;;;OAiBG;IACG,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC;IAwF9E;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAgE7B;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IAS9B;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAqCpB;;;;;;;;;OASG;IACH,OAAO,CAAC,kBAAkB;IA0E1B;;;;;;;;OAQG;IACH,OAAO,CAAC,qBAAqB;IAmD7B;;;;;OAKG;IACH,OAAO,CAAC,yBAAyB;IAyCjC;;;;;;;;OAQG;YACW,yBAAyB;IAuDvC;;;;;;;;;;;OAWG;YACW,OAAO;IA+IrB;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA0BzB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAexB;;;;;;;OAOG;YACW,eAAe;IA0B7B;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;IAkChB;;;;;;OAMG;IACH,OAAO,CAAC,QAAQ;CAwBjB"}
|