@effect-uai/inworld 0.4.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/LICENSE +21 -0
- package/dist/InworldRealtimeSynthesizer.d.mts +30 -0
- package/dist/InworldRealtimeSynthesizer.d.mts.map +1 -0
- package/dist/InworldRealtimeSynthesizer.mjs +43 -0
- package/dist/InworldRealtimeSynthesizer.mjs.map +1 -0
- package/dist/InworldRealtimeTranscriber.d.mts +30 -0
- package/dist/InworldRealtimeTranscriber.d.mts.map +1 -0
- package/dist/InworldRealtimeTranscriber.mjs +37 -0
- package/dist/InworldRealtimeTranscriber.mjs.map +1 -0
- package/dist/InworldSynthesizer.d.mts +71 -0
- package/dist/InworldSynthesizer.d.mts.map +1 -0
- package/dist/InworldSynthesizer.mjs +131 -0
- package/dist/InworldSynthesizer.mjs.map +1 -0
- package/dist/InworldTranscriber.d.mts +48 -0
- package/dist/InworldTranscriber.d.mts.map +1 -0
- package/dist/InworldTranscriber.mjs +116 -0
- package/dist/InworldTranscriber.mjs.map +1 -0
- package/dist/chunk-CfYAbeIz.mjs +13 -0
- package/dist/codec-C-2ssVAz.d.mts +35 -0
- package/dist/codec-C-2ssVAz.d.mts.map +1 -0
- package/dist/codec.d.mts +2 -0
- package/dist/codec.mjs +100 -0
- package/dist/codec.mjs.map +1 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/models-CPLZDcgt.d.mts +47 -0
- package/dist/models-CPLZDcgt.d.mts.map +1 -0
- package/dist/models.d.mts +2 -0
- package/dist/models.mjs +1 -0
- package/dist/realtimeStt.d.mts +38 -0
- package/dist/realtimeStt.d.mts.map +1 -0
- package/dist/realtimeStt.mjs +145 -0
- package/dist/realtimeStt.mjs.map +1 -0
- package/dist/realtimeTts.d.mts +29 -0
- package/dist/realtimeTts.d.mts.map +1 -0
- package/dist/realtimeTts.mjs +94 -0
- package/dist/realtimeTts.mjs.map +1 -0
- package/dist/wsAuth.d.mts +8 -0
- package/dist/wsAuth.d.mts.map +1 -0
- package/dist/wsAuth.mjs +8 -0
- package/dist/wsAuth.mjs.map +1 -0
- package/package.json +80 -0
- package/src/InworldRealtimeSynthesizer.ts +76 -0
- package/src/InworldRealtimeTranscriber.ts +64 -0
- package/src/InworldSynthesizer.ts +235 -0
- package/src/InworldTranscriber.ts +225 -0
- package/src/codec.ts +141 -0
- package/src/index.ts +6 -0
- package/src/models.ts +69 -0
- package/src/realtimeStt.ts +236 -0
- package/src/realtimeTts.ts +167 -0
- package/src/wsAuth.ts +21 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Betalyra Sociedade Unipessoal Lda.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Config, InworldSynthesizeRequest, InworldSynthesizer, InworldSynthesizerService } from "./InworldSynthesizer.mjs";
|
|
2
|
+
import { Effect, Layer, Stream } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
import { CommonStreamSynthesizeRequest, SpeechSynthesizer, TtsIncrementalText } from "@effect-uai/core/SpeechSynthesizer";
|
|
5
|
+
import * as _$_effect_uai_core_AiError0 from "@effect-uai/core/AiError";
|
|
6
|
+
import * as _$_effect_uai_core_Audio0 from "@effect-uai/core/Audio";
|
|
7
|
+
|
|
8
|
+
//#region src/InworldRealtimeSynthesizer.d.ts
|
|
9
|
+
declare namespace InworldRealtimeSynthesizer_d_exports {
|
|
10
|
+
export { Config, InworldSynthesizeRequest, InworldSynthesizer, InworldSynthesizerService, layer, make };
|
|
11
|
+
}
|
|
12
|
+
declare const make: (cfg: Config) => Effect.Effect<{
|
|
13
|
+
synthesize: (r: InworldSynthesizeRequest) => Effect.Effect<{
|
|
14
|
+
format: _$_effect_uai_core_Audio0.AudioFormat;
|
|
15
|
+
bytes: Uint8Array<ArrayBufferLike>;
|
|
16
|
+
}, _$_effect_uai_core_AiError0.AiError, never>;
|
|
17
|
+
streamSynthesis: (r: InworldSynthesizeRequest) => Stream.Stream<_$_effect_uai_core_Audio0.AudioChunk, _$_effect_uai_core_AiError0.AiError, never>;
|
|
18
|
+
streamSynthesisFrom: <E, R>(textIn: Stream.Stream<string, E, R>, request: CommonStreamSynthesizeRequest) => Stream.Stream<_$_effect_uai_core_Audio0.AudioChunk, _$_effect_uai_core_AiError0.AiError | E, R>;
|
|
19
|
+
}, never, HttpClient.HttpClient>;
|
|
20
|
+
/**
|
|
21
|
+
* Realtime Layer. Registers `InworldSynthesizer`, the generic
|
|
22
|
+
* `SpeechSynthesizer`, **and** the `TtsIncrementalText` capability
|
|
23
|
+
* marker. The WS constructor uses the `ws` peer dep internally to set
|
|
24
|
+
* the `Authorization: Basic` header — no `Socket.WebSocketConstructor`
|
|
25
|
+
* needed at the call site.
|
|
26
|
+
*/
|
|
27
|
+
declare const layer: (cfg: Config) => Layer.Layer<InworldSynthesizer | SpeechSynthesizer | TtsIncrementalText, never, HttpClient.HttpClient>;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { type Config, type InworldSynthesizeRequest, InworldSynthesizer, type InworldSynthesizerService, layer, make, InworldRealtimeSynthesizer_d_exports as t };
|
|
30
|
+
//# sourceMappingURL=InworldRealtimeSynthesizer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldRealtimeSynthesizer.d.mts","names":[],"sources":["../src/InworldRealtimeSynthesizer.ts"],"mappings":";;;;;;;;;;;cAoCa,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;;;;;cAqBnB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,CAAA,kBAAA,GAAA,iBAAA,GAAA,kBAAA,SAAA,UAAA,CAAA,UAAA"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { InworldSynthesizer, streamSynthesisImpl, synthesizeImpl } from "./InworldSynthesizer.mjs";
|
|
3
|
+
import { streamSynthesis } from "./realtimeTts.mjs";
|
|
4
|
+
import { Effect, Layer, Stream } from "effect";
|
|
5
|
+
import { HttpClient } from "effect/unstable/http";
|
|
6
|
+
import { SpeechSynthesizer, TtsIncrementalText } from "@effect-uai/core/SpeechSynthesizer";
|
|
7
|
+
//#region src/InworldRealtimeSynthesizer.ts
|
|
8
|
+
/**
|
|
9
|
+
* Realtime variant of `InworldSynthesizer`. Wires `streamSynthesisFrom` to
|
|
10
|
+
* `wss://api.inworld.ai/tts/v1/voice:streamBidirectional` and registers
|
|
11
|
+
* the `TtsIncrementalText` capability marker, so the call compiles
|
|
12
|
+
* against this Layer alone.
|
|
13
|
+
*
|
|
14
|
+
* Sync `synthesize` and chunked `streamSynthesis` reuse the implementations
|
|
15
|
+
* exported from `./InworldSynthesizer.js` — the only added surface here is
|
|
16
|
+
* the WS path.
|
|
17
|
+
*/
|
|
18
|
+
var InworldRealtimeSynthesizer_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
+
InworldSynthesizer: () => InworldSynthesizer,
|
|
20
|
+
layer: () => layer,
|
|
21
|
+
make: () => make
|
|
22
|
+
});
|
|
23
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
|
|
24
|
+
synthesize: (r) => synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
25
|
+
streamSynthesis: (r) => streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),
|
|
26
|
+
streamSynthesisFrom: (textIn, request) => streamSynthesis(cfg)(textIn, request)
|
|
27
|
+
}));
|
|
28
|
+
/**
|
|
29
|
+
* Realtime Layer. Registers `InworldSynthesizer`, the generic
|
|
30
|
+
* `SpeechSynthesizer`, **and** the `TtsIncrementalText` capability
|
|
31
|
+
* marker. The WS constructor uses the `ws` peer dep internally to set
|
|
32
|
+
* the `Authorization: Basic` header — no `Socket.WebSocketConstructor`
|
|
33
|
+
* needed at the call site.
|
|
34
|
+
*/
|
|
35
|
+
const layer = (cfg) => Layer.mergeAll(Layer.effect(InworldSynthesizer, make(cfg)), Layer.effect(SpeechSynthesizer, Effect.map(make(cfg), (s) => ({
|
|
36
|
+
synthesize: (req) => s.synthesize(req),
|
|
37
|
+
streamSynthesis: (req) => s.streamSynthesis(req),
|
|
38
|
+
streamSynthesisFrom: (textIn, req) => s.streamSynthesisFrom(textIn, req)
|
|
39
|
+
}))), Layer.succeed(TtsIncrementalText, void 0));
|
|
40
|
+
//#endregion
|
|
41
|
+
export { InworldSynthesizer, layer, make, InworldRealtimeSynthesizer_exports as t };
|
|
42
|
+
|
|
43
|
+
//# sourceMappingURL=InworldRealtimeSynthesizer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldRealtimeSynthesizer.mjs","names":["realtimeStream"],"sources":["../src/InworldRealtimeSynthesizer.ts"],"sourcesContent":["/**\n * Realtime variant of `InworldSynthesizer`. Wires `streamSynthesisFrom` to\n * `wss://api.inworld.ai/tts/v1/voice:streamBidirectional` and registers\n * the `TtsIncrementalText` capability marker, so the call compiles\n * against this Layer alone.\n *\n * Sync `synthesize` and chunked `streamSynthesis` reuse the implementations\n * exported from `./InworldSynthesizer.js` — the only added surface here is\n * the WS path.\n */\nimport { Effect, Layer, Stream } from \"effect\"\nimport { HttpClient } from \"effect/unstable/http\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeRequest,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n TtsIncrementalText,\n} from \"@effect-uai/core/SpeechSynthesizer\"\nimport {\n type Config,\n InworldSynthesizer,\n type InworldSynthesizerService,\n type InworldSynthesizeRequest,\n streamSynthesisImpl,\n synthesizeImpl,\n} from \"./InworldSynthesizer.js\"\nimport { streamSynthesis as realtimeStream } from \"./realtimeTts.js\"\n\nexport type {\n Config,\n InworldSynthesizerService,\n InworldSynthesizeRequest,\n} from \"./InworldSynthesizer.js\"\nexport { InworldSynthesizer } from \"./InworldSynthesizer.js\"\n\nexport const make = (cfg: Config) =>\n Effect.map(\n HttpClient.HttpClient.asEffect(),\n (client) =>\n ({\n synthesize: (r) =>\n synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamSynthesis: (r) =>\n streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamSynthesisFrom: (textIn, request) =>\n realtimeStream(cfg)(textIn, request as InworldSynthesizeRequest),\n }) satisfies InworldSynthesizerService,\n )\n\n/**\n * Realtime Layer. Registers `InworldSynthesizer`, the generic\n * `SpeechSynthesizer`, **and** the `TtsIncrementalText` capability\n * marker. The WS constructor uses the `ws` peer dep internally to set\n * the `Authorization: Basic` header — no `Socket.WebSocketConstructor`\n * needed at the call site.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(InworldSynthesizer, make(cfg)),\n Layer.effect(\n SpeechSynthesizer,\n Effect.map(\n make(cfg),\n (s): SpeechSynthesizerService => ({\n synthesize: (req: CommonSynthesizeRequest) =>\n s.synthesize(req as InworldSynthesizeRequest),\n streamSynthesis: (req: CommonSynthesizeRequest) =>\n s.streamSynthesis(req as InworldSynthesizeRequest),\n streamSynthesisFrom: (textIn, req: CommonStreamSynthesizeRequest) =>\n s.streamSynthesisFrom(textIn, req),\n }),\n ),\n ),\n Layer.succeed(TtsIncrementalText, undefined),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAa,QAAQ,QACnB,OAAO,IACL,WAAW,WAAW,UAAU,GAC/B,YACE;CACC,aAAa,MACX,eAAe,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,OAAO,CAAC;CACnF,kBAAkB,MAChB,oBAAoB,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,OAAO,CAAC;CACxF,sBAAsB,QAAQ,YAC5BA,gBAAe,IAAI,CAAC,QAAQ,QAAoC;CACnE,EACJ;;;;;;;;AASH,MAAa,SAAS,QACpB,MAAM,SACJ,MAAM,OAAO,oBAAoB,KAAK,IAAI,CAAC,EAC3C,MAAM,OACJ,mBACA,OAAO,IACL,KAAK,IAAI,GACR,OAAiC;CAChC,aAAa,QACX,EAAE,WAAW,IAAgC;CAC/C,kBAAkB,QAChB,EAAE,gBAAgB,IAAgC;CACpD,sBAAsB,QAAQ,QAC5B,EAAE,oBAAoB,QAAQ,IAAI;CACrC,EACF,CACF,EACD,MAAM,QAAQ,oBAAoB,KAAA,EAAU,CAC7C"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Config, InworldTranscribeRequest, InworldTranscriber, InworldTranscriberService } from "./InworldTranscriber.mjs";
|
|
2
|
+
import { Effect, Layer } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
import * as _$_effect_uai_core_AiError0 from "@effect-uai/core/AiError";
|
|
5
|
+
import * as _$_effect_uai_core_Transcriber0 from "@effect-uai/core/Transcriber";
|
|
6
|
+
import { SttStreaming, Transcriber } from "@effect-uai/core/Transcriber";
|
|
7
|
+
import * as _$effect_Schema0 from "effect/Schema";
|
|
8
|
+
import * as _$_effect_uai_core_Transcript0 from "@effect-uai/core/Transcript";
|
|
9
|
+
import * as _$effect_Stream0 from "effect/Stream";
|
|
10
|
+
|
|
11
|
+
//#region src/InworldRealtimeTranscriber.d.ts
|
|
12
|
+
declare namespace InworldRealtimeTranscriber_d_exports {
|
|
13
|
+
export { Config, InworldTranscribeRequest, InworldTranscriber, InworldTranscriberService, layer, make };
|
|
14
|
+
}
|
|
15
|
+
declare const make: (cfg: Config) => Effect.Effect<{
|
|
16
|
+
transcribe: (r: InworldTranscribeRequest) => Effect.Effect<{
|
|
17
|
+
raw: _$effect_Schema0.Json;
|
|
18
|
+
words?: _$_effect_uai_core_Transcript0.WordTimestamp[];
|
|
19
|
+
text: string;
|
|
20
|
+
}, _$_effect_uai_core_AiError0.AiError, never>;
|
|
21
|
+
streamTranscriptionFrom: <E, R>(audioIn: _$effect_Stream0.Stream<Uint8Array<ArrayBufferLike>, E, R>, request: _$_effect_uai_core_Transcriber0.CommonStreamTranscribeRequest) => _$effect_Stream0.Stream<_$_effect_uai_core_Transcript0.TranscriptEvent, _$_effect_uai_core_AiError0.AiError | E, R>;
|
|
22
|
+
}, never, HttpClient.HttpClient>;
|
|
23
|
+
/**
|
|
24
|
+
* Realtime Layer. Registers `InworldTranscriber`, the generic
|
|
25
|
+
* `Transcriber`, **and** the `SttStreaming` capability marker.
|
|
26
|
+
*/
|
|
27
|
+
declare const layer: (cfg: Config) => Layer.Layer<InworldTranscriber | Transcriber | SttStreaming, never, HttpClient.HttpClient>;
|
|
28
|
+
//#endregion
|
|
29
|
+
export { type Config, type InworldTranscribeRequest, InworldTranscriber, type InworldTranscriberService, layer, make, InworldRealtimeTranscriber_d_exports as t };
|
|
30
|
+
//# sourceMappingURL=InworldRealtimeTranscriber.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldRealtimeTranscriber.d.mts","names":[],"sources":["../src/InworldRealtimeTranscriber.ts"],"mappings":";;;;;;;;;;;;;;cAiCa,IAAA,GAAQ,GAAA,EAAK,MAAA,KAAM,MAAA,CAAA,MAAA;;;;;;;;;;;;cAenB,KAAA,GAAS,GAAA,EAAK,MAAA,KAAM,KAAA,CAAA,KAAA,CAAA,kBAAA,GAAA,WAAA,GAAA,YAAA,SAAA,UAAA,CAAA,UAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { InworldTranscriber, transcribeImpl } from "./InworldTranscriber.mjs";
|
|
3
|
+
import { streamTranscription } from "./realtimeStt.mjs";
|
|
4
|
+
import { Effect, Layer } from "effect";
|
|
5
|
+
import { HttpClient } from "effect/unstable/http";
|
|
6
|
+
import { SttStreaming, Transcriber } from "@effect-uai/core/Transcriber";
|
|
7
|
+
//#region src/InworldRealtimeTranscriber.ts
|
|
8
|
+
/**
|
|
9
|
+
* Realtime variant of `InworldTranscriber`. Wires `streamTranscriptionFrom`
|
|
10
|
+
* to `wss://api.inworld.ai/stt/v1/transcribe:streamBidirectional` and
|
|
11
|
+
* registers the `SttStreaming` capability marker, so calls compile against
|
|
12
|
+
* this Layer alone.
|
|
13
|
+
*
|
|
14
|
+
* Sync `transcribe` reuses `transcribeImpl` from `./InworldTranscriber.js` —
|
|
15
|
+
* the only added surface is the WS path.
|
|
16
|
+
*/
|
|
17
|
+
var InworldRealtimeTranscriber_exports = /* @__PURE__ */ __exportAll({
|
|
18
|
+
InworldTranscriber: () => InworldTranscriber,
|
|
19
|
+
layer: () => layer,
|
|
20
|
+
make: () => make
|
|
21
|
+
});
|
|
22
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
|
|
23
|
+
transcribe: (r) => transcribeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
24
|
+
streamTranscriptionFrom: (audioIn, request) => streamTranscription(cfg)(audioIn, request)
|
|
25
|
+
}));
|
|
26
|
+
/**
|
|
27
|
+
* Realtime Layer. Registers `InworldTranscriber`, the generic
|
|
28
|
+
* `Transcriber`, **and** the `SttStreaming` capability marker.
|
|
29
|
+
*/
|
|
30
|
+
const layer = (cfg) => Layer.mergeAll(Layer.effect(InworldTranscriber, make(cfg)), Layer.effect(Transcriber, Effect.map(make(cfg), (s) => ({
|
|
31
|
+
transcribe: (req) => s.transcribe(req),
|
|
32
|
+
streamTranscriptionFrom: s.streamTranscriptionFrom
|
|
33
|
+
}))), Layer.succeed(SttStreaming, void 0));
|
|
34
|
+
//#endregion
|
|
35
|
+
export { InworldTranscriber, layer, make, InworldRealtimeTranscriber_exports as t };
|
|
36
|
+
|
|
37
|
+
//# sourceMappingURL=InworldRealtimeTranscriber.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldRealtimeTranscriber.mjs","names":[],"sources":["../src/InworldRealtimeTranscriber.ts"],"sourcesContent":["/**\n * Realtime variant of `InworldTranscriber`. Wires `streamTranscriptionFrom`\n * to `wss://api.inworld.ai/stt/v1/transcribe:streamBidirectional` and\n * registers the `SttStreaming` capability marker, so calls compile against\n * this Layer alone.\n *\n * Sync `transcribe` reuses `transcribeImpl` from `./InworldTranscriber.js` —\n * the only added surface is the WS path.\n */\nimport { Effect, Layer } from \"effect\"\nimport { HttpClient } from \"effect/unstable/http\"\nimport {\n type CommonTranscribeRequest,\n SttStreaming,\n Transcriber,\n type TranscriberService,\n} from \"@effect-uai/core/Transcriber\"\nimport {\n type Config,\n InworldTranscriber,\n type InworldTranscriberService,\n type InworldTranscribeRequest,\n transcribeImpl,\n} from \"./InworldTranscriber.js\"\nimport { streamTranscription } from \"./realtimeStt.js\"\n\nexport type {\n Config,\n InworldTranscriberService,\n InworldTranscribeRequest,\n} from \"./InworldTranscriber.js\"\nexport { InworldTranscriber } from \"./InworldTranscriber.js\"\n\nexport const make = (cfg: Config) =>\n Effect.map(\n HttpClient.HttpClient.asEffect(),\n (client) =>\n ({\n transcribe: (r) =>\n transcribeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamTranscriptionFrom: (audioIn, request) => streamTranscription(cfg)(audioIn, request),\n }) satisfies InworldTranscriberService,\n )\n\n/**\n * Realtime Layer. Registers `InworldTranscriber`, the generic\n * `Transcriber`, **and** the `SttStreaming` capability marker.\n */\nexport const layer = (cfg: Config) =>\n Layer.mergeAll(\n Layer.effect(InworldTranscriber, make(cfg)),\n Layer.effect(\n Transcriber,\n Effect.map(\n make(cfg),\n (s): TranscriberService => ({\n transcribe: (req: CommonTranscribeRequest) =>\n s.transcribe(req as InworldTranscribeRequest),\n streamTranscriptionFrom: s.streamTranscriptionFrom,\n }),\n ),\n ),\n Layer.succeed(SttStreaming, undefined),\n )\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAiCA,MAAa,QAAQ,QACnB,OAAO,IACL,WAAW,WAAW,UAAU,GAC/B,YACE;CACC,aAAa,MACX,eAAe,IAAI,CAAC,EAAE,CAAC,KAAK,OAAO,eAAe,WAAW,YAAY,OAAO,CAAC;CACnF,0BAA0B,SAAS,YAAY,oBAAoB,IAAI,CAAC,SAAS,QAAQ;CAC1F,EACJ;;;;;AAMH,MAAa,SAAS,QACpB,MAAM,SACJ,MAAM,OAAO,oBAAoB,KAAK,IAAI,CAAC,EAC3C,MAAM,OACJ,aACA,OAAO,IACL,KAAK,IAAI,GACR,OAA2B;CAC1B,aAAa,QACX,EAAE,WAAW,IAAgC;CAC/C,yBAAyB,EAAE;CAC5B,EACF,CACF,EACD,MAAM,QAAQ,cAAc,KAAA,EAAU,CACvC"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { a as InworldVoiceId, i as InworldTtsModel, n as InworldDeliveryMode } from "./models-CPLZDcgt.mjs";
|
|
2
|
+
import { t as AudioConfig } from "./codec-C-2ssVAz.mjs";
|
|
3
|
+
import { Context, Effect, Layer, Redacted, Stream } from "effect";
|
|
4
|
+
import { HttpClient } from "effect/unstable/http";
|
|
5
|
+
import { CommonSynthesizeRequest, SpeechSynthesizer, SpeechSynthesizerService } from "@effect-uai/core/SpeechSynthesizer";
|
|
6
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
7
|
+
import * as _$_effect_uai_core_Audio0 from "@effect-uai/core/Audio";
|
|
8
|
+
import { AudioBlob, AudioChunk } from "@effect-uai/core/Audio";
|
|
9
|
+
|
|
10
|
+
//#region src/InworldSynthesizer.d.ts
|
|
11
|
+
declare namespace InworldSynthesizer_d_exports {
|
|
12
|
+
export { Config, InworldSynthesizeRequest, InworldSynthesizer, InworldSynthesizerService, buildBody, layer, make, streamSynthesisImpl, synthesizeImpl };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Inworld-typed synthesize request.
|
|
16
|
+
*
|
|
17
|
+
* - `deliveryMode` only honored by `inworld-tts-2` (`STABLE` / `BALANCED` /
|
|
18
|
+
* `CREATIVE` — older models ignore it).
|
|
19
|
+
* - `temperature` valid range is `(0, 2]` per docs.
|
|
20
|
+
* - `applyTextNormalization` defaults to `ON`; pass `"OFF"` to feed the
|
|
21
|
+
* model raw text (faster, but punctuation pacing is on you).
|
|
22
|
+
*/
|
|
23
|
+
type InworldSynthesizeRequest = Omit<CommonSynthesizeRequest, "model" | "voiceId"> & {
|
|
24
|
+
readonly model: InworldTtsModel;
|
|
25
|
+
readonly voiceId: InworldVoiceId;
|
|
26
|
+
readonly temperature?: number;
|
|
27
|
+
readonly deliveryMode?: InworldDeliveryMode;
|
|
28
|
+
readonly applyTextNormalization?: "ON" | "OFF";
|
|
29
|
+
};
|
|
30
|
+
type InworldSynthesizerService = {
|
|
31
|
+
readonly synthesize: (r: InworldSynthesizeRequest) => Effect.Effect<AudioBlob, AiError.AiError>;
|
|
32
|
+
readonly streamSynthesis: (r: InworldSynthesizeRequest) => Stream.Stream<AudioChunk, AiError.AiError>;
|
|
33
|
+
readonly streamSynthesisFrom: SpeechSynthesizerService["streamSynthesisFrom"];
|
|
34
|
+
};
|
|
35
|
+
declare const InworldSynthesizer_base: Context.ServiceClass<InworldSynthesizer, "@betalyra/effect-uai/providers/inworld/InworldSynthesizer", InworldSynthesizerService>;
|
|
36
|
+
declare class InworldSynthesizer extends InworldSynthesizer_base {}
|
|
37
|
+
type Config = {
|
|
38
|
+
readonly apiKey: Redacted.Redacted;
|
|
39
|
+
readonly baseUrl?: string;
|
|
40
|
+
};
|
|
41
|
+
/** Exported for reuse by the realtime variant's BOS `create` frame. */
|
|
42
|
+
declare const buildBody: (r: InworldSynthesizeRequest) => Effect.Effect<{
|
|
43
|
+
body: {
|
|
44
|
+
applyTextNormalization?: "ON" | "OFF";
|
|
45
|
+
temperature?: number;
|
|
46
|
+
deliveryMode?: InworldDeliveryMode;
|
|
47
|
+
language?: string;
|
|
48
|
+
text: string;
|
|
49
|
+
voiceId: string;
|
|
50
|
+
modelId: InworldTtsModel;
|
|
51
|
+
audioConfig: AudioConfig;
|
|
52
|
+
};
|
|
53
|
+
format: _$_effect_uai_core_Audio0.AudioFormat;
|
|
54
|
+
}, AiError.AiError, never>;
|
|
55
|
+
/** Exported for reuse by `InworldRealtimeSynthesizer`. */
|
|
56
|
+
declare const synthesizeImpl: (cfg: Config) => (request: InworldSynthesizeRequest) => Effect.Effect<{
|
|
57
|
+
format: _$_effect_uai_core_Audio0.AudioFormat;
|
|
58
|
+
bytes: Uint8Array<ArrayBufferLike>;
|
|
59
|
+
}, AiError.AiError, HttpClient.HttpClient>;
|
|
60
|
+
/** Exported for reuse by `InworldRealtimeSynthesizer`. */
|
|
61
|
+
declare const streamSynthesisImpl: (cfg: Config) => (request: InworldSynthesizeRequest) => Stream.Stream<AudioChunk, AiError.AiError, HttpClient.HttpClient>;
|
|
62
|
+
declare const make: (cfg: Config) => Effect.Effect<InworldSynthesizerService, never, HttpClient.HttpClient>;
|
|
63
|
+
/**
|
|
64
|
+
* Sync-only Layer. Registers `InworldSynthesizer` + the generic
|
|
65
|
+
* `SpeechSynthesizer`. Does **not** register `TtsIncrementalText` — for
|
|
66
|
+
* incremental text-in use `@effect-uai/inworld/InworldRealtimeSynthesizer`.
|
|
67
|
+
*/
|
|
68
|
+
declare const layer: (cfg: Config) => Layer.Layer<InworldSynthesizer | SpeechSynthesizer, never, HttpClient.HttpClient>;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { Config, InworldSynthesizeRequest, InworldSynthesizer, InworldSynthesizerService, buildBody, layer, make, streamSynthesisImpl, synthesizeImpl, InworldSynthesizer_d_exports as t };
|
|
71
|
+
//# sourceMappingURL=InworldSynthesizer.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldSynthesizer.d.mts","names":[],"sources":["../src/InworldSynthesizer.ts"],"mappings":";;;;;;;;;;;;;;;;;AA8CA;;;;;KAAY,wBAAA,GAA2B,IAAA,CAAK,uBAAA;EAAA,SACjC,KAAA,EAAO,eAAA;EAAA,SACP,OAAA,EAAS,cAAA;EAAA,SACT,WAAA;EAAA,SACA,YAAA,GAAe,mBAAA;EAAA,SACf,sBAAA;AAAA;AAAA,KAGC,yBAAA;EAAA,SACD,UAAA,GAAa,CAAA,EAAG,wBAAA,KAA6B,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,OAAA;EAAA,SAC9E,eAAA,GACP,CAAA,EAAG,wBAAA,KACA,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,OAAA,CAAQ,OAAA;EAAA,SAC9B,mBAAA,EAAqB,wBAAA;AAAA;AAAA,cAC/B,uBAAA;cAEY,kBAAA,SAA2B,uBAAA;AAAA,KAK5B,MAAA;EAAA,SAAoB,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SAAmB,OAAA;AAAA;;cAOvD,SAAA,GAAa,CAAA,EAAG,wBAAA,KAAwB,MAAA,CAAA,MAAA;;;;;;;;;;;;;;cA4CxC,cAAA,GAAkB,GAAA,EAAK,MAAA,MAAY,OAAA,EAAS,wBAAA,KAAwB,MAAA,CAAA,MAAA;UAAA,yBAAA,CAAA,WAAA;;;;cAoCpE,mBAAA,GAAuB,GAAA,EAAK,MAAA,MAAY,OAAA,EAAS,wBAAA,KAAwB,MAAA,CAAA,MAAA,CAAA,UAAA,EAAA,OAAA,CAAA,OAAA,EAAA,UAAA,CAAA,UAAA;AAAA,cA8CzE,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,yBAAA,SAAkC,UAAA,CAAW,UAAA;;;;;;cAcjD,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,kBAAA,GAAqB,iBAAA,SAA0B,UAAA,CAAW,UAAA"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { audioConfigFor, authHeader, decodeAudioContent, defaultFormat, httpStatusError, transportFailure } from "./codec.mjs";
|
|
3
|
+
import { Context, Effect, Layer, Schema, Stream } from "effect";
|
|
4
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
5
|
+
import { SpeechSynthesizer } from "@effect-uai/core/SpeechSynthesizer";
|
|
6
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
7
|
+
import * as JSONL from "@effect-uai/core/JSONL";
|
|
8
|
+
//#region src/InworldSynthesizer.ts
|
|
9
|
+
/**
|
|
10
|
+
* Sync + chunked-HTTP TTS via Inworld's REST endpoints.
|
|
11
|
+
*
|
|
12
|
+
* - `synthesize` → `POST /tts/v1/voice` (JSON body, single base64 audio in
|
|
13
|
+
* response).
|
|
14
|
+
* - `streamSynthesis` → `POST /tts/v1/voice:stream` (NDJSON response, one
|
|
15
|
+
* JSON object per line, each carrying a base64 audio chunk).
|
|
16
|
+
*
|
|
17
|
+
* Incremental text-in (`streamSynthesisFrom`) lives at the
|
|
18
|
+
* `InworldRealtimeSynthesizer` subpath, which adds the WS path and the
|
|
19
|
+
* `TtsIncrementalText` capability marker.
|
|
20
|
+
*/
|
|
21
|
+
var InworldSynthesizer_exports = /* @__PURE__ */ __exportAll({
|
|
22
|
+
InworldSynthesizer: () => InworldSynthesizer,
|
|
23
|
+
buildBody: () => buildBody,
|
|
24
|
+
layer: () => layer,
|
|
25
|
+
make: () => make,
|
|
26
|
+
streamSynthesisImpl: () => streamSynthesisImpl,
|
|
27
|
+
synthesizeImpl: () => synthesizeImpl
|
|
28
|
+
});
|
|
29
|
+
var InworldSynthesizer = class extends Context.Service()("@betalyra/effect-uai/providers/inworld/InworldSynthesizer") {};
|
|
30
|
+
/** Exported for reuse by the realtime variant's BOS `create` frame. */
|
|
31
|
+
const buildBody = (r) => Effect.gen(function* () {
|
|
32
|
+
const format = r.outputFormat ?? defaultFormat;
|
|
33
|
+
const audioConfig = yield* audioConfigFor(format, r.speed);
|
|
34
|
+
return {
|
|
35
|
+
body: {
|
|
36
|
+
text: r.text,
|
|
37
|
+
voiceId: r.voiceId,
|
|
38
|
+
modelId: r.model,
|
|
39
|
+
audioConfig,
|
|
40
|
+
...r.languageCode !== void 0 && { language: r.languageCode },
|
|
41
|
+
...r.deliveryMode !== void 0 && { deliveryMode: r.deliveryMode },
|
|
42
|
+
...r.temperature !== void 0 && { temperature: r.temperature },
|
|
43
|
+
...r.applyTextNormalization !== void 0 && { applyTextNormalization: r.applyTextNormalization }
|
|
44
|
+
},
|
|
45
|
+
format
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
const SyncResponse = Schema.Struct({ audioContent: Schema.String });
|
|
49
|
+
const decodeSyncResponse = Schema.decodeUnknownEffect(SyncResponse);
|
|
50
|
+
const StreamLine = Schema.Struct({
|
|
51
|
+
result: Schema.optional(Schema.Struct({ audioContent: Schema.optional(Schema.String) })),
|
|
52
|
+
error: Schema.optional(Schema.Unknown)
|
|
53
|
+
});
|
|
54
|
+
const baseUrl = (cfg) => cfg.baseUrl ?? "https://api.inworld.ai";
|
|
55
|
+
const buildRequest = (cfg, path, body) => HttpClientRequest.post(`${baseUrl(cfg)}${path}`).pipe(HttpClientRequest.setHeader("Authorization", authHeader(cfg.apiKey)), HttpClientRequest.bodyJsonUnsafe(body));
|
|
56
|
+
/** Exported for reuse by `InworldRealtimeSynthesizer`. */
|
|
57
|
+
const synthesizeImpl = (cfg) => (request) => Effect.gen(function* () {
|
|
58
|
+
const client = yield* HttpClient.HttpClient;
|
|
59
|
+
const { body, format } = yield* buildBody(request);
|
|
60
|
+
const httpRequest = buildRequest(cfg, "/tts/v1/voice", body);
|
|
61
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
|
|
62
|
+
if (response.status >= 400) {
|
|
63
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
64
|
+
return yield* Effect.fail(httpStatusError(response.status, text));
|
|
65
|
+
}
|
|
66
|
+
const json = yield* response.json.pipe(Effect.mapError(transportFailure));
|
|
67
|
+
return {
|
|
68
|
+
format,
|
|
69
|
+
bytes: yield* decodeAudioContent((yield* decodeSyncResponse(json).pipe(Effect.mapError((cause) => new AiError.GenerationFailed({
|
|
70
|
+
provider: "inworld",
|
|
71
|
+
raw: {
|
|
72
|
+
message: "sync TTS response missing `audioContent`",
|
|
73
|
+
cause,
|
|
74
|
+
json
|
|
75
|
+
}
|
|
76
|
+
})))).audioContent)
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
const lineToStream = (line) => {
|
|
80
|
+
if (line.error !== void 0) return Stream.fail(new AiError.GenerationFailed({
|
|
81
|
+
provider: "inworld",
|
|
82
|
+
raw: line.error
|
|
83
|
+
}));
|
|
84
|
+
const b64 = line.result?.audioContent;
|
|
85
|
+
if (b64 === void 0) return Stream.empty;
|
|
86
|
+
return Stream.fromEffect(decodeAudioContent(b64).pipe(Effect.map((bytes) => ({ bytes }))));
|
|
87
|
+
};
|
|
88
|
+
/** Exported for reuse by `InworldRealtimeSynthesizer`. */
|
|
89
|
+
const streamSynthesisImpl = (cfg) => (request) => Stream.unwrap(Effect.gen(function* () {
|
|
90
|
+
const client = yield* HttpClient.HttpClient;
|
|
91
|
+
const { body } = yield* buildBody(request);
|
|
92
|
+
const httpRequest = buildRequest(cfg, "/tts/v1/voice:stream", body);
|
|
93
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
|
|
94
|
+
if (response.status >= 400) {
|
|
95
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
96
|
+
return Stream.fail(httpStatusError(response.status, text));
|
|
97
|
+
}
|
|
98
|
+
return response.stream.pipe(Stream.mapError(transportFailure), JSONL.fromBytes, JSONL.parse(StreamLine), Stream.mapError((cause) => new AiError.GenerationFailed({
|
|
99
|
+
provider: "inworld",
|
|
100
|
+
raw: {
|
|
101
|
+
message: "NDJSON parse failed",
|
|
102
|
+
cause
|
|
103
|
+
}
|
|
104
|
+
})), Stream.flatMap(lineToStream));
|
|
105
|
+
}));
|
|
106
|
+
const streamUnsupported = (_textIn, _request) => {
|
|
107
|
+
return Stream.fail(new AiError.Unsupported({
|
|
108
|
+
provider: "inworld",
|
|
109
|
+
capability: "streamSynthesisFrom",
|
|
110
|
+
reason: "This Layer is sync-only. Import `@effect-uai/inworld/InworldRealtimeSynthesizer` to use the WS streamSynthesisFrom path."
|
|
111
|
+
}));
|
|
112
|
+
};
|
|
113
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
|
|
114
|
+
synthesize: (r) => synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
115
|
+
streamSynthesis: (r) => streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),
|
|
116
|
+
streamSynthesisFrom: streamUnsupported
|
|
117
|
+
}));
|
|
118
|
+
/**
|
|
119
|
+
* Sync-only Layer. Registers `InworldSynthesizer` + the generic
|
|
120
|
+
* `SpeechSynthesizer`. Does **not** register `TtsIncrementalText` — for
|
|
121
|
+
* incremental text-in use `@effect-uai/inworld/InworldRealtimeSynthesizer`.
|
|
122
|
+
*/
|
|
123
|
+
const layer = (cfg) => Layer.merge(Layer.effect(InworldSynthesizer, make(cfg)), Layer.effect(SpeechSynthesizer, Effect.map(make(cfg), (s) => ({
|
|
124
|
+
synthesize: (req) => s.synthesize(req),
|
|
125
|
+
streamSynthesis: (req) => s.streamSynthesis(req),
|
|
126
|
+
streamSynthesisFrom: s.streamSynthesisFrom
|
|
127
|
+
}))));
|
|
128
|
+
//#endregion
|
|
129
|
+
export { InworldSynthesizer, buildBody, layer, make, streamSynthesisImpl, synthesizeImpl, InworldSynthesizer_exports as t };
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=InworldSynthesizer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldSynthesizer.mjs","names":[],"sources":["../src/InworldSynthesizer.ts"],"sourcesContent":["/**\n * Sync + chunked-HTTP TTS via Inworld's REST endpoints.\n *\n * - `synthesize` → `POST /tts/v1/voice` (JSON body, single base64 audio in\n * response).\n * - `streamSynthesis` → `POST /tts/v1/voice:stream` (NDJSON response, one\n * JSON object per line, each carrying a base64 audio chunk).\n *\n * Incremental text-in (`streamSynthesisFrom`) lives at the\n * `InworldRealtimeSynthesizer` subpath, which adds the WS path and the\n * `TtsIncrementalText` capability marker.\n */\nimport { Context, Effect, Layer, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioBlob, AudioChunk } from \"@effect-uai/core/Audio\"\nimport * as JSONL from \"@effect-uai/core/JSONL\"\nimport {\n type CommonStreamSynthesizeRequest,\n type CommonSynthesizeRequest,\n SpeechSynthesizer,\n type SpeechSynthesizerService,\n} from \"@effect-uai/core/SpeechSynthesizer\"\nimport {\n audioConfigFor,\n authHeader,\n decodeAudioContent,\n defaultFormat,\n httpStatusError,\n transportFailure,\n} from \"./codec.js\"\nimport type { InworldDeliveryMode, InworldTtsModel, InworldVoiceId } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Inworld-typed synthesize request.\n *\n * - `deliveryMode` only honored by `inworld-tts-2` (`STABLE` / `BALANCED` /\n * `CREATIVE` — older models ignore it).\n * - `temperature` valid range is `(0, 2]` per docs.\n * - `applyTextNormalization` defaults to `ON`; pass `\"OFF\"` to feed the\n * model raw text (faster, but punctuation pacing is on you).\n */\nexport type InworldSynthesizeRequest = Omit<CommonSynthesizeRequest, \"model\" | \"voiceId\"> & {\n readonly model: InworldTtsModel\n readonly voiceId: InworldVoiceId\n readonly temperature?: number\n readonly deliveryMode?: InworldDeliveryMode\n readonly applyTextNormalization?: \"ON\" | \"OFF\"\n}\n\nexport type InworldSynthesizerService = {\n readonly synthesize: (r: InworldSynthesizeRequest) => Effect.Effect<AudioBlob, AiError.AiError>\n readonly streamSynthesis: (\n r: InworldSynthesizeRequest,\n ) => Stream.Stream<AudioChunk, AiError.AiError>\n readonly streamSynthesisFrom: SpeechSynthesizerService[\"streamSynthesisFrom\"]\n}\n\nexport class InworldSynthesizer extends Context.Service<\n InworldSynthesizer,\n InworldSynthesizerService\n>()(\"@betalyra/effect-uai/providers/inworld/InworldSynthesizer\") {}\n\nexport type Config = { readonly apiKey: Redacted.Redacted; readonly baseUrl?: string }\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\n/** Exported for reuse by the realtime variant's BOS `create` frame. */\nexport const buildBody = (r: InworldSynthesizeRequest) =>\n Effect.gen(function* () {\n const format = r.outputFormat ?? defaultFormat\n const audioConfig = yield* audioConfigFor(format, r.speed)\n const body = {\n text: r.text,\n voiceId: r.voiceId,\n modelId: r.model,\n audioConfig,\n ...(r.languageCode !== undefined && { language: r.languageCode }),\n ...(r.deliveryMode !== undefined && { deliveryMode: r.deliveryMode }),\n ...(r.temperature !== undefined && { temperature: r.temperature }),\n ...(r.applyTextNormalization !== undefined && {\n applyTextNormalization: r.applyTextNormalization,\n }),\n }\n return { body, format }\n })\n\n// ---------------------------------------------------------------------------\n// Wire schemas\n// ---------------------------------------------------------------------------\n\nconst SyncResponse = Schema.Struct({ audioContent: Schema.String })\nconst decodeSyncResponse = Schema.decodeUnknownEffect(SyncResponse)\n\nconst StreamLine = Schema.Struct({\n result: Schema.optional(Schema.Struct({ audioContent: Schema.optional(Schema.String) })),\n error: Schema.optional(Schema.Unknown),\n})\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config) => cfg.baseUrl ?? \"https://api.inworld.ai\"\n\nconst buildRequest = (cfg: Config, path: \"/tts/v1/voice\" | \"/tts/v1/voice:stream\", body: unknown) =>\n HttpClientRequest.post(`${baseUrl(cfg)}${path}`).pipe(\n HttpClientRequest.setHeader(\"Authorization\", authHeader(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n\n/** Exported for reuse by `InworldRealtimeSynthesizer`. */\nexport const synthesizeImpl = (cfg: Config) => (request: InworldSynthesizeRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { body, format } = yield* buildBody(request)\n const httpRequest = buildRequest(cfg, \"/tts/v1/voice\", body)\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeSyncResponse(json).pipe(\n Effect.mapError(\n (cause) =>\n new AiError.GenerationFailed({\n provider: \"inworld\",\n raw: { message: \"sync TTS response missing `audioContent`\", cause, json },\n }),\n ),\n )\n const bytes = yield* decodeAudioContent(wire.audioContent)\n return { format, bytes } satisfies AudioBlob\n })\n\nconst lineToStream = (line: typeof StreamLine.Type): Stream.Stream<AudioChunk, AiError.AiError> => {\n if (line.error !== undefined) {\n return Stream.fail(new AiError.GenerationFailed({ provider: \"inworld\", raw: line.error }))\n }\n const b64 = line.result?.audioContent\n if (b64 === undefined) return Stream.empty\n return Stream.fromEffect(\n decodeAudioContent(b64).pipe(Effect.map((bytes): AudioChunk => ({ bytes }))),\n )\n}\n\n/** Exported for reuse by `InworldRealtimeSynthesizer`. */\nexport const streamSynthesisImpl = (cfg: Config) => (request: InworldSynthesizeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const { body } = yield* buildBody(request)\n const httpRequest = buildRequest(cfg, \"/tts/v1/voice:stream\", body)\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return Stream.fail(httpStatusError(response.status, text))\n }\n return response.stream.pipe(\n Stream.mapError(transportFailure),\n JSONL.fromBytes,\n JSONL.parse(StreamLine),\n Stream.mapError(\n (cause): AiError.AiError =>\n new AiError.GenerationFailed({\n provider: \"inworld\",\n raw: { message: \"NDJSON parse failed\", cause },\n }),\n ),\n Stream.flatMap(lineToStream),\n )\n }),\n )\n\nconst streamUnsupported = <E, R>(\n _textIn: Stream.Stream<string, E, R>,\n _request: CommonStreamSynthesizeRequest,\n): Stream.Stream<AudioChunk, AiError.AiError | E, R> => {\n const fail: Stream.Stream<AudioChunk, AiError.AiError | E, R> = Stream.fail(\n new AiError.Unsupported({\n provider: \"inworld\",\n capability: \"streamSynthesisFrom\",\n reason:\n \"This Layer is sync-only. Import `@effect-uai/inworld/InworldRealtimeSynthesizer` to use the WS streamSynthesisFrom path.\",\n }),\n )\n return fail\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<InworldSynthesizerService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({\n synthesize: (r) =>\n synthesizeImpl(cfg)(r).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamSynthesis: (r) =>\n streamSynthesisImpl(cfg)(r).pipe(Stream.provideService(HttpClient.HttpClient, client)),\n streamSynthesisFrom: streamUnsupported,\n }))\n\n/**\n * Sync-only Layer. Registers `InworldSynthesizer` + the generic\n * `SpeechSynthesizer`. Does **not** register `TtsIncrementalText` — for\n * incremental text-in use `@effect-uai/inworld/InworldRealtimeSynthesizer`.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<InworldSynthesizer | SpeechSynthesizer, never, HttpClient.HttpClient> =>\n Layer.merge(\n Layer.effect(InworldSynthesizer, make(cfg)),\n Layer.effect(\n SpeechSynthesizer,\n Effect.map(\n make(cfg),\n (s): SpeechSynthesizerService => ({\n synthesize: (req: CommonSynthesizeRequest) =>\n s.synthesize(req as InworldSynthesizeRequest),\n streamSynthesis: (req: CommonSynthesizeRequest) =>\n s.streamSynthesis(req as InworldSynthesizeRequest),\n streamSynthesisFrom: s.streamSynthesisFrom,\n }),\n ),\n ),\n )\n"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { r as InworldSttModel } from "./models-CPLZDcgt.mjs";
|
|
2
|
+
import { Context, Effect, Layer, Redacted, Schema } from "effect";
|
|
3
|
+
import { HttpClient } from "effect/unstable/http";
|
|
4
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
5
|
+
import { CommonTranscribeRequest, Transcriber, TranscriberService } from "@effect-uai/core/Transcriber";
|
|
6
|
+
import { TranscriptResult, WordTimestamp } from "@effect-uai/core/Transcript";
|
|
7
|
+
|
|
8
|
+
//#region src/InworldTranscriber.d.ts
|
|
9
|
+
declare namespace InworldTranscriber_d_exports {
|
|
10
|
+
export { Config, InworldTranscribeRequest, InworldTranscriber, InworldTranscriberService, layer, make, transcribeImpl };
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inworld-typed transcribe request. `numberOfChannels` defaults to 1.
|
|
14
|
+
* `audioEncoding` is autodetected from the audio MIME by default; pass
|
|
15
|
+
* an explicit value to override (useful for raw PCM bytes).
|
|
16
|
+
*/
|
|
17
|
+
type InworldTranscribeRequest = Omit<CommonTranscribeRequest, "model"> & {
|
|
18
|
+
readonly model: InworldSttModel;
|
|
19
|
+
readonly audioEncoding?: "AUTO_DETECT" | "LINEAR16" | "MP3" | "OGG_OPUS" | "FLAC";
|
|
20
|
+
readonly sampleRateHertz?: number;
|
|
21
|
+
readonly numberOfChannels?: number;
|
|
22
|
+
};
|
|
23
|
+
type InworldTranscriberService = {
|
|
24
|
+
readonly transcribe: (r: InworldTranscribeRequest) => Effect.Effect<TranscriptResult, AiError.AiError>;
|
|
25
|
+
readonly streamTranscriptionFrom: TranscriberService["streamTranscriptionFrom"];
|
|
26
|
+
};
|
|
27
|
+
declare const InworldTranscriber_base: Context.ServiceClass<InworldTranscriber, "@betalyra/effect-uai/providers/inworld/InworldTranscriber", InworldTranscriberService>;
|
|
28
|
+
declare class InworldTranscriber extends InworldTranscriber_base {}
|
|
29
|
+
type Config = {
|
|
30
|
+
readonly apiKey: Redacted.Redacted;
|
|
31
|
+
readonly baseUrl?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Exported for reuse by `InworldRealtimeTranscriber`. */
|
|
34
|
+
declare const transcribeImpl: (cfg: Config) => (request: InworldTranscribeRequest) => Effect.Effect<{
|
|
35
|
+
raw: Schema.Json;
|
|
36
|
+
words?: WordTimestamp[];
|
|
37
|
+
text: string;
|
|
38
|
+
}, AiError.AiError, HttpClient.HttpClient>;
|
|
39
|
+
declare const make: (cfg: Config) => Effect.Effect<InworldTranscriberService, never, HttpClient.HttpClient>;
|
|
40
|
+
/**
|
|
41
|
+
* Sync-only Layer. Registers `InworldTranscriber` + the generic
|
|
42
|
+
* `Transcriber`. Does **not** register `SttStreaming` — for live
|
|
43
|
+
* transcription use `@effect-uai/inworld/InworldRealtimeTranscriber`.
|
|
44
|
+
*/
|
|
45
|
+
declare const layer: (cfg: Config) => Layer.Layer<InworldTranscriber | Transcriber, never, HttpClient.HttpClient>;
|
|
46
|
+
//#endregion
|
|
47
|
+
export { Config, InworldTranscribeRequest, InworldTranscriber, InworldTranscriberService, layer, make, InworldTranscriber_d_exports as t, transcribeImpl };
|
|
48
|
+
//# sourceMappingURL=InworldTranscriber.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldTranscriber.d.mts","names":[],"sources":["../src/InworldTranscriber.ts"],"mappings":";;;;;;;;;;;;;AAiCA;;;KAAY,wBAAA,GAA2B,IAAA,CAAK,uBAAA;EAAA,SACjC,KAAA,EAAO,eAAA;EAAA,SACP,aAAA;EAAA,SACA,eAAA;EAAA,SACA,gBAAA;AAAA;AAAA,KAGC,yBAAA;EAAA,SACD,UAAA,GACP,CAAA,EAAG,wBAAA,KACA,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,CAAQ,OAAA;EAAA,SACpC,uBAAA,EAAyB,kBAAA;AAAA;AAAA,cACnC,uBAAA;cAEY,kBAAA,SAA2B,uBAAA;AAAA,KAK5B,MAAA;EAAA,SAAoB,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SAAmB,OAAA;AAAA;;cAsFvD,cAAA,GAAkB,GAAA,EAAK,MAAA,MAAY,OAAA,EAAS,wBAAA,KAAwB,MAAA,CAAA,MAAA;;;;;cAwDpE,IAAA,GACX,GAAA,EAAK,MAAA,KACJ,MAAA,CAAO,MAAA,CAAO,yBAAA,SAAkC,UAAA,CAAW,UAAA;;;;;;cAYjD,KAAA,GACX,GAAA,EAAK,MAAA,KACJ,KAAA,CAAM,KAAA,CAAM,kBAAA,GAAqB,WAAA,SAAoB,UAAA,CAAW,UAAA"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-CfYAbeIz.mjs";
|
|
2
|
+
import { authHeader, httpStatusError, transportFailure } from "./codec.mjs";
|
|
3
|
+
import { Context, Effect, Encoding, Layer, Match, Schema, Stream } from "effect";
|
|
4
|
+
import { HttpClient, HttpClientRequest } from "effect/unstable/http";
|
|
5
|
+
import * as AiError from "@effect-uai/core/AiError";
|
|
6
|
+
import { Transcriber } from "@effect-uai/core/Transcriber";
|
|
7
|
+
//#region src/InworldTranscriber.ts
|
|
8
|
+
/**
|
|
9
|
+
* Sync transcription via Inworld's REST endpoint.
|
|
10
|
+
*
|
|
11
|
+
* - `transcribe` → `POST /stt/v1/transcribe` with audio as base64 in
|
|
12
|
+
* `audioData.content`. URL input is not accepted by Inworld — the
|
|
13
|
+
* adapter rejects `_tag: "url"` audio sources with `InvalidRequest`.
|
|
14
|
+
*
|
|
15
|
+
* Streaming transcription lives at the `InworldRealtimeTranscriber`
|
|
16
|
+
* subpath, which adds the WS path and the `SttStreaming` capability marker.
|
|
17
|
+
*/
|
|
18
|
+
var InworldTranscriber_exports = /* @__PURE__ */ __exportAll({
|
|
19
|
+
InworldTranscriber: () => InworldTranscriber,
|
|
20
|
+
layer: () => layer,
|
|
21
|
+
make: () => make,
|
|
22
|
+
transcribeImpl: () => transcribeImpl
|
|
23
|
+
});
|
|
24
|
+
var InworldTranscriber = class extends Context.Service()("@betalyra/effect-uai/providers/inworld/InworldTranscriber") {};
|
|
25
|
+
const urlNotSupported = new AiError.InvalidRequest({
|
|
26
|
+
provider: "inworld",
|
|
27
|
+
param: "audio",
|
|
28
|
+
raw: "Inworld accepts only inline base64. Fetch the URL yourself and pass `{ _tag: \"bytes\", bytes, mimeType }`."
|
|
29
|
+
});
|
|
30
|
+
const audioToBase64 = Match.type().pipe(Match.tag("bytes", (a) => Effect.succeed(Encoding.encodeBase64(a.bytes))), Match.tag("base64", (a) => Effect.succeed(a.base64)), Match.tag("url", () => Effect.fail(urlNotSupported)), Match.exhaustive);
|
|
31
|
+
const promptToTerms = (prompt) => prompt === void 0 ? void 0 : typeof prompt === "string" ? [prompt] : prompt.terms;
|
|
32
|
+
const buildBody = (request) => Effect.gen(function* () {
|
|
33
|
+
const content = yield* audioToBase64(request.audio);
|
|
34
|
+
const prompts = promptToTerms(request.prompt);
|
|
35
|
+
return {
|
|
36
|
+
transcribeConfig: {
|
|
37
|
+
modelId: request.model,
|
|
38
|
+
audioEncoding: request.audioEncoding ?? "AUTO_DETECT",
|
|
39
|
+
...request.sampleRateHertz !== void 0 && { sampleRateHertz: request.sampleRateHertz },
|
|
40
|
+
numberOfChannels: request.numberOfChannels ?? 1,
|
|
41
|
+
...request.language !== void 0 && { language: request.language },
|
|
42
|
+
...prompts !== void 0 && { prompts },
|
|
43
|
+
...request.wordTimestamps === true && { includeWordTimestamps: true }
|
|
44
|
+
},
|
|
45
|
+
audioData: { content }
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
const WireWord = Schema.Struct({
|
|
49
|
+
word: Schema.String,
|
|
50
|
+
startTimeMs: Schema.optional(Schema.Number),
|
|
51
|
+
endTimeMs: Schema.optional(Schema.Number),
|
|
52
|
+
confidence: Schema.optional(Schema.Number)
|
|
53
|
+
});
|
|
54
|
+
const WireResponse = Schema.Struct({ transcription: Schema.Struct({
|
|
55
|
+
transcript: Schema.String,
|
|
56
|
+
isFinal: Schema.optional(Schema.Boolean),
|
|
57
|
+
wordTimestamps: Schema.optional(Schema.NullOr(Schema.Array(WireWord)))
|
|
58
|
+
}) });
|
|
59
|
+
const decodeResponse = Schema.decodeUnknownEffect(WireResponse);
|
|
60
|
+
const wireWordToCommon = (w) => w.startTimeMs === void 0 || w.endTimeMs === void 0 ? void 0 : {
|
|
61
|
+
text: w.word,
|
|
62
|
+
startSeconds: w.startTimeMs / 1e3,
|
|
63
|
+
endSeconds: w.endTimeMs / 1e3,
|
|
64
|
+
...w.confidence !== void 0 && { confidence: w.confidence }
|
|
65
|
+
};
|
|
66
|
+
const baseUrl = (cfg) => cfg.baseUrl ?? "https://api.inworld.ai";
|
|
67
|
+
/** Exported for reuse by `InworldRealtimeTranscriber`. */
|
|
68
|
+
const transcribeImpl = (cfg) => (request) => Effect.gen(function* () {
|
|
69
|
+
const client = yield* HttpClient.HttpClient;
|
|
70
|
+
const body = yield* buildBody(request);
|
|
71
|
+
const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/stt/v1/transcribe`).pipe(HttpClientRequest.setHeader("Authorization", authHeader(cfg.apiKey)), HttpClientRequest.bodyJsonUnsafe(body));
|
|
72
|
+
const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure));
|
|
73
|
+
if (response.status >= 400) {
|
|
74
|
+
const text = yield* response.text.pipe(Effect.orElseSucceed(() => ""));
|
|
75
|
+
return yield* Effect.fail(httpStatusError(response.status, text));
|
|
76
|
+
}
|
|
77
|
+
const json = yield* response.json.pipe(Effect.mapError(transportFailure));
|
|
78
|
+
const wire = yield* decodeResponse(json).pipe(Effect.mapError((cause) => new AiError.GenerationFailed({
|
|
79
|
+
provider: "inworld",
|
|
80
|
+
raw: {
|
|
81
|
+
message: "STT response missing `transcription.transcript`",
|
|
82
|
+
cause,
|
|
83
|
+
json
|
|
84
|
+
}
|
|
85
|
+
})));
|
|
86
|
+
const words = wire.transcription.wordTimestamps?.map(wireWordToCommon).filter((w) => w !== void 0);
|
|
87
|
+
return {
|
|
88
|
+
text: wire.transcription.transcript,
|
|
89
|
+
...words !== void 0 && words.length > 0 && { words },
|
|
90
|
+
raw: json
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
const streamUnsupported = (_audioIn, _request) => {
|
|
94
|
+
return Stream.fail(new AiError.Unsupported({
|
|
95
|
+
provider: "inworld",
|
|
96
|
+
capability: "streamTranscriptionFrom",
|
|
97
|
+
reason: "This Layer is sync-only. Import `@effect-uai/inworld/InworldRealtimeTranscriber` to use the WS streamTranscriptionFrom path."
|
|
98
|
+
}));
|
|
99
|
+
};
|
|
100
|
+
const make = (cfg) => Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({
|
|
101
|
+
transcribe: (request) => transcribeImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),
|
|
102
|
+
streamTranscriptionFrom: streamUnsupported
|
|
103
|
+
}));
|
|
104
|
+
/**
|
|
105
|
+
* Sync-only Layer. Registers `InworldTranscriber` + the generic
|
|
106
|
+
* `Transcriber`. Does **not** register `SttStreaming` — for live
|
|
107
|
+
* transcription use `@effect-uai/inworld/InworldRealtimeTranscriber`.
|
|
108
|
+
*/
|
|
109
|
+
const layer = (cfg) => Layer.merge(Layer.effect(InworldTranscriber, make(cfg)), Layer.effect(Transcriber, Effect.map(make(cfg), (s) => ({
|
|
110
|
+
transcribe: (req) => s.transcribe(req),
|
|
111
|
+
streamTranscriptionFrom: s.streamTranscriptionFrom
|
|
112
|
+
}))));
|
|
113
|
+
//#endregion
|
|
114
|
+
export { InworldTranscriber, layer, make, InworldTranscriber_exports as t, transcribeImpl };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=InworldTranscriber.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InworldTranscriber.mjs","names":[],"sources":["../src/InworldTranscriber.ts"],"sourcesContent":["/**\n * Sync transcription via Inworld's REST endpoint.\n *\n * - `transcribe` → `POST /stt/v1/transcribe` with audio as base64 in\n * `audioData.content`. URL input is not accepted by Inworld — the\n * adapter rejects `_tag: \"url\"` audio sources with `InvalidRequest`.\n *\n * Streaming transcription lives at the `InworldRealtimeTranscriber`\n * subpath, which adds the WS path and the `SttStreaming` capability marker.\n */\nimport { Context, Effect, Encoding, Layer, Match, Redacted, Schema, Stream } from \"effect\"\nimport { HttpClient, HttpClientRequest } from \"effect/unstable/http\"\nimport * as AiError from \"@effect-uai/core/AiError\"\nimport type { AudioSource } from \"@effect-uai/core/Audio\"\nimport type { TranscriptEvent, TranscriptResult, WordTimestamp } from \"@effect-uai/core/Transcript\"\nimport {\n type CommonStreamTranscribeRequest,\n type CommonTranscribeRequest,\n Transcriber,\n type TranscriberService,\n} from \"@effect-uai/core/Transcriber\"\nimport { authHeader, httpStatusError, transportFailure } from \"./codec.js\"\nimport type { InworldSttModel } from \"./models.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\n/**\n * Inworld-typed transcribe request. `numberOfChannels` defaults to 1.\n * `audioEncoding` is autodetected from the audio MIME by default; pass\n * an explicit value to override (useful for raw PCM bytes).\n */\nexport type InworldTranscribeRequest = Omit<CommonTranscribeRequest, \"model\"> & {\n readonly model: InworldSttModel\n readonly audioEncoding?: \"AUTO_DETECT\" | \"LINEAR16\" | \"MP3\" | \"OGG_OPUS\" | \"FLAC\"\n readonly sampleRateHertz?: number\n readonly numberOfChannels?: number\n}\n\nexport type InworldTranscriberService = {\n readonly transcribe: (\n r: InworldTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n readonly streamTranscriptionFrom: TranscriberService[\"streamTranscriptionFrom\"]\n}\n\nexport class InworldTranscriber extends Context.Service<\n InworldTranscriber,\n InworldTranscriberService\n>()(\"@betalyra/effect-uai/providers/inworld/InworldTranscriber\") {}\n\nexport type Config = { readonly apiKey: Redacted.Redacted; readonly baseUrl?: string }\n\n// ---------------------------------------------------------------------------\n// AudioSource → base64. Inworld's sync endpoint takes inline base64 only.\n// ---------------------------------------------------------------------------\n\nconst urlNotSupported: AiError.AiError = new AiError.InvalidRequest({\n provider: \"inworld\",\n param: \"audio\",\n raw: 'Inworld accepts only inline base64. Fetch the URL yourself and pass `{ _tag: \"bytes\", bytes, mimeType }`.',\n})\n\nconst audioToBase64: (audio: AudioSource) => Effect.Effect<string, AiError.AiError> =\n Match.type<AudioSource>().pipe(\n Match.tag(\"bytes\", (a) => Effect.succeed(Encoding.encodeBase64(a.bytes))),\n Match.tag(\"base64\", (a) => Effect.succeed(a.base64)),\n Match.tag(\"url\", () => Effect.fail(urlNotSupported)),\n Match.exhaustive,\n )\n\n// ---------------------------------------------------------------------------\n// Request body\n// ---------------------------------------------------------------------------\n\nconst promptToTerms = (\n prompt: string | { readonly terms: ReadonlyArray<string> } | undefined,\n): ReadonlyArray<string> | undefined =>\n prompt === undefined ? undefined : typeof prompt === \"string\" ? [prompt] : prompt.terms\n\nconst buildBody = (request: InworldTranscribeRequest) =>\n Effect.gen(function* () {\n const content = yield* audioToBase64(request.audio)\n const prompts = promptToTerms(request.prompt)\n return {\n transcribeConfig: {\n modelId: request.model,\n audioEncoding: request.audioEncoding ?? \"AUTO_DETECT\",\n ...(request.sampleRateHertz !== undefined && {\n sampleRateHertz: request.sampleRateHertz,\n }),\n numberOfChannels: request.numberOfChannels ?? 1,\n ...(request.language !== undefined && { language: request.language }),\n ...(prompts !== undefined && { prompts }),\n ...(request.wordTimestamps === true && { includeWordTimestamps: true }),\n },\n audioData: { content },\n }\n })\n\n// ---------------------------------------------------------------------------\n// Wire schema\n// ---------------------------------------------------------------------------\n\nconst WireWord = Schema.Struct({\n word: Schema.String,\n startTimeMs: Schema.optional(Schema.Number),\n endTimeMs: Schema.optional(Schema.Number),\n confidence: Schema.optional(Schema.Number),\n})\n\nconst WireResponse = Schema.Struct({\n transcription: Schema.Struct({\n transcript: Schema.String,\n isFinal: Schema.optional(Schema.Boolean),\n wordTimestamps: Schema.optional(Schema.NullOr(Schema.Array(WireWord))),\n }),\n})\nconst decodeResponse = Schema.decodeUnknownEffect(WireResponse)\n\nconst wireWordToCommon = (w: typeof WireWord.Type): WordTimestamp | undefined =>\n w.startTimeMs === undefined || w.endTimeMs === undefined\n ? undefined\n : {\n text: w.word,\n startSeconds: w.startTimeMs / 1000,\n endSeconds: w.endTimeMs / 1000,\n ...(w.confidence !== undefined && { confidence: w.confidence }),\n }\n\n// ---------------------------------------------------------------------------\n// HTTP plumbing\n// ---------------------------------------------------------------------------\n\nconst baseUrl = (cfg: Config) => cfg.baseUrl ?? \"https://api.inworld.ai\"\n\n/** Exported for reuse by `InworldRealtimeTranscriber`. */\nexport const transcribeImpl = (cfg: Config) => (request: InworldTranscribeRequest) =>\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n const body = yield* buildBody(request)\n const httpRequest = HttpClientRequest.post(`${baseUrl(cfg)}/stt/v1/transcribe`).pipe(\n HttpClientRequest.setHeader(\"Authorization\", authHeader(cfg.apiKey)),\n HttpClientRequest.bodyJsonUnsafe(body),\n )\n const response = yield* client.execute(httpRequest).pipe(Effect.mapError(transportFailure))\n if (response.status >= 400) {\n const text = yield* response.text.pipe(Effect.orElseSucceed(() => \"\"))\n return yield* Effect.fail(httpStatusError(response.status, text))\n }\n const json = yield* response.json.pipe(Effect.mapError(transportFailure))\n const wire = yield* decodeResponse(json).pipe(\n Effect.mapError(\n (cause) =>\n new AiError.GenerationFailed({\n provider: \"inworld\",\n raw: { message: \"STT response missing `transcription.transcript`\", cause, json },\n }),\n ),\n )\n const words = wire.transcription.wordTimestamps\n ?.map(wireWordToCommon)\n .filter((w): w is WordTimestamp => w !== undefined)\n return {\n text: wire.transcription.transcript,\n ...(words !== undefined && words.length > 0 && { words }),\n raw: json,\n } satisfies TranscriptResult\n })\n\n// ---------------------------------------------------------------------------\n// streamTranscriptionFrom Unsupported on this Layer\n// ---------------------------------------------------------------------------\n\nconst streamUnsupported = <E, R>(\n _audioIn: Stream.Stream<Uint8Array, E, R>,\n _request: CommonStreamTranscribeRequest,\n): Stream.Stream<TranscriptEvent, AiError.AiError | E, R> => {\n const fail: Stream.Stream<TranscriptEvent, AiError.AiError | E, R> = Stream.fail(\n new AiError.Unsupported({\n provider: \"inworld\",\n capability: \"streamTranscriptionFrom\",\n reason:\n \"This Layer is sync-only. Import `@effect-uai/inworld/InworldRealtimeTranscriber` to use the WS streamTranscriptionFrom path.\",\n }),\n )\n return fail\n}\n\n// ---------------------------------------------------------------------------\n// Constructors\n// ---------------------------------------------------------------------------\n\nexport const make = (\n cfg: Config,\n): Effect.Effect<InworldTranscriberService, never, HttpClient.HttpClient> =>\n Effect.map(HttpClient.HttpClient.asEffect(), (client) => ({\n transcribe: (request) =>\n transcribeImpl(cfg)(request).pipe(Effect.provideService(HttpClient.HttpClient, client)),\n streamTranscriptionFrom: streamUnsupported,\n }))\n\n/**\n * Sync-only Layer. Registers `InworldTranscriber` + the generic\n * `Transcriber`. Does **not** register `SttStreaming` — for live\n * transcription use `@effect-uai/inworld/InworldRealtimeTranscriber`.\n */\nexport const layer = (\n cfg: Config,\n): Layer.Layer<InworldTranscriber | Transcriber, never, HttpClient.HttpClient> =>\n Layer.merge(\n Layer.effect(InworldTranscriber, make(cfg)),\n Layer.effect(\n Transcriber,\n Effect.map(\n make(cfg),\n (s): TranscriberService => ({\n transcribe: (req: CommonTranscribeRequest) =>\n s.transcribe(req as InworldTranscribeRequest),\n streamTranscriptionFrom: s.streamTranscriptionFrom,\n }),\n ),\n ),\n )\n"],"mappings":"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"}
|