@effect-uai/core 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.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/dist/{Audio-BfCTGnH3.d.mts → Audio-DPlTsNdz.d.mts} +3 -2
  3. package/dist/Audio-DPlTsNdz.d.mts.map +1 -0
  4. package/dist/{Items-pXR5BAgW.d.mts → Items-D8UaXQx_.d.mts} +4 -4
  5. package/dist/{Items-pXR5BAgW.d.mts.map → Items-D8UaXQx_.d.mts.map} +1 -1
  6. package/dist/{Tool-DP-AwKOf.d.mts → Tool-CiFXYjmR.d.mts} +2 -2
  7. package/dist/{Tool-DP-AwKOf.d.mts.map → Tool-CiFXYjmR.d.mts.map} +1 -1
  8. package/dist/capabilities/Capabilities.d.mts +78 -0
  9. package/dist/capabilities/Capabilities.d.mts.map +1 -0
  10. package/dist/capabilities/Capabilities.mjs +65 -0
  11. package/dist/capabilities/Capabilities.mjs.map +1 -0
  12. package/dist/domain/Audio.d.mts +1 -1
  13. package/dist/domain/Audio.mjs.map +1 -1
  14. package/dist/domain/Items.d.mts +1 -1
  15. package/dist/domain/Music.d.mts +131 -72
  16. package/dist/domain/Music.d.mts.map +1 -1
  17. package/dist/domain/Music.mjs +36 -9
  18. package/dist/domain/Music.mjs.map +1 -1
  19. package/dist/domain/Transcript.d.mts +8 -2
  20. package/dist/domain/Transcript.d.mts.map +1 -1
  21. package/dist/domain/Transcript.mjs.map +1 -1
  22. package/dist/domain/Turn.d.mts +1 -1
  23. package/dist/embedding-model/Embedding.d.mts +12 -2
  24. package/dist/embedding-model/Embedding.d.mts.map +1 -1
  25. package/dist/embedding-model/Embedding.mjs.map +1 -1
  26. package/dist/embedding-model/EmbeddingModel.d.mts +36 -23
  27. package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
  28. package/dist/embedding-model/EmbeddingModel.mjs +20 -1
  29. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
  30. package/dist/embedding-model/EmbeddingModel.test.mjs +15 -3
  31. package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -1
  32. package/dist/index.d.mts +5 -4
  33. package/dist/index.mjs +2 -1
  34. package/dist/language-model/LanguageModel.d.mts +2 -2
  35. package/dist/music-generator/MusicGenerator.d.mts +48 -25
  36. package/dist/music-generator/MusicGenerator.d.mts.map +1 -1
  37. package/dist/music-generator/MusicGenerator.mjs +17 -9
  38. package/dist/music-generator/MusicGenerator.mjs.map +1 -1
  39. package/dist/music-generator/MusicGenerator.test.mjs +81 -56
  40. package/dist/music-generator/MusicGenerator.test.mjs.map +1 -1
  41. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +21 -36
  42. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -1
  43. package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -1
  44. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +3 -3
  45. package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -1
  46. package/dist/testing/MockMusicGenerator.d.mts +12 -7
  47. package/dist/testing/MockMusicGenerator.d.mts.map +1 -1
  48. package/dist/testing/MockMusicGenerator.mjs +4 -3
  49. package/dist/testing/MockMusicGenerator.mjs.map +1 -1
  50. package/dist/testing/MockProvider.d.mts +1 -1
  51. package/dist/testing/MockSpeechSynthesizer.d.mts +1 -1
  52. package/dist/tool/Approval.d.mts +1 -1
  53. package/dist/tool/HistoryCheck.d.mts +1 -1
  54. package/dist/tool/Tool.d.mts +1 -1
  55. package/dist/tool/ToolResult.d.mts +1 -1
  56. package/dist/tool/Toolkit.d.mts +2 -2
  57. package/dist/transcriber/Transcriber.d.mts +11 -10
  58. package/dist/transcriber/Transcriber.d.mts.map +1 -1
  59. package/dist/transcriber/Transcriber.mjs.map +1 -1
  60. package/dist/transcriber/Transcriber.test.mjs +3 -3
  61. package/dist/transcriber/Transcriber.test.mjs.map +1 -1
  62. package/package.json +5 -1
  63. package/src/capabilities/Capabilities.ts +81 -0
  64. package/src/domain/Audio.ts +2 -1
  65. package/src/domain/Music.ts +142 -68
  66. package/src/domain/Transcript.ts +7 -1
  67. package/src/embedding-model/Embedding.ts +11 -0
  68. package/src/embedding-model/EmbeddingModel.test.ts +15 -4
  69. package/src/embedding-model/EmbeddingModel.ts +48 -21
  70. package/src/index.ts +1 -0
  71. package/src/music-generator/MusicGenerator.test.ts +78 -53
  72. package/src/music-generator/MusicGenerator.ts +65 -24
  73. package/src/speech-synthesizer/SpeechSynthesizer.test.ts +3 -3
  74. package/src/speech-synthesizer/SpeechSynthesizer.ts +18 -33
  75. package/src/testing/MockMusicGenerator.ts +28 -18
  76. package/src/transcriber/Transcriber.test.ts +3 -3
  77. package/src/transcriber/Transcriber.ts +10 -7
  78. package/dist/Audio-BfCTGnH3.d.mts.map +0 -1
@@ -1,77 +1,100 @@
1
1
  import { t as AiError } from "../AiError-CAX_48RU.mjs";
2
- import { n as AudioChunk } from "../Audio-BfCTGnH3.mjs";
3
- import { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, MusicResult, MusicSessionInput, WeightedPrompt } from "../domain/Music.mjs";
2
+ import { n as AudioChunk } from "../Audio-DPlTsNdz.mjs";
3
+ import { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, GenerateResult, MusicResult, MusicSection, MusicSessionControl, MusicSessionInput, MusicStreamEvent, Watermark, WeightedPrompt } from "../domain/Music.mjs";
4
4
  import { Context, Effect, Stream } from "effect";
5
5
 
6
6
  //#region src/music-generator/MusicGenerator.d.ts
7
7
  declare namespace MusicGenerator_d_exports {
8
- export { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, MusicGenerator, MusicGeneratorService, MusicInteractiveSession, MusicResult, MusicSessionInput, WeightedPrompt, generate, streamGeneration, streamGenerationFrom };
8
+ export { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, GenerateResult, MusicGenerator, MusicGeneratorService, MusicInteractiveSession, MusicResult, MusicSection, MusicSessionControl, MusicSessionInput, MusicStreamEvent, Watermark, WeightedPrompt, generate, streamGeneration, streamGenerationFrom };
9
9
  }
10
10
  type MusicGeneratorService = {
11
11
  /**
12
12
  * One-shot. Prompt in, full audio bytes out. Universally supported.
13
13
  * Async/poll-based providers (Suno, Mureka) hide their poll loop
14
- * inside the adapter — caller still sees a single `Effect`.
14
+ * inside the adapter, caller still sees a single `Effect`.
15
+ *
16
+ * Returns `GenerateResult` with `primary` plus a `variants` array.
17
+ * Suno and Mureka always return 2 tracks; every other provider
18
+ * returns 1 (and `primary === variants[0]`).
15
19
  */
16
- readonly generate: (request: CommonGenerateMusicRequest) => Effect.Effect<MusicResult, AiError>;
20
+ readonly generate: (request: CommonGenerateMusicRequest) => Effect.Effect<GenerateResult, AiError>;
17
21
  /**
18
22
  * Prompt in, audio chunks streamed out. Providers without a native
19
23
  * chunked-output endpoint (Lyria 3 sync, Mureka, MiniMax, Stable
20
24
  * Audio) emulate this by calling `generate` and emitting a single
21
- * `AudioChunk` — first-class, no `Unsupported`.
25
+ * `AudioChunk`, first-class, no `Unsupported`.
22
26
  */
23
27
  readonly streamGeneration: (request: CommonStreamGenerateMusicRequest) => Stream.Stream<AudioChunk, AiError>;
24
28
  /**
25
- * Bidirectional session: a `Stream` of prompt-or-config updates flows
26
- * in, a `Stream` of audio chunks flows out. The session WS / RPC is
27
- * acquired on first pull and released when the output stream is
28
- * finalized via `Stream.scoped`.
29
+ * Bidirectional session: a `Stream` of `MusicSessionInput` (prompts
30
+ * + playback control) flows in, a `Stream` of `MusicStreamEvent`
31
+ * flows out. The session WS / RPC is acquired on first pull and
32
+ * released when the output stream is finalized via `Stream.scoped`.
33
+ *
34
+ * Cross-provider callers send `prompts` (steer the active
35
+ * generation) and `control` (play / pause / stop / reset) — the two
36
+ * actions that converge across interactive-media protocols.
37
+ * Provider-specific model knobs (Lyria RealTime's density /
38
+ * brightness / mute-stems / BPM / scale, etc.) live on the
39
+ * provider-typed service (`LyriaRealtimeGenerator.streamGenerationFrom`),
40
+ * which extends this union with its own `config` variant.
41
+ *
42
+ * Output is `MusicStreamEvent`: `audio` chunks alongside in-band
43
+ * `warning` and `filteredPrompt` events the model emits server-side.
29
44
  *
30
45
  * Gated by the `MusicInteractiveSession` capability marker on the
31
- * top-level helper — providers without bidirectional support don't
46
+ * top-level helper. Providers without bidirectional support don't
32
47
  * ship the marker, so calls fail at `Effect.provide` with a type
33
48
  * error.
34
49
  */
35
- readonly streamGenerationFrom: <E, R>(input: Stream.Stream<MusicSessionInput, E, R>, request: CommonStreamGenerateMusicRequest) => Stream.Stream<AudioChunk, AiError | E, R>;
50
+ readonly streamGenerationFrom: <E, R>(input: Stream.Stream<MusicSessionInput, E, R>, request: CommonStreamGenerateMusicRequest) => Stream.Stream<MusicStreamEvent, AiError | E, R>;
36
51
  };
