Files
2026-05-27 23:29:53 +01:00

155 lines
8.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks: voice-elevenlabs
**Change ID**: voice-elevenlabs
**Package**: `@suaveplan/voice-elevenlabs`
**Target path**: `packages/audio/voice-elevenlabs`
**Prerequisite packages on Verdaccio**: `@suaveplan/voice`, `@suaveplan/third-party-elevenlabs`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/streams`
> Supersedes the 2026-04-24 fused-HTTP design. The prior `packages/audio/voice-elevenlabs` implementation MUST be deleted before this rebuild starts.
---
## 0. Pre-rebuild teardown
- [x] Confirm `git status` shows the prior `packages/audio/voice-elevenlabs` content is committed or accounted for; if uncommitted scratch work exists, stash it explicitly with the user.
- [x] `rm -rf packages/audio/voice-elevenlabs` — delete the entire prior implementation.
- [x] Confirm no other package imports from `@suaveplan/voice-elevenlabs` (`grep -rln '@suaveplan/voice-elevenlabs' packages/`); the only references should be inside the to-be-deleted folder itself.
---
## 1. Scaffold
- [x] Create `packages/audio/voice-elevenlabs/` with `src/`, `tests/`, `src/__benchmarks__/`.
- [x] Write `package.json` at version `0.1.0`, `"type": "module"`, `"sideEffects": false`, full subpath `exports` map per feature module, deps `@suaveplan/voice`, `@suaveplan/third-party-elevenlabs`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/streams`, `zod`, `tslib` (catalogs only).
- [x] Write `tsconfig.lib.json` extending `../../../tsconfig.base.json`.
- [x] Write `vite.config.ts` with multi-entry build for every feature module + factory.
- [x] Write `vitest.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`, coverage thresholds 100/100/100/100.
- [x] Add `lint`, `lint:fix`, `format`, `check`, `typecheck`, `test`, `test:coverage`, `build`, `docs:check` scripts.
---
## 2. Error remapping module
- [x] `src/errors/remap.ts` — `mapElevenLabsError(err: unknown, context: { operation: VoiceOperation; voiceId?: string }): VoiceError` covering every `ElevenLabsApiError` subclass per the proposal §Error remapping table. Preserves the original error via `cause`.
- [x] `src/errors/remap.schemas.ts` — Zod for `VoiceOperation = 'tts' | 'tts-stream' | 'stt' | 'voices' | 'sound-generation' | 'audio-isolation'`.
- [x] `src/errors/remap.md`
- [x] `src/errors/remap.test.ts` — every `ElevenLabsApiError` subclass produces the expected `VoiceError` code, preserves `cause`, and threads through `context.voiceId` where the table specifies.
---
## 3. Engine implementations
### 3.1 `tts`
- [x] `src/tts/tts.ts` — `ElevenLabsTtsEngine` implements `VoiceSynthesizer`.
- `synthesize(req)`: maps `SynthesisRequest` → ElevenLabs TTS body via `mapSynthesisRequest()`; calls `tts.synthesize(req.voiceId, body)`; maps `BinaryResponse` → `SynthesisResult` (audio bytes, format echoed from request, `charactersUsed` from `metadata.characterCost`).
- `synthesizeStream(req)`: maps request, calls `tts.synthesizeStream(req.voiceId, body)`, returns `response.body` directly.
- All errors flow through `mapElevenLabsError`.
- [x] `src/tts/tts.schemas.ts` — Zod for `ElevenLabsTtsAdapterOptions`, `mapSynthesisRequest` input/output.
- [x] `src/tts/tts.md`
- [x] `src/tts/tts.test.ts` — happy-path synthesize, happy-path stream, voice-not-found, validation error, rate-limited with `retryAfterMs`, abort, `cause` chain preserved.
### 3.2 `stt`
- [x] `src/stt/stt.ts` — `ElevenLabsSttEngine` implements `TranscriptionEngine`.
- `transcribe(req)`: maps `TranscriptionRequest` → ElevenLabs STT multipart body (audio: `Uint8Array | ReadableStream | string` passed through to third-party `MultipartFile`); calls `stt.transcribe(body)`; maps response to `TranscriptionResult` (words, derived speakers, entities split from word-stream).
- [x] `src/stt/stt.schemas.ts`
- [x] `src/stt/stt.md`
- [x] `src/stt/stt.test.ts` — transcribe from bytes / stream / URL, with diarization, with entity detection, error remapping.
### 3.3 `voices`
- [x] `src/voices/voices.ts` — `ElevenLabsVoiceRepository` implements `VoiceRepository`.
- `list(opts)`: calls `voices.list({ ...opts })`; maps each ElevenLabs voice DTO → `Voice`; `nextPageToken`/`hasMore` derived from response.
- `get(id)`: calls `voices.get(id)`; remaps `THIRD_PARTY_ELEVENLABS.REQUEST.NOT_FOUND` → `null` (per `VoiceRepository.get` contract); other errors flow through `mapElevenLabsError` with `voiceId` context.
- [x] `src/voices/voices.schemas.ts`
- [x] `src/voices/voices.md`
- [x] `src/voices/voices.test.ts`
### 3.4 `sound-effects`
- [x] `src/sound-effects/sound-effects.ts` — `ElevenLabsSoundEffectEngine` implements `SoundEffectEngine`.
- `generate(req)`: maps `SoundEffectRequest` → ElevenLabs sound-generation body; calls `soundGeneration.generate(body)`; maps response to `SoundEffectResult`.
- [x] `src/sound-effects/sound-effects.schemas.ts`
- [x] `src/sound-effects/sound-effects.md`
- [x] `src/sound-effects/sound-effects.test.ts`
### 3.5 `voice-isolation`
- [x] `src/voice-isolation/voice-isolation.ts` — `ElevenLabsVoiceIsolationEngine` implements `VoiceIsolationEngine`.
- `isolate(req)`: maps `IsolationRequest` → ElevenLabs isolation multipart body; calls `audioIsolation.isolate(body)`; maps response to `IsolationResult`.
- [x] `src/voice-isolation/voice-isolation.schemas.ts`
- [x] `src/voice-isolation/voice-isolation.md`
- [x] `src/voice-isolation/voice-isolation.test.ts`
---
## 4. Composite factory
- [x] `src/factory/factory.ts` — `createElevenLabsVoice(options)` returns `{ client, tts, stt, voices, effects, isolation }`.
- Accepts either an existing `ElevenLabsClient` or `ElevenLabsClientOptions` to construct one fresh.
- Wires every engine to share the same client instance.
- [x] `src/factory/factory.schemas.ts` — Zod for `CreateElevenLabsVoiceOptions`.
- [x] `src/factory/factory.md`
- [x] `src/factory/factory.test.ts` — factory returns five engines + client; engines share client; both option shapes (existing client vs construct fresh) work.
---
## 5. Index
- [x] `src/index.ts` — barrel re-export of all five engines + factory + adapter-options types + `mapElevenLabsError` (for advanced callers).
---
## 6. Property-based tests (`tests/property.test.ts`)
- [x] `AudioFormat` round-trip — every `@suaveplan/voice` `AudioFormat` value passes through to ElevenLabs `output_format` and back without modification.
- [x] `SynthesisRequest` round-trip — arbitrary valid request maps to ElevenLabs body and remaps to equivalent `SynthesisResult` shape (subject to documented field mappings).
- [x] Error remapping totality — every `ElevenLabsApiError` subclass produces a `VoiceError` for every documented operation; no unmapped paths.
---
## 7. Benchmarks (`src/__benchmarks__/voice-elevenlabs.bench.ts`)
- [x] `ElevenLabsTtsEngine.synthesize` round-trip with mocked `fetchImpl` (no network).
- [x] `ElevenLabsSttEngine.transcribe` round-trip with mocked `fetchImpl`.
---
## 8. Pre-publish gate
- [x] `bunx turbo lint typecheck test --filter=@suaveplan/voice-elevenlabs` — green.
- [x] `bunx turbo test:coverage --filter=@suaveplan/voice-elevenlabs` — 100/100/100/100.
- [x] `bun run docs:check --filter=@suaveplan/voice-elevenlabs` — ≥ 90%.
- [x] Banned-pattern scan in `src/` — zero hits.
- [x] `README.md` exists at package root.
- [x] Subpath exports count matches feature module count.
- [x] No bare versions in `package.json`.
- [x] `bunx turbo test --filter=...@suaveplan/voice-elevenlabs` — dependents pass (currently none).
---
## 9. Archive
- [x] Fold spec deltas into `openspec/specs/voice-elevenlabs/spec.md`.
- [x] Move `openspec/changes/voice-elevenlabs/` → `openspec/archive/2026-05-02-voice-elevenlabs/`.
- [x] Publish to Verdaccio: `bun scripts/publish.ts --package @suaveplan/voice-elevenlabs`.
- [x] Update `STATUS.md`.
---
## Audit Note (2026-05-02)
**Status**: DEFERRED.
State at audit:
- The package directory `packages/audio/voice-elevenlabs/` does not exist on genesis. The teardown step (Section 0) is a no-op for the audit.
- Upstream `@suaveplan/voice` is shipped (103 tests / 100% coverage / archived 2026-05-02).
- Upstream `@suaveplan/third-party-elevenlabs` is **also deferred** (typecheck green after this audit's edits, but missing `src/index.ts`, factory implementation, property tests, benchmarks, README) — `voice-elevenlabs` cannot ship until that prerequisite lands.
Resuming work:
1. Finish `third-party-elevenlabs` (see its audit note) so the third-party client surface is stable.
2. Then execute Sections 1–9 of this tasks file to scaffold `packages/audio/voice-elevenlabs/` and wire the five engines + factory.