@effect-uai/elevenlabs 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/dist/ElevenLabsMusicGenerator.d.mts +75 -0
  2. package/dist/ElevenLabsMusicGenerator.d.mts.map +1 -0
  3. package/dist/ElevenLabsMusicGenerator.mjs +191 -0
  4. package/dist/ElevenLabsMusicGenerator.mjs.map +1 -0
  5. package/dist/ElevenLabsMusicGenerator.test.mjs +98 -0
  6. package/dist/ElevenLabsMusicGenerator.test.mjs.map +1 -0
  7. package/dist/ElevenLabsSynthesizer.d.mts +10 -4
  8. package/dist/ElevenLabsSynthesizer.d.mts.map +1 -1
  9. package/dist/ElevenLabsSynthesizer.mjs +9 -33
  10. package/dist/ElevenLabsSynthesizer.mjs.map +1 -1
  11. package/dist/ElevenLabsTranscriber.d.mts +4 -4
  12. package/dist/ElevenLabsTranscriber.d.mts.map +1 -1
  13. package/dist/ElevenLabsTranscriber.mjs +9 -1
  14. package/dist/ElevenLabsTranscriber.mjs.map +1 -1
  15. package/dist/codec.d.mts +25 -2
  16. package/dist/codec.d.mts.map +1 -1
  17. package/dist/codec.mjs +18 -1
  18. package/dist/codec.mjs.map +1 -1
  19. package/dist/index.d.mts +4 -3
  20. package/dist/index.mjs +2 -1
  21. package/dist/models.d.mts +13 -1
  22. package/dist/models.d.mts.map +1 -1
  23. package/dist/musicCodec-CZni1gAQ.d.mts +42 -0
  24. package/dist/musicCodec-CZni1gAQ.d.mts.map +1 -0
  25. package/dist/musicCodec.d.mts +2 -0
  26. package/dist/musicCodec.mjs +38 -0
  27. package/dist/musicCodec.mjs.map +1 -0
  28. package/dist/realtimeStt.d.mts +2 -1
  29. package/dist/realtimeStt.d.mts.map +1 -1
  30. package/dist/realtimeStt.mjs +14 -3
  31. package/dist/realtimeStt.mjs.map +1 -1
  32. package/dist/realtimeTts.d.mts +8 -3
  33. package/dist/realtimeTts.d.mts.map +1 -1
  34. package/dist/realtimeTts.mjs +5 -3
  35. package/dist/realtimeTts.mjs.map +1 -1
  36. package/package.json +9 -3
  37. package/src/ElevenLabsMusicGenerator.test.ts +116 -0
  38. package/src/ElevenLabsMusicGenerator.ts +353 -0
  39. package/src/ElevenLabsSynthesizer.ts +17 -51
  40. package/src/ElevenLabsTranscriber.ts +11 -0
  41. package/src/codec.ts +47 -0
  42. package/src/index.ts +1 -0
  43. package/src/models.ts +17 -0
  44. package/src/musicCodec.ts +86 -0
  45. package/src/realtimeStt.ts +11 -0
  46. package/src/realtimeTts.ts +11 -0
  47. package/dist/ElevenLabsSynthesizer.test.mjs +0 -32
  48. package/dist/ElevenLabsSynthesizer.test.mjs.map +0 -1
  49. package/src/ElevenLabsSynthesizer.test.ts +0 -49
  50. /package/dist/{ElevenLabsSynthesizer.test.d.mts → ElevenLabsMusicGenerator.test.d.mts} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"ElevenLabsSynthesizer.mjs","names":["Arr","realtimeStream"],"sources":["../src/ElevenLabsSynthesizer.ts"],"sourcesContent":["import { Array as Arr, Context, Effect, Layer, Redacted, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport type * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioBlob, AudioChunk } from \"@effect-uai/core/Audio\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeDialogueRequest,\n type CommonSynthesizeRequest,\n type CustomPronunciation,\n MultiSpeakerTts,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"@effect-uai/core/SpeechSynthesizer\"\nimport {\n defaultFormat,\n formatToOutputSlug,\n httpStatusError,\n transportFailure,\n type VoiceSettings,\n wireVoiceSettings,\n} from \"./codec.js\"\nimport type { ElevenLabsTtsModel, ElevenLabsVoiceId } from \"./models.js\"\nimport { streamSynthesis as realtimeStream } from \"./realtimeTts.js\"\nimport { type ElevenLabsRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs-typed synthesize request. `voiceSettings` exposes the\n * provider's prosody/timbre controls; `seed` makes generation\n * deterministic; `previousText` / `nextText` thread context across\n * sequential calls for natural prosody.\n */\nexport type ElevenLabsSynthesizeRequest = Omit<CommonSynthesizeRequest, \"model\" | \"voiceId\"> & {\n readonly model: ElevenLabsTtsModel\n readonly voiceId: ElevenLabsVoiceId\n readonly voiceSettings?: VoiceSettings\n readonly seed?: number\n readonly previousText?: string\n readonly nextText?: string\n}\n\nexport type ElevenLabsSynthesizerService = {\n readonly synthesize: (r: ElevenLabsSynthesizeRequest) => Effect.Effect<AudioBlob, AiError.AiError>\n readonly streamSynthesis: (\n r: ElevenLabsSynthesizeRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamSynthesisFrom: SpeechSynthesizerService[\"streamSynthesisFrom\"]\n readonly synthesizeDialogue: SpeechSynthesizerService[\"synthesizeDialogue\"]\n readonly streamSynthesizeDialogue: SpeechSynthesizerService[\"streamSynthesizeDialogue\"]\n}\n\nexport class ElevenLabsSynthesizer extends Context.Service<\n ElevenLabsSynthesizer,\n ElevenLabsSynthesizerService\n>()(\"@betalyra/effect-uai/providers/elevenlabs/ElevenLabsSynthesizer\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: ElevenLabsRegion\n}\n\n// ---------------------------------------------------------------------------\n// Pronunciations — SSML <phoneme> rewrite (gated to legacy models)\n// ---------------------------------------------------------------------------\n\n/**\n * Models that honor inline `<phoneme>` SSML tags. Other models silently\n * drop the tags server-side, so we don't bother rewriting the text for\n * them — the pronunciation overrides are silently ignored.\n *\n * Reference: https://elevenlabs.io/docs/best-practices/prompting/controls\n */\nconst PHONEME_SUPPORTED_MODELS: ReadonlySet<string> = new Set([\n \"eleven_flash_v2\",\n \"eleven_english_v1\",\n \"eleven_monolingual_v1\",\n])\n\nconst wireAlphabet = (encoding: CustomPronunciation[\"encoding\"]): string | undefined =>\n encoding === \"ipa\" ? \"ipa\" : encoding === \"cmu-arpabet\" ? \"cmu-arpabet\" : undefined\n\nconst phonemeTag = (phrase: string, pronunciation: string, alphabet: string): string =>\n `<phoneme alphabet=\"${alphabet}\" ph=\"${pronunciation}\">${phrase}</phoneme>`\n\nconst escapeForRegex = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\")\n\n/**\n * Apply pronunciation overrides to a text string. Only applies for\n * phoneme-supported models; only IPA and CMU Arpabet are honored\n * (x-sampa is silently dropped). Whole-word, case-insensitive\n * replacement.\n */\nconst applyPronunciations = (\n text: string,\n model: string,\n pronunciations: ReadonlyArray<CustomPronunciation> | undefined,\n): string => {\n if (pronunciations === undefined || !PHONEME_SUPPORTED_MODELS.has(model)) return text\n return Arr.reduce(pronunciations, text, (acc, p) => {\n const alphabet = wireAlphabet(p.encoding)\n if (alphabet === undefined) return acc\n return acc.replace(\n new RegExp(`\\\\b${escapeForRegex(p.phrase)}\\\\b`, \"i\"),\n phonemeTag(p.phrase, p.pronunciation, alphabet),\n )\n })\n}\n\n// ---------------------------------------------------------------------------\n// Codec — request → JSON body\n// ---------------------------------------------------------------------------\n\nconst buildBody = (r: ElevenLabsSynthesizeRequest) => ({\n text: applyPronunciations(r.text, r.model, r.pronunciations),\n model_id: r.model,\n ...(r.languageCode !== undefined && { language_code: r.languageCode }),\n ...(r.seed !== undefined && { seed: r.seed }),\n ...(r.previousText !== undefined && { previous_text: r.previousText }),\n ...(r.nextText !== undefined && { next_text: r.nextText }),\n ...(r.voiceSettings !== undefined && { voice_settings: wireVoiceSettings(r.voiceSettings) }),\n})\n\n// ---------------------------------------------------------------------------\n// Dialogue — POST /v1/text-to-dialogue (+ /stream variant)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_DIALOGUE_MODEL = \"eleven_v3\"\n\nconst buildDialogueBody = (r: CommonSynthesizeDialogueRequest) => ({\n inputs: r.turns.map((t) => ({\n voice_id: t.voiceId,\n text: applyPronunciations(t.text, r.model, r.pronunciations),\n })),\n model_id: r.model ?? DEFAULT_DIALOGUE_MODEL,\n ...(r.languageCode !== undefined && { language_code: r.languageCode }),\n})\n\nconst buildDialogueHttpRequest = (\n cfg: Config,\n r: CommonSynthesizeDialogueRequest,\n path: \"\" | \"/stream\",\n) =>\n Effect.gen(function* () {\n const format = r.outputFormat ?? defaultFormat\n const slug = yield* formatToOutputSlug(format)\n const url = `${resolveHost(cfg)}/text-to-dialogue${path}?output_format=${slug}`\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(buildDialogueBody(r)),\n )\n return { httpRequest, format }\n })\n\nconst synthesizeDialogueImpl = (cfg: Config) => (request: CommonSynthesizeDialogueRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest, format } = yield* buildDialogueHttpRequest(cfg, request, \"\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const bytes = yield* response.arrayBuffer.pipe(Effect.mapError(transportFailure))\n return { format, bytes: new Uint8Array(bytes) } satisfies AudioBlob\n })\n\nconst streamSynthesizeDialogueImpl = (cfg: Config) => (request: CommonSynthesizeDialogueRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest } = yield* buildDialogueHttpRequest(cfg, request, \"/stream\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n Stream.map((bytes): AudioChunk => ({ bytes })),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing (sync + chunked-HTTP streaming)\n// ---------------------------------------------------------------------------\n\nconst buildHttpRequest = (cfg: Config, r: ElevenLabsSynthesizeRequest, path: \"\" | \"/stream\") =>\n Effect.gen(function* () {\n const format = r.outputFormat ?? defaultFormat\n const slug = yield* formatToOutputSlug(format)\n const url = `${resolveHost(cfg)}/text-to-speech/${r.voiceId}${path}?output_format=${slug}`\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(buildBody(r)),\n )\n return { httpRequest, format }\n })\n\nconst synthesizeImpl = (cfg: Config) => (request: ElevenLabsSynthesizeRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest, format } = yield* buildHttpRequest(cfg, request, \"\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const bytes = yield* response.arrayBuffer.pipe(Effect.mapError(transportFailure))\n return { format, bytes: new Uint8Array(bytes) } satisfies AudioBlob\n })\n\nconst streamSynthesisImpl = (cfg: Config) => (request: ElevenLabsSynthesizeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest } = yield* buildHttpRequest(cfg, request, \"/stream\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n Stream.map((bytes): AudioChunk => ({ bytes })),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (cfg: Config) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const ctor = yield* Socket.WebSocketConstructor\n return {\n synthesize: (r) =>\n synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamSynthesis: (r) =>\n streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamSynthesisFrom: (textIn, request) =>\n realtimeStream(cfg)(textIn, request as ElevenLabsSynthesizeRequest).pipe(\n Stream.provideService(Socket.WebSocketConstructor, ctor),\n ),\n synthesizeDialogue: (request) =>\n synthesizeDialogueImpl(cfg)(request).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ),\n streamSynthesizeDialogue: (request) =>\n streamSynthesizeDialogueImpl(cfg)(request).pipe(\n Stream.provideService(HttpClient.HttpClient, client),\n ),\n } satisfies ElevenLabsSynthesizerService\n })\n\n/**\n * Layer registers `ElevenLabsSynthesizer`, the generic\n * `SpeechSynthesizer`, the `TtsIncrementalText` marker (incremental\n * text-in via `/stream-input` WebSocket), **and `MultiSpeakerTts`** —\n * multi-speaker dialogue is wired to `POST /v1/text-to-dialogue`\n * (sync) and `/v1/text-to-dialogue/stream` (chunked). Provide\n * `Socket.layerWebSocketConstructorGlobal` and an `HttpClient` Layer\n * at the call site.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(ElevenLabsSynthesizer, make(cfg)),\n Layer.effect(\n SpeechSynthesizer,\n Effect.map(\n make(cfg),\n (s): SpeechSynthesizerService => ({\n synthesize: (req: CommonSynthesizeRequest) =>\n s.synthesize(req as ElevenLabsSynthesizeRequest),\n streamSynthesis: (req: CommonSynthesizeRequest) =>\n s.streamSynthesis(req as ElevenLabsSynthesizeRequest),\n streamSynthesisFrom: (textIn, req: CommonStreamSynthesizeRequest) =>\n s.streamSynthesisFrom(textIn, req),\n synthesizeDialogue: s.synthesizeDialogue,\n streamSynthesizeDialogue: s.streamSynthesizeDialogue,\n }),\n ),\n ),\n Layer.succeed(TtsIncrementalText, undefined),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"ElevenLabsSynthesizer.mjs","names":["realtimeStream"],"sources":["../src/ElevenLabsSynthesizer.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport type * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioBlob, AudioChunk } from \"@effect-uai/core/Audio\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeDialogueRequest,\n type CommonSynthesizeRequest,\n MultiSpeakerTts,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"@effect-uai/core/SpeechSynthesizer\"\nimport {\n defaultFormat,\n formatToOutputSlug,\n httpStatusError,\n type PronunciationDictionaryLocator,\n rejectInlinePronunciations,\n transportFailure,\n type VoiceSettings,\n wirePronunciationLocators,\n wireVoiceSettings,\n} from \"./codec.js\"\nimport type { ElevenLabsTtsModel, ElevenLabsVoiceId } from \"./models.js\"\nimport { streamSynthesis as realtimeStream } from \"./realtimeTts.js\"\nimport { type ElevenLabsRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs-typed synthesize request. `voiceSettings` exposes the\n * provider's prosody/timbre controls; `seed` makes generation\n * deterministic; `previousText` / `nextText` thread context across\n * sequential calls for natural prosody.\n *\n * `pronunciationDictionaryLocators` references pre-provisioned\n * pronunciation dictionaries by ID. Inline `pronunciations` (IPA) on\n * the Common request are not supported by ElevenLabs and fail\n * `Unsupported`; use a dictionary for phonetic control.\n */\nexport type ElevenLabsSynthesizeRequest = Omit<CommonSynthesizeRequest, \"model\" | \"voiceId\"> & {\n readonly model: ElevenLabsTtsModel\n readonly voiceId: ElevenLabsVoiceId\n readonly voiceSettings?: VoiceSettings\n readonly seed?: number\n readonly previousText?: string\n readonly nextText?: string\n readonly pronunciationDictionaryLocators?: ReadonlyArray<PronunciationDictionaryLocator>\n}\n\nexport type ElevenLabsSynthesizerService = {\n readonly synthesize: (r: ElevenLabsSynthesizeRequest) => Effect.Effect<AudioBlob, AiError.AiError>\n readonly streamSynthesis: (\n r: ElevenLabsSynthesizeRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamSynthesisFrom: SpeechSynthesizerService[\"streamSynthesisFrom\"]\n readonly synthesizeDialogue: SpeechSynthesizerService[\"synthesizeDialogue\"]\n readonly streamSynthesizeDialogue: SpeechSynthesizerService[\"streamSynthesizeDialogue\"]\n}\n\nexport class ElevenLabsSynthesizer extends Context.Service<\n ElevenLabsSynthesizer,\n ElevenLabsSynthesizerService\n>()(\"@betalyra/effect-uai/providers/elevenlabs/ElevenLabsSynthesizer\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: ElevenLabsRegion\n}\n\n// ---------------------------------------------------------------------------\n// Codec — request → JSON body\n// ---------------------------------------------------------------------------\n\nconst buildBody = (r: ElevenLabsSynthesizeRequest) => ({\n text: r.text,\n model_id: r.model,\n ...(r.languageCode !== undefined && { language_code: r.languageCode }),\n ...(r.seed !== undefined && { seed: r.seed }),\n ...(r.previousText !== undefined && { previous_text: r.previousText }),\n ...(r.nextText !== undefined && { next_text: r.nextText }),\n ...wirePronunciationLocators(r.pronunciationDictionaryLocators),\n ...(r.voiceSettings !== undefined && { voice_settings: wireVoiceSettings(r.voiceSettings) }),\n})\n\n// ---------------------------------------------------------------------------\n// Dialogue — POST /v1/text-to-dialogue (+ /stream variant)\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_DIALOGUE_MODEL = \"eleven_v3\"\n\nconst buildDialogueBody = (r: CommonSynthesizeDialogueRequest) => ({\n inputs: r.turns.map((t) => ({\n voice_id: t.voiceId,\n text: t.text,\n })),\n model_id: r.model ?? DEFAULT_DIALOGUE_MODEL,\n ...(r.languageCode !== undefined && { language_code: r.languageCode }),\n})\n\nconst buildDialogueHttpRequest = (\n cfg: Config,\n r: CommonSynthesizeDialogueRequest,\n path: \"\" | \"/stream\",\n) =>\n Effect.gen(function* () {\n const format = r.outputFormat ?? defaultFormat\n const slug = yield* formatToOutputSlug(format)\n const url = `${resolveHost(cfg)}/text-to-dialogue${path}?output_format=${slug}`\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(buildDialogueBody(r)),\n )\n return { httpRequest, format }\n })\n\nconst synthesizeDialogueImpl = (cfg: Config) => (request: CommonSynthesizeDialogueRequest) =>\n Effect.gen(function* () {\n yield* rejectInlinePronunciations(request.pronunciations)\n const client = yield* HttpClient.HttpClient\n const { httpRequest, format } = yield* buildDialogueHttpRequest(cfg, request, \"\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const bytes = yield* response.arrayBuffer.pipe(Effect.mapError(transportFailure))\n return { format, bytes: new Uint8Array(bytes) } satisfies AudioBlob\n })\n\nconst streamSynthesizeDialogueImpl = (cfg: Config) => (request: CommonSynthesizeDialogueRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* rejectInlinePronunciations(request.pronunciations)\n const client = yield* HttpClient.HttpClient\n const { httpRequest } = yield* buildDialogueHttpRequest(cfg, request, \"/stream\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n Stream.map((bytes): AudioChunk => ({ bytes })),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing (sync + chunked-HTTP streaming)\n// ---------------------------------------------------------------------------\n\nconst buildHttpRequest = (cfg: Config, r: ElevenLabsSynthesizeRequest, path: \"\" | \"/stream\") =>\n Effect.gen(function* () {\n const format = r.outputFormat ?? defaultFormat\n const slug = yield* formatToOutputSlug(format)\n const url = `${resolveHost(cfg)}/text-to-speech/${r.voiceId}${path}?output_format=${slug}`\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(buildBody(r)),\n )\n return { httpRequest, format }\n })\n\nconst synthesizeImpl = (cfg: Config) => (request: ElevenLabsSynthesizeRequest) =>\n Effect.gen(function* () {\n yield* rejectInlinePronunciations(request.pronunciations)\n const client = yield* HttpClient.HttpClient\n const { httpRequest, format } = yield* buildHttpRequest(cfg, request, \"\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const bytes = yield* response.arrayBuffer.pipe(Effect.mapError(transportFailure))\n return { format, bytes: new Uint8Array(bytes) } satisfies AudioBlob\n })\n\nconst streamSynthesisImpl = (cfg: Config) => (request: ElevenLabsSynthesizeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n yield* rejectInlinePronunciations(request.pronunciations)\n const client = yield* HttpClient.HttpClient\n const { httpRequest } = yield* buildHttpRequest(cfg, request, \"/stream\")\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n Stream.map((bytes): AudioChunk => ({ bytes })),\n )\n }),\n )\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (cfg: Config) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const ctor = yield* Socket.WebSocketConstructor\n return {\n synthesize: (r) =>\n synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamSynthesis: (r) =>\n streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamSynthesisFrom: (textIn, request) =>\n realtimeStream(cfg)(textIn, request as ElevenLabsSynthesizeRequest).pipe(\n Stream.provideService(Socket.WebSocketConstructor, ctor),\n ),\n synthesizeDialogue: (request) =>\n synthesizeDialogueImpl(cfg)(request).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ),\n streamSynthesizeDialogue: (request) =>\n streamSynthesizeDialogueImpl(cfg)(request).pipe(\n Stream.provideService(HttpClient.HttpClient, client),\n ),\n } satisfies ElevenLabsSynthesizerService\n })\n\n/**\n * Layer registers `ElevenLabsSynthesizer`, the generic\n * `SpeechSynthesizer`, the `TtsIncrementalText` marker (incremental\n * text-in via `/stream-input` WebSocket), **and `MultiSpeakerTts`** —\n * multi-speaker dialogue is wired to `POST /v1/text-to-dialogue`\n * (sync) and `/v1/text-to-dialogue/stream` (chunked). Provide\n * `Socket.layerWebSocketConstructorGlobal` and an `HttpClient` Layer\n * at the call site.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(ElevenLabsSynthesizer, make(cfg)),\n Layer.effect(\n SpeechSynthesizer,\n Effect.map(\n make(cfg),\n (s): SpeechSynthesizerService => ({\n synthesize: (req: CommonSynthesizeRequest) =>\n s.synthesize(req as ElevenLabsSynthesizeRequest),\n streamSynthesis: (req: CommonSynthesizeRequest) =>\n s.streamSynthesis(req as ElevenLabsSynthesizeRequest),\n streamSynthesisFrom: (textIn, req: CommonStreamSynthesizeRequest) =>\n s.streamSynthesisFrom(textIn, req),\n synthesizeDialogue: s.synthesizeDialogue,\n streamSynthesizeDialogue: s.streamSynthesizeDialogue,\n }),\n ),\n ),\n Layer.succeed(TtsIncrementalText, undefined),\n Layer.succeed(MultiSpeakerTts, undefined),\n )\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import { ElevenLabsSttModel } from "./models.mjs";