37
52
  declare const MusicGenerator_base: Context.ServiceClass<MusicGenerator, "@betalyra/effect-uai/MusicGenerator", MusicGeneratorService>;
38
53
  declare class MusicGenerator extends MusicGenerator_base {}
39
54
  declare const MusicInteractiveSession_base: Context.ServiceClass<MusicInteractiveSession, "@betalyra/effect-uai/capability/MusicInteractiveSession", void>;
40
55
  /**
41
- * Capability marker — provided by provider layers whose
56
+ * Capability marker, provided by provider layers whose
42
57
  * `streamGenerationFrom` is wired up at the wire level. Currently only
43
58
  * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.
44
59
  * Calling `streamGenerationFrom` while only a non-interactive Layer is
45
60
  * in scope fails at `Effect.provide` with a type error.
46
61
  *
47
- * Phantom — the value is `void`; providers register with
62
+ * Phantom: the value is `void`; providers register with
48
63
  * `Layer.succeed(MusicInteractiveSession, undefined)`.
49
64
  */
50
65
  declare class MusicInteractiveSession extends MusicInteractiveSession_base {}
51
- /** One-shot generation. */
52
- declare const generate: (request: CommonGenerateMusicRequest) => Effect.Effect<MusicResult, AiError, MusicGenerator>;
66
+ /** One-shot generation. Returns the full `GenerateResult`. */
67
+ declare const generate: (request: CommonGenerateMusicRequest) => Effect.Effect<GenerateResult, AiError, MusicGenerator>;
53
68
  /** Prompt in, audio chunks out. */
54
69
  declare const streamGeneration: (request: CommonStreamGenerateMusicRequest) => Stream.Stream<AudioChunk, AiError, MusicGenerator>;
55
70
  /**
56
71
  * Bidirectional generation. Dual-arity: pipeable (data-last) and
57
- * direct (data-first). Requires `MusicInteractiveSession` in R —
72
+ * direct (data-first). Requires `MusicInteractiveSession` in R,
58
73
  * providers without bidirectional support are a type error at provide
59
74
  * time.
60
75
  *
76
+ * On the cross-provider surface, `input` is `Stream<unknown>` and
77
+ * output is `Stream<MusicStreamEvent>`. Use the provider-typed
78
+ * service (`LyriaRealtimeGenerator`) when you want to construct
79
+ * typed session messages.
80
+ *
61
81
  * @example
62
82
  * ```ts
63
- * const audio = Stream.fromIterable([
64
- * Music.promptsInput([{ text: "minimal techno", weight: 1.0 }]),
65
- * Music.configInput({ bpm: 124 }),
66
- * ]).pipe(
67
- * MusicGenerator.streamGenerationFrom({ model: "lyria-realtime-001", prompts: "" }),
83
+ * // Generic surface (opaque input):
84
+ * const audio = inputs.pipe(
85
+ * MusicGenerator.streamGenerationFrom({ model: "lyria-realtime-exp", prompt: "" }),
86
+ * )
87
+ *
88
+ * // Lyria-typed surface (full config knobs):
89
+ * const audio = inputs.pipe(
90
+ * LyriaRealtimeGenerator.streamGenerationFrom({ model: "lyria-realtime-exp", prompt: "" }),
68
91
  * )
69
92
  * ```
70
93
  */
71
94
  declare const streamGenerationFrom: {
72
- (request: CommonStreamGenerateMusicRequest): <E, R>(input: Stream.Stream<MusicSessionInput, E, R>) => Stream.Stream<AudioChunk, AiError | E, R | MusicGenerator | MusicInteractiveSession>;
73
- <E, R>(input: Stream.Stream<MusicSessionInput, E, R>, request: CommonStreamGenerateMusicRequest): Stream.Stream<AudioChunk, AiError | E, R | MusicGenerator | MusicInteractiveSession>;
95
+ (request: CommonStreamGenerateMusicRequest): <E, R>(input: Stream.Stream<MusicSessionInput, E, R>) => Stream.Stream<MusicStreamEvent, AiError | E, R | MusicGenerator | MusicInteractiveSession>;
96
+ <E, R>(input: Stream.Stream<MusicSessionInput, E, R>, request: CommonStreamGenerateMusicRequest): Stream.Stream<MusicStreamEvent, AiError | E, R | MusicGenerator | MusicInteractiveSession>;
74
97
  };
75
98
  //#endregion
76
- export { type CommonGenerateMusicRequest, type CommonStreamGenerateMusicRequest, MusicGenerator, MusicGeneratorService, MusicInteractiveSession, type MusicResult, type MusicSessionInput, type WeightedPrompt, generate, streamGeneration, streamGenerationFrom, MusicGenerator_d_exports as t };
99
+ export { type CommonGenerateMusicRequest, type CommonStreamGenerateMusicRequest, type GenerateResult, MusicGenerator, MusicGeneratorService, MusicInteractiveSession, type MusicResult, type MusicSection, type MusicSessionControl, type MusicSessionInput, type MusicStreamEvent, type Watermark, type WeightedPrompt, generate, streamGeneration, streamGenerationFrom, MusicGenerator_d_exports as t };
77
100
  //# sourceMappingURL=MusicGenerator.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MusicGenerator.d.mts","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"mappings":";;;;;;;;;KAkBY,qBAAA;;;;;;WAMD,QAAA,GACP,OAAA,EAAS,0BAAA,KACN,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa,OAAA;;;;;;;WAOvB,gBAAA,GACP,OAAA,EAAS,gCAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;;AAjBjC;;;;;;;;;;WA6BW,oBAAA,SACP,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,GAC3C,OAAA,EAAS,gCAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA;AAAA;AAAA,cACrD,mBAAA;cAEY,cAAA,SAAuB,mBAAA;AAAA,cAEhC,4BAAA;;;;;;;;;;;cAYS,uBAAA,SAAgC,4BAAA;;cAKhC,QAAA,GACX,OAAA,EAAS,0BAAA,KACR,MAAA,CAAO,MAAA,CAAO,WAAA,EAAa,OAAA,EAAiB,cAAA;;cAIlC,gBAAA,GACX,OAAA,EAAS,gCAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,cAAA;;;;;;;;;;;;;;;;;cAmBjC,oBAAA;EAAA,CAET,OAAA,EAAS,gCAAA,UAET,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,MACxC,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,cAAA,GAAiB,uBAAA;EAAA,OAEvE,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,GAC3C,OAAA,EAAS,gCAAA,GACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,cAAA,GAAiB,uBAAA;AAAA"}
