@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
|
@@ -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,
|
|
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,
|
|
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":"
|
|
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 {
|
|
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:
|
|
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:
|
|
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));
|