2
2
  import { ElevenLabsRegion } from "./region.mjs";
3
3
  import { Context, Effect, Layer, Redacted, Stream } from "effect";
4
4
  import { HttpClient } from "effect/unstable/http";
5
- import * as Socket from "effect/unstable/socket/Socket";
6
5
  import * as AiError from "@effect-uai/core/AiError";
6
+ import * as Socket from "effect/unstable/socket/Socket";
7
7
  import { CommonTranscribeRequest, TranscriberService } from "@effect-uai/core/Transcriber";
8
8
  import { TranscriptResult } from "@effect-uai/core/Transcript";
9
9
 
@@ -36,9 +36,9 @@ type Config = {
36
36
  readonly region?: ElevenLabsRegion;
37
37
  };
38
38
  declare const make: (cfg: Config) => Effect.Effect<{
39
- transcribe: (r: ElevenLabsTranscribeRequest) => Effect.Effect<any, any, never>;
39
+ transcribe: (r: ElevenLabsTranscribeRequest) => Effect.Effect<any, any, unknown>;
40
40
  streamTranscriptionFrom: (audioIn: any, request: any) => Stream.Stream<TranscriptEvent, any, unknown>;
41
- }, never, Socket.WebSocketConstructor | HttpClient.HttpClient>;
41
+ }, never, HttpClient.HttpClient | Socket.WebSocketConstructor>;
42
42
  /**
43
43
  * Layer registers `ElevenLabsTranscriber`, the generic `Transcriber`,
44
44
  * **and the `SttStreaming` capability marker** — `streamTranscriptionFrom`
@@ -46,7 +46,7 @@ declare const make: (cfg: Config) => Effect.Effect<{
46
46
  * `Socket.layerWebSocketConstructorGlobal` (or a custom constructor) +
47
47
  * an `HttpClient` Layer at the call site.
48
48
  */
