@effect-uai/elevenlabs 0.6.0 → 0.7.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.
- package/dist/ElevenLabsMusicGenerator.d.mts +75 -0
- package/dist/ElevenLabsMusicGenerator.d.mts.map +1 -0
- package/dist/ElevenLabsMusicGenerator.mjs +191 -0
- package/dist/ElevenLabsMusicGenerator.mjs.map +1 -0
- package/dist/ElevenLabsMusicGenerator.test.mjs +98 -0
- package/dist/ElevenLabsMusicGenerator.test.mjs.map +1 -0
- package/dist/ElevenLabsSynthesizer.d.mts +10 -4
- package/dist/ElevenLabsSynthesizer.d.mts.map +1 -1
- package/dist/ElevenLabsSynthesizer.mjs +9 -33
- package/dist/ElevenLabsSynthesizer.mjs.map +1 -1
- package/dist/ElevenLabsTranscriber.d.mts +4 -4
- package/dist/ElevenLabsTranscriber.d.mts.map +1 -1
- package/dist/ElevenLabsTranscriber.mjs +9 -1
- package/dist/ElevenLabsTranscriber.mjs.map +1 -1
- package/dist/codec.d.mts +25 -2
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +18 -1
- package/dist/codec.mjs.map +1 -1
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +2 -1
- package/dist/models.d.mts +13 -1
- package/dist/models.d.mts.map +1 -1
- package/dist/musicCodec-CZni1gAQ.d.mts +42 -0
- package/dist/musicCodec-CZni1gAQ.d.mts.map +1 -0
- package/dist/musicCodec.d.mts +2 -0
- package/dist/musicCodec.mjs +38 -0
- package/dist/musicCodec.mjs.map +1 -0
- package/dist/realtimeStt.d.mts +2 -1
- package/dist/realtimeStt.d.mts.map +1 -1
- package/dist/realtimeStt.mjs +14 -3
- package/dist/realtimeStt.mjs.map +1 -1
- package/dist/realtimeTts.d.mts +8 -3
- package/dist/realtimeTts.d.mts.map +1 -1
- package/dist/realtimeTts.mjs +5 -3
- package/dist/realtimeTts.mjs.map +1 -1
- package/package.json +9 -3
- package/src/ElevenLabsMusicGenerator.test.ts +116 -0
- package/src/ElevenLabsMusicGenerator.ts +353 -0
- package/src/ElevenLabsSynthesizer.ts +17 -51
- package/src/ElevenLabsTranscriber.ts +11 -0
- package/src/codec.ts +47 -0
- package/src/index.ts +1 -0
- package/src/models.ts +17 -0
- package/src/musicCodec.ts +86 -0
- package/src/realtimeStt.ts +11 -0
- package/src/realtimeTts.ts +11 -0
- package/dist/ElevenLabsSynthesizer.test.mjs +0 -32
- package/dist/ElevenLabsSynthesizer.test.mjs.map +0 -1
- package/src/ElevenLabsSynthesizer.test.ts +0 -49
- /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,
|
|
39
|
+
transcribe: (r: ElevenLabsTranscribeRequest) => Effect.Effect<any, any, unknown>;
|
|
40
40
|
streamTranscriptionFrom: (audioIn: any, request: any) => Stream.Stream<TranscriptEvent, any, unknown>;
|
|
41
|
-
}, never,
|
|
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,
|
|
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":";;;;;;;;;;;;;;;;;;;;
|
|
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":";;;;;;;;;;;;;;;AAyCA,IAAa,wBAAb,cAA2C,QAAQ,SAGhD,CAAC,kEAAkE,CAAC;AAYvE,MAAM,OAAO,OAAO,OAAO;CACzB,MAAM,OAAO;CACb,OAAO,OAAO,SAAS,OAAO,OAAO;CACrC,KAAK,OAAO,SAAS,OAAO,OAAO;CACnC,MAAM,OAAO,SAAS,OAAO,OAAO;CACpC,YAAY,OAAO,SAAS,OAAO,OAAO;CAC1C,SAAS,OAAO,SAAS,OAAO,OAAO;CACxC,CAAC;AAEF,MAAM,OAAO,OAAO,OAAO;CACzB,MAAM,OAAO;CACb,eAAe,OAAO,SAAS,OAAO,OAAO;CAC7C,sBAAsB,OAAO,SAAS,OAAO,OAAO;CACpD,OAAO,OAAO,SAAS,OAAO,MAAM,KAAK,CAAC;CAC1C,kBAAkB,OAAO,SAAS,OAAO,OAAO;CACjD,CAAC;AACF,MAAM,aAAa,OAAO,oBAAoB,KAAK;AAEnD,MAAM,mBAAmB,MACvB,EAAE,UAAU,KAAA,KAAa,EAAE,QAAQ,KAAA,IAC/B,KAAA,IACA;CACE,MAAM,EAAE;CACR,cAAc,EAAE;CAChB,YAAY,EAAE;CACd,GAAI,EAAE,YAAY,KAAA,KAAa,EAAE,YAAY,KAAK,IAAI,EAAE,QAAQ,EAAE;CAClE,GAAI,EAAE,eAAe,KAAA,KAAa,EAAE,WAAW,EAAE,YAAY;CAC9D;AAMP,MAAM,aAAa,YACjB,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,YAAY,QAAQ,MAAM;CAC9C,MAAM,WAAW,QAAQ,YAAY,gBAAgB,QAAQ,MAAM,YAAY,GAAG;CAClF,MAAM,OAAO,IAAI,UAAU;AAC3B,MAAK,IAAI,YAAY,QAAQ,MAAM;AACnC,MAAK,IAAI,QAAQ,MAAM,SAAS;AAChC,KAAI,QAAQ,aAAa,KAAA,EAAW,MAAK,IAAI,iBAAiB,QAAQ,SAAS;AAC/E,KAAI,QAAQ,gBAAgB,KAAA,EAC1B,MAAK,IAAI,WAAW,QAAQ,cAAc,SAAS,QAAQ;AAC7D,KAAI,QAAQ,mBAAmB,KAAM,MAAK,IAAI,0BAA0B,OAAO;AAC/E,KAAI,QAAQ,gBAAgB,KAAA,EAAW,MAAK,IAAI,gBAAgB,OAAO,QAAQ,YAAY,CAAC;AAC5F,KAAI,QAAQ,gBAAgB,KAAA,EAC1B,MAAK,IAAI,oBAAoB,QAAQ,cAAc,SAAS,QAAQ;AACtE,QAAO;EACP;AAMJ,MAAM,kBAAkB,SAAiB,YACvC,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,OAAO,OAAO,UAAU,QAAQ;CACtC,MAAM,cAAc,kBAAkB,KAAK,GAAG,YAAY,IAAI,CAAC,iBAAiB,CAAC,KAC/E,kBAAkB,UAAU,cAAc,SAAS,MAAM,IAAI,OAAO,CAAC,EACrE,kBAAkB,aAAa,KAAK,CACrC;CACD,MAAM,WAAW,OAAO,OAAO,QAAQ,YAAY,CAAC,KAAK,OAAO,SAAS,iBAAiB,CAAC;AAC3F,KAAI,SAAS,UAAU,KAAK;EAC1B,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,oBAAoB,GAAG,CAAC;AACtE,SAAO,OAAO,OAAO,KAAK,gBAAgB,SAAS,QAAQ,KAAK,CAAC;;CAEnE,MAAM,OAAO,OAAO,SAAS,KAAK,KAAK,OAAO,SAAS,iBAAiB,CAAC;CACzE,MAAM,OAAO,OAAO,WAAW,KAAK,CAAC,KACnC,OAAO,UACJ,UAAU,IAAI,QAAQ,iBAAiB;EAAE,UAAU;EAAc,KAAK;EAAO,CAAC,CAChF,CACF;CACD,MAAM,QAAQ,KAAK,OACf,IAAI,gBAAgB,CACrB,QAAQ,MAA0B,MAAM,KAAA,EAAU;AACrD,QAAO;EACL,MAAM,KAAK;EACX,GAAI,KAAK,kBAAkB,KAAA,KAAa,EAAE,cAAc,KAAK,eAAe;EAC5E,GAAI,UAAU,KAAA,KAAa,MAAM,SAAS,KAAK,EAAE,OAAO;EACxD,KAAK;EACN;EACD;AAMJ,MAAa,QAAQ,QACnB,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW;CACjC,MAAM,OAAO,OAAO,OAAO;AAC3B,QAAO;EACL,aAAa,MACX,eAAe,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,OAAO,CAAC;EACnF,0BAA0B,SAAS,YACjC,oBAAoB,IAAI,CAAC,SAAS,QAAQ,CAAC,KACzC,OAAO,eAAe,WAAW,YAAY,OAAO,EACpD,OAAO,eAAe,OAAO,sBAAsB,KAAK,CACzD;EACJ;EACD;;;;;;;;AASJ,MAAa,SAAS,QACpB,MAAM,SACJ,MAAM,OAAO,uBAAuB,KAAK,IAAI,CAAC,EAC9C,MAAM,OACJ,aACA,OAAO,IACL,KAAK,IAAI,GACR,OAA2B;CAC1B,aAAa,QACX,EAAE,WAAW,IAAmC;CAClD,yBAAyB,EAAE;CAC5B,EACF,CACF,EACD,MAAM,QAAQ,cAAc,KAAA,EAAU,CACvC"}
|
|
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
|
package/dist/codec.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":"
|
|
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
|
package/dist/codec.mjs.map
CHANGED
|
@@ -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 {
|
|
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
|
package/dist/models.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.mts","names":[],"sources":["../src/models.ts"],"mappings":";;AAeA;;;;;AAiBA;;;;;AAaA;;;;KA9BY,kBAAA
|
|
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"}
|
package/dist/realtimeStt.d.mts
CHANGED
|
@@ -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":";;;;;;;;;;
|
|
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"}
|
package/dist/realtimeStt.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
})
|
|
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 });
|