@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,86 @@
|
|
|
1
|
+
import { Duration, Effect } from "effect"
|
|
2
|
+
import { warnDroppedWhen } from "@effect-uai/core/Capabilities"
|
|
3
|
+
import type { CommonGenerateMusicRequest } from "@effect-uai/core/Music"
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Composition plan (camelCase; snake-cased at the wire boundary)
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Composition plan accepted by `POST /v1/music` (and returned by
|
|
11
|
+
* `POST /v1/music/plan`). Mirrors the wire `MusicPrompt` schema with
|
|
12
|
+
* camelCase fields. Validation lives server-side (3 s to 10 min total,
|
|
13
|
+
* up to 30 sections, 3 s to 2 min per section, ≤200 chars per lyric
|
|
14
|
+
* line); we pass the body through and surface 422 errors via the
|
|
15
|
+
* HTTP-status mapping.
|
|
16
|
+
*
|
|
17
|
+
* Reference: https://elevenlabs.io/docs/api-reference/music/compose
|
|
18
|
+
*/
|
|
19
|
+
export type ElevenLabsCompositionPlan = {
|
|
20
|
+
readonly positiveGlobalStyles: ReadonlyArray<string>
|
|
21
|
+
readonly negativeGlobalStyles: ReadonlyArray<string>
|
|
22
|
+
readonly sections: ReadonlyArray<ElevenLabsSongSection>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ElevenLabsSongSection = {
|
|
26
|
+
readonly sectionName: string
|
|
27
|
+
readonly positiveLocalStyles: ReadonlyArray<string>
|
|
28
|
+
readonly negativeLocalStyles: ReadonlyArray<string>
|
|
29
|
+
readonly duration: Duration.Duration
|
|
30
|
+
readonly lines: ReadonlyArray<string>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const wireCompositionPlan = (plan: ElevenLabsCompositionPlan) => ({
|
|
34
|
+
positive_global_styles: plan.positiveGlobalStyles,
|
|
35
|
+
negative_global_styles: plan.negativeGlobalStyles,
|
|
36
|
+
sections: plan.sections.map((s) => ({
|
|
37
|
+
section_name: s.sectionName,
|
|
38
|
+
positive_local_styles: s.positiveLocalStyles,
|
|
39
|
+
negative_local_styles: s.negativeLocalStyles,
|
|
40
|
+
duration_ms: Duration.toMillis(s.duration),
|
|
41
|
+
lines: s.lines,
|
|
42
|
+
})),
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
export const decodeCompositionPlan = (raw: unknown): ElevenLabsCompositionPlan => {
|
|
46
|
+
const r = raw as {
|
|
47
|
+
positive_global_styles?: ReadonlyArray<string>
|
|
48
|
+
negative_global_styles?: ReadonlyArray<string>
|
|
49
|
+
sections?: ReadonlyArray<{
|
|
50
|
+
section_name: string
|
|
51
|
+
positive_local_styles?: ReadonlyArray<string>
|
|
52
|
+
negative_local_styles?: ReadonlyArray<string>
|
|
53
|
+
duration_ms: number
|
|
54
|
+
lines?: ReadonlyArray<string>
|
|
55
|
+
}>
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
positiveGlobalStyles: r.positive_global_styles ?? [],
|
|
59
|
+
negativeGlobalStyles: r.negative_global_styles ?? [],
|
|
60
|
+
sections: (r.sections ?? []).map((s) => ({
|
|
61
|
+
sectionName: s.section_name,
|
|
62
|
+
positiveLocalStyles: s.positive_local_styles ?? [],
|
|
63
|
+
negativeLocalStyles: s.negative_local_styles ?? [],
|
|
64
|
+
duration: Duration.millis(s.duration_ms),
|
|
65
|
+
lines: s.lines ?? [],
|
|
66
|
+
})),
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Bucket-2 warn-and-drop for Common fields ElevenLabs can't honor
|
|
72
|
+
// in prompt mode. No prompt construction; prompts always come from
|
|
73
|
+
// the caller verbatim. Migrate to `dropUnsupported` when capabilities
|
|
74
|
+
// Phase 0 lands.
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
|
|
77
|
+
export const warnDroppedPromptModeHints = (
|
|
78
|
+
request: CommonGenerateMusicRequest,
|
|
79
|
+
): Effect.Effect<void> =>
|
|
80
|
+
warnDroppedWhen(request.lyrics, {
|
|
81
|
+
provider: "elevenlabs-music",
|
|
82
|
+
capability: "lyrics",
|
|
83
|
+
field: "lyrics",
|
|
84
|
+
reason:
|
|
85
|
+
"Prompt mode has no lyrics field. Use `compositionPlan` with per-section `lines` to provide lyrics.",
|
|
86
|
+
})
|
package/src/realtimeStt.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { HttpClient, HttpClientRequest } from "effect/unstable/http"
|
|
|
3
3
|
import * as Socket from "effect/unstable/socket/Socket"
|
|
4
4
|
import * as AiError from "@effect-uai/core/AiError"
|
|
5
5
|
import type { AudioFormat } from "@effect-uai/core/Audio"
|
|
6
|
+
import * as Capabilities from "@effect-uai/core/Capabilities"
|
|
6
7
|
import * as JSONL from "@effect-uai/core/JSONL"
|
|
7
8
|
import type { TranscriptEvent, WordTimestamp } from "@effect-uai/core/Transcript"
|
|
8
9
|
import type { CommonStreamTranscribeRequest } from "@effect-uai/core/Transcriber"
|
|
@@ -175,6 +176,7 @@ export type RealtimeOptions = {
|
|
|
175
176
|
readonly languageCode?: string
|
|
176
177
|
readonly includeTimestamps?: boolean
|
|
177
178
|
readonly commitStrategy?: "manual" | "vad"
|
|
179
|
+
readonly keyterms?: ReadonlyArray<string>
|
|
178
180
|
}
|
|
179
181
|
|
|
180
182
|
export const buildWsUrl = (
|
|
@@ -192,6 +194,8 @@ export const buildWsUrl = (
|
|
|
192
194
|
...(opts.languageCode !== undefined && { language_code: opts.languageCode }),
|
|
193
195
|
...(opts.includeTimestamps === true && { include_timestamps: "true" }),
|
|
194
196
|
})
|
|
197
|
+
// Vocab biasing — repeated `keyterms` param (≤50 on realtime).
|
|
198
|
+
for (const term of opts.keyterms ?? []) params.append("keyterms", term)
|
|
195
199
|
return `${wsBase}/speech-to-text/realtime?${params.toString()}`
|
|
196
200
|
}
|
|
197
201
|
|
|
@@ -247,9 +251,16 @@ export const streamTranscription =
|
|
|
247
251
|
const slug = yield* inputFormatToSlug(request.inputFormat)
|
|
248
252
|
const token = yield* fetchSingleUseToken(cfg)
|
|
249
253
|
const includeTimestamps = request.wordTimestamps === true
|
|
254
|
+
yield* Capabilities.warnDroppedWhen(request.prompt, {
|
|
255
|
+
provider: "elevenlabs",
|
|
256
|
+
capability: "prompt",
|
|
257
|
+
field: "prompt",
|
|
258
|
+
reason: "ElevenLabs Scribe has no free-form prompt field; bias via `biasingTerms`.",
|
|
259
|
+
})
|
|
250
260
|
const url = buildWsUrl(cfg, token, slug, {
|
|
251
261
|
...(request.model !== undefined && { model: request.model }),
|
|
252
262
|
...(request.language !== undefined && { languageCode: request.language }),
|
|
263
|
+
...(request.biasingTerms !== undefined && { keyterms: request.biasingTerms }),
|
|
253
264
|
includeTimestamps,
|
|
254
265
|
})
|
|
255
266
|
const socket = yield* Socket.makeWebSocket(url, {
|
package/src/realtimeTts.ts
CHANGED
|
@@ -3,10 +3,14 @@ import * as Socket from "effect/unstable/socket/Socket"
|
|
|
3
3
|
import * as AiError from "@effect-uai/core/AiError"
|
|
4
4
|
import type { AudioChunk, AudioFormat } from "@effect-uai/core/Audio"
|
|
5
5
|
import * as JSONL from "@effect-uai/core/JSONL"
|
|
6
|
+
import type { CustomPronunciation } from "@effect-uai/core/SpeechSynthesizer"
|
|
6
7
|
import {
|
|
7
8
|
defaultFormat,
|
|
8
9
|
formatToOutputSlug,
|
|
10
|
+
type PronunciationDictionaryLocator,
|
|
11
|
+
rejectInlinePronunciations,
|
|
9
12
|
type VoiceSettings,
|
|
13
|
+
wirePronunciationLocators,
|
|
10
14
|
wireVoiceSettings,
|
|
11
15
|
} from "./codec.js"
|
|
12
16
|
import type { ElevenLabsTtsModel, ElevenLabsVoiceId } from "./models.js"
|
|
@@ -33,6 +37,11 @@ export type StreamSynthesizeRequest = {
|
|
|
33
37
|
readonly languageCode?: string
|
|
34
38
|
readonly voiceSettings?: VoiceSettings
|
|
35
39
|
readonly autoMode?: boolean
|
|
40
|
+
/** Pre-provisioned pronunciation dictionaries, sent on the BOS frame. */
|
|
41
|
+
readonly pronunciationDictionaryLocators?: ReadonlyArray<PronunciationDictionaryLocator>
|
|
42
|
+
/** Carried from the Common request so the WS path can reject inline
|
|
43
|
+
* pronunciations (ElevenLabs has no stateless inline IPA path). */
|
|
44
|
+
readonly pronunciations?: ReadonlyArray<CustomPronunciation>
|
|
36
45
|
}
|
|
37
46
|
|
|
38
47
|
// ---------------------------------------------------------------------------
|
|
@@ -56,6 +65,7 @@ const bosFrame = (cfg: Config, request: StreamSynthesizeRequest) => {
|
|
|
56
65
|
text: " ",
|
|
57
66
|
"xi-api-key": Redacted.value(cfg.apiKey),
|
|
58
67
|
...(vs !== undefined && { voice_settings: vs }),
|
|
68
|
+
...wirePronunciationLocators(request.pronunciationDictionaryLocators),
|
|
59
69
|
})
|
|
60
70
|
}
|
|
61
71
|
|
|
@@ -117,6 +127,7 @@ export const streamSynthesis =
|
|
|
117
127
|
): Stream.Stream<AudioChunk, AiError.AiError | E, R | Socket.WebSocketConstructor> =>
|
|
118
128
|
Stream.unwrap(
|
|
119
129
|
Effect.gen(function* () {
|
|
130
|
+
yield* rejectInlinePronunciations(request.pronunciations)
|
|
120
131
|
const slug = yield* formatToOutputSlug(request.outputFormat ?? defaultFormat)
|
|
121
132
|
// ElevenLabs closes `/stream-input` with code 1000 after delivering the
|
|
122
133
|
// final audio chunk. Effect's default treats all close codes as errors,
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { i as it, n as globalExpect, r as describe, t as import_dist } from "./dist-DMsg8YLb.mjs";
|
|
2
|
-
import { layer } from "./ElevenLabsSynthesizer.mjs";
|
|
3
|
-
import { Effect, Layer, Redacted, Stream } from "effect";
|
|
4
|
-
import { FetchHttpClient } from "effect/unstable/http";
|
|
5
|
-
import * as Socket from "effect/unstable/socket/Socket";
|
|
6
|
-
import * as SpeechSynthesizer$1 from "@effect-uai/core/SpeechSynthesizer";
|
|
7
|
-
const live = layer({ apiKey: Redacted.make("test-key") }).pipe(Layer.provide(FetchHttpClient.layer), Layer.provide(Socket.layerWebSocketConstructorGlobal));
|
|
8
|
-
describe("ElevenLabsSynthesizer Layer (compile-time)", () => {
|
|
9
|
-
it("registers `TtsIncrementalText` — `streamSynthesisFrom` clears R to never", () => {
|
|
10
|
-
const audio = Stream.fromIterable(["hello ", "world"]).pipe(SpeechSynthesizer$1.streamSynthesisFrom({
|
|
11
|
-
model: "eleven_flash_v2_5",
|
|
12
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb"
|
|
13
|
-
}));
|
|
14
|
-
(0, import_dist.expectTypeOf)(Stream.runDrain(audio).pipe(Effect.provide(live))).toEqualTypeOf();
|
|
15
|
-
});
|
|
16
|
-
it("sync `synthesize` and chunked `streamSynthesis` require no marker", () => {
|
|
17
|
-
(0, import_dist.expectTypeOf)(SpeechSynthesizer$1.synthesize({
|
|
18
|
-
text: "hi",
|
|
19
|
-
model: "eleven_multilingual_v2",
|
|
20
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb"
|
|
21
|
-
}).pipe(Effect.provide(live))).toEqualTypeOf();
|
|
22
|
-
globalExpect(Stream.runCollect(SpeechSynthesizer$1.streamSynthesis({
|
|
23
|
-
text: "hi",
|
|
24
|
-
model: "eleven_multilingual_v2",
|
|
25
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb"
|
|
26
|
-
})).pipe(Effect.provide(live))).toBeDefined();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
//#endregion
|
|
30
|
-
export {};
|
|
31
|
-
|
|
32
|
-
//# sourceMappingURL=ElevenLabsSynthesizer.test.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ElevenLabsSynthesizer.test.mjs","names":["ElevenLabsSynthesizer.layer","SpeechSynthesizer"],"sources":["../src/ElevenLabsSynthesizer.test.ts"],"sourcesContent":["import { Effect, Layer, Redacted, Stream } from \"effect\"\nimport { FetchHttpClient } from \"effect/unstable/http\"\nimport * as Socket from \"effect/unstable/socket/Socket\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioBlob } from \"@effect-uai/core/Audio\"\nimport * as SpeechSynthesizer from \"@effect-uai/core/SpeechSynthesizer\"\nimport * as ElevenLabsSynthesizer from \"./ElevenLabsSynthesizer.js\"\n\nconst cfg: ElevenLabsSynthesizer.Config = { apiKey: Redacted.make(\"test-key\") }\n// FetchHttpClient + globalThis.WebSocket are required for `make`, but these\n// tests only exercise the codec and compile-time gating — no real network\n// call is made.\nconst live = ElevenLabsSynthesizer.layer(cfg).pipe(\n Layer.provide(FetchHttpClient.layer),\n Layer.provide(Socket.layerWebSocketConstructorGlobal),\n)\n\ndescribe(\"ElevenLabsSynthesizer Layer (compile-time)\", () => {\n it(\"registers `TtsIncrementalText` — `streamSynthesisFrom` clears R to never\", () => {\n const tokens: Stream.Stream<string> = Stream.fromIterable([\"hello \", \"world\"])\n const audio = tokens.pipe(\n SpeechSynthesizer.streamSynthesisFrom({\n model: \"eleven_flash_v2_5\",\n voiceId: \"JBFqnCBsd6RMkjVDRZzb\",\n }),\n )\n const program = Stream.runDrain(audio).pipe(Effect.provide(live))\n expectTypeOf(program).toEqualTypeOf<Effect.Effect<void, AiError.AiError, never>>()\n })\n\n it(\"sync `synthesize` and chunked `streamSynthesis` require no marker\", () => {\n const synth = SpeechSynthesizer.synthesize({\n text: \"hi\",\n model: \"eleven_multilingual_v2\",\n voiceId: \"JBFqnCBsd6RMkjVDRZzb\",\n }).pipe(Effect.provide(live))\n expectTypeOf(synth).toEqualTypeOf<Effect.Effect<AudioBlob, AiError.AiError, never>>()\n\n const _stream = Stream.runCollect(\n SpeechSynthesizer.streamSynthesis({\n text: \"hi\",\n model: \"eleven_multilingual_v2\",\n voiceId: \"JBFqnCBsd6RMkjVDRZzb\",\n }),\n ).pipe(Effect.provide(live))\n expect(_stream).toBeDefined()\n })\n})\n"],"mappings":";;;;;;AAaA,MAAM,OAAOA,MAA4B,EAJG,QAAQ,SAAS,KAAK,WAAW,EAIpC,CAAI,CAAC,KAC5C,MAAM,QAAQ,gBAAgB,MAAM,EACpC,MAAM,QAAQ,OAAO,gCAAgC,CACtD;AAED,SAAS,oDAAoD;AAC3D,IAAG,kFAAkF;EAEnF,MAAM,QADgC,OAAO,aAAa,CAAC,UAAU,QAAQ,CACzD,CAAC,KACnBC,oBAAkB,oBAAoB;GACpC,OAAO;GACP,SAAS;GACV,CAAC,CACH;AAED,GAAA,GAAA,YAAA,cADgB,OAAO,SAAS,MAAM,CAAC,KAAK,OAAO,QAAQ,KAAK,CAC5C,CAAC,CAAC,eAA4D;GAClF;AAEF,IAAG,2EAA2E;AAM5E,GAAA,GAAA,YAAA,cALcA,oBAAkB,WAAW;GACzC,MAAM;GACN,OAAO;GACP,SAAS;GACV,CAAC,CAAC,KAAK,OAAO,QAAQ,KAAK,CACV,CAAC,CAAC,eAAiE;AASrF,eAPgB,OAAO,WACrBA,oBAAkB,gBAAgB;GAChC,MAAM;GACN,OAAO;GACP,SAAS;GACV,CAAC,CACH,CAAC,KAAK,OAAO,QAAQ,KAAK,CACb,CAAC,CAAC,aAAa;GAC7B;EACF"}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { Effect, Layer, Redacted, Stream } from "effect"
|
|
2
|
-
import { FetchHttpClient } from "effect/unstable/http"
|
|
3
|
-
import * as Socket from "effect/unstable/socket/Socket"
|
|
4
|
-
import { describe, expect, expectTypeOf, it } from "vitest"
|
|
5
|
-
import type * as AiError from "@effect-uai/core/AiError"
|
|
6
|
-
import type { AudioBlob } from "@effect-uai/core/Audio"
|
|
7
|
-
import * as SpeechSynthesizer from "@effect-uai/core/SpeechSynthesizer"
|
|
8
|
-
import * as ElevenLabsSynthesizer from "./ElevenLabsSynthesizer.js"
|
|
9
|
-
|
|
10
|
-
const cfg: ElevenLabsSynthesizer.Config = { apiKey: Redacted.make("test-key") }
|
|
11
|
-
// FetchHttpClient + globalThis.WebSocket are required for `make`, but these
|
|
12
|
-
// tests only exercise the codec and compile-time gating — no real network
|
|
13
|
-
// call is made.
|
|
14
|
-
const live = ElevenLabsSynthesizer.layer(cfg).pipe(
|
|
15
|
-
Layer.provide(FetchHttpClient.layer),
|
|
16
|
-
Layer.provide(Socket.layerWebSocketConstructorGlobal),
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
describe("ElevenLabsSynthesizer Layer (compile-time)", () => {
|
|
20
|
-
it("registers `TtsIncrementalText` — `streamSynthesisFrom` clears R to never", () => {
|
|
21
|
-
const tokens: Stream.Stream<string> = Stream.fromIterable(["hello ", "world"])
|
|
22
|
-
const audio = tokens.pipe(
|
|
23
|
-
SpeechSynthesizer.streamSynthesisFrom({
|
|
24
|
-
model: "eleven_flash_v2_5",
|
|
25
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb",
|
|
26
|
-
}),
|
|
27
|
-
)
|
|
28
|
-
const program = Stream.runDrain(audio).pipe(Effect.provide(live))
|
|
29
|
-
expectTypeOf(program).toEqualTypeOf<Effect.Effect<void, AiError.AiError, never>>()
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it("sync `synthesize` and chunked `streamSynthesis` require no marker", () => {
|
|
33
|
-
const synth = SpeechSynthesizer.synthesize({
|
|
34
|
-
text: "hi",
|
|
35
|
-
model: "eleven_multilingual_v2",
|
|
36
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb",
|
|
37
|
-
}).pipe(Effect.provide(live))
|
|
38
|
-
expectTypeOf(synth).toEqualTypeOf<Effect.Effect<AudioBlob, AiError.AiError, never>>()
|
|
39
|
-
|
|
40
|
-
const _stream = Stream.runCollect(
|
|
41
|
-
SpeechSynthesizer.streamSynthesis({
|
|
42
|
-
text: "hi",
|
|
43
|
-
model: "eleven_multilingual_v2",
|
|
44
|
-
voiceId: "JBFqnCBsd6RMkjVDRZzb",
|
|
45
|
-
}),
|
|
46
|
-
).pipe(Effect.provide(live))
|
|
47
|
-
expect(_stream).toBeDefined()
|
|
48
|
-
})
|
|
49
|
-
})
|
|
File without changes
|