49
- declare const layer: (cfg: Config) => Layer.Layer<unknown, never, Socket.WebSocketConstructor | HttpClient.HttpClient>;
49
+ declare const layer: (cfg: Config) => Layer.Layer<unknown, never, HttpClient.HttpClient | Socket.WebSocketConstructor>;
50
50
  //#endregion
51
51
  export { Config, ElevenLabsTranscribeRequest, ElevenLabsTranscriber, ElevenLabsTranscriberService, layer, make, ElevenLabsTranscriber_d_exports as t };
52
52
  //# sourceMappingURL=ElevenLabsTranscriber.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ElevenLabsTranscriber.d.mts","names":[],"sources":["../src/ElevenLabsTranscriber.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;KA2BY,2BAAA,GAA8B,IAAA,CAAK,uBAAA;EAAA,SACpC,KAAA,EAAO,kBAAA;EAAA,SACP,WAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,4BAAA;EAAA,SACD,UAAA,GACP,CAAA,EAAG,2BAAA,KACA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,CAAQ,OAAA;EAAA,SACpC,uBAAA,EAAyB,kBAAA;AAAA;AAAA,cACnC,0BAAA;cAEY,qBAAA,SAA8B,0BAAA;AAAA,KAK/B,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;EAAA,SACA,MAAA,GAAS,gBAAA;AAAA;AAAA,cA+FP,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;cAsBnB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,iBAAA,MAAA,CAAA,oBAAA,GAAA,UAAA,CAAA,UAAA"}