1
+ {"version":3,"file":"MusicGenerator.d.mts","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"mappings":";;;;;;;;;KAwBY,qBAAA;;;;;;;;;;WAUD,QAAA,GACP,OAAA,EAAS,0BAAA,KACN,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA;;;;;;;WAO1B,gBAAA,GACP,OAAA,EAAS,gCAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;;;AArBjC;;;;;;;;;;;;;;;;;;;;WA4CW,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,GAAkB,CAAA,EAAG,CAAA;AAAA;AAAA,cAC3D,mBAAA;cAEY,cAAA,SAAuB,mBAAA;AAAA,cAEhC,4BAAA;;;;;;;;;;;cAYS,uBAAA,SAAgC,4BAAA;;cAKhC,QAAA,GACX,OAAA,EAAS,0BAAA,KACR,MAAA,CAAO,MAAA,CAAO,cAAA,EAAgB,OAAA,EAAiB,cAAA;;cAIrC,gBAAA,GACX,OAAA,EAAS,gCAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,cAAA;;;;;;;;;;;;;;;;;;AA7B7C;;;;;;;cAwDY,oBAAA;EAAA,CAET,OAAA,EAAS,gCAAA,UAET,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,MACxC,MAAA,CAAO,MAAA,CACV,gBAAA,EACA,OAAA,GAAkB,CAAA,EAClB,CAAA,GAAI,cAAA,GAAiB,uBAAA;EAAA,OAGrB,KAAA,EAAO,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,CAAA,EAAG,CAAA,GAC3C,OAAA,EAAS,gCAAA,GACR,MAAA,CAAO,MAAA,CACR,gBAAA,EACA,OAAA,GAAkB,CAAA,EAClB,CAAA,GAAI,cAAA,GAAiB,uBAAA;AAAA"}
@@ -10,33 +10,41 @@ var MusicGenerator_exports = /* @__PURE__ */ __exportAll({
10
10
  });
11
11
  var MusicGenerator = class extends Context.Service()("@betalyra/effect-uai/MusicGenerator") {};
12
12
  /**
13
- * Capability marker — provided by provider layers whose
13
+ * Capability marker, provided by provider layers whose
14
14
  * `streamGenerationFrom` is wired up at the wire level. Currently only
15
15
  * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.
16
16
  * Calling `streamGenerationFrom` while only a non-interactive Layer is
17
17
  * in scope fails at `Effect.provide` with a type error.
18
18
  *
19
- * Phantom — the value is `void`; providers register with
19
+ * Phantom: the value is `void`; providers register with
20
20
  * `Layer.succeed(MusicInteractiveSession, undefined)`.
21
21
  */
22
22
  var MusicInteractiveSession = class extends Context.Service()("@betalyra/effect-uai/capability/MusicInteractiveSession") {};
23
- /** One-shot generation. */
23
+ /** One-shot generation. Returns the full `GenerateResult`. */
24
24
  const generate = (request) => Effect.flatMap(MusicGenerator.asEffect(), (s) => s.generate(request));
25
25
  /** Prompt in, audio chunks out. */
26
26
  const streamGeneration = (request) => Stream.unwrap(Effect.map(MusicGenerator.asEffect(), (s) => s.streamGeneration(request)));
27
27
  /**
28
28
  * Bidirectional generation. Dual-arity: pipeable (data-last) and
29
- * direct (data-first). Requires `MusicInteractiveSession` in R —
29
+ * direct (data-first). Requires `MusicInteractiveSession` in R,
30
30
  * providers without bidirectional support are a type error at provide
31
31
  * time.
32
32
  *
33
+ * On the cross-provider surface, `input` is `Stream<unknown>` and
34
+ * output is `Stream<MusicStreamEvent>`. Use the provider-typed
35
+ * service (`LyriaRealtimeGenerator`) when you want to construct
36
+ * typed session messages.
37
+ *
33
38
  * @example
34
39
  * ```ts
35
- * const audio = Stream.fromIterable([
36
- * Music.promptsInput([{ text: "minimal techno", weight: 1.0 }]),
37
- * Music.configInput({ bpm: 124 }),
38
- * ]).pipe(
39
- * MusicGenerator.streamGenerationFrom({ model: "lyria-realtime-001", prompts: "" }),
40
+ * // Generic surface (opaque input):
41
+ * const audio = inputs.pipe(
42
+ * MusicGenerator.streamGenerationFrom({ model: "lyria-realtime-exp", prompt: "" }),
43
+ * )
44
+ *
45
+ * // Lyria-typed surface (full config knobs):
46
+ * const audio = inputs.pipe(
47
+ * LyriaRealtimeGenerator.streamGenerationFrom({ model: "lyria-realtime-exp", prompt: "" }),
40
48
  * )
41
49
  * ```
42
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"MusicGenerator.mjs","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { AudioChunk } from \"../domain/Audio.js\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n MusicResult,\n MusicSessionInput,\n} from \"../domain/Music.js\"\n\nexport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n MusicResult,\n MusicSessionInput,\n WeightedPrompt,\n} from \"../domain/Music.js\"\n\nexport type MusicGeneratorService = {\n /**\n * One-shot. Prompt in, full audio bytes out. Universally supported.\n * Async/poll-based providers (Suno, Mureka) hide their poll loop\n * inside the adapter — caller still sees a single `Effect`.\n */\n readonly generate: (\n request: CommonGenerateMusicRequest,\n ) => Effect.Effect<MusicResult, AiError.AiError>\n /**\n * Prompt in, audio chunks streamed out. Providers without a native\n * chunked-output endpoint (Lyria 3 sync, Mureka, MiniMax, Stable\n * Audio) emulate this by calling `generate` and emitting a single\n * `AudioChunk` — first-class, no `Unsupported`.\n */\n readonly streamGeneration: (\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n /**\n * Bidirectional session: a `Stream` of prompt-or-config updates flows\n * in, a `Stream` of audio chunks flows out. The session WS / RPC is\n * acquired on first pull and released when the output stream is\n * finalized via `Stream.scoped`.\n *\n * Gated by the `MusicInteractiveSession` capability marker on the\n * top-level helper — providers without bidirectional support don't\n * ship the marker, so calls fail at `Effect.provide` with a type\n * error.\n */\n readonly streamGenerationFrom: <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError | E, R>\n}\n\nexport class MusicGenerator extends Context.Service<MusicGenerator, MusicGeneratorService>()(\n \"@betalyra/effect-uai/MusicGenerator\",\n) {}\n\n/**\n * Capability marker — provided by provider layers whose\n * `streamGenerationFrom` is wired up at the wire level. Currently only\n * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.\n * Calling `streamGenerationFrom` while only a non-interactive Layer is\n * in scope fails at `Effect.provide` with a type error.\n *\n * Phantom — the value is `void`; providers register with\n * `Layer.succeed(MusicInteractiveSession, undefined)`.\n */\nexport class MusicInteractiveSession extends Context.Service<MusicInteractiveSession, void>()(\n \"@betalyra/effect-uai/capability/MusicInteractiveSession\",\n) {}\n\n/** One-shot generation. */\nexport const generate = (\n request: CommonGenerateMusicRequest,\n): Effect.Effect<MusicResult, AiError.AiError, MusicGenerator> =>\n Effect.flatMap(MusicGenerator.asEffect(), (s) => s.generate(request))\n\n/** Prompt in, audio chunks out. */\nexport const streamGeneration = (\n request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator> =>\n Stream.unwrap(Effect.map(MusicGenerator.asEffect(), (s) => s.streamGeneration(request)))\n\n/**\n * Bidirectional generation. Dual-arity: pipeable (data-last) and\n * direct (data-first). Requires `MusicInteractiveSession` in R —\n * providers without bidirectional support are a type error at provide\n * time.\n *\n * @example\n * ```ts\n * const audio = Stream.fromIterable([\n * Music.promptsInput([{ text: \"minimal techno\", weight: 1.0 }]),\n * Music.configInput({ bpm: 124 }),\n * ]).pipe(\n * MusicGenerator.streamGenerationFrom({ model: \"lyria-realtime-001\", prompts: \"\" }),\n * )\n * ```\n */\nexport const streamGenerationFrom: {\n (\n request: CommonStreamGenerateMusicRequest,\n ): <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n ) => Stream.Stream<AudioChunk, AiError.AiError | E, R | MusicGenerator | MusicInteractiveSession>\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<AudioChunk, AiError.AiError | E, R | MusicGenerator | MusicInteractiveSession>\n} = Function.dual(\n 2,\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const s = yield* MusicGenerator.asEffect()\n yield* MusicInteractiveSession.asEffect()\n return s.streamGenerationFrom(input, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;;AAqDA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;;;;;;;;;;AAYF,IAAa,0BAAb,cAA6C,QAAQ,SAAwC,CAC3F,0DACD,CAAC;;AAGF,MAAa,YACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,SAAS,QAAQ,CAAC;;AAGvE,MAAa,oBACX,YAEA,OAAO,OAAO,OAAO,IAAI,eAAe,UAAU,GAAG,MAAM,EAAE,iBAAiB,QAAQ,CAAC,CAAC;;;;;;;;;;;;;;;;;AAkB1F,MAAa,uBAUT,SAAS,KACX,IAEE,OACA,YAEA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO,eAAe,UAAU;AAC1C,QAAO,wBAAwB,UAAU;AACzC,QAAO,EAAE,qBAAqB,OAAO,QAAQ;EAC7C,CACH,CACJ"}
1
+ {"version":3,"file":"MusicGenerator.mjs","names":[],"sources":["../../src/music-generator/MusicGenerator.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicSessionInput,\n MusicStreamEvent,\n} from \"../domain/Music.js\"\nimport type { AudioChunk } from \"../domain/Audio.js\"\n\nexport type {\n CommonGenerateMusicRequest,\n CommonStreamGenerateMusicRequest,\n GenerateResult,\n MusicResult,\n MusicSection,\n MusicSessionControl,\n MusicSessionInput,\n MusicStreamEvent,\n Watermark,\n WeightedPrompt,\n} from \"../domain/Music.js\"\n\nexport type MusicGeneratorService = {\n /**\n * One-shot. Prompt in, full audio bytes out. Universally supported.\n * Async/poll-based providers (Suno, Mureka) hide their poll loop\n * inside the adapter, caller still sees a single `Effect`.\n *\n * Returns `GenerateResult` with `primary` plus a `variants` array.\n * Suno and Mureka always return 2 tracks; every other provider\n * returns 1 (and `primary === variants[0]`).\n */\n readonly generate: (\n request: CommonGenerateMusicRequest,\n ) => Effect.Effect<GenerateResult, AiError.AiError>\n /**\n * Prompt in, audio chunks streamed out. Providers without a native\n * chunked-output endpoint (Lyria 3 sync, Mureka, MiniMax, Stable\n * Audio) emulate this by calling `generate` and emitting a single\n * `AudioChunk`, first-class, no `Unsupported`.\n */\n readonly streamGeneration: (\n request: CommonStreamGenerateMusicRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n /**\n * Bidirectional session: a `Stream` of `MusicSessionInput` (prompts\n * + playback control) flows in, a `Stream` of `MusicStreamEvent`\n * flows out. The session WS / RPC is acquired on first pull and\n * released when the output stream is finalized via `Stream.scoped`.\n *\n * Cross-provider callers send `prompts` (steer the active\n * generation) and `control` (play / pause / stop / reset) — the two\n * actions that converge across interactive-media protocols.\n * Provider-specific model knobs (Lyria RealTime's density /\n * brightness / mute-stems / BPM / scale, etc.) live on the\n * provider-typed service (`LyriaRealtimeGenerator.streamGenerationFrom`),\n * which extends this union with its own `config` variant.\n *\n * Output is `MusicStreamEvent`: `audio` chunks alongside in-band\n * `warning` and `filteredPrompt` events the model emits server-side.\n *\n * Gated by the `MusicInteractiveSession` capability marker on the\n * top-level helper. Providers without bidirectional support don't\n * ship the marker, so calls fail at `Effect.provide` with a type\n * error.\n */\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\nexport class MusicGenerator extends Context.Service<MusicGenerator, MusicGeneratorService>()(\n \"@betalyra/effect-uai/MusicGenerator\",\n) {}\n\n/**\n * Capability marker, provided by provider layers whose\n * `streamGenerationFrom` is wired up at the wire level. Currently only\n * Lyria RealTime (via the BidiGenerateMusic WebSocket) ships it.\n * Calling `streamGenerationFrom` while only a non-interactive Layer is\n * in scope fails at `Effect.provide` with a type error.\n *\n * Phantom: the value is `void`; providers register with\n * `Layer.succeed(MusicInteractiveSession, undefined)`.\n */\nexport class MusicInteractiveSession extends Context.Service<MusicInteractiveSession, void>()(\n \"@betalyra/effect-uai/capability/MusicInteractiveSession\",\n) {}\n\n/** One-shot generation. Returns the full `GenerateResult`. */\nexport const generate = (\n request: CommonGenerateMusicRequest,\n): Effect.Effect<GenerateResult, AiError.AiError, MusicGenerator> =>\n Effect.flatMap(MusicGenerator.asEffect(), (s) => s.generate(request))\n\n/** Prompt in, audio chunks out. */\nexport const streamGeneration = (\n request: CommonStreamGenerateMusicRequest,\n): Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator> =>\n Stream.unwrap(Effect.map(MusicGenerator.asEffect(), (s) => s.streamGeneration(request)))\n\n/**\n * Bidirectional generation. Dual-arity: pipeable (data-last) and\n * direct (data-first). Requires `MusicInteractiveSession` in R,\n * providers without bidirectional support are a type error at provide\n * time.\n *\n * On the cross-provider surface, `input` is `Stream<unknown>` and\n * output is `Stream<MusicStreamEvent>`. Use the provider-typed\n * service (`LyriaRealtimeGenerator`) when you want to construct\n * typed session messages.\n *\n * @example\n * ```ts\n * // Generic surface (opaque input):\n * const audio = inputs.pipe(\n * MusicGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n *\n * // Lyria-typed surface (full config knobs):\n * const audio = inputs.pipe(\n * LyriaRealtimeGenerator.streamGenerationFrom({ model: \"lyria-realtime-exp\", prompt: \"\" }),\n * )\n * ```\n */\nexport const streamGenerationFrom: {\n (\n request: CommonStreamGenerateMusicRequest,\n ): <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n ) => Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n >\n} = Function.dual(\n 2,\n <E, R>(\n input: Stream.Stream<MusicSessionInput, E, R>,\n request: CommonStreamGenerateMusicRequest,\n ): Stream.Stream<\n MusicStreamEvent,\n AiError.AiError | E,\n R | MusicGenerator | MusicInteractiveSession\n > =>\n Stream.unwrap(\n Effect.gen(function* () {\n const s = yield* MusicGenerator.asEffect()\n yield* MusicInteractiveSession.asEffect()\n return s.streamGenerationFrom(input, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;;AA0EA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;;;;;;;;;;AAYF,IAAa,0BAAb,cAA6C,QAAQ,SAAwC,CAC3F,0DACD,CAAC;;AAGF,MAAa,YACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,SAAS,QAAQ,CAAC;;AAGvE,MAAa,oBACX,YAEA,OAAO,OAAO,OAAO,IAAI,eAAe,UAAU,GAAG,MAAM,EAAE,iBAAiB,QAAQ,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;AA0B1F,MAAa,uBAkBT,SAAS,KACX,IAEE,OACA,YAMA,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO,eAAe,UAAU;AAC1C,QAAO,wBAAwB,UAAU;AACzC,QAAO,EAAE,qBAAqB,OAAO,QAAQ;EAC7C,CACH,CACJ"}
@@ -1,66 +1,90 @@
1
- import { configInput, promptsInput } from "../domain/Music.mjs";
1
+ import { audioEvent, controlInput, promptsInput, singleVariant } from "../domain/Music.mjs";
2
2
  import { generate, streamGeneration, streamGenerationFrom } from "./MusicGenerator.mjs";
3
3
  import { i as it, n as globalExpect, r as describe, t as import_dist } from "../dist-DV5ISja1.mjs";
4
4
  import { layer, layerWithoutInteractive } from "../testing/MockMusicGenerator.mjs";
5
- import { Effect, Stream } from "effect";
5
+ import { Duration, Effect, Stream } from "effect";
6
6
  //#region src/music-generator/MusicGenerator.test.ts
7
- const result = {
8
- format: {
9
- container: "mp3",
10
- encoding: "mp3",
11
- sampleRate: 44100,
12
- channels: 2
7
+ const oneResult = {
8
+ audio: {
9
+ format: {
10
+ container: "mp3",
11
+ encoding: "mp3",
12
+ sampleRate: 44100,
13
+ channels: 2
14
+ },
15
+ bytes: new Uint8Array([
16
+ 255,
17
+ 251,
18
+ 144,
19
+ 0
20
+ ]),
21
+ duration: Duration.seconds(30)
13
22
  },
14
- bytes: new Uint8Array([
15
- 255,
16
- 251,
17
- 144,
18
- 0
19
- ]),
20
- durationSeconds: 30,
23
+ provider: "mock-music",
21
24
  lyrics: "[Verse]\nhello\n",
22
- watermark: { kind: "synthid" }
25
+ watermark: "synthid"
23
26
  };
27
+ const oneVariant = singleVariant(oneResult);
24
28
  const chunk = (n) => ({ bytes: new Uint8Array([n]) });
25
29
  describe("MusicGenerator.generate", () => {
26
- it("returns the scripted MusicResult", async () => {
27
- const mock = layer({ results: [result] });
30
+ it("returns the scripted GenerateResult", async () => {
31
+ const mock = layer({ results: [oneVariant] });
28
32
  const program = generate({
29
33
  model: "mock-music",
30
- prompts: "upbeat indie pop"
34
+ prompt: "upbeat indie pop"
31
35
  });
32
36
  const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)));
33
- globalExpect(out.bytes).toEqual(result.bytes);
34
- globalExpect(out.durationSeconds).toBe(30);
35
- globalExpect(out.watermark?.kind).toBe("synthid");
36
- globalExpect(out.lyrics).toContain("[Verse]");
37
+ globalExpect(out.primary.audio.bytes).toEqual(oneResult.audio.bytes);
38
+ globalExpect(out.primary.audio.duration).toEqual(Duration.seconds(30));
39
+ globalExpect(out.primary.watermark).toBe("synthid");
40
+ globalExpect(out.primary.lyrics).toContain("[Verse]");
41
+ globalExpect(out.variants).toHaveLength(1);
42
+ globalExpect(out.variants[0]).toBe(out.primary);
43
+ });
44
+ it("exposes multiple variants when the provider returned more than one", async () => {
45
+ const secondTrack = {
46
+ ...oneResult,
47
+ audio: {
48
+ ...oneResult.audio,
49
+ bytes: new Uint8Array([
50
+ 255,
51
+ 251,
52
+ 160,
53
+ 0
54
+ ])
55
+ }
56
+ };
57
+ const mock = layer({ results: [{
58
+ primary: oneResult,
59
+ variants: [oneResult, secondTrack]
60
+ }] });
61
+ const program = generate({
62
+ model: "m",
63
+ prompt: "house"
64
+ });
65
+ const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)));
66
+ globalExpect(out.variants).toHaveLength(2);
67
+ globalExpect(out.variants[1].audio.bytes).toEqual(secondTrack.audio.bytes);
37
68
  });
38
69
  it("records the request shape on the recorder", async () => {
39
- const mock = layer({ results: [result, result] });
70
+ const mock = layer({ results: [oneVariant, oneVariant] });
40
71
  const program = Effect.gen(function* () {
41
72
  yield* generate({
42
73
  model: "m",
43
- prompts: "techno"
74
+ prompt: "techno"
44
75
  });
45
76
  yield* generate({
46
77
  model: "m",
47
- prompts: [{
48
- text: "synthwave",
49
- weight: 1
50
- }, {
51
- text: "80s movie OST",
52
- weight: .4
53
- }],
54
- bpm: 120,
55
- instrumental: true
78
+ prompt: "synthwave",
79
+ duration: Duration.seconds(45),
80
+ seed: 42
56
81
  });
57
82
  return yield* mock.recorder;
58
83
  });
59
84
  const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)));
60
85
  globalExpect(rec.generateCalls.length).toBe(2);
61
- globalExpect(rec.generateCalls[1].bpm).toBe(120);
62
- globalExpect(rec.generateCalls[1].instrumental).toBe(true);
63
- globalExpect(Array.isArray(rec.generateCalls[1].prompts)).toBe(true);
86
+ globalExpect(rec.generateCalls[1].duration).toEqual(Duration.seconds(45));
87
+ globalExpect(rec.generateCalls[1].seed).toBe(42);
64
88
  });
