first commit
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# `factory/create-elevenlabs-voice`
|
||||
|
||||
Composition root for the `@suaveplan/voice` engine adapters that target
|
||||
ElevenLabs. One call, one shared transport, five voice engines wired up.
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`@suaveplan/voice-elevenlabs` exposes five domain adapters (TTS, STT,
|
||||
voices, sound-effects, voice-isolation). Each needs an
|
||||
`ElevenLabsClient` from `@suaveplan/third-party-elevenlabs`. Without a
|
||||
factory, every consumer would manually construct each adapter and
|
||||
remember to share the same client between them — duplicating the same
|
||||
boilerplate over and over and leaving room for subtle bugs (one adapter
|
||||
using a different timeout, one created on every request and leaking
|
||||
event listeners). `createElevenLabsVoice` makes the right thing easy:
|
||||
either supply a pre-constructed client (so the same transport can be
|
||||
shared with `ElevenLabsModelCatalog` or `ElevenLabsSpeechToSpeech` from
|
||||
`@suaveplan/third-party-elevenlabs`), or pass the same
|
||||
`ElevenLabsClientOptions` a fresh client would accept, and get all five
|
||||
voice engines back as a typed bundle.
|
||||
|
||||
## Features
|
||||
|
||||
- Single function: `createElevenLabsVoice(options)`.
|
||||
- Returns `ElevenLabsVoiceBundle` — `{ client, tts, stt, voices, effects,
|
||||
isolation }`.
|
||||
- Every engine shares the same `client`, which means they share the same
|
||||
retry policy, timeout, event bus, logger, and observability context.
|
||||
- The `client` field is exposed so consumers can issue ad-hoc requests
|
||||
(e.g. account-info endpoints not covered by the engines) against the
|
||||
same transport, or wire it into a sibling provider catalogue.
|
||||
- `options.client` overrides every transport-construction field; pass a
|
||||
client explicitly to compose with surfaces outside the voice domain.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { createElevenLabsVoice } from "@suaveplan/voice-elevenlabs";
|
||||
|
||||
const { tts, voices } = createElevenLabsVoice({
|
||||
apiKey: process.env.ELEVENLABS_KEY!,
|
||||
});
|
||||
|
||||
const all = await voices.list();
|
||||
const result = await tts.synthesize({
|
||||
text: "hello",
|
||||
voiceId: all.voices[0]!.id,
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
ElevenLabsClient,
|
||||
ElevenLabsModelCatalog,
|
||||
} from "@suaveplan/third-party-elevenlabs";
|
||||
import { createElevenLabsVoice } from "@suaveplan/voice-elevenlabs";
|
||||
|
||||
// One transport shared by both the voice engines AND the model catalogue.
|
||||
const client = new ElevenLabsClient({
|
||||
apiKey: process.env.ELEVENLABS_KEY!,
|
||||
baseUrl: "https://eu.api.elevenlabs.io",
|
||||
timeoutMs: 60_000,
|
||||
maxRetries: 5,
|
||||
initialRetryDelayMs: 250,
|
||||
fetchImpl: instrumentedFetch,
|
||||
webSocketImpl: WS, // 'ws' on Node
|
||||
events: appEventBus,
|
||||
logger: appLogger,
|
||||
userAgent: "my-app/1.4.2",
|
||||
defaultHeaders: { "x-tenant": "acme" },
|
||||
});
|
||||
|
||||
const voice = createElevenLabsVoice({ client });
|
||||
const models = new ElevenLabsModelCatalog(client);
|
||||
|
||||
const [voiceList, modelList] = await Promise.all([
|
||||
voice.voices.list(),
|
||||
models.list(),
|
||||
]);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `interface ElevenLabsVoiceBundle { client, tts, stt, voices, effects, isolation }`
|
||||
- `type CreateElevenLabsVoiceOptions = { client: ElevenLabsClient } | (ElevenLabsClientOptions & { client?: undefined })`
|
||||
- `function createElevenLabsVoice(options: CreateElevenLabsVoiceOptions): ElevenLabsVoiceBundle`
|
||||
|
||||
Each field is the corresponding `@suaveplan/voice` engine implementation
|
||||
instantiated against the shared `client`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- When `options.client` is present it is reused as-is; every other field
|
||||
on `options` is ignored. This is by design so consumers can wire their
|
||||
own transport (e.g. an `ElevenLabsClient` already configured for an EU
|
||||
shard, a private base URL, or a shared event bus) and the voice
|
||||
adapters drop straight in.
|
||||
- When `options.client` is absent the factory delegates to
|
||||
`new ElevenLabsClient(options)` so every `ElevenLabsClientOptions`
|
||||
field is honoured. Invalid options surface as the same construction
|
||||
errors a direct `new ElevenLabsClient(opts)` would raise.
|
||||
- The factory does not validate `apiKey` — passing an empty string
|
||||
surfaces as a 401 / `VOICE.PROVIDER.NOT_CONFIGURED` on the first call
|
||||
rather than a synchronous throw.
|
||||
- The bundle is a plain object literal; consumers can destructure freely
|
||||
(`const { tts } = createElevenLabsVoice(...)`) without losing the
|
||||
shared client reference.
|
||||
- Stateful adapters (none currently) would still be instantiated per
|
||||
call. Call `createElevenLabsVoice` once per process / per long-lived
|
||||
consumer, not once per request.
|
||||
Reference in New Issue
Block a user