1
+ {"version":3,"file":"ElevenLabsTranscriber.d.mts","names":[],"sources":["../src/ElevenLabsTranscriber.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;KA4BY,2BAAA,GAA8B,IAAA,CAAK,uBAAA;EAAA,SACpC,KAAA,EAAO,kBAAA;EAAA,SACP,WAAA;EAAA,SACA,WAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,4BAAA;EAAA,SACD,UAAA,GACP,CAAA,EAAG,2BAAA,KACA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,CAAQ,OAAA;EAAA,SACpC,uBAAA,EAAyB,kBAAA;AAAA;AAAA,cACnC,0BAAA;cAEY,qBAAA,SAA8B,0BAAA;AAAA,KAK/B,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;EAAA,SACA,MAAA,GAAS,gBAAA;AAAA;AAAA,cAyGP,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;cAsBnB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,iBAAA,UAAA,CAAA,UAAA,GAAA,MAAA,CAAA,oBAAA"}
@@ -4,8 +4,9 @@ import { resolveHost } from "./region.mjs";
4
4
  import { streamTranscription } from "./realtimeStt.mjs";
5
5
  import { Context, Effect, Layer, Redacted, Schema, Stream } from "effect";
6
6
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
7
- import * as Socket from "effect/unstable/socket/Socket";
8
7
  import * as AiError from "@effect-uai/core/AiError";
8
+ import * as Capabilities from "@effect-uai/core/Capabilities";
9
+ import * as Socket from "effect/unstable/socket/Socket";
9
10
  import { SttStreaming, Transcriber } from "@effect-uai/core/Transcriber";
10
11
  //#region src/ElevenLabsTranscriber.ts
11
12
  var ElevenLabsTranscriber_exports = /* @__PURE__ */ __exportAll({
@@ -48,6 +49,13 @@ const buildForm = (request) => Effect.gen(function* () {
48
49
  if (request.wordTimestamps === true) form.set("timestamps_granularity", "word");
49
50
  if (request.numSpeakers !== void 0) form.set("num_speakers", String(request.numSpeakers));
50
51
  if (request.audioEvents !== void 0) form.set("tag_audio_events", request.audioEvents ? "true" : "false");
52
+ for (const term of request.biasingTerms ?? []) form.append("keyterms", term);
53
+ yield* Capabilities.warnDroppedWhen(request.prompt, {
54
+ provider: "elevenlabs",
55
+ capability: "prompt",
56
+ field: "prompt",
57
+ reason: "ElevenLabs Scribe has no free-form prompt field; bias via `biasingTerms`."
58
+ });
51
59
  return form;
52
60
  });
53
61
  const transcribeImpl = (cfg) => (request) => Effect.gen(function* () {
@@ -1 +1 @@
1
- {"version":3,"file":"ElevenLabsTranscriber.mjs","names":[],"sources":["../src/ElevenLabsTranscriber.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { TranscriptResult, WordTimestamp } from \"@effect-uai/core/Transcript\"\nimport {\n type CommonTranscribeRequest,\n SttStreaming,\n Transcriber,\n type TranscriberService,\n} from \"@effect-uai/core/Transcriber\"\nimport { audioToBlob, defaultFileName, httpStatusError, transportFailure } from \"./codec.js\"\nimport type { ElevenLabsSttModel } from \"./models.js\"\nimport { streamTranscription } from \"./realtimeStt.js\"\nimport { type ElevenLabsRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs-typed transcribe request. `model` narrows to\n * `ElevenLabsSttModel`. `numSpeakers` hints diarization. `audioEvents`\n * surfaces `(laughter)` / `(music)` tags inline in `text` (provider\n * default: true). `fileName` overrides the multipart upload's\n * `filename`; ElevenLabs uses the extension to detect format.\n */\nexport type ElevenLabsTranscribeRequest = Omit<CommonTranscribeRequest, \"model\"> & {\n readonly model: ElevenLabsSttModel\n readonly numSpeakers?: number\n readonly audioEvents?: boolean\n readonly fileName?: string\n}\n\nexport type ElevenLabsTranscriberService = {\n readonly transcribe: (\n r: ElevenLabsTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n readonly streamTranscriptionFrom: TranscriberService[\"streamTranscriptionFrom\"]\n}\n\nexport class ElevenLabsTranscriber extends Context.Service<\n ElevenLabsTranscriber,\n ElevenLabsTranscriberService\n>()(\"@betalyra/effect-uai/providers/elevenlabs/ElevenLabsTranscriber\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: ElevenLabsRegion\n}\n\n// ---------------------------------------------------------------------------\n// Response schema\n// ---------------------------------------------------------------------------\n\nconst Word = Schema.Struct({\n text: Schema.String,\n start: Schema.optional(Schema.Number),\n end: Schema.optional(Schema.Number),\n type: Schema.optional(Schema.String),\n speaker_id: Schema.optional(Schema.String),\n logprob: Schema.optional(Schema.Number),\n})\n\nconst Wire = Schema.Struct({\n text: Schema.String,\n language_code: Schema.optional(Schema.String),\n language_probability: Schema.optional(Schema.Number),\n words: Schema.optional(Schema.Array(Word)),\n transcription_id: Schema.optional(Schema.String),\n})\nconst decodeWire = Schema.decodeUnknownEffect(Wire)\n\nconst toWordTimestamp = (w: typeof Word.Type): WordTimestamp | undefined =>\n w.start === undefined || w.end === undefined\n ? undefined\n : {\n text: w.text,\n startSeconds: w.start,\n endSeconds: w.end,\n ...(w.logprob !== undefined && { confidence: Math.exp(w.logprob) }),\n ...(w.speaker_id !== undefined && { speakerId: w.speaker_id }),\n }\n\n// ---------------------------------------------------------------------------\n// Multipart body\n// ---------------------------------------------------------------------------\n\nconst buildForm = (request: ElevenLabsTranscribeRequest) =>\n Effect.gen(function* () {\n const blob = yield* audioToBlob(request.audio)\n const fileName = request.fileName ?? defaultFileName(request.audio.mimeType ?? \"\")\n const form = new FormData()\n form.set(\"model_id\", request.model)\n form.set(\"file\", blob, fileName)\n if (request.language !== undefined) form.set(\"language_code\", request.language)\n if (request.diarization !== undefined)\n form.set(\"diarize\", request.diarization ? \"true\" : \"false\")\n if (request.wordTimestamps === true) form.set(\"timestamps_granularity\", \"word\")\n if (request.numSpeakers !== undefined) form.set(\"num_speakers\", String(request.numSpeakers))\n if (request.audioEvents !== undefined)\n form.set(\"tag_audio_events\", request.audioEvents ? \"true\" : \"false\")\n return form\n })\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst transcribeImpl = (cfg: Config) => (request: ElevenLabsTranscribeRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const form = yield* buildForm(request)\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/speech-to-text`).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyFormData(form),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeWire(json).pipe(\n Effect.mapError(\n (cause) => new AiError.GenerationFailed({ provider: \"elevenlabs\", raw: cause }),\n ),\n )\n const words = wire.words\n ?.map(toWordTimestamp)\n .filter((w): w is WordTimestamp => w !== undefined)\n return {\n text: wire.text,\n ...(wire.language_code !== undefined && { languageCode: wire.language_code }),\n ...(words !== undefined && words.length > 0 && { words }),\n raw: json,\n } satisfies TranscriptResult\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (cfg: Config) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const ctor = yield* Socket.WebSocketConstructor\n return {\n transcribe: (r) =>\n transcribeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamTranscriptionFrom: (audioIn, request) =>\n streamTranscription(cfg)(audioIn, request).pipe(\n Stream.provideService(HttpClient.HttpClient, client),\n Stream.provideService(Socket.WebSocketConstructor, ctor),\n ),\n } satisfies ElevenLabsTranscriberService\n })\n\n/**\n * Layer registers `ElevenLabsTranscriber`, the generic `Transcriber`,\n * **and the `SttStreaming` capability marker** — `streamTranscriptionFrom`\n * is wired to the realtime WebSocket. Provide\n * `Socket.layerWebSocketConstructorGlobal` (or a custom constructor) +\n * an `HttpClient` Layer at the call site.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(ElevenLabsTranscriber, make(cfg)),\n Layer.effect(\n Transcriber,\n Effect.map(\n make(cfg),\n (s): TranscriberService => ({\n transcribe: (req: CommonTranscribeRequest) =>\n s.transcribe(req as ElevenLabsTranscribeRequest),\n streamTranscriptionFrom: s.streamTranscriptionFrom,\n }),\n ),\n ),\n Layer.succeed(SttStreaming, undefined),\n )\n"],"mappings":"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"}
1
+ {"version":3,"file":"ElevenLabsTranscriber.mjs","names":[],"sources":["../src/ElevenLabsTranscriber.ts"],"sourcesContent":["import { Context, Effect, Layer, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport * as Capabilities from \"@effect-uai/core/Capabilities\"\nimport type { TranscriptResult, WordTimestamp } from \"@effect-uai/core/Transcript\"\nimport {\n type CommonTranscribeRequest,\n SttStreaming,\n Transcriber,\n type TranscriberService,\n} from \"@effect-uai/core/Transcriber\"\nimport { audioToBlob, defaultFileName, httpStatusError, transportFailure } from \"./codec.js\"\nimport type { ElevenLabsSttModel } from \"./models.js\"\nimport { streamTranscription } from \"./realtimeStt.js\"\nimport { type ElevenLabsRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs-typed transcribe request. `model` narrows to\n * `ElevenLabsSttModel`. `numSpeakers` hints diarization. `audioEvents`\n * surfaces `(laughter)` / `(music)` tags inline in `text` (provider\n * default: true). `fileName` overrides the multipart upload's\n * `filename`; ElevenLabs uses the extension to detect format.\n */\nexport type ElevenLabsTranscribeRequest = Omit<CommonTranscribeRequest, \"model\"> & {\n readonly model: ElevenLabsSttModel\n readonly numSpeakers?: number\n readonly audioEvents?: boolean\n readonly fileName?: string\n}\n\nexport type ElevenLabsTranscriberService = {\n readonly transcribe: (\n r: ElevenLabsTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n readonly streamTranscriptionFrom: TranscriberService[\"streamTranscriptionFrom\"]\n}\n\nexport class ElevenLabsTranscriber extends Context.Service<\n ElevenLabsTranscriber,\n ElevenLabsTranscriberService\n>()(\"@betalyra/effect-uai/providers/elevenlabs/ElevenLabsTranscriber\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: ElevenLabsRegion\n}\n\n// ---------------------------------------------------------------------------\n// Response schema\n// ---------------------------------------------------------------------------\n\nconst Word = Schema.Struct({\n text: Schema.String,\n start: Schema.optional(Schema.Number),\n end: Schema.optional(Schema.Number),\n type: Schema.optional(Schema.String),\n speaker_id: Schema.optional(Schema.String),\n logprob: Schema.optional(Schema.Number),\n})\n\nconst Wire = Schema.Struct({\n text: Schema.String,\n language_code: Schema.optional(Schema.String),\n language_probability: Schema.optional(Schema.Number),\n words: Schema.optional(Schema.Array(Word)),\n transcription_id: Schema.optional(Schema.String),\n})\nconst decodeWire = Schema.decodeUnknownEffect(Wire)\n\nconst toWordTimestamp = (w: typeof Word.Type): WordTimestamp | undefined =>\n w.start === undefined || w.end === undefined\n ? undefined\n : {\n text: w.text,\n startSeconds: w.start,\n endSeconds: w.end,\n ...(w.logprob !== undefined && { confidence: Math.exp(w.logprob) }),\n ...(w.speaker_id !== undefined && { speakerId: w.speaker_id }),\n }\n\n// ---------------------------------------------------------------------------\n// Multipart body\n// ---------------------------------------------------------------------------\n\nconst buildForm = (request: ElevenLabsTranscribeRequest) =>\n Effect.gen(function* () {\n const blob = yield* audioToBlob(request.audio)\n const fileName = request.fileName ?? defaultFileName(request.audio.mimeType ?? \"\")\n const form = new FormData()\n form.set(\"model_id\", request.model)\n form.set(\"file\", blob, fileName)\n if (request.language !== undefined) form.set(\"language_code\", request.language)\n if (request.diarization !== undefined)\n form.set(\"diarize\", request.diarization ? \"true\" : \"false\")\n if (request.wordTimestamps === true) form.set(\"timestamps_granularity\", \"word\")\n if (request.numSpeakers !== undefined) form.set(\"num_speakers\", String(request.numSpeakers))\n if (request.audioEvents !== undefined)\n form.set(\"tag_audio_events\", request.audioEvents ? \"true\" : \"false\")\n // Vocab biasing → Scribe `keyterms` (≤1000 terms, ≤50 chars). Each\n // term is a repeated form field (`keyterms=a&keyterms=b`), matching\n // the ElevenLabs SDK. Scribe has no free-form prompt field.\n for (const term of request.biasingTerms ?? []) form.append(\"keyterms\", term)\n yield* Capabilities.warnDroppedWhen(request.prompt, {\n provider: \"elevenlabs\",\n capability: \"prompt\",\n field: \"prompt\",\n reason: \"ElevenLabs Scribe has no free-form prompt field; bias via `biasingTerms`.\",\n })\n return form\n })\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst transcribeImpl = (cfg: Config) => (request: ElevenLabsTranscribeRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const form = yield* buildForm(request)\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/speech-to-text`).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyFormData(form),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeWire(json).pipe(\n Effect.mapError(\n (cause) => new AiError.GenerationFailed({ provider: \"elevenlabs\", raw: cause }),\n ),\n )\n const words = wire.words\n ?.map(toWordTimestamp)\n .filter((w): w is WordTimestamp => w !== undefined)\n return {\n text: wire.text,\n ...(wire.language_code !== undefined && { languageCode: wire.language_code }),\n ...(words !== undefined && words.length > 0 && { words }),\n raw: json,\n } satisfies TranscriptResult\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (cfg: Config) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const ctor = yield* Socket.WebSocketConstructor\n return {\n transcribe: (r) =>\n transcribeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamTranscriptionFrom: (audioIn, request) =>\n streamTranscription(cfg)(audioIn, request).pipe(\n Stream.provideService(HttpClient.HttpClient, client),\n Stream.provideService(Socket.WebSocketConstructor, ctor),\n ),\n } satisfies ElevenLabsTranscriberService\n })\n\n/**\n * Layer registers `ElevenLabsTranscriber`, the generic `Transcriber`,\n * **and the `SttStreaming` capability marker** — `streamTranscriptionFrom`\n * is wired to the realtime WebSocket. Provide\n * `Socket.layerWebSocketConstructorGlobal` (or a custom constructor) +\n * an `HttpClient` Layer at the call site.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(ElevenLabsTranscriber, make(cfg)),\n Layer.effect(\n Transcriber,\n Effect.map(\n make(cfg),\n (s): TranscriberService => ({\n transcribe: (req: CommonTranscribeRequest) =>\n s.transcribe(req as ElevenLabsTranscribeRequest),\n streamTranscriptionFrom: s.streamTranscriptionFrom,\n }),\n ),\n ),\n Layer.succeed(SttStreaming, undefined),\n )\n"],"mappings":"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"}
package/dist/codec.d.mts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { Effect } from "effect";
2
2
  import * as AiError from "@effect-uai/core/AiError";
3
+ import { CustomPronunciation } from "@effect-uai/core/SpeechSynthesizer";
3
4
  import { AudioFormat, AudioSource } from "@effect-uai/core/Audio";
4
5
 
5
6
  //#region src/codec.d.ts
6
7
  declare namespace codec_d_exports {
7
- export { VoiceSettings, audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, transportFailure, wireVoiceSettings };
8
+ export { PronunciationDictionaryLocator, VoiceSettings, audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, rejectInlinePronunciations, transportFailure, wirePronunciationLocators, wireVoiceSettings };
8
9
  }
9
10
  type VoiceSettings = {
10
11
  readonly stability?: number;
@@ -20,6 +21,28 @@ declare const wireVoiceSettings: (v: VoiceSettings | undefined) => {
20
21
  similarity_boost?: number;
21
22
  stability?: number;
22
23
  } | undefined;
24
+ /**
25
+ * Reference to a pre-provisioned ElevenLabs pronunciation dictionary.
26
+ * The caller provisions and versions the dictionary out-of-band
27
+ * (dashboard or the pronunciation-dictionary API); we only reference it.
28
+ */
29
+ type PronunciationDictionaryLocator = {
30
+ readonly dictionaryId: string;
31
+ readonly versionId: string;
32
+ };
33
+ declare const wirePronunciationLocators: (locators: ReadonlyArray<PronunciationDictionaryLocator> | undefined) => {
34
+ pronunciation_dictionary_locators: {
35
+ pronunciation_dictionary_id: string;
36
+ version_id: string;
37
+ }[];
38
+ } | undefined;
39
+ /**
40
+ * ElevenLabs has no stateless inline IPA path (legacy `<phoneme>` SSML
41
+ * is dropped; modern phonetics live in pre-uploaded dictionaries).
42
+ * Inline `pronunciations` are load-bearing (bucket 1), so reject rather
43
+ * than silently mispronounce. Use `pronunciationDictionaryLocators`.
44
+ */
45
+ declare const rejectInlinePronunciations: (pronunciations: ReadonlyArray<CustomPronunciation> | undefined) => Effect.Effect<void, AiError.AiError>;
23
46
  /**
24
47
  * ElevenLabs encodes container + sample rate + bitrate in a single
25
48
  * dot-less slug (`mp3_44100_128`, `pcm_16000`, `wav_48000`, `ulaw_8000`,
@@ -39,5 +62,5 @@ declare const defaultFileName: (mimeType: string) => string;
39
62
  declare const httpStatusError: (status: number, body: string) => AiError.AiError;
40
63
  declare const transportFailure: (cause: unknown) => AiError.AiError;
41
64
  //#endregion
42
- export { VoiceSettings, audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, codec_d_exports as t, transportFailure, wireVoiceSettings };
65
+ export { PronunciationDictionaryLocator, VoiceSettings, audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, rejectInlinePronunciations, codec_d_exports as t, transportFailure, wirePronunciationLocators, wireVoiceSettings };
43
66
  //# sourceMappingURL=codec.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;;KAQY,aAAA;EAAA,SACD,SAAA;EAAA,SACA,eAAA;EAAA,SACA,KAAA;EAAA,SACA,eAAA;EAAA,SACA,KAAA;AAAA;AAAA,cAGE,iBAAA,GAAqB,CAAA,EAAG,aAAA;;;;;;;;;;;;;;;;;AAArC;cA0Ba,kBAAA,GAAqB,MAAA,EAAQ,WAAA,KAAgB,MAAA,CAAO,MAAA,SAAe,OAAA,CAAQ,OAAA;;cAgC3E,aAAA,EAAe,WAAA;AAAA,cA6Bf,WAAA,GAAc,KAAA,EAAO,WAAA,KAAgB,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA,CAAQ,OAAA;AAAA,cA2BjE,eAAA,GAAmB,QAAA;AAAA,cAYnB,eAAA,GAAkB,MAAA,UAAgB,IAAA,aAAiB,OAAA,CAAQ,OAAA;AAAA,cAyC3D,gBAAA,GAAoB,KAAA,cAAiB,OAAA,CAAQ,OAAA"}
1
+ {"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;;;KASY,aAAA;EAAA,SACD,SAAA;EAAA,SACA,eAAA;EAAA,SACA,KAAA;EAAA,SACA,eAAA;EAAA,SACA,KAAA;AAAA;AAAA,cAGE,iBAAA,GAAqB,CAAA,EAAG,aAAA;;;;;;;;;AARrC;;;KA4BY,8BAAA;EAAA,SACD,YAAA;EAAA,SACA,SAAA;AAAA;AAAA,cAGE,yBAAA,GACX,QAAA,EAAU,aAAA,CAAc,8BAAA;;;;;;;;;;;;cAiBb,0BAAA,GACX,cAAA,EAAgB,aAAA,CAAc,mBAAA,kBAC7B,MAAA,CAAO,MAAA,OAAa,OAAA,CAAQ,OAAA;;;;;AAzB/B;;;;;AAKA;;cA+Ca,kBAAA,GAAqB,MAAA,EAAQ,WAAA,KAAgB,MAAA,CAAO,MAAA,SAAe,OAAA,CAAQ,OAAA;;cAgC3E,aAAA,EAAe,WAAA;AAAA,cA6Bf,WAAA,GAAc,KAAA,EAAO,WAAA,KAAgB,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA,CAAQ,OAAA;AAAA,cA2BjE,eAAA,GAAmB,QAAA;AAAA,cAYnB,eAAA,GAAkB,MAAA,UAAgB,IAAA,aAAiB,OAAA,CAAQ,OAAA;AAAA,cAyC3D,gBAAA,GAAoB,KAAA,cAAiB,OAAA,CAAQ,OAAA"}
package/dist/codec.mjs CHANGED
@@ -8,7 +8,9 @@ var codec_exports = /* @__PURE__ */ __exportAll({
8
8
  defaultFormat: () => defaultFormat,
9
9
  formatToOutputSlug: () => formatToOutputSlug,
10
10
  httpStatusError: () => httpStatusError,
11
+ rejectInlinePronunciations: () => rejectInlinePronunciations,
11
12
  transportFailure: () => transportFailure,
13
+ wirePronunciationLocators: () => wirePronunciationLocators,
12
14
  wireVoiceSettings: () => wireVoiceSettings
13
15
  });
14
16
  const wireVoiceSettings = (v) => v === void 0 ? void 0 : {
@@ -18,6 +20,21 @@ const wireVoiceSettings = (v) => v === void 0 ? void 0 : {
18
20
  ...v.useSpeakerBoost !== void 0 && { use_speaker_boost: v.useSpeakerBoost },
19
21
  ...v.speed !== void 0 && { speed: v.speed }
20
22
  };
23
+ const wirePronunciationLocators = (locators) => locators === void 0 || locators.length === 0 ? void 0 : { pronunciation_dictionary_locators: locators.map((l) => ({
24
+ pronunciation_dictionary_id: l.dictionaryId,
25
+ version_id: l.versionId
26
+ })) };
27
+ /**
28
+ * ElevenLabs has no stateless inline IPA path (legacy `<phoneme>` SSML
29
+ * is dropped; modern phonetics live in pre-uploaded dictionaries).
30
+ * Inline `pronunciations` are load-bearing (bucket 1), so reject rather
31
+ * than silently mispronounce. Use `pronunciationDictionaryLocators`.
32
+ */
33
+ const rejectInlinePronunciations = (pronunciations) => pronunciations !== void 0 && pronunciations.length > 0 ? Effect.fail(new AiError.Unsupported({
34
+ provider: "elevenlabs",
35
+ capability: "pronunciations",
36
+ reason: "ElevenLabs has no stateless inline IPA path. Provision a pronunciation dictionary and pass `pronunciationDictionaryLocators`, or use a provider with inline phonemes (Inworld, Google)."
37
+ })) : Effect.void;
21
38
  /**
22
39
  * ElevenLabs encodes container + sample rate + bitrate in a single
23
40
  * dot-less slug (`mp3_44100_128`, `pcm_16000`, `wav_48000`, `ulaw_8000`,
@@ -115,6 +132,6 @@ const transportFailure = (cause) => new AiError.Unavailable({
115
132
  raw: cause
116
133
  });
117
134
  //#endregion
118
- export { audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, codec_exports as t, transportFailure, wireVoiceSettings };
135
+ export { audioToBlob, defaultFileName, defaultFormat, formatToOutputSlug, httpStatusError, rejectInlinePronunciations, codec_exports as t, transportFailure, wirePronunciationLocators, wireVoiceSettings };
119
136
 
120
137
  //# sourceMappingURL=codec.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Effect, Encoding, Match, Option, Result } from \"effect\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioFormat, AudioSource } from \"@effect-uai/core/Audio\"\n\n// ---------------------------------------------------------------------------\n// Shared TTS voice-settings shape (sync + streaming use the same fields)\n// ---------------------------------------------------------------------------\n\nexport type VoiceSettings = {\n readonly stability?: number\n readonly similarityBoost?: number\n readonly style?: number\n readonly useSpeakerBoost?: boolean\n readonly speed?: number\n}\n\nexport const wireVoiceSettings = (v: VoiceSettings | undefined) =>\n v === undefined\n ? undefined\n : {\n ...(v.stability !== undefined && { stability: v.stability }),\n ...(v.similarityBoost !== undefined && { similarity_boost: v.similarityBoost }),\n ...(v.style !== undefined && { style: v.style }),\n ...(v.useSpeakerBoost !== undefined && { use_speaker_boost: v.useSpeakerBoost }),\n ...(v.speed !== undefined && { speed: v.speed }),\n }\n\n// ---------------------------------------------------------------------------\n// AudioFormat → ElevenLabs `output_format` query value\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs encodes container + sample rate + bitrate in a single\n * dot-less slug (`mp3_44100_128`, `pcm_16000`, `wav_48000`, `ulaw_8000`,\n * `opus_48000_128`, etc.). This helper inverts the common cases of our\n * `AudioFormat` shape into that slug.\n *\n * Only the formats that map cleanly are produced. Anything we can't\n * encode (mismatched bitrate, unsupported sample rate combination)\n * fails `Unsupported` with the requested format echoed so the user can\n * fix it.\n */\nexport const formatToOutputSlug: (format: AudioFormat) => Effect.Effect<string, AiError.AiError> = (\n format,\n) =>\n Match.value(format).pipe(\n Match.when({ container: \"mp3\" }, (f) =>\n Effect.succeed(`mp3_${f.sampleRate}_${f.bitRate ?? 128}`),\n ),\n Match.when({ container: \"opus\" }, (f) =>\n Effect.succeed(`opus_${f.sampleRate}_${f.bitRate ?? 128}`),\n ),\n Match.when({ container: \"wav\" }, (f) => Effect.succeed(`wav_${f.sampleRate}`)),\n Match.when({ container: \"raw\", encoding: \"pcm_s16le\" }, (f) =>\n Effect.succeed(`pcm_${f.sampleRate}`),\n ),\n Match.when({ container: \"raw\", encoding: \"pcm_mulaw\" }, (f) =>\n Effect.succeed(`ulaw_${f.sampleRate}`),\n ),\n Match.when({ container: \"raw\", encoding: \"pcm_alaw\" }, (f) =>\n Effect.succeed(`alaw_${f.sampleRate}`),\n ),\n Match.orElse((f) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"elevenlabs\",\n capability: \"outputFormat\",\n reason: `Cannot encode ${JSON.stringify(f)} as ElevenLabs output_format. Supported: mp3 + (22050|44100), opus 48000, wav 48000, pcm/ulaw/alaw at any sample rate.`,\n }),\n ),\n ),\n )\n\n/** Default output: 128 kbps MP3 at 44.1 kHz. */\nexport const defaultFormat: AudioFormat = {\n container: \"mp3\",\n encoding: \"mp3\",\n sampleRate: 44100,\n bitRate: 128,\n}\n\n// ---------------------------------------------------------------------------\n// AudioSource → Blob (STT multipart upload)\n// ---------------------------------------------------------------------------\n\nconst urlNotSupported: AiError.AiError = new AiError.InvalidRequest({\n provider: \"elevenlabs\",\n param: \"audio\",\n raw: 'ElevenLabs accepts URLs via the `cloud_storage_url` field, not the `file` field. Fetch the URL yourself and pass `{ _tag: \"bytes\", bytes, mimeType }` to use the inline path.',\n})\n\nconst decodeBase64ToBytes = (b64: string): Effect.Effect<Uint8Array, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onSuccess: Effect.succeed,\n onFailure: (cause) =>\n Effect.fail(\n new AiError.InvalidRequest({ provider: \"elevenlabs\", param: \"audio\", raw: cause }),\n ),\n })\n\nconst bytesToBlob = (bytes: Uint8Array, mimeType: string): Blob =>\n new Blob([bytes as Uint8Array<ArrayBuffer>], { type: mimeType })\n\nexport const audioToBlob: (audio: AudioSource) => Effect.Effect<Blob, AiError.AiError> =\n Match.type<AudioSource>().pipe(\n Match.tag(\"bytes\", (a) => Effect.succeed(bytesToBlob(a.bytes, a.mimeType))),\n Match.tag(\"base64\", (a) =>\n decodeBase64ToBytes(a.base64).pipe(Effect.map((bytes) => bytesToBlob(bytes, a.mimeType))),\n ),\n Match.tag(\"url\", () => Effect.fail(urlNotSupported)),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// MIME → file extension (for the multipart `filename` field)\n// ---------------------------------------------------------------------------\n\nconst EXTENSION_BY_MIME: ReadonlyArray<readonly [string, string]> = [\n [\"mpeg\", \"mp3\"],\n [\"mp3\", \"mp3\"],\n [\"wav\", \"wav\"],\n [\"ogg\", \"ogg\"],\n [\"opus\", \"ogg\"],\n [\"flac\", \"flac\"],\n [\"aac\", \"aac\"],\n [\"mp4\", \"m4a\"],\n [\"m4a\", \"m4a\"],\n [\"webm\", \"webm\"],\n]\n\nexport const defaultFileName = (mimeType: string): string =>\n Arr.findFirst(EXTENSION_BY_MIME, ([pattern]) => mimeType.includes(pattern)).pipe(\n Option.match({\n onNone: () => \"audio\",\n onSome: ([, ext]) => `audio.${ext}`,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nexport const httpStatusError: (status: number, body: string) => AiError.AiError = (status, body) =>\n Match.value(status).pipe(\n Match.when(\n 429,\n (): AiError.AiError => new AiError.RateLimited({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.whenOr(\n 408,\n 504,\n (): AiError.AiError => new AiError.Timeout({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.when(\n 401,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"billing\", raw: body }),\n ),\n Match.when(\n 413,\n (): AiError.AiError =>\n new AiError.ContextLengthExceeded({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError =>\n new AiError.Unavailable({ provider: \"elevenlabs\", status: n, raw: body }),\n ),\n Match.orElse(\n (): AiError.AiError => new AiError.InvalidRequest({ provider: \"elevenlabs\", raw: body }),\n ),\n )\n\nexport const transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"elevenlabs\", raw: cause })\n"],"mappings":"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"}
1
+ {"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Effect, Encoding, Match, Option, Result } from \"effect\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioFormat, AudioSource } from \"@effect-uai/core/Audio\"\nimport type { CustomPronunciation } from \"@effect-uai/core/SpeechSynthesizer\"\n\n// ---------------------------------------------------------------------------\n// Shared TTS voice-settings shape (sync + streaming use the same fields)\n// ---------------------------------------------------------------------------\n\nexport type VoiceSettings = {\n readonly stability?: number\n readonly similarityBoost?: number\n readonly style?: number\n readonly useSpeakerBoost?: boolean\n readonly speed?: number\n}\n\nexport const wireVoiceSettings = (v: VoiceSettings | undefined) =>\n v === undefined\n ? undefined\n : {\n ...(v.stability !== undefined && { stability: v.stability }),\n ...(v.similarityBoost !== undefined && { similarity_boost: v.similarityBoost }),\n ...(v.style !== undefined && { style: v.style }),\n ...(v.useSpeakerBoost !== undefined && { use_speaker_boost: v.useSpeakerBoost }),\n ...(v.speed !== undefined && { speed: v.speed }),\n }\n\n// ---------------------------------------------------------------------------\n// Pronunciation: dictionary locators (supported) vs inline IPA (rejected)\n// ---------------------------------------------------------------------------\n\n/**\n * Reference to a pre-provisioned ElevenLabs pronunciation dictionary.\n * The caller provisions and versions the dictionary out-of-band\n * (dashboard or the pronunciation-dictionary API); we only reference it.\n */\nexport type PronunciationDictionaryLocator = {\n readonly dictionaryId: string\n readonly versionId: string\n}\n\nexport const wirePronunciationLocators = (\n locators: ReadonlyArray<PronunciationDictionaryLocator> | undefined,\n) =>\n locators === undefined || locators.length === 0\n ? undefined\n : {\n pronunciation_dictionary_locators: locators.map((l) => ({\n pronunciation_dictionary_id: l.dictionaryId,\n version_id: l.versionId,\n })),\n }\n\n/**\n * ElevenLabs has no stateless inline IPA path (legacy `<phoneme>` SSML\n * is dropped; modern phonetics live in pre-uploaded dictionaries).\n * Inline `pronunciations` are load-bearing (bucket 1), so reject rather\n * than silently mispronounce. Use `pronunciationDictionaryLocators`.\n */\nexport const rejectInlinePronunciations = (\n pronunciations: ReadonlyArray<CustomPronunciation> | undefined,\n): Effect.Effect<void, AiError.AiError> =>\n pronunciations !== undefined && pronunciations.length > 0\n ? Effect.fail(\n new AiError.Unsupported({\n provider: \"elevenlabs\",\n capability: \"pronunciations\",\n reason:\n \"ElevenLabs has no stateless inline IPA path. Provision a pronunciation dictionary and pass `pronunciationDictionaryLocators`, or use a provider with inline phonemes (Inworld, Google).\",\n }),\n )\n : Effect.void\n\n// ---------------------------------------------------------------------------\n// AudioFormat → ElevenLabs `output_format` query value\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs encodes container + sample rate + bitrate in a single\n * dot-less slug (`mp3_44100_128`, `pcm_16000`, `wav_48000`, `ulaw_8000`,\n * `opus_48000_128`, etc.). This helper inverts the common cases of our\n * `AudioFormat` shape into that slug.\n *\n * Only the formats that map cleanly are produced. Anything we can't\n * encode (mismatched bitrate, unsupported sample rate combination)\n * fails `Unsupported` with the requested format echoed so the user can\n * fix it.\n */\nexport const formatToOutputSlug: (format: AudioFormat) => Effect.Effect<string, AiError.AiError> = (\n format,\n) =>\n Match.value(format).pipe(\n Match.when({ container: \"mp3\" }, (f) =>\n Effect.succeed(`mp3_${f.sampleRate}_${f.bitRate ?? 128}`),\n ),\n Match.when({ container: \"opus\" }, (f) =>\n Effect.succeed(`opus_${f.sampleRate}_${f.bitRate ?? 128}`),\n ),\n Match.when({ container: \"wav\" }, (f) => Effect.succeed(`wav_${f.sampleRate}`)),\n Match.when({ container: \"raw\", encoding: \"pcm_s16le\" }, (f) =>\n Effect.succeed(`pcm_${f.sampleRate}`),\n ),\n Match.when({ container: \"raw\", encoding: \"pcm_mulaw\" }, (f) =>\n Effect.succeed(`ulaw_${f.sampleRate}`),\n ),\n Match.when({ container: \"raw\", encoding: \"pcm_alaw\" }, (f) =>\n Effect.succeed(`alaw_${f.sampleRate}`),\n ),\n Match.orElse((f) =>\n Effect.fail(\n new AiError.Unsupported({\n provider: \"elevenlabs\",\n capability: \"outputFormat\",\n reason: `Cannot encode ${JSON.stringify(f)} as ElevenLabs output_format. Supported: mp3 + (22050|44100), opus 48000, wav 48000, pcm/ulaw/alaw at any sample rate.`,\n }),\n ),\n ),\n )\n\n/** Default output: 128 kbps MP3 at 44.1 kHz. */\nexport const defaultFormat: AudioFormat = {\n container: \"mp3\",\n encoding: \"mp3\",\n sampleRate: 44100,\n bitRate: 128,\n}\n\n// ---------------------------------------------------------------------------\n// AudioSource → Blob (STT multipart upload)\n// ---------------------------------------------------------------------------\n\nconst urlNotSupported: AiError.AiError = new AiError.InvalidRequest({\n provider: \"elevenlabs\",\n param: \"audio\",\n raw: 'ElevenLabs accepts URLs via the `cloud_storage_url` field, not the `file` field. Fetch the URL yourself and pass `{ _tag: \"bytes\", bytes, mimeType }` to use the inline path.',\n})\n\nconst decodeBase64ToBytes = (b64: string): Effect.Effect<Uint8Array, AiError.AiError> =>\n Result.match(Encoding.decodeBase64(b64), {\n onSuccess: Effect.succeed,\n onFailure: (cause) =>\n Effect.fail(\n new AiError.InvalidRequest({ provider: \"elevenlabs\", param: \"audio\", raw: cause }),\n ),\n })\n\nconst bytesToBlob = (bytes: Uint8Array, mimeType: string): Blob =>\n new Blob([bytes as Uint8Array<ArrayBuffer>], { type: mimeType })\n\nexport const audioToBlob: (audio: AudioSource) => Effect.Effect<Blob, AiError.AiError> =\n Match.type<AudioSource>().pipe(\n Match.tag(\"bytes\", (a) => Effect.succeed(bytesToBlob(a.bytes, a.mimeType))),\n Match.tag(\"base64\", (a) =>\n decodeBase64ToBytes(a.base64).pipe(Effect.map((bytes) => bytesToBlob(bytes, a.mimeType))),\n ),\n Match.tag(\"url\", () => Effect.fail(urlNotSupported)),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// MIME → file extension (for the multipart `filename` field)\n// ---------------------------------------------------------------------------\n\nconst EXTENSION_BY_MIME: ReadonlyArray<readonly [string, string]> = [\n [\"mpeg\", \"mp3\"],\n [\"mp3\", \"mp3\"],\n [\"wav\", \"wav\"],\n [\"ogg\", \"ogg\"],\n [\"opus\", \"ogg\"],\n [\"flac\", \"flac\"],\n [\"aac\", \"aac\"],\n [\"mp4\", \"m4a\"],\n [\"m4a\", \"m4a\"],\n [\"webm\", \"webm\"],\n]\n\nexport const defaultFileName = (mimeType: string): string =>\n Arr.findFirst(EXTENSION_BY_MIME, ([pattern]) => mimeType.includes(pattern)).pipe(\n Option.match({\n onNone: () => \"audio\",\n onSome: ([, ext]) => `audio.${ext}`,\n }),\n )\n\n// ---------------------------------------------------------------------------\n// HTTP errors\n// ---------------------------------------------------------------------------\n\nexport const httpStatusError: (status: number, body: string) => AiError.AiError = (status, body) =>\n Match.value(status).pipe(\n Match.when(\n 429,\n (): AiError.AiError => new AiError.RateLimited({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.whenOr(\n 408,\n 504,\n (): AiError.AiError => new AiError.Timeout({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.when(\n 401,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"auth\", raw: body }),\n ),\n Match.when(\n 403,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"permission\", raw: body }),\n ),\n Match.when(\n 402,\n (): AiError.AiError =>\n new AiError.AuthFailed({ provider: \"elevenlabs\", subtype: \"billing\", raw: body }),\n ),\n Match.when(\n 413,\n (): AiError.AiError =>\n new AiError.ContextLengthExceeded({ provider: \"elevenlabs\", raw: body }),\n ),\n Match.when(\n (n) => n >= 500,\n (n): AiError.AiError =>\n new AiError.Unavailable({ provider: \"elevenlabs\", status: n, raw: body }),\n ),\n Match.orElse(\n (): AiError.AiError => new AiError.InvalidRequest({ provider: \"elevenlabs\", raw: body }),\n ),\n )\n\nexport const transportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"elevenlabs\", raw: cause })\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,6 +1,7 @@
1
- import { t as codec_d_exports } from "./codec.mjs";
2
- import { ElevenLabsSttModel, ElevenLabsTtsModel, ElevenLabsVoiceId } from "./models.mjs";
1
+ import { ElevenLabsMusicModel, ElevenLabsSttModel, ElevenLabsTtsModel, ElevenLabsVoiceId } from "./models.mjs";
3
2
  import { ElevenLabsRegion, resolveHost } from "./region.mjs";
3
+ import { t as ElevenLabsMusicGenerator_d_exports } from "./ElevenLabsMusicGenerator.mjs";
4
+ import { t as codec_d_exports } from "./codec.mjs";
4
5
  import { t as ElevenLabsSynthesizer_d_exports } from "./ElevenLabsSynthesizer.mjs";
5
6
  import { t as ElevenLabsTranscriber_d_exports } from "./ElevenLabsTranscriber.mjs";
6
- export { ElevenLabsRegion, ElevenLabsSttModel, ElevenLabsSynthesizer_d_exports as ElevenLabsSynthesizer, ElevenLabsTranscriber_d_exports as ElevenLabsTranscriber, ElevenLabsTtsModel, ElevenLabsVoiceId, codec_d_exports as codec, resolveHost };
7
+ export { ElevenLabsMusicGenerator_d_exports as ElevenLabsMusicGenerator, ElevenLabsMusicModel, ElevenLabsRegion, ElevenLabsSttModel, ElevenLabsSynthesizer_d_exports as ElevenLabsSynthesizer, ElevenLabsTranscriber_d_exports as ElevenLabsTranscriber, ElevenLabsTtsModel, ElevenLabsVoiceId, codec_d_exports as codec, resolveHost };
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { t as codec_exports } from "./codec.mjs";
2
2
  import { resolveHost } from "./region.mjs";
3
+ import { t as ElevenLabsMusicGenerator_exports } from "./ElevenLabsMusicGenerator.mjs";
3
4
  import { t as ElevenLabsSynthesizer_exports } from "./ElevenLabsSynthesizer.mjs";
4
5
  import { t as ElevenLabsTranscriber_exports } from "./ElevenLabsTranscriber.mjs";
5
6
  import "./models.mjs";
6
- export { ElevenLabsSynthesizer_exports as ElevenLabsSynthesizer, ElevenLabsTranscriber_exports as ElevenLabsTranscriber, codec_exports as codec, resolveHost };
7
+ export { ElevenLabsMusicGenerator_exports as ElevenLabsMusicGenerator, ElevenLabsSynthesizer_exports as ElevenLabsSynthesizer, ElevenLabsTranscriber_exports as ElevenLabsTranscriber, codec_exports as codec, resolveHost };
package/dist/models.d.mts CHANGED
@@ -32,6 +32,18 @@ type ElevenLabsSttModel = "scribe_v2" | "scribe_v1" | "scribe_v2_realtime" | (st
32
32
  * full catalog is fetched dynamically via `GET /v1/voices`.
33
33
  */
34
34
  type ElevenLabsVoiceId = string;
35
+ /**
36
+ * Known ElevenLabs music model identifiers.
37
+ *
38
+ * - `music_v1` — current API default (as of May 2026). General-purpose
39
+ * music composition.
40
+ * - `music_v2` — UI default; API plumbing in progress per the
41
+ * May 26 2026 v2 launch blog. Include in the literal union so
42
+ * callers can opt in when it ships.
43
+ *
44
+ * Reference: https://elevenlabs.io/docs/api-reference/music/compose
45
+ */
46
+ type ElevenLabsMusicModel = "music_v1" | "music_v2" | (string & {});
35
47
  //#endregion
36
- export { ElevenLabsSttModel, ElevenLabsTtsModel, ElevenLabsVoiceId };
48
+ export { ElevenLabsMusicModel, ElevenLabsSttModel, ElevenLabsTtsModel, ElevenLabsVoiceId };
37
49
  //# sourceMappingURL=models.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAeA;;;;;AAiBA;;;;;AAaA;;;;KA9BY,kBAAA;;;;;;;;;;KAiBA,kBAAA;;;;;;;KAaA,iBAAA"}
1
+ {"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAeA;;;;;AAiBA;;;;;AAaA;;;;KA9BY,kBAAA;AA2CZ;;;;;;;;;AAAA,KA1BY,kBAAA;;;;;;;KAaA,iBAAA;;;;;;;;;;;;KAaA,oBAAA"}
@@ -0,0 +1,42 @@
1
+ import { Duration, Effect } from "effect";
2
+ import { CommonGenerateMusicRequest } from "@effect-uai/core/Music";
3
+
4
+ //#region src/musicCodec.d.ts
5
+ /**
6
+ * Composition plan accepted by `POST /v1/music` (and returned by
7
+ * `POST /v1/music/plan`). Mirrors the wire `MusicPrompt` schema with
8
+ * camelCase fields. Validation lives server-side (3 s to 10 min total,
9
+ * up to 30 sections, 3 s to 2 min per section, ≤200 chars per lyric
10
+ * line); we pass the body through and surface 422 errors via the
11
+ * HTTP-status mapping.
12
+ *
13
+ * Reference: https://elevenlabs.io/docs/api-reference/music/compose
14
+ */
15
+ type ElevenLabsCompositionPlan = {
16
+ readonly positiveGlobalStyles: ReadonlyArray<string>;
17
+ readonly negativeGlobalStyles: ReadonlyArray<string>;
18
+ readonly sections: ReadonlyArray<ElevenLabsSongSection>;
19
+ };
20
+ type ElevenLabsSongSection = {
21
+ readonly sectionName: string;
22
+ readonly positiveLocalStyles: ReadonlyArray<string>;
23
+ readonly negativeLocalStyles: ReadonlyArray<string>;
24
+ readonly duration: Duration.Duration;
25
+ readonly lines: ReadonlyArray<string>;
26
+ };
27
+ declare const wireCompositionPlan: (plan: ElevenLabsCompositionPlan) => {
28
+ positive_global_styles: readonly string[];
29
+ negative_global_styles: readonly string[];
30
+ sections: {
31
+ section_name: string;
32
+ positive_local_styles: readonly string[];
33
+ negative_local_styles: readonly string[];
34
+ duration_ms: number;
35
+ lines: readonly string[];
36
+ }[];
37
+ };
38
+ declare const decodeCompositionPlan: (raw: unknown) => ElevenLabsCompositionPlan;
39
+ declare const warnDroppedPromptModeHints: (request: CommonGenerateMusicRequest) => Effect.Effect<void>;
40
+ //#endregion
41
+ export { wireCompositionPlan as a, warnDroppedPromptModeHints as i, ElevenLabsSongSection as n, decodeCompositionPlan as r, ElevenLabsCompositionPlan as t };
42
+ //# sourceMappingURL=musicCodec-CZni1gAQ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"musicCodec-CZni1gAQ.d.mts","names":[],"sources":["../src/musicCodec.ts"],"mappings":";;;;;;AAkBA;;;;;;;;KAAY,yBAAA;EAAA,SACD,oBAAA,EAAsB,aAAA;EAAA,SACtB,oBAAA,EAAsB,aAAA;EAAA,SACtB,QAAA,EAAU,aAAA,CAAc,qBAAA;AAAA;AAAA,KAGvB,qBAAA;EAAA,SACD,WAAA;EAAA,SACA,mBAAA,EAAqB,aAAA;EAAA,SACrB,mBAAA,EAAqB,aAAA;EAAA,SACrB,QAAA,EAAU,QAAA,CAAS,QAAA;EAAA,SACnB,KAAA,EAAO,aAAA;AAAA;AAAA,cAGL,mBAAA,GAAuB,IAAA,EAAM,yBAAA;;;;;;;;;;;cAY7B,qBAAA,GAAyB,GAAA,cAAe,yBAAA;AAAA,cAgCxC,0BAAA,GACX,OAAA,EAAS,0BAAA,KACR,MAAA,CAAO,MAAA"}
@@ -0,0 +1,2 @@
1
+ import { a as wireCompositionPlan, i as warnDroppedPromptModeHints, n as ElevenLabsSongSection, r as decodeCompositionPlan, t as ElevenLabsCompositionPlan } from "./musicCodec-CZni1gAQ.mjs";
2
+ export { ElevenLabsCompositionPlan, ElevenLabsSongSection, decodeCompositionPlan, warnDroppedPromptModeHints, wireCompositionPlan };
@@ -0,0 +1,38 @@
1
+ import { Duration } from "effect";
2
+ import { warnDroppedWhen } from "@effect-uai/core/Capabilities";
3
+ //#region src/musicCodec.ts
4
+ const wireCompositionPlan = (plan) => ({
5
+ positive_global_styles: plan.positiveGlobalStyles,
6
+ negative_global_styles: plan.negativeGlobalStyles,
7
+ sections: plan.sections.map((s) => ({
8
+ section_name: s.sectionName,
9
+ positive_local_styles: s.positiveLocalStyles,
10
+ negative_local_styles: s.negativeLocalStyles,
11
+ duration_ms: Duration.toMillis(s.duration),
12
+ lines: s.lines
13
+ }))
14
+ });
15
+ const decodeCompositionPlan = (raw) => {
16
+ const r = raw;
17
+ return {
18
+ positiveGlobalStyles: r.positive_global_styles ?? [],
19
+ negativeGlobalStyles: r.negative_global_styles ?? [],
20
+ sections: (r.sections ?? []).map((s) => ({
21
+ sectionName: s.section_name,
22
+ positiveLocalStyles: s.positive_local_styles ?? [],
23
+ negativeLocalStyles: s.negative_local_styles ?? [],
24
+ duration: Duration.millis(s.duration_ms),
25
+ lines: s.lines ?? []
26
+ }))
27
+ };
28
+ };
29
+ const warnDroppedPromptModeHints = (request) => warnDroppedWhen(request.lyrics, {
30
+ provider: "elevenlabs-music",
31
+ capability: "lyrics",
32
+ field: "lyrics",
33
+ reason: "Prompt mode has no lyrics field. Use `compositionPlan` with per-section `lines` to provide lyrics."
34
+ });
35
+ //#endregion
36
+ export { decodeCompositionPlan, warnDroppedPromptModeHints, wireCompositionPlan };
37
+
38
+ //# sourceMappingURL=musicCodec.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"musicCodec.mjs","names":[],"sources":["../src/musicCodec.ts"],"sourcesContent":["import { Duration, Effect } from \"effect\"\nimport { warnDroppedWhen } from \"@effect-uai/core/Capabilities\"\nimport type { CommonGenerateMusicRequest } from \"@effect-uai/core/Music\"\n\n// ---------------------------------------------------------------------------\n// Composition plan (camelCase; snake-cased at the wire boundary)\n// ---------------------------------------------------------------------------\n\n/**\n * Composition plan accepted by `POST /v1/music` (and returned by\n * `POST /v1/music/plan`). Mirrors the wire `MusicPrompt` schema with\n * camelCase fields. Validation lives server-side (3 s to 10 min total,\n * up to 30 sections, 3 s to 2 min per section, ≤200 chars per lyric\n * line); we pass the body through and surface 422 errors via the\n * HTTP-status mapping.\n *\n * Reference: https://elevenlabs.io/docs/api-reference/music/compose\n */\nexport type ElevenLabsCompositionPlan = {\n readonly positiveGlobalStyles: ReadonlyArray<string>\n readonly negativeGlobalStyles: ReadonlyArray<string>\n readonly sections: ReadonlyArray<ElevenLabsSongSection>\n}\n\nexport type ElevenLabsSongSection = {\n readonly sectionName: string\n readonly positiveLocalStyles: ReadonlyArray<string>\n readonly negativeLocalStyles: ReadonlyArray<string>\n readonly duration: Duration.Duration\n readonly lines: ReadonlyArray<string>\n}\n\nexport const wireCompositionPlan = (plan: ElevenLabsCompositionPlan) => ({\n positive_global_styles: plan.positiveGlobalStyles,\n negative_global_styles: plan.negativeGlobalStyles,\n sections: plan.sections.map((s) => ({\n section_name: s.sectionName,\n positive_local_styles: s.positiveLocalStyles,\n negative_local_styles: s.negativeLocalStyles,\n duration_ms: Duration.toMillis(s.duration),\n lines: s.lines,\n })),\n})\n\nexport const decodeCompositionPlan = (raw: unknown): ElevenLabsCompositionPlan => {\n const r = raw as {\n positive_global_styles?: ReadonlyArray<string>\n negative_global_styles?: ReadonlyArray<string>\n sections?: ReadonlyArray<{\n section_name: string\n positive_local_styles?: ReadonlyArray<string>\n negative_local_styles?: ReadonlyArray<string>\n duration_ms: number\n lines?: ReadonlyArray<string>\n }>\n }\n return {\n positiveGlobalStyles: r.positive_global_styles ?? [],\n negativeGlobalStyles: r.negative_global_styles ?? [],\n sections: (r.sections ?? []).map((s) => ({\n sectionName: s.section_name,\n positiveLocalStyles: s.positive_local_styles ?? [],\n negativeLocalStyles: s.negative_local_styles ?? [],\n duration: Duration.millis(s.duration_ms),\n lines: s.lines ?? [],\n })),\n }\n}\n\n// ---------------------------------------------------------------------------\n// Bucket-2 warn-and-drop for Common fields ElevenLabs can't honor\n// in prompt mode. No prompt construction; prompts always come from\n// the caller verbatim. Migrate to `dropUnsupported` when capabilities\n// Phase 0 lands.\n// ---------------------------------------------------------------------------\n\nexport const warnDroppedPromptModeHints = (\n request: CommonGenerateMusicRequest,\n): Effect.Effect<void> =>\n warnDroppedWhen(request.lyrics, {\n provider: \"elevenlabs-music\",\n capability: \"lyrics\",\n field: \"lyrics\",\n reason:\n \"Prompt mode has no lyrics field. Use `compositionPlan` with per-section `lines` to provide lyrics.\",\n })\n"],"mappings":";;;AAgCA,MAAa,uBAAuB,UAAqC;CACvE,wBAAwB,KAAK;CAC7B,wBAAwB,KAAK;CAC7B,UAAU,KAAK,SAAS,KAAK,OAAO;EAClC,cAAc,EAAE;EAChB,uBAAuB,EAAE;EACzB,uBAAuB,EAAE;EACzB,aAAa,SAAS,SAAS,EAAE,SAAS;EAC1C,OAAO,EAAE;EACV,EAAE;CACJ;AAED,MAAa,yBAAyB,QAA4C;CAChF,MAAM,IAAI;AAWV,QAAO;EACL,sBAAsB,EAAE,0BAA0B,EAAE;EACpD,sBAAsB,EAAE,0BAA0B,EAAE;EACpD,WAAW,EAAE,YAAY,EAAE,EAAE,KAAK,OAAO;GACvC,aAAa,EAAE;GACf,qBAAqB,EAAE,yBAAyB,EAAE;GAClD,qBAAqB,EAAE,yBAAyB,EAAE;GAClD,UAAU,SAAS,OAAO,EAAE,YAAY;GACxC,OAAO,EAAE,SAAS,EAAE;GACrB,EAAE;EACJ;;AAUH,MAAa,8BACX,YAEA,gBAAgB,QAAQ,QAAQ;CAC9B,UAAU;CACV,YAAY;CACZ,OAAO;CACP,QACE;CACH,CAAC"}
@@ -1,8 +1,8 @@
1
1
  import { ElevenLabsRegion } from "./region.mjs";
2
2
  import { Effect, Redacted, Schema, Stream } from "effect";
3
3
  import { HttpClient } from "effect/unstable/http";
4
- import * as Socket from "effect/unstable/socket/Socket";
5
4
  import * as AiError from "@effect-uai/core/AiError";
5
+ import * as Socket from "effect/unstable/socket/Socket";
6
6
  import { CommonStreamTranscribeRequest } from "@effect-uai/core/Transcriber";
7
7
  import { AudioFormat } from "@effect-uai/core/Audio";
8
8
  import { TranscriptEvent } from "@effect-uai/core/Transcript";
@@ -60,6 +60,7 @@ type RealtimeOptions = {
60
60
  readonly languageCode?: string;
61
61
  readonly includeTimestamps?: boolean;
62
62
  readonly commitStrategy?: "manual" | "vad";
63
+ readonly keyterms?: ReadonlyArray<string>;
63
64
  };
64
65
  declare const buildWsUrl: (cfg: Config, token: string, audioFormat: string, opts: RealtimeOptions) => string;
65
66
  declare const encodeAudioFrame: (bytes: Uint8Array, sampleRate: number) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"realtimeStt.d.mts","names":[],"sources":["../src/realtimeStt.ts"],"mappings":";;;;;;;;;;KAWY,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;EAAA,SACA,MAAA,GAAS,gBAAA;AAAA;AAAA,cAcP,iBAAA,GAAoB,MAAA,EAAQ,WAAA,KAAgB,MAAA,CAAO,MAAA,SAAe,OAAA,CAAQ,OAAA;AAAA,cAmB1E,mBAAA,GAAuB,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA,cAAA,UAAA,CAAA,UAAA;AAAA,cAsCzC,aAAA,EAAa,MAAA,CAAA,KAAA,WAAA,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgDN,WAAA,GACV,iBAAA,eACA,GAAA,SAAY,aAAA,CAAc,IAAA,KAAO,eAAA;AAAA,KAqCxB,eAAA;EAAA,SACD,KAAA;EAAA,SACA,YAAA;EAAA,SACA,iBAAA;EAAA,SACA,cAAA;AAAA;AAAA,cAGE,UAAA,GACX,GAAA,EAAK,MAAA,EACL,KAAA,UACA,WAAA,UACA,IAAA,EAAM,eAAA;AAAA,cAcK,gBAAA,GAAoB,KAAA,EAAO,UAAA,EAAY,UAAA;AAAA,cAqCvC,mBAAA,GACV,GAAA,EAAK,MAAA,YAEJ,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,GACtC,OAAA,EAAS,6BAAA,KACR,MAAA,CAAO,MAAA,CACR,eAAA,EACA,OAAA,CAAQ,OAAA,GAAU,CAAA,EAClB,CAAA,GAAI,UAAA,CAAW,UAAA,GAAa,MAAA,CAAO,oBAAA"}
1
+ {"version":3,"file":"realtimeStt.d.mts","names":[],"sources":["../src/realtimeStt.ts"],"mappings":";;;;;;;;;;KAYY,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;EAAA,SACA,MAAA,GAAS,gBAAA;AAAA;AAAA,cAcP,iBAAA,GAAoB,MAAA,EAAQ,WAAA,KAAgB,MAAA,CAAO,MAAA,SAAe,OAAA,CAAQ,OAAA;AAAA,cAmB1E,mBAAA,GAAuB,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA,cAAA,UAAA,CAAA,UAAA;AAAA,cAsCzC,aAAA,EAAa,MAAA,CAAA,KAAA,WAAA,MAAA,CAAA,MAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAgDN,WAAA,GACV,iBAAA,eACA,GAAA,SAAY,aAAA,CAAc,IAAA,KAAO,eAAA;AAAA,KAqCxB,eAAA;EAAA,SACD,KAAA;EAAA,SACA,YAAA;EAAA,SACA,iBAAA;EAAA,SACA,cAAA;EAAA,SACA,QAAA,GAAW,aAAA;AAAA;AAAA,cAGT,UAAA,GACX,GAAA,EAAK,MAAA,EACL,KAAA,UACA,WAAA,UACA,IAAA,EAAM,eAAA;AAAA,cAgBK,gBAAA,GAAoB,KAAA,EAAO,UAAA,EAAY,UAAA;AAAA,cAqCvC,mBAAA,GACV,GAAA,EAAK,MAAA,YAEJ,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,GACtC,OAAA,EAAS,6BAAA,KACR,MAAA,CAAO,MAAA,CACR,eAAA,EACA,OAAA,CAAQ,OAAA,GAAU,CAAA,EAClB,CAAA,GAAI,UAAA,CAAW,UAAA,GAAa,MAAA,CAAO,oBAAA"}
@@ -2,8 +2,9 @@ import { httpStatusError, transportFailure } from "./codec.mjs";
2
2
  import { resolveHost } from "./region.mjs";
3
3
  import { Effect, Encoding, Match, Queue, Redacted, Schema, Stream } from "effect";
4
4
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
5
- import * as Socket from "effect/unstable/socket/Socket";
6
5
  import * as AiError from "@effect-uai/core/AiError";
6
+ import * as Capabilities from "@effect-uai/core/Capabilities";
7
+ import * as Socket from "effect/unstable/socket/Socket";
7
8
  import * as JSONL from "@effect-uai/core/JSONL";
8
9
  //#region src/realtimeStt.ts
9
10
  const unsupportedInputFormat = (format) => new AiError.Unsupported({
@@ -119,14 +120,17 @@ const wireToEvent = (includeTimestamps) => (msg) => Match.value(msg).pipe(Match.
119
120
  message: typeof m.error === "string" ? m.error : JSON.stringify(m.error ?? `ElevenLabs ${m.message_type}`)
120
121
  })));
121
122
  const buildWsUrl = (cfg, token, audioFormat, opts) => {
122
- return `${resolveHost(cfg).replace(/^http/, "ws")}/speech-to-text/realtime?${new URLSearchParams({
123
+ const wsBase = resolveHost(cfg).replace(/^http/, "ws");
124
+ const params = new URLSearchParams({
123
125
  token,
124
126
  audio_format: audioFormat,
125
127
  commit_strategy: opts.commitStrategy ?? "vad",
126
128
  ...opts.model !== void 0 && { model_id: opts.model },
127
129
  ...opts.languageCode !== void 0 && { language_code: opts.languageCode },
128
130
  ...opts.includeTimestamps === true && { include_timestamps: "true" }
129
- }).toString()}`;
131
+ });
132
+ for (const term of opts.keyterms ?? []) params.append("keyterms", term);
133
+ return `${wsBase}/speech-to-text/realtime?${params.toString()}`;
130
134
  };
131
135
  const encodeAudioFrame = (bytes, sampleRate) => JSON.stringify({
132
136
  message_type: "input_audio_chunk",
@@ -145,9 +149,16 @@ const streamTranscription = (cfg) => (audioIn, request) => Stream.unwrap(Effect.
145
149
  const slug = yield* inputFormatToSlug(request.inputFormat);
146
150
  const token = yield* fetchSingleUseToken(cfg);
147
151
  const includeTimestamps = request.wordTimestamps === true;
152
+ yield* Capabilities.warnDroppedWhen(request.prompt, {
153
+ provider: "elevenlabs",
154
+ capability: "prompt",
155
+ field: "prompt",
156
+ reason: "ElevenLabs Scribe has no free-form prompt field; bias via `biasingTerms`."
157
+ });
148
158
  const url = buildWsUrl(cfg, token, slug, {
149
159
  ...request.model !== void 0 && { model: request.model },
150
160
  ...request.language !== void 0 && { languageCode: request.language },
161
+ ...request.biasingTerms !== void 0 && { keyterms: request.biasingTerms },
151
162
  includeTimestamps
152
163
  });
153
164
  const socket = yield* Socket.makeWebSocket(url, { closeCodeIsError: (code) => code !== 1e3 && code !== 1001 && code !== 1005 });