65
89
  });
66
90
  describe("MusicGenerator.streamGeneration", () => {
@@ -72,7 +96,7 @@ describe("MusicGenerator.streamGeneration", () => {
72
96
  ]] });
73
97
  const program = Stream.runCollect(streamGeneration({
74
98
  model: "m",
75
- prompts: "ambient"
99
+ prompt: "ambient"
76
100
  }));
77
101
  globalExpect((await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))).map((c) => Array.from(c.bytes))).toEqual([
78
102
  [1],
@@ -84,7 +108,7 @@ describe("MusicGenerator.streamGeneration", () => {
84
108
  describe("MusicGenerator capability marker (compile-time)", () => {
85
109
  const sgfReq = {
86
110
  model: "m",
87
- prompts: ""
111
+ prompt: ""
88
112
  };
89
113
  it("requires `MusicInteractiveSession` on the R channel of streamGenerationFrom", () => {
90
114
  (0, import_dist.expectTypeOf)(Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(streamGenerationFrom(sgfReq))).toEqualTypeOf();
@@ -92,39 +116,39 @@ describe("MusicGenerator capability marker (compile-time)", () => {
92
116
  it("does NOT require `MusicInteractiveSession` for sync `generate`", () => {
93
117
  (0, import_dist.expectTypeOf)(generate({
94
118
  model: "m",
95
- prompts: "ambient"
119
+ prompt: "ambient"
96
120
  })).toEqualTypeOf();
97
121
  });
98
122
  it("does NOT require `MusicInteractiveSession` for `streamGeneration`", () => {
99
123
  (0, import_dist.expectTypeOf)(streamGeneration({
100
124
  model: "m",
101
- prompts: "ambient"
125
+ prompt: "ambient"
102
126
  })).toEqualTypeOf();
103
127
  });
104
128
  it("a layer without the marker leaves `MusicInteractiveSession` unsatisfied in R", () => {
105
129
  const noMarker = layerWithoutInteractive({});
106
- const audio = Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(streamGenerationFrom(sgfReq));
107
- (0, import_dist.expectTypeOf)(Stream.runDrain(audio).pipe(Effect.provide(noMarker.layer))).toEqualTypeOf();
130
+ const events = Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(streamGenerationFrom(sgfReq));
131
+ (0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(noMarker.layer))).toEqualTypeOf();
108
132
  });
109
133
  it("a full layer (with marker) clears R to never", () => {
110
- const fullMock = layer({ streamGenerationFromChunks: [[]] });
111
- const audio = Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(streamGenerationFrom(sgfReq));
112
- (0, import_dist.expectTypeOf)(Stream.runDrain(audio).pipe(Effect.provide(fullMock.layer))).toEqualTypeOf();
134
+ const fullMock = layer({ streamGenerationFromEvents: [[]] });
135
+ const events = Stream.fromIterable([promptsInput([{ text: "techno" }])]).pipe(streamGenerationFrom(sgfReq));
136
+ (0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(fullMock.layer))).toEqualTypeOf();
113
137
  });
114
138
  });
115
139
  describe("MusicGenerator.streamGenerationFrom", () => {
116
140
  const sgfReq = {
117
- model: "lyria-realtime-001",
118
- prompts: ""
141
+ model: "lyria-realtime-exp",
142
+ prompt: ""
119
143
  };
120
- it("drains a session-input stream and emits scripted audio", async () => {
121
- const mock = layer({ streamGenerationFromChunks: [[chunk(10), chunk(20)]] });
122
- const audio = Stream.fromIterable([
144
+ it("drains a session-input stream and emits scripted events", async () => {
145
+ const mock = layer({ streamGenerationFromEvents: [[audioEvent(chunk(10)), audioEvent(chunk(20))]] });
146
+ const events = Stream.fromIterable([
123
147
  promptsInput([{
124
148
  text: "minimal techno",
125
149
  weight: 1
126
150
  }]),
127
- configInput({ bpm: 124 }),
151
+ controlInput("play"),
128
152
  promptsInput([{
129
153
  text: "minimal techno",
130
154
  weight: 1
@@ -133,17 +157,18 @@ describe("MusicGenerator.streamGenerationFrom", () => {
133
157
  weight: .3
134
158
  }])
135
159
  ]).pipe(streamGenerationFrom(sgfReq));
136
- globalExpect((await Effect.runPromise(Stream.runCollect(audio).pipe(Effect.provide(mock.layer)))).map((c) => Array.from(c.bytes))).toEqual([[10], [20]]);
160
+ globalExpect((await Effect.runPromise(Stream.runCollect(events).pipe(Effect.provide(mock.layer)))).flatMap((e) => e._tag === "audio" ? [e.chunk] : []).map((c) => Array.from(c.bytes))).toEqual([[10], [20]]);
137
161
  });
138
162
  it("records the request on the streamGenerationFrom call channel", async () => {
139
- const mock = layer({ streamGenerationFromChunks: [[chunk(42)]] });
163
+ const mock = layer({ streamGenerationFromEvents: [[audioEvent(chunk(42))]] });
140
164
  const program = Effect.gen(function* () {
141
- yield* Stream.runDrain(Stream.fromIterable([promptsInput([{ text: "x" }])]).pipe(streamGenerationFrom(sgfReq)));
165
+ const inputs = Stream.fromIterable([promptsInput([{ text: "x" }])]);
166
+ yield* Stream.runDrain(inputs.pipe(streamGenerationFrom(sgfReq)));
142
167
  return yield* mock.recorder;
143
168
  });
144
169
  const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)));
145
170
  globalExpect(rec.streamGenerationFromCalls.length).toBe(1);
146
- globalExpect(rec.streamGenerationFromCalls[0].model).toBe("lyria-realtime-001");
171
+ globalExpect(rec.streamGenerationFromCalls[0].model).toBe("lyria-realtime-exp");
147
172
  globalExpect(rec.generateCalls.length).toBe(0);
148
173
  globalExpect(rec.streamGenerationCalls.length).toBe(0);
149
174
  });
@@ -1 +1 @@
1
- {"version":3,"file":"MusicGenerator.test.mjs","names":["MockMusicGenerator.layer","MusicGenerator.generate","MusicGenerator.streamGeneration","MusicGenerator.streamGenerationFrom","MockMusicGenerator.layerWithoutInteractive"],"sources":["../../src/music-generator/MusicGenerator.test.ts"],"sourcesContent":["import { Effect, Stream } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"../domain/AiError.js\"\nimport type { AudioChunk, AudioFormat } from \"../domain/Audio.js\"\nimport { configInput, promptsInput, type MusicResult } from \"../domain/Music.js\"\nimport * as MockMusicGenerator from \"../testing/MockMusicGenerator.js\"\nimport * as MusicGenerator from \"./MusicGenerator.js\"\n\nconst mp3Format: AudioFormat = {\n container: \"mp3\",\n encoding: \"mp3\",\n sampleRate: 44100,\n channels: 2,\n}\n\nconst result: MusicResult = {\n format: mp3Format,\n bytes: new Uint8Array([0xff, 0xfb, 0x90, 0x00]),\n durationSeconds: 30,\n lyrics: \"[Verse]\\nhello\\n\",\n watermark: { kind: \"synthid\" },\n}\n\nconst chunk = (n: number): AudioChunk => ({ bytes: new Uint8Array([n]) })\n\ndescribe(\"MusicGenerator.generate\", () => {\n it(\"returns the scripted MusicResult\", async () => {\n const mock = MockMusicGenerator.layer({ results: [result] })\n const program = MusicGenerator.generate({\n model: \"mock-music\",\n prompts: \"upbeat indie pop\",\n })\n const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(out.bytes).toEqual(result.bytes)\n expect(out.durationSeconds).toBe(30)\n expect(out.watermark?.kind).toBe(\"synthid\")\n expect(out.lyrics).toContain(\"[Verse]\")\n })\n\n it(\"records the request shape on the recorder\", async () => {\n const mock = MockMusicGenerator.layer({ results: [result, result] })\n const program = Effect.gen(function* () {\n yield* MusicGenerator.generate({ model: \"m\", prompts: \"techno\" })\n yield* MusicGenerator.generate({\n model: \"m\",\n prompts: [\n { text: \"synthwave\", weight: 1.0 },\n { text: \"80s movie OST\", weight: 0.4 },\n ],\n bpm: 120,\n instrumental: true,\n })\n return yield* mock.recorder\n })\n const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(rec.generateCalls.length).toBe(2)\n expect(rec.generateCalls[1]!.bpm).toBe(120)\n expect(rec.generateCalls[1]!.instrumental).toBe(true)\n expect(Array.isArray(rec.generateCalls[1]!.prompts)).toBe(true)\n })\n})\n\ndescribe(\"MusicGenerator.streamGeneration\", () => {\n it(\"emits scripted chunks\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationChunks: [[chunk(1), chunk(2), chunk(3)]],\n })\n const program = Stream.runCollect(\n MusicGenerator.streamGeneration({ model: \"m\", prompts: \"ambient\" }),\n )\n const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(out.map((c) => Array.from(c.bytes))).toEqual([[1], [2], [3]])\n })\n})\n\ndescribe(\"MusicGenerator capability marker (compile-time)\", () => {\n const sgfReq: MusicGenerator.CommonStreamGenerateMusicRequest = {\n model: \"m\",\n prompts: \"\",\n }\n\n it(\"requires `MusicInteractiveSession` on the R channel of streamGenerationFrom\", () => {\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const audio = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n expectTypeOf(audio).toEqualTypeOf<\n Stream.Stream<\n AudioChunk,\n AiError.AiError,\n MusicGenerator.MusicGenerator | MusicGenerator.MusicInteractiveSession\n >\n >()\n })\n\n it(\"does NOT require `MusicInteractiveSession` for sync `generate`\", () => {\n const eff = MusicGenerator.generate({ model: \"m\", prompts: \"ambient\" })\n expectTypeOf(eff).toEqualTypeOf<\n Effect.Effect<MusicResult, AiError.AiError, MusicGenerator.MusicGenerator>\n >()\n })\n\n it(\"does NOT require `MusicInteractiveSession` for `streamGeneration`\", () => {\n const audio = MusicGenerator.streamGeneration({ model: \"m\", prompts: \"ambient\" })\n expectTypeOf(audio).toEqualTypeOf<\n Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator.MusicGenerator>\n >()\n })\n\n it(\"a layer without the marker leaves `MusicInteractiveSession` unsatisfied in R\", () => {\n const noMarker = MockMusicGenerator.layerWithoutInteractive({})\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const audio = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const program = Stream.runDrain(audio).pipe(Effect.provide(noMarker.layer))\n expectTypeOf(program).toEqualTypeOf<\n Effect.Effect<void, AiError.AiError, MusicGenerator.MusicInteractiveSession>\n >()\n })\n\n it(\"a full layer (with marker) clears R to never\", () => {\n const fullMock = MockMusicGenerator.layer({\n streamGenerationFromChunks: [[]],\n })\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const audio = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const program = Stream.runDrain(audio).pipe(Effect.provide(fullMock.layer))\n expectTypeOf(program).toEqualTypeOf<Effect.Effect<void, AiError.AiError, never>>()\n })\n})\n\ndescribe(\"MusicGenerator.streamGenerationFrom\", () => {\n const sgfReq: MusicGenerator.CommonStreamGenerateMusicRequest = {\n model: \"lyria-realtime-001\",\n prompts: \"\",\n }\n\n it(\"drains a session-input stream and emits scripted audio\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationFromChunks: [[chunk(10), chunk(20)]],\n })\n const inputs = Stream.fromIterable([\n promptsInput([{ text: \"minimal techno\", weight: 1.0 }]),\n configInput({ bpm: 124 }),\n promptsInput([\n { text: \"minimal techno\", weight: 1.0 },\n { text: \"1980s synthwave\", weight: 0.3 },\n ]),\n ])\n const audio = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const out = await Effect.runPromise(Stream.runCollect(audio).pipe(Effect.provide(mock.layer)))\n expect(out.map((c) => Array.from(c.bytes))).toEqual([[10], [20]])\n })\n\n it(\"records the request on the streamGenerationFrom call channel\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationFromChunks: [[chunk(42)]],\n })\n const program = Effect.gen(function* () {\n yield* Stream.runDrain(\n Stream.fromIterable([promptsInput([{ text: \"x\" }])]).pipe(\n MusicGenerator.streamGenerationFrom(sgfReq),\n ),\n )\n return yield* mock.recorder\n })\n const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(rec.streamGenerationFromCalls.length).toBe(1)\n expect(rec.streamGenerationFromCalls[0]!.model).toBe(\"lyria-realtime-001\")\n expect(rec.generateCalls.length).toBe(0)\n expect(rec.streamGenerationCalls.length).toBe(0)\n })\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"MusicGenerator.test.mjs","names":["MockMusicGenerator.layer","MusicGenerator.generate","MusicGenerator.streamGeneration","MusicGenerator.streamGenerationFrom","MockMusicGenerator.layerWithoutInteractive"],"sources":["../../src/music-generator/MusicGenerator.test.ts"],"sourcesContent":["import { Duration, Effect, Stream } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"../domain/AiError.js\"\nimport type { AudioChunk, AudioFormat } from \"../domain/Audio.js\"\nimport {\n audioEvent,\n controlInput,\n type GenerateResult,\n type MusicResult,\n type MusicStreamEvent,\n promptsInput,\n singleVariant,\n} from \"../domain/Music.js\"\nimport * as MockMusicGenerator from \"../testing/MockMusicGenerator.js\"\nimport * as MusicGenerator from \"./MusicGenerator.js\"\n\nconst mp3Format: AudioFormat = {\n container: \"mp3\",\n encoding: \"mp3\",\n sampleRate: 44100,\n channels: 2,\n}\n\nconst oneResult: MusicResult = {\n audio: {\n format: mp3Format,\n bytes: new Uint8Array([0xff, 0xfb, 0x90, 0x00]),\n duration: Duration.seconds(30),\n },\n provider: \"mock-music\",\n lyrics: \"[Verse]\\nhello\\n\",\n watermark: \"synthid\",\n}\n\nconst oneVariant: GenerateResult = singleVariant(oneResult)\n\nconst chunk = (n: number): AudioChunk => ({ bytes: new Uint8Array([n]) })\n\ndescribe(\"MusicGenerator.generate\", () => {\n it(\"returns the scripted GenerateResult\", async () => {\n const mock = MockMusicGenerator.layer({ results: [oneVariant] })\n const program = MusicGenerator.generate({\n model: \"mock-music\",\n prompt: \"upbeat indie pop\",\n })\n const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(out.primary.audio.bytes).toEqual(oneResult.audio.bytes)\n expect(out.primary.audio.duration).toEqual(Duration.seconds(30))\n expect(out.primary.watermark).toBe(\"synthid\")\n expect(out.primary.lyrics).toContain(\"[Verse]\")\n expect(out.variants).toHaveLength(1)\n expect(out.variants[0]).toBe(out.primary)\n })\n\n it(\"exposes multiple variants when the provider returned more than one\", async () => {\n const secondTrack: MusicResult = {\n ...oneResult,\n audio: { ...oneResult.audio, bytes: new Uint8Array([0xff, 0xfb, 0xa0, 0x00]) },\n }\n const twoVariants: GenerateResult = {\n primary: oneResult,\n variants: [oneResult, secondTrack],\n }\n const mock = MockMusicGenerator.layer({ results: [twoVariants] })\n const program = MusicGenerator.generate({ model: \"m\", prompt: \"house\" })\n const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(out.variants).toHaveLength(2)\n expect(out.variants[1]!.audio.bytes).toEqual(secondTrack.audio.bytes)\n })\n\n it(\"records the request shape on the recorder\", async () => {\n const mock = MockMusicGenerator.layer({ results: [oneVariant, oneVariant] })\n const program = Effect.gen(function* () {\n yield* MusicGenerator.generate({ model: \"m\", prompt: \"techno\" })\n yield* MusicGenerator.generate({\n model: \"m\",\n prompt: \"synthwave\",\n duration: Duration.seconds(45),\n seed: 42,\n })\n return yield* mock.recorder\n })\n const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(rec.generateCalls.length).toBe(2)\n expect(rec.generateCalls[1]!.duration).toEqual(Duration.seconds(45))\n expect(rec.generateCalls[1]!.seed).toBe(42)\n })\n})\n\ndescribe(\"MusicGenerator.streamGeneration\", () => {\n it(\"emits scripted chunks\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationChunks: [[chunk(1), chunk(2), chunk(3)]],\n })\n const program = Stream.runCollect(\n MusicGenerator.streamGeneration({ model: \"m\", prompt: \"ambient\" }),\n )\n const out = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(out.map((c) => Array.from(c.bytes))).toEqual([[1], [2], [3]])\n })\n})\n\ndescribe(\"MusicGenerator capability marker (compile-time)\", () => {\n const sgfReq: MusicGenerator.CommonStreamGenerateMusicRequest = {\n model: \"m\",\n prompt: \"\",\n }\n\n it(\"requires `MusicInteractiveSession` on the R channel of streamGenerationFrom\", () => {\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const events = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n expectTypeOf(events).toEqualTypeOf<\n Stream.Stream<\n MusicStreamEvent,\n AiError.AiError,\n MusicGenerator.MusicGenerator | MusicGenerator.MusicInteractiveSession\n >\n >()\n })\n\n it(\"does NOT require `MusicInteractiveSession` for sync `generate`\", () => {\n const eff = MusicGenerator.generate({ model: \"m\", prompt: \"ambient\" })\n expectTypeOf(eff).toEqualTypeOf<\n Effect.Effect<GenerateResult, AiError.AiError, MusicGenerator.MusicGenerator>\n >()\n })\n\n it(\"does NOT require `MusicInteractiveSession` for `streamGeneration`\", () => {\n const audio = MusicGenerator.streamGeneration({ model: \"m\", prompt: \"ambient\" })\n expectTypeOf(audio).toEqualTypeOf<\n Stream.Stream<AudioChunk, AiError.AiError, MusicGenerator.MusicGenerator>\n >()\n })\n\n it(\"a layer without the marker leaves `MusicInteractiveSession` unsatisfied in R\", () => {\n const noMarker = MockMusicGenerator.layerWithoutInteractive({})\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const events = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const program = Stream.runDrain(events).pipe(Effect.provide(noMarker.layer))\n expectTypeOf(program).toEqualTypeOf<\n Effect.Effect<void, AiError.AiError, MusicGenerator.MusicInteractiveSession>\n >()\n })\n\n it(\"a full layer (with marker) clears R to never\", () => {\n const fullMock = MockMusicGenerator.layer({\n streamGenerationFromEvents: [[]],\n })\n const inputs = Stream.fromIterable([promptsInput([{ text: \"techno\" }])])\n const events = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const program = Stream.runDrain(events).pipe(Effect.provide(fullMock.layer))\n expectTypeOf(program).toEqualTypeOf<Effect.Effect<void, AiError.AiError, never>>()\n })\n})\n\ndescribe(\"MusicGenerator.streamGenerationFrom\", () => {\n const sgfReq: MusicGenerator.CommonStreamGenerateMusicRequest = {\n model: \"lyria-realtime-exp\",\n prompt: \"\",\n }\n\n it(\"drains a session-input stream and emits scripted events\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationFromEvents: [[audioEvent(chunk(10)), audioEvent(chunk(20))]],\n })\n const inputs = Stream.fromIterable([\n promptsInput([{ text: \"minimal techno\", weight: 1.0 }]),\n controlInput(\"play\"),\n promptsInput([\n { text: \"minimal techno\", weight: 1.0 },\n { text: \"1980s synthwave\", weight: 0.3 },\n ]),\n ])\n const events = inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq))\n const out = await Effect.runPromise(Stream.runCollect(events).pipe(Effect.provide(mock.layer)))\n const audioChunks = out.flatMap((e) => (e._tag === \"audio\" ? [e.chunk] : []))\n expect(audioChunks.map((c) => Array.from(c.bytes))).toEqual([[10], [20]])\n })\n\n it(\"records the request on the streamGenerationFrom call channel\", async () => {\n const mock = MockMusicGenerator.layer({\n streamGenerationFromEvents: [[audioEvent(chunk(42))]],\n })\n const program = Effect.gen(function* () {\n const inputs = Stream.fromIterable([promptsInput([{ text: \"x\" }])])\n yield* Stream.runDrain(inputs.pipe(MusicGenerator.streamGenerationFrom(sgfReq)))\n return yield* mock.recorder\n })\n const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(rec.streamGenerationFromCalls.length).toBe(1)\n expect(rec.streamGenerationFromCalls[0]!.model).toBe(\"lyria-realtime-exp\")\n expect(rec.generateCalls.length).toBe(0)\n expect(rec.streamGenerationCalls.length).toBe(0)\n })\n})\n"],"mappings":"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"}
@@ -1,36 +1,25 @@
1
1
  import { t as AiError } from "../AiError-CAX_48RU.mjs";
2
- import { n as AudioChunk, r as AudioFormat, t as AudioBlob } from "../Audio-BfCTGnH3.mjs";
2
+ import { n as AudioChunk, r as AudioFormat, t as AudioBlob } from "../Audio-DPlTsNdz.mjs";
3
3
  import { Context, Effect, Stream } from "effect";
4
4
 
5
5
  //#region src/speech-synthesizer/SpeechSynthesizer.d.ts
6
6
  declare namespace SpeechSynthesizer_d_exports {
7
- export { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, CustomPronunciation, DialogueTurn, MultiSpeakerTts, PhoneticEncoding, SpeechSynthesizer, SpeechSynthesizerService, TtsIncrementalText, streamSynthesis, streamSynthesisFrom, streamSynthesizeDialogue, synthesize, synthesizeDialogue };
7
+ export { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, CustomPronunciation, DialogueTurn, MultiSpeakerTts, SpeechSynthesizer, SpeechSynthesizerService, TtsIncrementalText, streamSynthesis, streamSynthesisFrom, streamSynthesizeDialogue, synthesize, synthesizeDialogue };
8
8
  }
9
9
  /**
10
- * Phonetic alphabets accepted by `CustomPronunciation`. Not every
11
- * provider supports every alphabet — adapters translate or warn+drop
12
- * unsupported entries.
10
+ * Per-phrase pronunciation override. `pronunciation` is always IPA, the
11
+ * universal phonetic interchange format. Adapters translate to the
12
+ * provider's wire form (inline `/ipa/` token, SSML `<phoneme>`, or a
13
+ * structured field), converting IPA to X-SAMPA internally where the wire
14
+ * requires it (Google, Azure).
13
15
  *
14
- * - `ipa` — universal lowest common denominator (Google, ElevenLabs,
15
- * Inworld, Deepgram, Cartesia, MiniMax).
16
- * - `x-sampa` — Google Cloud TTS.
17
- * - `cmu-arpabet` — ElevenLabs `eleven_flash_v2` / `eleven_english_v1`.
18
- */
19
- type PhoneticEncoding = "ipa" | "x-sampa" | "cmu-arpabet";
20
- /**
21
- * Per-phrase pronunciation override. The same shape unifies the
22
- * structured-field providers (Google) and the inline-markup providers
23
- * (ElevenLabs SSML, Inworld `/ipa/`, Deepgram, Cartesia, MiniMax) — the
24
- * adapter does the wire translation.
25
- *
26
- * Unsupported alphabets are dropped at the adapter with a structured
27
- * `logWarning` (one per call). The audio still renders, just without
28
- * the override.
16
+ * Pronunciations are load-bearing (the configured word is mispronounced
17
+ * if dropped), so a provider with no stateless IPA path fails the call
18
+ * with `AiError.Unsupported` rather than silently degrading.
29
19
  */
30
20
  type CustomPronunciation = {
31
- readonly phrase: string;
21
+ readonly phrase: string; /** IPA notation, e.g. `"ænˈθrɒpɪk"`. */
32
22
  readonly pronunciation: string;
33
- readonly encoding: PhoneticEncoding;
34
23
  };
35
24
  /**
36
25
  * Cross-provider synthesis request. Provider-specific extensions
@@ -53,11 +42,10 @@ type CommonSynthesizeRequest = {
53
42
  readonly speed?: number;
54
43
  readonly languageCode?: string;
55
44
  /**
56
- * Phoneme overrides for specific phrases. 7 of 9 TTS providers
57
- * support some form of this; adapters either translate to a
58
- * structured field, rewrite `text` inline, or `warn+drop` per-entry
59
- * for alphabets they don't accept. OpenAI rejects non-empty arrays
60
- * with `Unsupported`.
45
+ * IPA phoneme overrides for specific phrases. Adapters render them to
46
+ * the provider's wire form (inline token, SSML `<phoneme>`, structured
47
+ * field). Load-bearing: a provider with no stateless IPA path fails
48
+ * the call with `AiError.Unsupported` rather than mispronouncing.
61
49
  */
62
50
  readonly pronunciations?: ReadonlyArray<CustomPronunciation>;
63
51
  };
@@ -72,18 +60,15 @@ type CommonSynthesizeRequest = {
72
60
  */
73
61
  type CommonStreamSynthesizeRequest = Omit<CommonSynthesizeRequest, "text">;
74
62
  /**
75
- * One turn in a multi-speaker dialogue.
76
- *
77
- * `styleDescription` and `speed` are honored by providers that expose
78
- * per-turn knobs (Hume Octave-2) and silently ignored by others
79
- * (ElevenLabs `/v1/text-to-dialogue` accepts `{voice_id, text}` only;
80
- * Google Gemini TTS multi-speaker has no per-turn styling).
63
+ * One turn in a multi-speaker dialogue. Just a voice and its line; the
64
+ * only in-tree dialogue endpoint (ElevenLabs `/v1/text-to-dialogue`)
65
+ * accepts `{ voice_id, text }` per turn. Per-turn styling knobs
66
+ * (Hume Octave-2 `styleDescription` / `speed`) are provider-specific
67
+ * and belong on a provider-typed turn extension, not here.
81
68
  */
82
69
  type DialogueTurn = {
83
70
  readonly voiceId: string;
84
71
  readonly text: string;
85
- readonly styleDescription?: string;
86
- readonly speed?: number;
87
72
  };
88
73
  /**
89
74
  * Cross-provider dialogue request. Same return type as `synthesize` —
@@ -188,5 +173,5 @@ declare const synthesizeDialogue: (request: CommonSynthesizeDialogueRequest) =>
188
173
  */
189
174
  declare const streamSynthesizeDialogue: (request: CommonSynthesizeDialogueRequest) => Stream.Stream<AudioChunk, AiError, SpeechSynthesizer | MultiSpeakerTts>;
190
175
  //#endregion
191
- export { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, CustomPronunciation, DialogueTurn, MultiSpeakerTts, PhoneticEncoding, SpeechSynthesizer, SpeechSynthesizerService, TtsIncrementalText, streamSynthesis, streamSynthesisFrom, streamSynthesizeDialogue, synthesize, synthesizeDialogue, SpeechSynthesizer_d_exports as t };
176
+ export { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, CustomPronunciation, DialogueTurn, MultiSpeakerTts, SpeechSynthesizer, SpeechSynthesizerService, TtsIncrementalText, streamSynthesis, streamSynthesisFrom, streamSynthesizeDialogue, synthesize, synthesizeDialogue, SpeechSynthesizer_d_exports as t };
192
177
  //# sourceMappingURL=SpeechSynthesizer.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpeechSynthesizer.d.mts","names":[],"sources":["../../src/speech-synthesizer/SpeechSynthesizer.ts"],"mappings":";;;;;;;;;;;;;;;;;;KAcY,gBAAA;;;;;;;;;;AAAZ;KAYY,mBAAA;EAAA,SACD,MAAA;EAAA,SACA,aAAA;EAAA,SACA,QAAA,EAAU,gBAAA;AAAA;;;;;;;;KAUT,uBAAA;EAAA,SACD,IAAA,UADC;EAAA,SAGD,KAAA;;;;;;;WAOA,OAAA;EAAA,SACA,YAAA,GAAe,WAAA;EAAA,SACf,KAAA;EAAA,SACA,YAAA;EAFe;;;;;;;EAAA,SAUf,cAAA,GAAiB,aAAA,CAAc,mBAAA;AAAA;;;;;AAsB1C;;;;;KAVY,6BAAA,GAAgC,IAAA,CAAK,uBAAA;;;;;AAsBjD;;;;KAZY,YAAA;EAAA,SACD,OAAA;EAAA,SACA,IAAA;EAAA,SACA,gBAAA;EAAA,SACA,KAAA;AAAA;;;;;;KAQC,+BAAA;EAAA,SACD,KAAA;EAAA,SACA,KAAA,EAAO,aAAA,CAAc,YAAA;EAAA,SACrB,YAAA,GAAe,WAAA;EAAA,SACf,YAAA;EAAA,SACA,cAAA,GAAiB,aAAA,CAAc,mBAAA;AAAA;AAAA,KAG9B,wBAAA;EAAwB,oFAEzB,UAAA,GACP,OAAA,EAAS,uBAAA,KACN,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;EADnB;;;;EAAA,SAMF,eAAA,GACP,OAAA,EAAS,uBAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;EAAZ;;;;;;;;;;EAAA,SAWV,mBAAA,SACP,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,GACjC,OAAA,EAAS,6BAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA;EAA/C;;;;;;EAAA,SAOI,kBAAA,GACP,OAAA,EAAS,+BAAA,KACN,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;EAWC;;;;;;;;EAAA,SAFtB,wBAAA,GACP,OAAA,EAAS,+BAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;AAAA;AAAA,cAChC,sBAAA;cAEY,iBAAA,SAA0B,sBAAA;AAAA,cAGS,uBAAA;;;;;;;;;;;cAYnC,kBAAA,SAA2B,uBAAA;AAAA,cAEpC,oBAAA;;;;;;;;;;cAWS,eAAA,SAAwB,oBAAA;;cAKxB,UAAA,GACX,OAAA,EAAS,uBAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,iBAAA;;cAIhC,eAAA,GACX,OAAA,EAAS,uBAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,iBAAA;;;;;;;;;;;;;;cAgBjC,mBAAA;EAAA,CAET,OAAA,EAAS,6BAAA,UAET,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,MAC9B,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,iBAAA,GAAoB,kBAAA;EAAA,OAE1E,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,GACjC,OAAA,EAAS,6BAAA,GACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,iBAAA,GAAoB,kBAAA;AAAA;;;;;cAiB/D,kBAAA,GACX,OAAA,EAAS,+BAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,iBAAA,GAAoB,eAAA;;;;cAUpD,wBAAA,GACX,OAAA,EAAS,+BAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,iBAAA,GAAoB,eAAA"}
1
+ {"version":3,"file":"SpeechSynthesizer.d.mts","names":[],"sources":["../../src/speech-synthesizer/SpeechSynthesizer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KAeY,mBAAA;EAAA,SACD,MAAA;WAEA,aAAA;AAAA;;;;AAHX;;;;KAaY,uBAAA;EAAA,SACD,IAAA,UADwB;EAAA,SAGxB,KAAA;EAQe;;;;;;EAAA,SADf,OAAA;EAAA,SACA,YAAA,GAAe,WAAA;EAAA,SACf,KAAA;EAAA,SACA,YAAA;EADA;;;;;;EAAA,SAQA,cAAA,GAAiB,aAAA,CAAc,mBAAA;AAAA;;;;;AAqB1C;;;;;KATY,6BAAA,GAAgC,IAAA,CAAK,uBAAA;;;;;;;;KASrC,YAAA;EAAA,SACD,OAAA;EAAA,SACA,IAAA;AAAA;;;;;;KAQC,+BAAA;EAAA,SACD,KAAA;EAAA,SACA,KAAA,EAAO,aAAA,CAAc,YAAA;EAAA,SACrB,YAAA,GAAe,WAAA;EAAA,SACf,YAAA;EAAA,SACA,cAAA,GAAiB,aAAA,CAAc,mBAAA;AAAA;AAAA,KAG9B,wBAAA;EAGC,oFADF,UAAA,GACP,OAAA,EAAS,uBAAA,KACN,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;EAAA;;;;EAAA,SAKrB,eAAA,GACP,OAAA,EAAS,uBAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;EAA1B;;;;;;;;;;EAAA,SAWI,mBAAA,SACP,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,GACjC,OAAA,EAAS,6BAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA;EASjC;;;;;;EAAA,SAFV,kBAAA,GACP,OAAA,EAAS,+BAAA,KACN,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;EAWZ;;;;;;;;EAAA,SAFT,wBAAA,GACP,OAAA,EAAS,+BAAA,KACN,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA;AAAA;AAAA,cAChC,sBAAA;cAEY,iBAAA,SAA0B,sBAAA;AAAA,cAGS,uBAAA;;;;;;;;;;;cAYnC,kBAAA,SAA2B,uBAAA;AAAA,cAEpC,oBAAA;;;;;;;;;;cAWS,eAAA,SAAwB,oBAAA;;cAKxB,UAAA,GACX,OAAA,EAAS,uBAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,iBAAA;;cAIhC,eAAA,GACX,OAAA,EAAS,uBAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,iBAAA;;;;;;;;;;;;AA3C7C;;cA2DY,mBAAA;EAAA,CAET,OAAA,EAAS,6BAAA,UAET,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,MAC9B,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,iBAAA,GAAoB,kBAAA;EAAA,OAE1E,MAAA,EAAQ,MAAA,CAAO,MAAA,SAAe,CAAA,EAAG,CAAA,GACjC,OAAA,EAAS,6BAAA,GACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,iBAAA,GAAoB,kBAAA;AAAA;;;;;cAiB/D,kBAAA,GACX,OAAA,EAAS,+BAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,iBAAA,GAAoB,eAAA;;;;cAUpD,wBAAA,GACX,OAAA,EAAS,+BAAA,KACR,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,EAAiB,iBAAA,GAAoB,eAAA"}