@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
@@ -0,0 +1,75 @@
1
+ import { ElevenLabsMusicModel } from "./models.mjs";
2
+ import { t as ElevenLabsCompositionPlan } from "./musicCodec-CZni1gAQ.mjs";
3
+ import { ElevenLabsRegion } from "./region.mjs";
4
+ import { Context, Duration, Effect, Layer, Redacted, Stream } from "effect";
5
+ import { HttpClient } from "effect/unstable/http";
6
+ import { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, GenerateResult, MusicSessionInput, MusicStreamEvent } from "@effect-uai/core/Music";
7
+ import { MusicGenerator } from "@effect-uai/core/MusicGenerator";
8
+ import * as AiError from "@effect-uai/core/AiError";
9
+ import { AudioChunk } from "@effect-uai/core/Audio";
10
+
11
+ //#region src/ElevenLabsMusicGenerator.d.ts
12
+ declare namespace ElevenLabsMusicGenerator_d_exports {
13
+ export { Config, CreateCompositionPlanRequest, ElevenLabsMusicGenerateRequest, ElevenLabsMusicGenerator, ElevenLabsMusicGeneratorService, layer, make };
14
+ }
15
+ /**
16
+ * ElevenLabs-typed music request. Narrows `model` to
17
+ * `ElevenLabsMusicModel` and adds provider-specific extras:
18
+ *
19
+ * - `compositionPlan` — switch from prompt mode to plan mode. Mutually
20
+ * exclusive with `prompt` on the wire.
21
+ * - `forceInstrumental` — skip vocals. ElevenLabs has no `instrumental`
22
+ * on the cross-provider Common request (the field was removed in
23
+ * v0.7 because it has wildly different semantics per provider); the
24
+ * typed surface restores it.
25
+ * - `signWithC2pa` — opt-in C2PA Content Credentials, MP3 output only.
26
+ * - `respectSectionsDurations` — composition-plan mode only. False
27
+ * trades section-duration accuracy for quality + latency.
28
+ *
29
+ * Setting `compositionPlan` with a non-empty `prompt`, or with
30
+ * `duration`, fails `InvalidRequest` at the codec layer.
31
+ */
32
+ type ElevenLabsMusicGenerateRequest = Omit<CommonGenerateMusicRequest, "model"> & {
33
+ readonly model?: ElevenLabsMusicModel;
34
+ readonly compositionPlan?: ElevenLabsCompositionPlan;
35
+ readonly forceInstrumental?: boolean;
36
+ readonly signWithC2pa?: boolean;
37
+ readonly respectSectionsDurations?: boolean;
38
+ };
39
+ type CreateCompositionPlanRequest = {
40
+ readonly prompt: string;
41
+ readonly duration?: Duration.Duration;
42
+ readonly sourceCompositionPlan?: ElevenLabsCompositionPlan;
43
+ readonly model?: ElevenLabsMusicModel;
44
+ };
45
+ type ElevenLabsMusicGeneratorService = {
46
+ readonly generate: (request: ElevenLabsMusicGenerateRequest) => Effect.Effect<GenerateResult, AiError.AiError>;
47
+ readonly streamGeneration: (request: ElevenLabsMusicGenerateRequest) => Stream.Stream<AudioChunk, AiError.AiError>;
48
+ readonly streamGenerationFrom: <E, R>(input: Stream.Stream<MusicSessionInput, E, R>, request: CommonStreamGenerateMusicRequest) => Stream.Stream<MusicStreamEvent, AiError.AiError | E, R>;
49
+ /**
50
+ * Free endpoint (rate-limited, no credit cost): turn a prompt into a
51
+ * structured `MusicPrompt` plan. Feed the result back into
52
+ * `generate` / `streamGeneration` via `compositionPlan`.
53
+ */
54
+ readonly createCompositionPlan: (request: CreateCompositionPlanRequest) => Effect.Effect<ElevenLabsCompositionPlan, AiError.AiError>;
55
+ };
56
+ declare const ElevenLabsMusicGenerator_base: Context.ServiceClass<ElevenLabsMusicGenerator, "@betalyra/effect-uai/providers/elevenlabs/ElevenLabsMusicGenerator", ElevenLabsMusicGeneratorService>;
57
+ declare class ElevenLabsMusicGenerator extends ElevenLabsMusicGenerator_base {}
58
+ type Config = {
59
+ readonly apiKey: Redacted.Redacted;
60
+ readonly baseUrl?: string;
61
+ readonly region?: ElevenLabsRegion;
62
+ };
63
+ declare const make: (cfg: Config) => Effect.Effect<ElevenLabsMusicGeneratorService, never, HttpClient.HttpClient>;
64
+ /**
65
+ * Layer that registers the provider-specific `ElevenLabsMusicGenerator`
66
+ * tag and the generic `MusicGenerator` tag.
67
+ *
68
+ * Does NOT register `MusicInteractiveSession`. ElevenLabs music has no
69
+ * bidirectional wire endpoint; `streamGenerationFrom` against this
70
+ * Layer alone fails to typecheck.
71
+ */
72
+ declare const layer: (cfg: Config) => Layer.Layer<ElevenLabsMusicGenerator | MusicGenerator, never, HttpClient.HttpClient>;
73
+ //#endregion
74
+ export { Config, CreateCompositionPlanRequest, ElevenLabsMusicGenerateRequest, ElevenLabsMusicGenerator, ElevenLabsMusicGeneratorService, layer, make, ElevenLabsMusicGenerator_d_exports as t };
75
+ //# sourceMappingURL=ElevenLabsMusicGenerator.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElevenLabsMusicGenerator.d.mts","names":[],"sources":["../src/ElevenLabsMusicGenerator.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6CA;;;;;KAAY,8BAAA,GAAiC,IAAA,CAAK,0BAAA;EAAA,SACvC,KAAA,GAAQ,oBAAA;EAAA,SACR,eAAA,GAAkB,yBAAA;EAAA,SAClB,iBAAA;EAAA,SACA,YAAA;EAAA,SACA,wBAAA;AAAA;AAAA,KAGC,4BAAA;EAAA,SACD,MAAA;EAAA,SACA,QAAA,GAAW,QAAA,CAAS,QAAA;EAAA,SACpB,qBAAA,GAAwB,yBAAA;EAAA,SACxB,KAAA,GAAQ,oBAAA;AAAA;AAAA,KAGP,+BAAA;EAAA,SACD,QAAA,GACP,OAAA,EAAS,8BAAA,KACN,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA,CAAQ,OAAA;EAAA,SAClC,gBAAA,GACP,OAAA,EAAS,8BAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,CAAQ,OAAA;EAAA,SAC9B,oBAAA,SACP,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,GAC3C,OAAA,EAAS,gCAAA,KACN,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,CAAQ,OAAA,GAAU,CAAA,EAAG,CAAA;;;;;;WAMjD,qBAAA,GACP,OAAA,EAAS,4BAAA,KACN,MAAA,CAAO,MAAA,CAAO,yBAAA,EAA2B,OAAA,CAAQ,OAAA;AAAA;AAAA,cACvD,6BAAA;cAEY,wBAAA,SAAiC,6BAAA;AAAA,KAKlC,MAAA;EAAA,SACD,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,OAAA;EAAA,SACA,MAAA,GAAS,gBAAA;AAAA;AAAA,cA8NP,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,+BAAA,SAAwC,UAAA,CAAW,UAAA;AA7PpE;;;;;;;;AAAA,cAkRa,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,wBAAA,GAA2B,cAAA,SAAuB,UAAA,CAAW,UAAA"}
@@ -0,0 +1,191 @@
1
+ import { n as __exportAll } from "./chunk-uyGKjUfl.mjs";
2
+ import { defaultFormat, formatToOutputSlug } from "./codec.mjs";
3
+ import { decodeCompositionPlan, warnDroppedPromptModeHints, wireCompositionPlan } from "./musicCodec.mjs";
4
+ import { resolveHost } from "./region.mjs";
5
+ import { Context, Duration, Effect, Layer, Redacted, Stream } from "effect";
6
+ import { HttpClient, HttpClientRequest } from "effect/unstable/http";
7
+ import { singleVariant } from "@effect-uai/core/Music";
8
+ import { MusicGenerator } from "@effect-uai/core/MusicGenerator";
9
+ import * as AiError from "@effect-uai/core/AiError";
10
+ //#region src/ElevenLabsMusicGenerator.ts
11
+ var ElevenLabsMusicGenerator_exports = /* @__PURE__ */ __exportAll({
12
+ ElevenLabsMusicGenerator: () => ElevenLabsMusicGenerator,
13
+ layer: () => layer,
14
+ make: () => make
15
+ });
16
+ var ElevenLabsMusicGenerator = class extends Context.Service()("@betalyra/effect-uai/providers/elevenlabs/ElevenLabsMusicGenerator") {};
17
+ const DEFAULT_MODEL = "music_v1";
18
+ const musicTransportFailure = (cause) => new AiError.Unavailable({
19
+ provider: "elevenlabs-music",
20
+ raw: cause
21
+ });
22
+ const musicHttpStatusError = (status, body) => {
23
+ if (status === 429) return new AiError.RateLimited({
24
+ provider: "elevenlabs-music",
25
+ raw: body
26
+ });
27
+ if (status === 408 || status === 504) return new AiError.Timeout({
28
+ provider: "elevenlabs-music",
29
+ raw: body
30
+ });
31
+ if (status === 401) return new AiError.AuthFailed({
32
+ provider: "elevenlabs-music",
33
+ subtype: "auth",
34
+ raw: body
35
+ });
36
+ if (status === 403) return new AiError.AuthFailed({
37
+ provider: "elevenlabs-music",
38
+ subtype: "permission",
39
+ raw: body
40
+ });
41
+ if (status === 402) return new AiError.AuthFailed({
42
+ provider: "elevenlabs-music",
43
+ subtype: "billing",
44
+ raw: body
45
+ });
46
+ if (status >= 500) return new AiError.Unavailable({
47
+ provider: "elevenlabs-music",
48
+ status,
49
+ raw: body
50
+ });
51
+ return new AiError.InvalidRequest({
52
+ provider: "elevenlabs-music",
53
+ raw: body
54
+ });
55
+ };
56
+ /**
57
+ * Build the JSON body for `/v1/music` (sync + stream share the body).
58
+ *
59
+ * Mutual exclusion: `compositionPlan` and prompt-mode fields cannot
60
+ * coexist. When `compositionPlan` is set, `prompt` must be empty and
61
+ * `duration` must be unset (section durations live on the plan).
62
+ */
63
+ const buildBody = (request) => Effect.gen(function* () {
64
+ const model = request.model ?? DEFAULT_MODEL;
65
+ if (request.compositionPlan !== void 0) {
66
+ if (request.prompt.length > 0) return yield* Effect.fail(new AiError.InvalidRequest({
67
+ provider: "elevenlabs-music",
68
+ param: "prompt",
69
+ raw: "`prompt` and `compositionPlan` are mutually exclusive. Pass one or the other (compositionPlan carries its own per-section lyrics and styles)."
70
+ }));
71
+ if (request.duration !== void 0) return yield* Effect.fail(new AiError.InvalidRequest({
72
+ provider: "elevenlabs-music",
73
+ param: "duration",
74
+ raw: "`duration` is ignored when `compositionPlan` is set; section durations live on the plan. Remove `duration`."
75
+ }));
76
+ return {
77
+ composition_plan: wireCompositionPlan(request.compositionPlan),
78
+ model_id: model,
79
+ ...request.seed !== void 0 && { seed: request.seed },
80
+ ...request.forceInstrumental !== void 0 && { force_instrumental: request.forceInstrumental },
81
+ ...request.respectSectionsDurations !== void 0 && { respect_sections_durations: request.respectSectionsDurations },
82
+ ...request.signWithC2pa !== void 0 && { sign_with_c2pa: request.signWithC2pa }
83
+ };
84
+ }
85
+ yield* warnDroppedPromptModeHints(request);
86
+ return {
87
+ prompt: request.prompt,
88
+ model_id: model,
89
+ ...request.duration !== void 0 && { music_length_ms: Duration.toMillis(request.duration) },
90
+ ...request.seed !== void 0 && { seed: request.seed },
91
+ ...request.forceInstrumental !== void 0 && { force_instrumental: request.forceInstrumental },
92
+ ...request.signWithC2pa !== void 0 && { sign_with_c2pa: request.signWithC2pa }
93
+ };
94
+ });
95
+ const buildMusicHttpRequest = (cfg, request, path) => Effect.gen(function* () {
96
+ const format = request.outputFormat ?? defaultFormat;
97
+ const slug = yield* formatToOutputSlug(format);
98
+ const body = yield* buildBody(request);
99
+ const url = `${resolveHost(cfg)}/music${path}?output_format=${slug}`;
100
+ return {
101
+ httpRequest: HttpClientRequest.post(url).pipe(HttpClientRequest.setHeader("xi-api-key", Redacted.value(cfg.apiKey)), HttpClientRequest.bodyJsonUnsafe(body)),
102
+ format
103
+ };
104
+ });
105
+ const generateImpl = (cfg) => (request) => Effect.gen(function* () {
106
+ const client = yield* HttpClient.HttpClient;
107
+ const { httpRequest, format } = yield* buildMusicHttpRequest(cfg, request, "");
108
+ const response = yield* client.execute(httpRequest).pipe(Effect.mapError(musicTransportFailure));
109
+ if (response.status >= 400) {
110
+ const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
111
+ return yield* Effect.fail(musicHttpStatusError(response.status, text));
112
+ }
113
+ const buffer = yield* response.arrayBuffer.pipe(Effect.mapError(musicTransportFailure));
114
+ const bytes = new Uint8Array(buffer);
115
+ const songId = response.headers["song_id"] ?? response.headers["x-song-id"];
116
+ return singleVariant({
117
+ audio: {
118
+ format,
119
+ bytes
120
+ },
121
+ provider: "elevenlabs-music",
122
+ ...typeof songId === "string" && { songId },
123
+ ...request.signWithC2pa === true && { watermark: "c2pa" }
124
+ });
125
+ });
126
+ const streamGenerationImpl = (cfg) => (request) => Stream.unwrap(Effect.gen(function* () {
127
+ const client = yield* HttpClient.HttpClient;
128
+ const { httpRequest } = yield* buildMusicHttpRequest(cfg, request, "/stream");
129
+ const response = yield* client.execute(httpRequest).pipe(Effect.mapError(musicTransportFailure));
130
+ if (response.status >= 400) {
131
+ const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
132
+ return Stream.fail(musicHttpStatusError(response.status, text));
133
+ }
134
+ return response.stream.pipe(Stream.mapError(musicTransportFailure), Stream.map((bytes) => ({ bytes })));
135
+ }));
136
+ /**
137
+ * ElevenLabs music has no bidirectional session at the wire level.
138
+ * Layer does not register `MusicInteractiveSession`, so callers using
139
+ * `MusicGenerator.streamGenerationFrom` get a compile-time error
140
+ * before this runtime fallback fires.
141
+ */
142
+ const streamGenerationFromUnsupported = (_input, _request) => Stream.fail(new AiError.Unsupported({
143
+ provider: "elevenlabs-music",
144
+ capability: "streamGenerationFrom",
145
+ reason: "ElevenLabs music has no bidirectional session. Use `streamGeneration` for chunked output, or `createCompositionPlan` + `generate` for structured plans."
146
+ }));
147
+ const createCompositionPlanImpl = (cfg) => (request) => Effect.gen(function* () {
148
+ const client = yield* HttpClient.HttpClient;
149
+ const body = {
150
+ prompt: request.prompt,
151
+ model_id: request.model ?? DEFAULT_MODEL,
152
+ ...request.duration !== void 0 && { music_length_ms: Duration.toMillis(request.duration) },
153
+ ...request.sourceCompositionPlan !== void 0 && { source_composition_plan: wireCompositionPlan(request.sourceCompositionPlan) }
154
+ };
155
+ const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/music/plan`).pipe(HttpClientRequest.setHeader("xi-api-key", Redacted.value(cfg.apiKey)), HttpClientRequest.bodyJsonUnsafe(body));
156
+ const response = yield* client.execute(httpRequest).pipe(Effect.mapError(musicTransportFailure));
157
+ if (response.status >= 400) {
158
+ const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
159
+ return yield* Effect.fail(musicHttpStatusError(response.status, text));
160
+ }
161
+ return decodeCompositionPlan(yield* response.json.pipe(Effect.mapError(musicTransportFailure)));
162
+ });
163
+ const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
164
+ generate: (request) => generateImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
165
+ streamGeneration: (request) => streamGenerationImpl(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client)),
166
+ streamGenerationFrom: streamGenerationFromUnsupported,
167
+ createCompositionPlan: (request) => createCompositionPlanImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client))
168
+ }));
169
+ /**
170
+ * Layer that registers the provider-specific `ElevenLabsMusicGenerator`
171
+ * tag and the generic `MusicGenerator` tag.
172
+ *
173
+ * Does NOT register `MusicInteractiveSession`. ElevenLabs music has no
174
+ * bidirectional wire endpoint; `streamGenerationFrom` against this
175
+ * Layer alone fails to typecheck.
176
+ */
177
+ const layer = (cfg) => Layer.merge(Layer.effect(ElevenLabsMusicGenerator, make(cfg)), Layer.effect(MusicGenerator, Effect.map(make(cfg), (s) => ({
178
+ generate: (req) => s.generate({
179
+ ...req,
180
+ model: req.model
181
+ }),
182
+ streamGeneration: (req) => s.streamGeneration({
183
+ ...req,
184
+ model: req.model
185
+ }),
186
+ streamGenerationFrom: s.streamGenerationFrom
187
+ }))));
188
+ //#endregion
189
+ export { ElevenLabsMusicGenerator, layer, make, ElevenLabsMusicGenerator_exports as t };
190
+
191
+ //# sourceMappingURL=ElevenLabsMusicGenerator.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElevenLabsMusicGenerator.mjs","names":[],"sources":["../src/ElevenLabsMusicGenerator.ts"],"sourcesContent":["import { Context, Duration, Effect, Layer, Redacted, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioChunk, AudioFormat } from \"@effect-uai/core/Audio\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"@effect-uai/core/Music\"\nimport { singleVariant } from \"@effect-uai/core/Music\"\nimport { MusicGenerator, type MusicGeneratorService } from \"@effect-uai/core/MusicGenerator\"\nimport { defaultFormat, formatToOutputSlug } from \"./codec.js\"\nimport type { ElevenLabsMusicModel } from \"./models.js\"\nimport {\n decodeCompositionPlan,\n type ElevenLabsCompositionPlan,\n warnDroppedPromptModeHints,\n wireCompositionPlan,\n} from \"./musicCodec.js\"\nimport { type ElevenLabsRegion, resolveHost } from \"./region.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * ElevenLabs-typed music request. Narrows `model` to\n * `ElevenLabsMusicModel` and adds provider-specific extras:\n *\n * - `compositionPlan` — switch from prompt mode to plan mode. Mutually\n * exclusive with `prompt` on the wire.\n * - `forceInstrumental` — skip vocals. ElevenLabs has no `instrumental`\n * on the cross-provider Common request (the field was removed in\n * v0.7 because it has wildly different semantics per provider); the\n * typed surface restores it.\n * - `signWithC2pa` — opt-in C2PA Content Credentials, MP3 output only.\n * - `respectSectionsDurations` — composition-plan mode only. False\n * trades section-duration accuracy for quality + latency.\n *\n * Setting `compositionPlan` with a non-empty `prompt`, or with\n * `duration`, fails `InvalidRequest` at the codec layer.\n */\nexport type ElevenLabsMusicGenerateRequest = Omit<CommonGenerateMusicRequest, \"model\"> & {\n readonly model?: ElevenLabsMusicModel\n readonly compositionPlan?: ElevenLabsCompositionPlan\n readonly forceInstrumental?: boolean\n readonly signWithC2pa?: boolean\n readonly respectSectionsDurations?: boolean\n}\n\nexport type CreateCompositionPlanRequest = {\n readonly prompt: string\n readonly duration?: Duration.Duration\n readonly sourceCompositionPlan?: ElevenLabsCompositionPlan\n readonly model?: ElevenLabsMusicModel\n}\n\nexport type ElevenLabsMusicGeneratorService = {\n readonly generate: (\n request: ElevenLabsMusicGenerateRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n readonly streamGeneration: (\n request: ElevenLabsMusicGenerateRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamGenerationFrom: <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<MusicStreamEvent, AiError.AiError | E, R>\n /**\n * Free endpoint (rate-limited, no credit cost): turn a prompt into a\n * structured `MusicPrompt` plan. Feed the result back into\n * `generate` / `streamGeneration` via `compositionPlan`.\n */\n readonly createCompositionPlan: (\n request: CreateCompositionPlanRequest,\n ) => Effect.Effect<ElevenLabsCompositionPlan, AiError.AiError>\n}\n\nexport class ElevenLabsMusicGenerator extends Context.Service<\n ElevenLabsMusicGenerator,\n ElevenLabsMusicGeneratorService\n>()(\"@betalyra/effect-uai/providers/elevenlabs/ElevenLabsMusicGenerator\") {}\n\nexport type Config = {\n readonly apiKey: Redacted.Redacted\n readonly baseUrl?: string\n readonly region?: ElevenLabsRegion\n}\n\nconst DEFAULT_MODEL: ElevenLabsMusicModel = \"music_v1\"\n\n// ---------------------------------------------------------------------------\n// HTTP errors (music namespace; reuses the same status taxonomy)\n// ---------------------------------------------------------------------------\n\nconst musicTransportFailure = (cause: unknown): AiError.AiError =>\n new AiError.Unavailable({ provider: \"elevenlabs-music\", raw: cause })\n\nconst musicHttpStatusError = (status: number, body: string): AiError.AiError => {\n if (status === 429) return new AiError.RateLimited({ provider: \"elevenlabs-music\", raw: body })\n if (status === 408 || status === 504)\n return new AiError.Timeout({ provider: \"elevenlabs-music\", raw: body })\n if (status === 401)\n return new AiError.AuthFailed({ provider: \"elevenlabs-music\", subtype: \"auth\", raw: body })\n if (status === 403)\n return new AiError.AuthFailed({\n provider: \"elevenlabs-music\",\n subtype: \"permission\",\n raw: body,\n })\n if (status === 402)\n return new AiError.AuthFailed({\n provider: \"elevenlabs-music\",\n subtype: \"billing\",\n raw: body,\n })\n if (status >= 500)\n return new AiError.Unavailable({ provider: \"elevenlabs-music\", status, raw: body })\n return new AiError.InvalidRequest({ provider: \"elevenlabs-music\", raw: body })\n}\n\n// ---------------------------------------------------------------------------\n// Wire body\n// ---------------------------------------------------------------------------\n\n/**\n * Build the JSON body for `/v1/music` (sync + stream share the body).\n *\n * Mutual exclusion: `compositionPlan` and prompt-mode fields cannot\n * coexist. When `compositionPlan` is set, `prompt` must be empty and\n * `duration` must be unset (section durations live on the plan).\n */\nconst buildBody = (request: ElevenLabsMusicGenerateRequest) =>\n Effect.gen(function* () {\n const model = request.model ?? DEFAULT_MODEL\n if (request.compositionPlan !== undefined) {\n if (request.prompt.length > 0) {\n return yield* Effect.fail(\n new AiError.InvalidRequest({\n provider: \"elevenlabs-music\",\n param: \"prompt\",\n raw: \"`prompt` and `compositionPlan` are mutually exclusive. Pass one or the other (compositionPlan carries its own per-section lyrics and styles).\",\n }),\n )\n }\n if (request.duration !== undefined) {\n return yield* Effect.fail(\n new AiError.InvalidRequest({\n provider: \"elevenlabs-music\",\n param: \"duration\",\n raw: \"`duration` is ignored when `compositionPlan` is set; section durations live on the plan. Remove `duration`.\",\n }),\n )\n }\n return {\n composition_plan: wireCompositionPlan(request.compositionPlan),\n model_id: model,\n ...(request.seed !== undefined && { seed: request.seed }),\n ...(request.forceInstrumental !== undefined && {\n force_instrumental: request.forceInstrumental,\n }),\n ...(request.respectSectionsDurations !== undefined && {\n respect_sections_durations: request.respectSectionsDurations,\n }),\n ...(request.signWithC2pa !== undefined && { sign_with_c2pa: request.signWithC2pa }),\n }\n }\n yield* warnDroppedPromptModeHints(request as CommonGenerateMusicRequest)\n return {\n prompt: request.prompt,\n model_id: model,\n ...(request.duration !== undefined && {\n music_length_ms: Duration.toMillis(request.duration),\n }),\n ...(request.seed !== undefined && { seed: request.seed }),\n ...(request.forceInstrumental !== undefined && {\n force_instrumental: request.forceInstrumental,\n }),\n ...(request.signWithC2pa !== undefined && { sign_with_c2pa: request.signWithC2pa }),\n }\n })\n\nconst buildMusicHttpRequest = (\n cfg: Config,\n request: ElevenLabsMusicGenerateRequest,\n path: \"\" | \"/stream\",\n) =>\n Effect.gen(function* () {\n const format: AudioFormat = request.outputFormat ?? defaultFormat\n const slug = yield* formatToOutputSlug(format)\n const body = yield* buildBody(request)\n const url = `${resolveHost(cfg)}/music${path}?output_format=${slug}`\n const httpRequest = HttpClientRequest.post(url).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n return { httpRequest, format }\n })\n\n// ---------------------------------------------------------------------------\n// Service implementation\n// ---------------------------------------------------------------------------\n\nconst generateImpl =\n (cfg: Config) =>\n (\n request: ElevenLabsMusicGenerateRequest,\n ): Effect.Effect<GenerateResult, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest, format } = yield* buildMusicHttpRequest(cfg, request, \"\")\n const response = yield* client\n .execute(httpRequest)\n .pipe(Effect.mapError(musicTransportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(musicHttpStatusError(response.status, text))\n }\n const buffer = yield* response.arrayBuffer.pipe(Effect.mapError(musicTransportFailure))\n const bytes = new Uint8Array(buffer)\n const songId = response.headers[\"song_id\"] ?? response.headers[\"x-song-id\"]\n const result: MusicResult = {\n audio: { format, bytes },\n provider: \"elevenlabs-music\",\n ...(typeof songId === \"string\" && { songId }),\n ...(request.signWithC2pa === true && { watermark: \"c2pa\" as const }),\n }\n return singleVariant(result)\n })\n\nconst streamGenerationImpl =\n (cfg: Config) =>\n (\n request: ElevenLabsMusicGenerateRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError, HttpClient.HttpClient> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { httpRequest } = yield* buildMusicHttpRequest(cfg, request, \"/stream\")\n const response = yield* client\n .execute(httpRequest)\n .pipe(Effect.mapError(musicTransportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(musicHttpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(musicTransportFailure),\n Stream.map((bytes): AudioChunk => ({ bytes })),\n )\n }),\n )\n\n/**\n * ElevenLabs music has no bidirectional session at the wire level.\n * Layer does not register `MusicInteractiveSession`, so callers using\n * `MusicGenerator.streamGenerationFrom` get a compile-time error\n * before this runtime fallback fires.\n */\nconst streamGenerationFromUnsupported = <E, R>(\n _input: Stream.Stream<MusicSessionInput, E, R>,\n _request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<MusicStreamEvent, AiError.AiError | E, R> =>\n Stream.fail(\n new AiError.Unsupported({\n provider: \"elevenlabs-music\",\n capability: \"streamGenerationFrom\",\n reason:\n \"ElevenLabs music has no bidirectional session. Use `streamGeneration` for chunked output, or `createCompositionPlan` + `generate` for structured plans.\",\n }),\n )\n\nconst createCompositionPlanImpl =\n (cfg: Config) =>\n (\n request: CreateCompositionPlanRequest,\n ): Effect.Effect<ElevenLabsCompositionPlan, AiError.AiError, HttpClient.HttpClient> =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const body = {\n prompt: request.prompt,\n model_id: request.model ?? DEFAULT_MODEL,\n ...(request.duration !== undefined && {\n music_length_ms: Duration.toMillis(request.duration),\n }),\n ...(request.sourceCompositionPlan !== undefined && {\n source_composition_plan: wireCompositionPlan(request.sourceCompositionPlan),\n }),\n }\n const httpRequest = HttpClientRequest.post(`${resolveHost(cfg)}/music/plan`).pipe(\n HttpClientRequest.setHeader(\"xi-api-key\", Redacted.value(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(Effect.mapError(musicTransportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(musicHttpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(musicTransportFailure))\n return decodeCompositionPlan(json)\n })\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<ElevenLabsMusicGeneratorService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({\n generate: (request) =>\n generateImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamGeneration: (request) =>\n streamGenerationImpl(cfg)(request).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamGenerationFrom: streamGenerationFromUnsupported,\n createCompositionPlan: (request) =>\n createCompositionPlanImpl(cfg)(request).pipe(\n Effect.provideService(HttpClient.HttpClient, client),\n ),\n }))\n\n/**\n * Layer that registers the provider-specific `ElevenLabsMusicGenerator`\n * tag and the generic `MusicGenerator` tag.\n *\n * Does NOT register `MusicInteractiveSession`. ElevenLabs music has no\n * bidirectional wire endpoint; `streamGenerationFrom` against this\n * Layer alone fails to typecheck.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<ElevenLabsMusicGenerator | MusicGenerator, never, HttpClient.HttpClient> =>\n Layer.merge(\n Layer.effect(ElevenLabsMusicGenerator, make(cfg)),\n Layer.effect(\n MusicGenerator,\n Effect.map(\n make(cfg),\n (s): MusicGeneratorService => ({\n generate: (req: CommonGenerateMusicRequest) =>\n s.generate({ ...req, model: req.model as ElevenLabsMusicModel }),\n streamGeneration: (req: CommonStreamGenerateMusicRequest) =>\n s.streamGeneration({ ...req, model: req.model as ElevenLabsMusicModel }),\n streamGenerationFrom: s.streamGenerationFrom,\n }),\n ),\n ),\n )\n"],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ import { i as it, n as globalExpect, r as describe, t as import_dist } from "./dist-DMsg8YLb.mjs";
2
+ import { ElevenLabsMusicGenerator, layer } from "./ElevenLabsMusicGenerator.mjs";
3
+ import { Duration, Effect, Layer, Redacted, Stream } from "effect";
4
+ import { FetchHttpClient } from "effect/unstable/http";
5
+ import { promptsInput } from "@effect-uai/core/Music";
6
+ import * as MusicGenerator$1 from "@effect-uai/core/MusicGenerator";
7
+ //#region src/ElevenLabsMusicGenerator.test.ts
8
+ const cfg = { apiKey: Redacted.make("test-key") };
9
+ const live = Layer.provide(layer(cfg), FetchHttpClient.layer);
10
+ describe("ElevenLabsMusicGenerator capability guards (runtime)", () => {
11
+ it("streamGenerationFrom returns an Unsupported stream", async () => {
12
+ const program = ElevenLabsMusicGenerator.use((s) => Stream.runDrain(s.streamGenerationFrom(Stream.fromIterable([promptsInput([{ text: "x" }])]), {
13
+ model: "music_v1",
14
+ prompt: ""
15
+ })));
16
+ const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)));
17
+ globalExpect(exit._tag).toBe("Failure");
18
+ if (exit._tag === "Failure") {
19
+ globalExpect(JSON.stringify(exit.cause)).toContain("Unsupported");
20
+ globalExpect(JSON.stringify(exit.cause)).toContain("streamGenerationFrom");
21
+ }
22
+ });
23
+ });
24
+ describe("ElevenLabsMusicGenerator Layer (compile-time)", () => {
25
+ it("leaves `MusicInteractiveSession` unsatisfied via the generic surface", () => {
26
+ const events = Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(MusicGenerator$1.streamGenerationFrom({
27
+ model: "music_v1",
28
+ prompt: ""
29
+ }));
30
+ (0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(live))).toEqualTypeOf();
31
+ });
32
+ it("sync `generate` returns GenerateResult and requires no marker", () => {
33
+ (0, import_dist.expectTypeOf)(MusicGenerator$1.generate({
34
+ model: "music_v1",
35
+ prompt: "house"
36
+ }).pipe(Effect.provide(live))).toEqualTypeOf();
37
+ });
38
+ it("typed request accepts compositionPlan, forceInstrumental, signWithC2pa", () => {
39
+ globalExpect({
40
+ model: "music_v1",
41
+ prompt: "",
42
+ compositionPlan: {
43
+ positiveGlobalStyles: ["lo-fi", "warm"],
44
+ negativeGlobalStyles: ["distorted"],
45
+ sections: [{
46
+ sectionName: "Intro",
47
+ positiveLocalStyles: ["piano"],
48
+ negativeLocalStyles: [],
49
+ duration: Duration.seconds(15),
50
+ lines: []
51
+ }]
52
+ },
53
+ forceInstrumental: true,
54
+ signWithC2pa: true
55
+ }).toBeDefined();
56
+ });
57
+ });
58
+ describe("ElevenLabsMusicGenerator codec validation (runtime, no HTTP)", () => {
59
+ it("rejects compositionPlan + non-empty prompt", async () => {
60
+ const program = ElevenLabsMusicGenerator.use((s) => s.generate({
61
+ model: "music_v1",
62
+ prompt: "house",
63
+ compositionPlan: {
64
+ positiveGlobalStyles: [],
65
+ negativeGlobalStyles: [],
66
+ sections: []
67
+ }
68
+ }));
69
+ const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)));
70
+ globalExpect(exit._tag).toBe("Failure");
71
+ if (exit._tag === "Failure") {
72
+ globalExpect(JSON.stringify(exit.cause)).toContain("InvalidRequest");
73
+ globalExpect(JSON.stringify(exit.cause)).toContain("mutually exclusive");
74
+ }
75
+ });
76
+ it("rejects compositionPlan + duration", async () => {
77
+ const program = ElevenLabsMusicGenerator.use((s) => s.generate({
78
+ model: "music_v1",
79
+ prompt: "",
80
+ duration: Duration.seconds(30),
81
+ compositionPlan: {
82
+ positiveGlobalStyles: [],
83
+ negativeGlobalStyles: [],
84
+ sections: []
85
+ }
86
+ }));
87
+ const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)));
88
+ globalExpect(exit._tag).toBe("Failure");
89
+ if (exit._tag === "Failure") {
90
+ globalExpect(JSON.stringify(exit.cause)).toContain("InvalidRequest");
91
+ globalExpect(JSON.stringify(exit.cause)).toContain("`duration` is ignored");
92
+ }
93
+ });
94
+ });
95
+ //#endregion
96
+ export {};
97
+
98
+ //# sourceMappingURL=ElevenLabsMusicGenerator.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElevenLabsMusicGenerator.test.mjs","names":["ElevenLabsMusicGenerator.layer","MusicGenerator"],"sources":["../src/ElevenLabsMusicGenerator.test.ts"],"sourcesContent":["import { Duration, Effect, Layer, Redacted, Stream } from \"effect\"\nimport { FetchHttpClient } from \"effect/unstable/http\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"@effect-uai/core/AiError\"\nimport type { GenerateResult } from \"@effect-uai/core/Music\"\nimport { promptsInput } from \"@effect-uai/core/Music\"\nimport * as MusicGenerator from \"@effect-uai/core/MusicGenerator\"\nimport * as ElevenLabsMusicGenerator from \"./ElevenLabsMusicGenerator.js\"\n\nconst cfg: ElevenLabsMusicGenerator.Config = { apiKey: Redacted.make(\"test-key\") }\nconst live = Layer.provide(ElevenLabsMusicGenerator.layer(cfg), FetchHttpClient.layer)\n\ndescribe(\"ElevenLabsMusicGenerator capability guards (runtime)\", () => {\n it(\"streamGenerationFrom returns an Unsupported stream\", async () => {\n const program = ElevenLabsMusicGenerator.ElevenLabsMusicGenerator.use((s) =>\n Stream.runDrain(\n s.streamGenerationFrom(Stream.fromIterable([promptsInput([{ text: \"x\" }])]), {\n model: \"music_v1\",\n prompt: \"\",\n }),\n ),\n )\n const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)))\n expect(exit._tag).toBe(\"Failure\")\n if (exit._tag === \"Failure\") {\n expect(JSON.stringify(exit.cause)).toContain(\"Unsupported\")\n expect(JSON.stringify(exit.cause)).toContain(\"streamGenerationFrom\")\n }\n })\n})\n\ndescribe(\"ElevenLabsMusicGenerator Layer (compile-time)\", () => {\n it(\"leaves `MusicInteractiveSession` unsatisfied via the generic surface\", () => {\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const events = inputs.pipe(\n MusicGenerator.streamGenerationFrom({ model: \"music_v1\", prompt: \"\" }),\n )\n const provided = Stream.runDrain(events).pipe(Effect.provide(live))\n expectTypeOf(provided).toEqualTypeOf<\n Effect.Effect<void, AiError.AiError, MusicGenerator.MusicInteractiveSession>\n >()\n })\n\n it(\"sync `generate` returns GenerateResult and requires no marker\", () => {\n const gen = MusicGenerator.generate({ model: \"music_v1\", prompt: \"house\" }).pipe(\n Effect.provide(live),\n )\n expectTypeOf(gen).toEqualTypeOf<Effect.Effect<GenerateResult, AiError.AiError, never>>()\n })\n\n it(\"typed request accepts compositionPlan, forceInstrumental, signWithC2pa\", () => {\n const _req: ElevenLabsMusicGenerator.ElevenLabsMusicGenerateRequest = {\n model: \"music_v1\",\n prompt: \"\",\n compositionPlan: {\n positiveGlobalStyles: [\"lo-fi\", \"warm\"],\n negativeGlobalStyles: [\"distorted\"],\n sections: [\n {\n sectionName: \"Intro\",\n positiveLocalStyles: [\"piano\"],\n negativeLocalStyles: [],\n duration: Duration.seconds(15),\n lines: [],\n },\n ],\n },\n forceInstrumental: true,\n signWithC2pa: true,\n }\n expect(_req).toBeDefined()\n })\n})\n\ndescribe(\"ElevenLabsMusicGenerator codec validation (runtime, no HTTP)\", () => {\n it(\"rejects compositionPlan + non-empty prompt\", async () => {\n const program = ElevenLabsMusicGenerator.ElevenLabsMusicGenerator.use((s) =>\n s.generate({\n model: \"music_v1\",\n prompt: \"house\",\n compositionPlan: {\n positiveGlobalStyles: [],\n negativeGlobalStyles: [],\n sections: [],\n },\n }),\n )\n const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)))\n expect(exit._tag).toBe(\"Failure\")\n if (exit._tag === \"Failure\") {\n expect(JSON.stringify(exit.cause)).toContain(\"InvalidRequest\")\n expect(JSON.stringify(exit.cause)).toContain(\"mutually exclusive\")\n }\n })\n\n it(\"rejects compositionPlan + duration\", async () => {\n const program = ElevenLabsMusicGenerator.ElevenLabsMusicGenerator.use((s) =>\n s.generate({\n model: \"music_v1\",\n prompt: \"\",\n duration: Duration.seconds(30),\n compositionPlan: {\n positiveGlobalStyles: [],\n negativeGlobalStyles: [],\n sections: [],\n },\n }),\n )\n const exit = await Effect.runPromiseExit(program.pipe(Effect.provide(live)))\n expect(exit._tag).toBe(\"Failure\")\n if (exit._tag === \"Failure\") {\n expect(JSON.stringify(exit.cause)).toContain(\"InvalidRequest\")\n expect(JSON.stringify(exit.cause)).toContain(\"`duration` is ignored\")\n }\n })\n})\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- import { VoiceSettings } from "./codec.mjs";
2
1
  import { ElevenLabsTtsModel, ElevenLabsVoiceId } from "./models.mjs";
3
2
  import { ElevenLabsRegion } from "./region.mjs";
3
+ import { PronunciationDictionaryLocator, VoiceSettings } from "./codec.mjs";
4
4
  import { Context, Effect, Layer, Redacted, Stream } from "effect";
5
5
  import { HttpClient } from "effect/unstable/http";
6
+ import * as AiError from "@effect-uai/core/AiError";
6
7
  import * as Socket from "effect/unstable/socket/Socket";
7
8
  import { CommonSynthesizeRequest, SpeechSynthesizerService } from "@effect-uai/core/SpeechSynthesizer";
8
- import * as AiError from "@effect-uai/core/AiError";
9
9
  import { AudioBlob, AudioChunk } from "@effect-uai/core/Audio";
10
10
 
11
11
  //#region src/ElevenLabsSynthesizer.d.ts
@@ -17,6 +17,11 @@ declare namespace ElevenLabsSynthesizer_d_exports {
17
17
  * provider's prosody/timbre controls; `seed` makes generation
18
18
  * deterministic; `previousText` / `nextText` thread context across
19
19
  * sequential calls for natural prosody.
20
+ *
21
+ * `pronunciationDictionaryLocators` references pre-provisioned
22
+ * pronunciation dictionaries by ID. Inline `pronunciations` (IPA) on
23
+ * the Common request are not supported by ElevenLabs and fail
24
+ * `Unsupported`; use a dictionary for phonetic control.
20
25
  */
21
26
  type ElevenLabsSynthesizeRequest = Omit<CommonSynthesizeRequest, "model" | "voiceId"> & {
22
27
  readonly model: ElevenLabsTtsModel;
@@ -25,6 +30,7 @@ type ElevenLabsSynthesizeRequest = Omit<CommonSynthesizeRequest, "model" | "voic
25
30
  readonly seed?: number;
26
31
  readonly previousText?: string;
27
32
  readonly nextText?: string;
33
+ readonly pronunciationDictionaryLocators?: ReadonlyArray<PronunciationDictionaryLocator>;
28
34
  };
29
35
  type ElevenLabsSynthesizerService = {
30
36
  readonly synthesize: (r: ElevenLabsSynthesizeRequest) => Effect.Effect<AudioBlob, AiError.AiError>;
@@ -46,7 +52,7 @@ declare const make: (cfg: Config) => Effect.Effect<{
46
52
  streamSynthesisFrom: (textIn: any, request: any) => Stream.Stream<AudioChunk, any, unknown>;
47
53
  synthesizeDialogue: (request: any) => Effect.Effect<any, any, never>;
48
54
  streamSynthesizeDialogue: (request: any) => Stream.Stream<AudioChunk, any, never>;
49
- }, never, Socket.WebSocketConstructor | HttpClient.HttpClient>;
55
+ }, never, HttpClient.HttpClient | Socket.WebSocketConstructor>;
50
56
  /**
51
57
  * Layer registers `ElevenLabsSynthesizer`, the generic
52
58
  * `SpeechSynthesizer`, the `TtsIncrementalText` marker (incremental
@@ -56,7 +62,7 @@ declare const make: (cfg: Config) => Effect.Effect<{
56
62
  * `Socket.layerWebSocketConstructorGlobal` and an `HttpClient` Layer
57
63
  * at the call site.
58
64
  */
59
- declare const layer: (cfg: Config) => Layer.Layer<unknown, never, Socket.WebSocketConstructor | HttpClient.HttpClient>;
65
+ declare const layer: (cfg: Config) => Layer.Layer<unknown, never, HttpClient.HttpClient | Socket.WebSocketConstructor>;
60
66
  //#endregion
61
67
  export { Config, ElevenLabsSynthesizeRequest, ElevenLabsSynthesizer, ElevenLabsSynthesizerService, layer, make, ElevenLabsSynthesizer_d_exports as t };
62
68
  //# sourceMappingURL=ElevenLabsSynthesizer.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ElevenLabsSynthesizer.d.mts","names":[],"sources":["../src/ElevenLabsSynthesizer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;KAqCY,2BAAA,GAA8B,IAAA,CAAK,uBAAA;EAAA,SACpC,KAAA,EAAO,kBAAA;EAAA,SACP,OAAA,EAAS,iBAAA;EAAA,SACT,aAAA,GAAgB,aAAA;EAAA,SAChB,IAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,4BAAA;EAAA,SACD,UAAA,GAAa,CAAA,EAAG,2BAAA,KAAgC,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,OAAA;EAAA,SACjF,eAAA,GACP,CAAA,EAAG,2BAAA,KACA,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,CAAQ,OAAA;EAAA,SAC9B,mBAAA,EAAqB,wBAAA;EAAA,SACrB,kBAAA,EAAoB,wBAAA;EAAA,SACpB,wBAAA,EAA0B,wBAAA;AAAA;AAAA,cACpC,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+KP,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;;;;;;cAiCnB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,iBAAA,MAAA,CAAA,oBAAA,GAAA,UAAA,CAAA,UAAA"}
1
+ {"version":3,"file":"ElevenLabsSynthesizer.d.mts","names":[],"sources":["../src/ElevenLabsSynthesizer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;KA4CY,2BAAA,GAA8B,IAAA,CAAK,uBAAA;EAAA,SACpC,KAAA,EAAO,kBAAA;EAAA,SACP,OAAA,EAAS,iBAAA;EAAA,SACT,aAAA,GAAgB,aAAA;EAAA,SAChB,IAAA;EAAA,SACA,YAAA;EAAA,SACA,QAAA;EAAA,SACA,+BAAA,GAAkC,aAAA,CAAc,8BAAA;AAAA;AAAA,KAG/C,4BAAA;EAAA,SACD,UAAA,GAAa,CAAA,EAAG,2BAAA,KAAgC,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,OAAA;EAAA,SACjF,eAAA,GACP,CAAA,EAAG,2BAAA,KACA,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,CAAQ,OAAA;EAAA,SAC9B,mBAAA,EAAqB,wBAAA;EAAA,SACrB,kBAAA,EAAoB,wBAAA;EAAA,SACpB,wBAAA,EAA0B,wBAAA;AAAA;AAAA,cACpC,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,cAqIP,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;;;;;;cAiCnB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,iBAAA,UAAA,CAAA,UAAA,GAAA,MAAA,CAAA,oBAAA"}
@@ -1,8 +1,8 @@
1
1
  import { n as __exportAll } from "./chunk-uyGKjUfl.mjs";
2
- import { defaultFormat, formatToOutputSlug, httpStatusError, transportFailure, wireVoiceSettings } from "./codec.mjs";
2
+ import { defaultFormat, formatToOutputSlug, httpStatusError, rejectInlinePronunciations, transportFailure, wirePronunciationLocators, wireVoiceSettings } from "./codec.mjs";
3
3
  import { resolveHost } from "./region.mjs";
4
4
  import { streamSynthesis } from "./realtimeTts.mjs";
5
- import { Array, Context, Effect, Layer, Redacted, Stream } from "effect";
5
+ import { Context, Effect, Layer, Redacted, Stream } from "effect";
6
6
  import { HttpClient, HttpClientRequest } from "effect/unstable/http";
7
7
  import * as Socket from "effect/unstable/socket/Socket";
8
8
  import { MultiSpeakerTts, SpeechSynthesizer, TtsIncrementalText } from "@effect-uai/core/SpeechSynthesizer";
@@ -13,49 +13,21 @@ var ElevenLabsSynthesizer_exports = /* @__PURE__ */ __exportAll({
13
13
  make: () => make
14
14
  });
15
15
  var ElevenLabsSynthesizer = class extends Context.Service()("@betalyra/effect-uai/providers/elevenlabs/ElevenLabsSynthesizer") {};
16
- /**
17
- * Models that honor inline `<phoneme>` SSML tags. Other models silently
18
- * drop the tags server-side, so we don't bother rewriting the text for
19
- * them — the pronunciation overrides are silently ignored.
20
- *
21
- * Reference: https://elevenlabs.io/docs/best-practices/prompting/controls
22
- */
23
- const PHONEME_SUPPORTED_MODELS = new Set([
24
- "eleven_flash_v2",
25
- "eleven_english_v1",
26
- "eleven_monolingual_v1"
27
- ]);
28
- const wireAlphabet = (encoding) => encoding === "ipa" ? "ipa" : encoding === "cmu-arpabet" ? "cmu-arpabet" : void 0;
29
- const phonemeTag = (phrase, pronunciation, alphabet) => `<phoneme alphabet="${alphabet}" ph="${pronunciation}">${phrase}</phoneme>`;
30
- const escapeForRegex = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
31
- /**
32
- * Apply pronunciation overrides to a text string. Only applies for
33
- * phoneme-supported models; only IPA and CMU Arpabet are honored
34
- * (x-sampa is silently dropped). Whole-word, case-insensitive
35
- * replacement.
36
- */
37
- const applyPronunciations = (text, model, pronunciations) => {
38
- if (pronunciations === void 0 || !PHONEME_SUPPORTED_MODELS.has(model)) return text;
39
- return Array.reduce(pronunciations, text, (acc, p) => {
40
- const alphabet = wireAlphabet(p.encoding);
41
- if (alphabet === void 0) return acc;
42
- return acc.replace(new RegExp(`\\b${escapeForRegex(p.phrase)}\\b`, "i"), phonemeTag(p.phrase, p.pronunciation, alphabet));
43
- });
44
- };
45
16
  const buildBody = (r) => ({
46
- text: applyPronunciations(r.text, r.model, r.pronunciations),
17
+ text: r.text,
47
18
  model_id: r.model,
48
19
  ...r.languageCode !== void 0 && { language_code: r.languageCode },
49
20
  ...r.seed !== void 0 && { seed: r.seed },
50
21
  ...r.previousText !== void 0 && { previous_text: r.previousText },
51
22
  ...r.nextText !== void 0 && { next_text: r.nextText },
23
+ ...wirePronunciationLocators(r.pronunciationDictionaryLocators),
52
24
  ...r.voiceSettings !== void 0 && { voice_settings: wireVoiceSettings(r.voiceSettings) }
53
25
  });
54
26
  const DEFAULT_DIALOGUE_MODEL = "eleven_v3";
55
27
  const buildDialogueBody = (r) => ({
56
28
  inputs: r.turns.map((t) => ({
57
29
  voice_id: t.voiceId,
58
- text: applyPronunciations(t.text, r.model, r.pronunciations)
30
+ text: t.text
59
31
  })),
60
32
  model_id: r.model ?? DEFAULT_DIALOGUE_MODEL,
61
33
  ...r.languageCode !== void 0 && { language_code: r.languageCode }
@@ -70,6 +42,7 @@ const buildDialogueHttpRequest = (cfg, r, path) => Effect.gen(function* () {
70
42
  };
71
43
  });
72
44
  const synthesizeDialogueImpl = (cfg) => (request) => Effect.gen(function* () {
45
+ yield* rejectInlinePronunciations(request.pronunciations);
73
46
  const client = yield* HttpClient.HttpClient;
74
47
  const { httpRequest, format } = yield* buildDialogueHttpRequest(cfg, request, "");
75
48
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
@@ -84,6 +57,7 @@ const synthesizeDialogueImpl = (cfg) => (request) => Effect.gen(function* () {
84
57
  };
85
58
  });
86
59
  const streamSynthesizeDialogueImpl = (cfg) => (request) => Stream.unwrap(Effect.gen(function* () {
60
+ yield* rejectInlinePronunciations(request.pronunciations);
87
61
  const client = yield* HttpClient.HttpClient;
88
62
  const { httpRequest } = yield* buildDialogueHttpRequest(cfg, request, "/stream");
89
63
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
@@ -103,6 +77,7 @@ const buildHttpRequest = (cfg, r, path) => Effect.gen(function* () {
103
77
  };
104
78
  });
105
79
  const synthesizeImpl = (cfg) => (request) => Effect.gen(function* () {
80
+ yield* rejectInlinePronunciations(request.pronunciations);
106
81
  const client = yield* HttpClient.HttpClient;
107
82
  const { httpRequest, format } = yield* buildHttpRequest(cfg, request, "");
108
83
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
@@ -117,6 +92,7 @@ const synthesizeImpl = (cfg) => (request) => Effect.gen(function* () {
117
92
  };
118
93
  });
119
94
  const streamSynthesisImpl = (cfg) => (request) => Stream.unwrap(Effect.gen(function* () {
95
+ yield* rejectInlinePronunciations(request.pronunciations);
120
96
  const client = yield* HttpClient.HttpClient;
121
97
  const { httpRequest } = yield* buildHttpRequest(cfg, request, "/stream");
122
98
  const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));