@effect-uai/core 0.2.0 → 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/README.md +1 -1
- package/dist/{AiError-CqmYjXyx.d.mts → AiError-csR8Bhxx.d.mts} +26 -4
- package/dist/{AiError-CqmYjXyx.d.mts.map → AiError-csR8Bhxx.d.mts.map} +1 -1
- package/dist/Audio-BfCTGnH3.d.mts +61 -0
- package/dist/Audio-BfCTGnH3.d.mts.map +1 -0
- package/dist/Image-DxyXqzAM.d.mts +61 -0
- package/dist/Image-DxyXqzAM.d.mts.map +1 -0
- package/dist/{Items-D1C2686t.d.mts → Items-Hg5AsYxl.d.mts} +132 -80
- package/dist/Items-Hg5AsYxl.d.mts.map +1 -0
- package/dist/Media-D_CpcM1Z.d.mts +57 -0
- package/dist/Media-D_CpcM1Z.d.mts.map +1 -0
- package/dist/{StructuredFormat-B5ueioNr.d.mts → StructuredFormat-Cl41C56K.d.mts} +5 -5
- package/dist/StructuredFormat-Cl41C56K.d.mts.map +1 -0
- package/dist/{Tool-5wxOCuOh.d.mts → Tool-B8B5qVEy.d.mts} +13 -13
- package/dist/Tool-B8B5qVEy.d.mts.map +1 -0
- package/dist/{Turn-Bi83du4I.d.mts → Turn-7geUcKsf.d.mts} +5 -11
- package/dist/Turn-7geUcKsf.d.mts.map +1 -0
- package/dist/{chunk-CfYAbeIz.mjs → chunk-uyGKjUfl.mjs} +2 -1
- package/dist/dist-DV5ISja1.mjs +13782 -0
- package/dist/dist-DV5ISja1.mjs.map +1 -0
- package/dist/domain/AiError.d.mts +2 -2
- package/dist/domain/AiError.mjs +19 -3
- package/dist/domain/AiError.mjs.map +1 -1
- package/dist/domain/Audio.d.mts +2 -0
- package/dist/domain/Audio.mjs +14 -0
- package/dist/domain/Audio.mjs.map +1 -0
- package/dist/domain/Image.d.mts +2 -0
- package/dist/domain/Image.mjs +58 -0
- package/dist/domain/Image.mjs.map +1 -0
- package/dist/domain/Items.d.mts +2 -2
- package/dist/domain/Items.mjs +19 -42
- package/dist/domain/Items.mjs.map +1 -1
- package/dist/domain/Media.d.mts +2 -0
- package/dist/domain/Media.mjs +14 -0
- package/dist/domain/Media.mjs.map +1 -0
- package/dist/domain/Music.d.mts +116 -0
- package/dist/domain/Music.d.mts.map +1 -0
- package/dist/domain/Music.mjs +29 -0
- package/dist/domain/Music.mjs.map +1 -0
- package/dist/domain/Transcript.d.mts +95 -0
- package/dist/domain/Transcript.d.mts.map +1 -0
- package/dist/domain/Transcript.mjs +22 -0
- package/dist/domain/Transcript.mjs.map +1 -0
- package/dist/domain/Turn.d.mts +1 -1
- package/dist/domain/Turn.mjs +1 -1
- package/dist/embedding-model/Embedding.d.mts +107 -0
- package/dist/embedding-model/Embedding.d.mts.map +1 -0
- package/dist/embedding-model/Embedding.mjs +18 -0
- package/dist/embedding-model/Embedding.mjs.map +1 -0
- package/dist/embedding-model/EmbeddingModel.d.mts +97 -0
- package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -0
- package/dist/embedding-model/EmbeddingModel.mjs +17 -0
- package/dist/embedding-model/EmbeddingModel.mjs.map +1 -0
- package/dist/index.d.mts +21 -7
- package/dist/index.mjs +16 -2
- package/dist/language-model/LanguageModel.d.mts +12 -20
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs +3 -20
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/loop/Loop.d.mts +31 -7
- package/dist/loop/Loop.d.mts.map +1 -1
- package/dist/loop/Loop.mjs +39 -6
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.d.mts +1 -0
- package/dist/loop/Loop.test.mjs +411 -0
- package/dist/loop/Loop.test.mjs.map +1 -0
- package/dist/magic-string.es-BgIV5Mu3.mjs +1013 -0
- package/dist/magic-string.es-BgIV5Mu3.mjs.map +1 -0
- package/dist/math/Vector.d.mts +47 -0
- package/dist/math/Vector.d.mts.map +1 -0
- package/dist/math/Vector.mjs +117 -0
- package/dist/math/Vector.mjs.map +1 -0
- package/dist/music-generator/MusicGenerator.d.mts +77 -0
- package/dist/music-generator/MusicGenerator.d.mts.map +1 -0
- package/dist/music-generator/MusicGenerator.mjs +51 -0
- package/dist/music-generator/MusicGenerator.mjs.map +1 -0
- package/dist/music-generator/MusicGenerator.test.d.mts +1 -0
- package/dist/music-generator/MusicGenerator.test.mjs +154 -0
- package/dist/music-generator/MusicGenerator.test.mjs.map +1 -0
- package/dist/observability/Metrics.d.mts +2 -2
- package/dist/observability/Metrics.d.mts.map +1 -1
- package/dist/observability/Metrics.mjs +1 -1
- package/dist/observability/Metrics.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +96 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs +48 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.d.mts +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +112 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -0
- package/dist/streaming/JSONL.d.mts +10 -3
- package/dist/streaming/JSONL.d.mts.map +1 -1
- package/dist/streaming/JSONL.mjs +13 -2
- package/dist/streaming/JSONL.mjs.map +1 -1
- package/dist/streaming/JSONL.test.d.mts +1 -0
- package/dist/streaming/JSONL.test.mjs +70 -0
- package/dist/streaming/JSONL.test.mjs.map +1 -0
- package/dist/streaming/Lines.mjs +1 -1
- package/dist/streaming/SSE.d.mts +2 -2
- package/dist/streaming/SSE.d.mts.map +1 -1
- package/dist/streaming/SSE.mjs +1 -1
- package/dist/streaming/SSE.mjs.map +1 -1
- package/dist/streaming/SSE.test.d.mts +1 -0
- package/dist/streaming/SSE.test.mjs +72 -0
- package/dist/streaming/SSE.test.mjs.map +1 -0
- package/dist/structured-format/StructuredFormat.d.mts +1 -1
- package/dist/structured-format/StructuredFormat.mjs +1 -1
- package/dist/structured-format/StructuredFormat.mjs.map +1 -1
- package/dist/testing/MockMusicGenerator.d.mts +39 -0
- package/dist/testing/MockMusicGenerator.d.mts.map +1 -0
- package/dist/testing/MockMusicGenerator.mjs +96 -0
- package/dist/testing/MockMusicGenerator.mjs.map +1 -0
- package/dist/testing/MockProvider.d.mts +6 -6
- package/dist/testing/MockProvider.d.mts.map +1 -1
- package/dist/testing/MockProvider.mjs.map +1 -1
- package/dist/testing/MockSpeechSynthesizer.d.mts +37 -0
- package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -0
- package/dist/testing/MockSpeechSynthesizer.mjs +95 -0
- package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -0
- package/dist/testing/MockTranscriber.d.mts +37 -0
- package/dist/testing/MockTranscriber.d.mts.map +1 -0
- package/dist/testing/MockTranscriber.mjs +77 -0
- package/dist/testing/MockTranscriber.mjs.map +1 -0
- package/dist/tool/HistoryCheck.d.mts +6 -3
- package/dist/tool/HistoryCheck.d.mts.map +1 -1
- package/dist/tool/HistoryCheck.mjs +7 -1
- package/dist/tool/HistoryCheck.mjs.map +1 -1
- package/dist/tool/Outcome.d.mts +138 -2
- package/dist/tool/Outcome.d.mts.map +1 -0
- package/dist/tool/Outcome.mjs +32 -10
- package/dist/tool/Outcome.mjs.map +1 -1
- package/dist/tool/Resolvers.d.mts +11 -8
- package/dist/tool/Resolvers.d.mts.map +1 -1
- package/dist/tool/Resolvers.mjs +10 -1
- package/dist/tool/Resolvers.mjs.map +1 -1
- package/dist/tool/Resolvers.test.d.mts +1 -0
- package/dist/tool/Resolvers.test.mjs +317 -0
- package/dist/tool/Resolvers.test.mjs.map +1 -0
- package/dist/tool/Tool.d.mts +1 -1
- package/dist/tool/Tool.mjs +1 -1
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/ToolEvent.d.mts +151 -2
- package/dist/tool/ToolEvent.d.mts.map +1 -0
- package/dist/tool/ToolEvent.mjs +30 -4
- package/dist/tool/ToolEvent.mjs.map +1 -1
- package/dist/tool/Toolkit.d.mts +19 -10
- package/dist/tool/Toolkit.d.mts.map +1 -1
- package/dist/tool/Toolkit.mjs +5 -5
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/tool/Toolkit.test.d.mts +1 -0
- package/dist/tool/Toolkit.test.mjs +113 -0
- package/dist/tool/Toolkit.test.mjs.map +1 -0
- package/dist/transcriber/Transcriber.d.mts +101 -0
- package/dist/transcriber/Transcriber.d.mts.map +1 -0
- package/dist/transcriber/Transcriber.mjs +49 -0
- package/dist/transcriber/Transcriber.mjs.map +1 -0
- package/dist/transcriber/Transcriber.test.d.mts +1 -0
- package/dist/transcriber/Transcriber.test.mjs +130 -0
- package/dist/transcriber/Transcriber.test.mjs.map +1 -0
- package/package.json +65 -13
- package/src/domain/AiError.ts +21 -0
- package/src/domain/Audio.ts +88 -0
- package/src/domain/Image.ts +75 -0
- package/src/domain/Items.ts +18 -47
- package/src/domain/Media.ts +61 -0
- package/src/domain/Music.ts +121 -0
- package/src/domain/Transcript.ts +83 -0
- package/src/embedding-model/Embedding.ts +117 -0
- package/src/embedding-model/EmbeddingModel.ts +107 -0
- package/src/index.ts +15 -1
- package/src/language-model/LanguageModel.ts +2 -22
- package/src/loop/Loop.test.ts +114 -2
- package/src/loop/Loop.ts +69 -5
- package/src/math/Vector.ts +138 -0
- package/src/music-generator/MusicGenerator.test.ts +170 -0
- package/src/music-generator/MusicGenerator.ts +123 -0
- package/src/observability/Metrics.ts +1 -1
- package/src/speech-synthesizer/SpeechSynthesizer.test.ts +141 -0
- package/src/speech-synthesizer/SpeechSynthesizer.ts +131 -0
- package/src/streaming/JSONL.ts +12 -0
- package/src/streaming/SSE.ts +1 -1
- package/src/structured-format/StructuredFormat.ts +2 -2
- package/src/testing/MockMusicGenerator.ts +170 -0
- package/src/testing/MockProvider.ts +2 -2
- package/src/testing/MockSpeechSynthesizer.ts +165 -0
- package/src/testing/MockTranscriber.ts +139 -0
- package/src/tool/HistoryCheck.ts +2 -5
- package/src/tool/Outcome.ts +36 -36
- package/src/tool/Resolvers.test.ts +11 -35
- package/src/tool/Resolvers.ts +5 -14
- package/src/tool/Tool.ts +9 -9
- package/src/tool/ToolEvent.ts +28 -24
- package/src/tool/Toolkit.test.ts +97 -2
- package/src/tool/Toolkit.ts +57 -33
- package/src/transcriber/Transcriber.test.ts +125 -0
- package/src/transcriber/Transcriber.ts +127 -0
- package/dist/Items-D1C2686t.d.mts.map +0 -1
- package/dist/Outcome-GiaNvt7i.d.mts +0 -32
- package/dist/Outcome-GiaNvt7i.d.mts.map +0 -1
- package/dist/StructuredFormat-B5ueioNr.d.mts.map +0 -1
- package/dist/Tool-5wxOCuOh.d.mts.map +0 -1
- package/dist/ToolEvent-wTMgb2GO.d.mts +0 -29
- package/dist/ToolEvent-wTMgb2GO.d.mts.map +0 -1
- package/dist/Turn-Bi83du4I.d.mts.map +0 -1
- package/dist/match/Match.d.mts +0 -16
- package/dist/match/Match.d.mts.map +0 -1
- package/dist/match/Match.mjs +0 -15
- package/dist/match/Match.mjs.map +0 -1
- package/src/match/Match.ts +0 -9
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ToolEvent.mjs","names":[],"sources":["../../src/tool/ToolEvent.ts"],"sourcesContent":["/**\n * The event type emitted while handling tool calls.\n *\n * - ApprovalRequested : gated calls waiting for approval\n * - Intermediate : per-element passthrough from a streaming tool's run\n * - Output : terminal result (carries a structured ToolResult)\n *\n * Recipes thread `ToolEvent.Output.result` through `
|
|
1
|
+
{"version":3,"file":"ToolEvent.mjs","names":[],"sources":["../../src/tool/ToolEvent.ts"],"sourcesContent":["/**\n * The event type emitted while handling tool calls.\n *\n * - ApprovalRequested : gated calls waiting for approval\n * - Intermediate : per-element passthrough from a streaming tool's run\n * - Output : terminal result (carries a structured ToolResult)\n *\n * Recipes thread `ToolEvent.Output.result` through `continueWith` and apply\n * `toFunctionCallOutput` when appending to history.\n */\nimport { Data } from \"effect\"\nimport type { ToolResult } from \"./Outcome.js\"\n\nexport type ToolEvent = Data.TaggedEnum<{\n ApprovalRequested: {\n readonly call_id: string\n readonly tool: string\n readonly arguments: string\n }\n Intermediate: {\n readonly call_id: string\n readonly tool: string\n readonly data: unknown\n }\n Output: {\n readonly result: ToolResult\n }\n}>\n\n/**\n * Namespace of constructors, type guards, and matchers for `ToolEvent`,\n * provided by `Data.taggedEnum`. Use `ToolEvent.Output({ result })` to build\n * an event, `ToolEvent.$is(\"Output\")` for type narrowing,\n * `ToolEvent.$match({ ApprovalRequested, Intermediate, Output })` for\n * exhaustive pattern matching.\n */\nexport const ToolEvent = Data.taggedEnum<ToolEvent>()\n\nexport const isApprovalRequested = ToolEvent.$is(\"ApprovalRequested\")\nexport const isIntermediate = ToolEvent.$is(\"Intermediate\")\nexport const isOutput = ToolEvent.$is(\"Output\")\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAoCA,MAAa,YAAY,KAAK,YAAuB;AAErD,MAAa,sBAAsB,UAAU,IAAI,oBAAoB;AACrE,MAAa,iBAAiB,UAAU,IAAI,eAAe;AAC3D,MAAa,WAAW,UAAU,IAAI,SAAS"}
|
package/dist/tool/Toolkit.d.mts
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
|
-
import { o as FunctionCall } from "../Items-
|
|
2
|
-
import { a as Tool, o as ToolDescriptor, t as AnyKindTool } from "../Tool-
|
|
1
|
+
import { o as FunctionCall } from "../Items-Hg5AsYxl.mjs";
|
|
2
|
+
import { a as Tool, i as StreamingTool, o as ToolDescriptor, t as AnyKindTool } from "../Tool-B8B5qVEy.mjs";
|
|
3
3
|
import { Event } from "../loop/Loop.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { ToolResult } from "./Outcome.mjs";
|
|
5
|
+
import { ToolEvent } from "./ToolEvent.mjs";
|
|
6
6
|
import { Stream } from "effect";
|
|
7
7
|
|
|
8
8
|
//#region src/tool/Toolkit.d.ts
|
|
9
9
|
declare namespace Toolkit_d_exports {
|
|
10
|
-
export { AnyTool, ExecuteOptions, Toolkit, ToolsR, executeAll, make,
|
|
10
|
+
export { AnyTool, ExecuteOptions, ToolKindR, Toolkit, ToolsR, continueWith, executeAll, make, outputEvent, outputEvents, toDescriptors };
|
|
11
11
|
}
|
|
12
12
|
type AnyTool = Tool<string, any, any, any>;
|
|
13
13
|
type Toolkit<Tools extends ReadonlyArray<AnyTool>> = {
|
|
14
14
|
readonly tools: Tools;
|
|
15
15
|
};
|
|
16
16
|
type ToolsR<Tools extends ReadonlyArray<AnyTool>> = Tools[number] extends Tool<any, any, any, infer R> ? R : never;
|
|
17
|
+
/**
|
|
18
|
+
* Union of every tool's `R` requirements in a mixed plain + streaming array.
|
|
19
|
+
* Used by `executeAll` to surface the services tools need at the recipe
|
|
20
|
+
* level, so the loop's stream type carries them through to `Effect.provide`.
|
|
21
|
+
*/
|
|
22
|
+
type ToolKindR<Tools extends ReadonlyArray<AnyKindTool<any>>> = Tools[number] extends StreamingTool<any, any, any, any, infer R> ? R : Tools[number] extends Tool<any, any, any, infer R> ? R : never;
|
|
17
23
|
declare const make: <const Tools extends ReadonlyArray<AnyTool>>(tools: Tools) => Toolkit<Tools>;
|
|
18
24
|
/**
|
|
19
25
|
* Render every tool in a toolkit to a provider-agnostic descriptor.
|
|
@@ -21,14 +27,17 @@ declare const make: <const Tools extends ReadonlyArray<AnyTool>>(tools: Tools) =
|
|
|
21
27
|
* Standard Schema converter (draft 2020-12).
|
|
22
28
|
*/
|
|
23
29
|
declare const toDescriptors: <Tools extends ReadonlyArray<AnyTool>>(toolkit: Toolkit<Tools>) => ReadonlyArray<ToolDescriptor>;
|
|
24
|
-
|
|
30
|
+
type ExecuteOptions = {
|
|
25
31
|
readonly concurrency?: number | "unbounded";
|
|
26
|
-
}
|
|
32
|
+
};
|
|
27
33
|
/** Execute every provided call. Approval/rejection policy belongs upstream. */
|
|
28
|
-
declare const executeAll:
|
|
34
|
+
declare const executeAll: <Tools extends ReadonlyArray<AnyKindTool<any>>>(tools: Tools, calls: ReadonlyArray<FunctionCall>, options?: ExecuteOptions) => Stream.Stream<ToolEvent, never, ToolKindR<Tools>>;
|
|
29
35
|
declare const outputEvent: (result: ToolResult) => ToolEvent;
|
|
30
36
|
declare const outputEvents: (results: ReadonlyArray<ToolResult>) => Stream.Stream<ToolEvent>;
|
|
31
|
-
declare const
|
|
37
|
+
declare const continueWith: {
|
|
38
|
+
<S>(build: (results: ReadonlyArray<ToolResult>) => S): <R>(stream: Stream.Stream<ToolEvent, never, R>) => Stream.Stream<Event<ToolEvent, S>, never, R>;
|
|
39
|
+
<S, R>(stream: Stream.Stream<ToolEvent, never, R>, build: (results: ReadonlyArray<ToolResult>) => S): Stream.Stream<Event<ToolEvent, S>, never, R>;
|
|
40
|
+
};
|
|
32
41
|
//#endregion
|
|
33
|
-
export { AnyTool, ExecuteOptions, Toolkit, ToolsR, executeAll, make,
|
|
42
|
+
export { AnyTool, ExecuteOptions, ToolKindR, Toolkit, ToolsR, continueWith, executeAll, make, outputEvent, outputEvents, Toolkit_d_exports as t, toDescriptors };
|
|
34
43
|
//# sourceMappingURL=Toolkit.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolkit.d.mts","names":[],"sources":["../../src/tool/Toolkit.ts"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"Toolkit.d.mts","names":[],"sources":["../../src/tool/Toolkit.ts"],"mappings":";;;;;;;;;;;KAgBY,OAAA,GAAU,IAAA;AAAA,KAEV,OAAA,eAAsB,aAAA,CAAc,OAAA;EAAA,SACrC,KAAA,EAAO,KAAA;AAAA;AAAA,KAGN,MAAA,eAAqB,aAAA,CAAc,OAAA,KAC7C,KAAA,iBAAsB,IAAA,2BAA+B,CAAA;;;;;;KAO3C,SAAA,eAAwB,aAAA,CAAc,WAAA,UAChD,KAAA,iBAAsB,aAAA,gCAClB,CAAA,GACA,KAAA,iBAAsB,IAAA,2BACpB,CAAA;AAAA,cAGK,IAAA,uBAA4B,aAAA,CAAc,OAAA,GAAU,KAAA,EAAO,KAAA,KAAQ,OAAA,CAAQ,KAAA;;;;;AArBxF;cA8Ba,aAAA,iBAA+B,aAAA,CAAc,OAAA,GACxD,OAAA,EAAS,OAAA,CAAQ,KAAA,MAChB,aAAA,CAAc,cAAA;AAAA,KAgBL,cAAA;EAAA,SACD,WAAA;AAAA;AA/CX;AAAA,cAmDa,UAAA,iBAA4B,aAAA,CAAc,WAAA,QACrD,KAAA,EAAO,KAAA,EACP,KAAA,EAAO,aAAA,CAAc,YAAA,GACrB,OAAA,GAAU,cAAA,KACT,MAAA,CAAO,MAAA,CAAO,SAAA,SAAkB,SAAA,CAAU,KAAA;AAAA,cAOhC,WAAA,GAAe,MAAA,EAAQ,UAAA,KAAa,SAAA;AAAA,cAEpC,YAAA,GAAgB,OAAA,EAAS,aAAA,CAAc,UAAA,MAAc,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,cAyHnE,YAAA;EAAA,IAET,KAAA,GAAQ,OAAA,EAAS,aAAA,CAAc,UAAA,MAAgB,CAAA,OAE/C,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,SAAA,SAAkB,CAAA,MACrC,MAAA,CAAO,MAAA,CAAO,KAAA,CAAW,SAAA,EAAW,CAAA,UAAW,CAAA;EAAA,OAElD,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,SAAA,SAAkB,CAAA,GACxC,KAAA,GAAQ,OAAA,EAAS,aAAA,CAAc,UAAA,MAAgB,CAAA,GAC9C,MAAA,CAAO,MAAA,CAAO,KAAA,CAAW,SAAA,EAAW,CAAA,UAAW,CAAA;AAAA"}
|
package/dist/tool/Toolkit.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
|
|
2
2
|
import { nextAfterFold } from "../loop/Loop.mjs";
|
|
3
3
|
import { isStreamingTool } from "./Tool.mjs";
|
|
4
4
|
import { executionError, rejected } from "./Outcome.mjs";
|
|
5
5
|
import { isOutput } from "./ToolEvent.mjs";
|
|
6
|
-
import { Array, Effect, Ref, Stream } from "effect";
|
|
6
|
+
import { Array, Effect, Function, Ref, Stream } from "effect";
|
|
7
7
|
//#region src/tool/Toolkit.ts
|
|
8
8
|
var Toolkit_exports = /* @__PURE__ */ __exportAll({
|
|
9
|
+
continueWith: () => continueWith,
|
|
9
10
|
executeAll: () => executeAll,
|
|
10
11
|
make: () => make,
|
|
11
|
-
nextStateFrom: () => nextStateFrom,
|
|
12
12
|
outputEvent: () => outputEvent,
|
|
13
13
|
outputEvents: () => outputEvents,
|
|
14
14
|
toDescriptors: () => toDescriptors
|
|
@@ -99,8 +99,8 @@ const runStreaming = (tool, call) => Stream.unwrap(Effect.gen(function* () {
|
|
|
99
99
|
_tag: "Output",
|
|
100
100
|
result: executionError(call, "Tool execution failed")
|
|
101
101
|
})));
|
|
102
|
-
const
|
|
102
|
+
const continueWith = Function.dual(2, (stream, build) => nextAfterFold(stream, [], (acc, e) => isOutput(e) ? Array.append(acc, e.result) : acc, build));
|
|
103
103
|
//#endregion
|
|
104
|
-
export { executeAll, make,
|
|
104
|
+
export { continueWith, executeAll, make, outputEvent, outputEvents, Toolkit_exports as t, toDescriptors };
|
|
105
105
|
|
|
106
106
|
//# sourceMappingURL=Toolkit.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Toolkit.mjs","names":["Arr","Loop.nextAfterFold"],"sources":["../../src/tool/Toolkit.ts"],"sourcesContent":["import { Array as Arr, Effect, Ref, Stream } from \"effect\"\nimport * as Loop from \"../loop/Loop.js\"\nimport type { FunctionCall } from \"../domain/Items.js\"\nimport {\n type AnyKindTool,\n type AnyPlainTool,\n type AnyStreamingTool,\n isStreamingTool,\n type Tool,\n type ToolDescriptor,\n} from \"./Tool.js\"\nimport {\n type ToolResult,\n executionError,\n rejected,\n} from \"./Outcome.js\"\nimport type { ToolEvent } from \"./ToolEvent.js\"\nimport { isOutput } from \"./ToolEvent.js\"\n\nexport type AnyTool = Tool<string, any, any, any>\n\nexport type Toolkit<Tools extends ReadonlyArray<AnyTool>> = {\n readonly tools: Tools\n}\n\nexport type ToolsR<Tools extends ReadonlyArray<AnyTool>> =\n Tools[number] extends Tool<any, any, any, infer R> ? R : never\n\nexport const make = <const Tools extends ReadonlyArray<AnyTool>>(tools: Tools): Toolkit<Tools> => ({\n tools,\n})\n\n/**\n * Render every tool in a toolkit to a provider-agnostic descriptor.\n * `inputSchema` is the JSON Schema document produced by the tool's\n * Standard Schema converter (draft 2020-12).\n */\nexport const toDescriptors = <Tools extends ReadonlyArray<AnyTool>>(\n toolkit: Toolkit<Tools>,\n): ReadonlyArray<ToolDescriptor> =>\n toolkit.tools.map((tool) => {\n const inputSchema = tool.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n })\n return tool.strict !== undefined\n ? { name: tool.name, description: tool.description, inputSchema, strict: tool.strict }\n : { name: tool.name, description: tool.description, inputSchema }\n })\n\n// ---------------------------------------------------------------------------\n// Tool executor. Streams `ToolEvent`s in real time and dispatches streaming\n// and plain tools uniformly. Policy stays outside this module: callers pass\n// only the calls they have already decided should run.\n// ---------------------------------------------------------------------------\n\nexport interface ExecuteOptions {\n readonly concurrency?: number | \"unbounded\"\n}\n\n/** Execute every provided call. Approval/rejection policy belongs upstream. */\nexport const executeAll = (\n tools: ReadonlyArray<AnyKindTool>,\n calls: ReadonlyArray<FunctionCall>,\n options?: ExecuteOptions,\n): Stream.Stream<ToolEvent> =>\n Stream.fromIterable(calls).pipe(\n Stream.flatMap((call) => runOne(tools, call), {\n concurrency: options?.concurrency ?? \"unbounded\",\n }),\n )\n\nexport const outputEvent = (result: ToolResult): ToolEvent => ({ _tag: \"Output\", result })\n\nexport const outputEvents = (\n results: ReadonlyArray<ToolResult>,\n): Stream.Stream<ToolEvent> => Stream.fromIterable(results.map(outputEvent))\n\nconst valueResult = (call: FunctionCall, tool: string, value: unknown): ToolResult => ({\n _tag: \"Value\",\n call_id: call.call_id,\n tool,\n value,\n})\n\nconst runOne = (\n tools: ReadonlyArray<AnyKindTool>,\n call: FunctionCall,\n): Stream.Stream<ToolEvent> => {\n const tool = tools.find((t) => t.name === call.name)\n if (tool === undefined) {\n // Graceful: emit a synthetic Failure so OTHER calls in this turn\n // still execute. LLMs hallucinate tool names; MCP tools come and go.\n return Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: rejected(call, \"unknown_tool\", `No tool registered with name \"${call.name}\"`),\n })\n }\n if (isStreamingTool(tool)) return runStreaming(tool, call)\n return runPlain(tool, call)\n}\n\nconst runPlain = (\n tool: AnyPlainTool,\n call: FunctionCall,\n): Stream.Stream<ToolEvent> =>\n Stream.fromEffect(\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: () => \"json_parse_error\" as const,\n })\n const validated = yield* Effect.tryPromise({\n try: () => Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n catch: () => \"validation_threw\" as const,\n })\n if (validated.issues !== undefined) {\n return executionError(call, \"Tool input failed schema validation\")\n }\n const output = yield* tool.run(validated.value)\n return valueResult(call, tool.name, output)\n }).pipe(\n Effect.catchCause(() => Effect.succeed(executionError(call, \"Tool execution failed\"))),\n Effect.map((result) => ({ _tag: \"Output\", result }) satisfies ToolEvent),\n ),\n )\n\nconst runStreaming = (\n tool: AnyStreamingTool,\n call: FunctionCall,\n): Stream.Stream<ToolEvent> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: () => \"json_parse_error\" as const,\n })\n const validated = yield* Effect.tryPromise({\n try: () => Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n catch: () => \"validation_threw\" as const,\n })\n if (validated.issues !== undefined) {\n return Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: executionError(call, \"Tool input failed schema validation\"),\n })\n }\n\n // Real-time: tap each event into a Ref as it flows; emit one\n // Intermediate per event; then concat one synthetic Output element\n // built from the accumulated Ref via `finalize`.\n const ref = yield* Ref.make<Array<unknown>>([])\n const intermediates = tool.run(validated.value).pipe(\n Stream.tap((event) => Ref.update(ref, Arr.append(event))),\n Stream.map(\n (data) =>\n ({\n _tag: \"Intermediate\",\n call_id: call.call_id,\n tool: tool.name,\n data,\n }) satisfies ToolEvent,\n ),\n )\n const output = Stream.fromEffect(\n Ref.get(ref).pipe(\n Effect.map(\n (events) =>\n ({\n _tag: \"Output\",\n result: valueResult(call, tool.name, tool.finalize(events)),\n }) satisfies ToolEvent,\n ),\n ),\n )\n return intermediates.pipe(Stream.concat(output))\n }),\n ).pipe(\n Stream.catchCause(() =>\n Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: executionError(call, \"Tool execution failed\"),\n }),\n ),\n )\n\n// ---------------------------------------------------------------------------\n// `nextStateFrom` - bridge from a `Stream<ToolEvent>` to the loop's emit\n// shape. Drains the stream to the consumer in real-time, taps every\n// `Output` into an internal Ref, and at end-of-stream emits\n// `Loop.next(build(results))`. Recipe never sees the Ref.\n// ---------------------------------------------------------------------------\n\nexport const nextStateFrom = <S>(\n stream: Stream.Stream<ToolEvent>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n): Stream.Stream<Loop.Event<ToolEvent, S>> =>\n Loop.nextAfterFold(\n stream,\n [] as ReadonlyArray<ToolResult>,\n (acc, e) => (isOutput(e) ? Arr.append(acc, e.result) : acc),\n build,\n )\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Toolkit.mjs","names":["Arr","Loop.nextAfterFold"],"sources":["../../src/tool/Toolkit.ts"],"sourcesContent":["import { Array as Arr, Effect, Function, Ref, Stream } from \"effect\"\nimport * as Loop from \"../loop/Loop.js\"\nimport type { FunctionCall } from \"../domain/Items.js\"\nimport {\n type AnyKindTool,\n type AnyPlainTool,\n type AnyStreamingTool,\n isStreamingTool,\n type StreamingTool,\n type Tool,\n type ToolDescriptor,\n} from \"./Tool.js\"\nimport { type ToolResult, executionError, rejected } from \"./Outcome.js\"\nimport type { ToolEvent } from \"./ToolEvent.js\"\nimport { isOutput } from \"./ToolEvent.js\"\n\nexport type AnyTool = Tool<string, any, any, any>\n\nexport type Toolkit<Tools extends ReadonlyArray<AnyTool>> = {\n readonly tools: Tools\n}\n\nexport type ToolsR<Tools extends ReadonlyArray<AnyTool>> =\n Tools[number] extends Tool<any, any, any, infer R> ? R : never\n\n/**\n * Union of every tool's `R` requirements in a mixed plain + streaming array.\n * Used by `executeAll` to surface the services tools need at the recipe\n * level, so the loop's stream type carries them through to `Effect.provide`.\n */\nexport type ToolKindR<Tools extends ReadonlyArray<AnyKindTool<any>>> =\n Tools[number] extends StreamingTool<any, any, any, any, infer R>\n ? R\n : Tools[number] extends Tool<any, any, any, infer R>\n ? R\n : never\n\nexport const make = <const Tools extends ReadonlyArray<AnyTool>>(tools: Tools): Toolkit<Tools> => ({\n tools,\n})\n\n/**\n * Render every tool in a toolkit to a provider-agnostic descriptor.\n * `inputSchema` is the JSON Schema document produced by the tool's\n * Standard Schema converter (draft 2020-12).\n */\nexport const toDescriptors = <Tools extends ReadonlyArray<AnyTool>>(\n toolkit: Toolkit<Tools>,\n): ReadonlyArray<ToolDescriptor> =>\n toolkit.tools.map((tool) => {\n const inputSchema = tool.inputSchema[\"~standard\"].jsonSchema.input({\n target: \"draft-2020-12\",\n })\n return tool.strict !== undefined\n ? { name: tool.name, description: tool.description, inputSchema, strict: tool.strict }\n : { name: tool.name, description: tool.description, inputSchema }\n })\n\n// ---------------------------------------------------------------------------\n// Tool executor. Streams `ToolEvent`s in real time and dispatches streaming\n// and plain tools uniformly. Policy stays outside this module: callers pass\n// only the calls they have already decided should run.\n// ---------------------------------------------------------------------------\n\nexport type ExecuteOptions = {\n readonly concurrency?: number | \"unbounded\"\n}\n\n/** Execute every provided call. Approval/rejection policy belongs upstream. */\nexport const executeAll = <Tools extends ReadonlyArray<AnyKindTool<any>>>(\n tools: Tools,\n calls: ReadonlyArray<FunctionCall>,\n options?: ExecuteOptions,\n): Stream.Stream<ToolEvent, never, ToolKindR<Tools>> =>\n Stream.fromIterable(calls).pipe(\n Stream.flatMap((call) => runOne(tools, call), {\n concurrency: options?.concurrency ?? \"unbounded\",\n }),\n )\n\nexport const outputEvent = (result: ToolResult): ToolEvent => ({ _tag: \"Output\", result })\n\nexport const outputEvents = (results: ReadonlyArray<ToolResult>): Stream.Stream<ToolEvent> =>\n Stream.fromIterable(results.map(outputEvent))\n\nconst valueResult = (call: FunctionCall, tool: string, value: unknown): ToolResult => ({\n _tag: \"Value\",\n call_id: call.call_id,\n tool,\n value,\n})\n\nconst runOne = <R>(\n tools: ReadonlyArray<AnyKindTool<R>>,\n call: FunctionCall,\n): Stream.Stream<ToolEvent, never, R> => {\n const tool = tools.find((t) => t.name === call.name)\n if (tool === undefined) {\n // Graceful: emit a synthetic Failure so OTHER calls in this turn\n // still execute. LLMs hallucinate tool names; MCP tools come and go.\n return Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: rejected(call, \"unknown_tool\", `No tool registered with name \"${call.name}\"`),\n })\n }\n if (isStreamingTool(tool)) return runStreaming(tool, call)\n return runPlain(tool, call)\n}\n\nconst runPlain = <R>(\n tool: AnyPlainTool<R>,\n call: FunctionCall,\n): Stream.Stream<ToolEvent, never, R> =>\n Stream.fromEffect(\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: () => \"json_parse_error\" as const,\n })\n const validated = yield* Effect.tryPromise({\n try: () => Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n catch: () => \"validation_threw\" as const,\n })\n if (validated.issues !== undefined) {\n return executionError(call, \"Tool input failed schema validation\")\n }\n const output = yield* tool.run(validated.value)\n return valueResult(call, tool.name, output)\n }).pipe(\n Effect.catchCause(() => Effect.succeed(executionError(call, \"Tool execution failed\"))),\n Effect.map((result) => ({ _tag: \"Output\", result }) satisfies ToolEvent),\n ),\n )\n\nconst runStreaming = <R>(\n tool: AnyStreamingTool<R>,\n call: FunctionCall,\n): Stream.Stream<ToolEvent, never, R> =>\n Stream.unwrap(\n Effect.gen(function* () {\n const parsed = yield* Effect.try({\n try: () => JSON.parse(call.arguments) as unknown,\n catch: () => \"json_parse_error\" as const,\n })\n const validated = yield* Effect.tryPromise({\n try: () => Promise.resolve(tool.inputSchema[\"~standard\"].validate(parsed)),\n catch: () => \"validation_threw\" as const,\n })\n if (validated.issues !== undefined) {\n return Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: executionError(call, \"Tool input failed schema validation\"),\n })\n }\n\n // Real-time: tap each event into a Ref as it flows; emit one\n // Intermediate per event; then concat one synthetic Output element\n // built from the accumulated Ref via `finalize`.\n const ref = yield* Ref.make<Array<unknown>>([])\n const intermediates = tool.run(validated.value).pipe(\n Stream.tap((event) => Ref.update(ref, Arr.append(event))),\n Stream.map(\n (data) =>\n ({\n _tag: \"Intermediate\",\n call_id: call.call_id,\n tool: tool.name,\n data,\n }) satisfies ToolEvent,\n ),\n )\n const output = Stream.fromEffect(\n Ref.get(ref).pipe(\n Effect.map(\n (events) =>\n ({\n _tag: \"Output\",\n result: valueResult(call, tool.name, tool.finalize(events)),\n }) satisfies ToolEvent,\n ),\n ),\n )\n return intermediates.pipe(Stream.concat(output))\n }),\n ).pipe(\n Stream.catchCause(() =>\n Stream.succeed<ToolEvent>({\n _tag: \"Output\",\n result: executionError(call, \"Tool execution failed\"),\n }),\n ),\n )\n\n// ---------------------------------------------------------------------------\n// `continueWith` - bridge from a `Stream<ToolEvent>` to the loop's emit\n// shape. Drains the stream to the consumer in real-time, taps every\n// `Output` into an internal Ref, and at end-of-stream emits\n// `Loop.next(build(results))`. Recipe never sees the Ref.\n//\n// Dual: data-first `continueWith(stream, build)` and data-last\n// `stream.pipe(continueWith(build))` both work.\n// ---------------------------------------------------------------------------\n\nexport const continueWith: {\n <S>(\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): <R>(\n stream: Stream.Stream<ToolEvent, never, R>,\n ) => Stream.Stream<Loop.Event<ToolEvent, S>, never, R>\n <S, R>(\n stream: Stream.Stream<ToolEvent, never, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Event<ToolEvent, S>, never, R>\n} = Function.dual(\n 2,\n <S, R>(\n stream: Stream.Stream<ToolEvent, never, R>,\n build: (results: ReadonlyArray<ToolResult>) => S,\n ): Stream.Stream<Loop.Event<ToolEvent, S>, never, R> =>\n Loop.nextAfterFold(\n stream,\n [] as ReadonlyArray<ToolResult>,\n (acc, e) => (isOutput(e) ? Arr.append(acc, e.result) : acc),\n build,\n ),\n)\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { fromEffectSchema, make } from "./Tool.mjs";
|
|
2
|
+
import { isValue } from "./Outcome.mjs";
|
|
3
|
+
import { isOutput } from "./ToolEvent.mjs";
|
|
4
|
+
import { executeAll, make as make$1, toDescriptors } from "./Toolkit.mjs";
|
|
5
|
+
import { i as it, n as globalExpect, r as describe, t as import_dist } from "../dist-DV5ISja1.mjs";
|
|
6
|
+
import { Context, Effect, Layer, Schema, Stream } from "effect";
|
|
7
|
+
//#region src/tool/Toolkit.test.ts
|
|
8
|
+
describe("Toolkit.toDescriptors", () => {
|
|
9
|
+
const GetWeatherInput = Schema.Struct({ city: Schema.String });
|
|
10
|
+
const getWeather = make({
|
|
11
|
+
name: "get_weather",
|
|
12
|
+
description: "Look up the current temperature for a city.",
|
|
13
|
+
inputSchema: fromEffectSchema(GetWeatherInput),
|
|
14
|
+
run: ({ city }) => Effect.succeed({
|
|
15
|
+
city,
|
|
16
|
+
tempC: 18
|
|
17
|
+
})
|
|
18
|
+
});
|
|
19
|
+
it("renders the input schema as a JSON Schema document", () => {
|
|
20
|
+
const [desc] = toDescriptors(make$1([getWeather]));
|
|
21
|
+
globalExpect(desc?.name).toBe("get_weather");
|
|
22
|
+
globalExpect(desc?.description).toBe("Look up the current temperature for a city.");
|
|
23
|
+
globalExpect(desc?.inputSchema).toMatchObject({
|
|
24
|
+
type: "object",
|
|
25
|
+
properties: { city: { type: "string" } },
|
|
26
|
+
required: ["city"]
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
it("includes strict flag only when set on the tool", () => {
|
|
30
|
+
const [s, l] = toDescriptors(make$1([make({
|
|
31
|
+
name: "strict_one",
|
|
32
|
+
description: "",
|
|
33
|
+
inputSchema: fromEffectSchema(GetWeatherInput),
|
|
34
|
+
run: () => Effect.succeed({}),
|
|
35
|
+
strict: true
|
|
36
|
+
}), make({
|
|
37
|
+
name: "loose_one",
|
|
38
|
+
description: "",
|
|
39
|
+
inputSchema: fromEffectSchema(GetWeatherInput),
|
|
40
|
+
run: () => Effect.succeed({})
|
|
41
|
+
})]));
|
|
42
|
+
globalExpect(s?.strict).toBe(true);
|
|
43
|
+
globalExpect(l).not.toHaveProperty("strict");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe("Toolkit.executeAll - tools with R requirements", () => {
|
|
47
|
+
class WeatherApiKey extends Context.Service()("test/WeatherApiKey") {}
|
|
48
|
+
class GeoApiKey extends Context.Service()("test/GeoApiKey") {}
|
|
49
|
+
const Empty = Schema.Struct({});
|
|
50
|
+
const getWeather = make({
|
|
51
|
+
name: "get_weather",
|
|
52
|
+
description: "",
|
|
53
|
+
inputSchema: fromEffectSchema(Empty),
|
|
54
|
+
run: () => Effect.gen(function* () {
|
|
55
|
+
const { key } = yield* WeatherApiKey;
|
|
56
|
+
return {
|
|
57
|
+
source: "weather",
|
|
58
|
+
key
|
|
59
|
+
};
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
const getCoords = make({
|
|
63
|
+
name: "get_coords",
|
|
64
|
+
description: "",
|
|
65
|
+
inputSchema: fromEffectSchema(Empty),
|
|
66
|
+
run: () => Effect.gen(function* () {
|
|
67
|
+
const { key } = yield* GeoApiKey;
|
|
68
|
+
return {
|
|
69
|
+
source: "geo",
|
|
70
|
+
key
|
|
71
|
+
};
|
|
72
|
+
})
|
|
73
|
+
});
|
|
74
|
+
const call = (name, id) => ({
|
|
75
|
+
type: "function_call",
|
|
76
|
+
call_id: id,
|
|
77
|
+
name,
|
|
78
|
+
arguments: "{}"
|
|
79
|
+
});
|
|
80
|
+
it("propagates each tool's R into the resulting Stream's requirements", () => {
|
|
81
|
+
(0, import_dist.expectTypeOf)(executeAll([getWeather, getCoords], [])).toEqualTypeOf();
|
|
82
|
+
});
|
|
83
|
+
it("runs each tool with its own service injected", async () => {
|
|
84
|
+
const layer = Layer.mergeAll(Layer.succeed(WeatherApiKey, { key: "weather-123" }), Layer.succeed(GeoApiKey, { key: "geo-456" }));
|
|
85
|
+
const program = executeAll([getWeather, getCoords], [call("get_weather", "c1"), call("get_coords", "c2")]).pipe(Stream.runCollect, Effect.provide(layer));
|
|
86
|
+
const events = await Effect.runPromise(program);
|
|
87
|
+
const outputs = Array.from(events).filter(isOutput);
|
|
88
|
+
const byCall = new Map(outputs.map((e) => [e.result.call_id, e.result]));
|
|
89
|
+
const w = byCall.get("c1");
|
|
90
|
+
const g = byCall.get("c2");
|
|
91
|
+
globalExpect(w !== void 0 && isValue(w) && w.value).toEqual({
|
|
92
|
+
source: "weather",
|
|
93
|
+
key: "weather-123"
|
|
94
|
+
});
|
|
95
|
+
globalExpect(g !== void 0 && isValue(g) && g.value).toEqual({
|
|
96
|
+
source: "geo",
|
|
97
|
+
key: "geo-456"
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
it("with no service-needing tools, R is never", () => {
|
|
101
|
+
const plain = make({
|
|
102
|
+
name: "plain",
|
|
103
|
+
description: "",
|
|
104
|
+
inputSchema: fromEffectSchema(Empty),
|
|
105
|
+
run: () => Effect.succeed(0)
|
|
106
|
+
});
|
|
107
|
+
(0, import_dist.expectTypeOf)(executeAll([plain], [])).toEqualTypeOf();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//#endregion
|
|
111
|
+
export {};
|
|
112
|
+
|
|
113
|
+
//# sourceMappingURL=Toolkit.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Toolkit.test.mjs","names":["Tool.make","Tool.fromEffectSchema","Toolkit.toDescriptors","Toolkit.make","Toolkit.executeAll"],"sources":["../../src/tool/Toolkit.test.ts"],"sourcesContent":["import { Context, Effect, Layer, Schema, Stream } from \"effect\"\nimport { describe, expect, expectTypeOf, it } from \"vitest\"\nimport type { FunctionCall } from \"../domain/Items.js\"\nimport { isOutput } from \"./ToolEvent.js\"\nimport { isValue } from \"./Outcome.js\"\nimport * as Tool from \"./Tool.js\"\nimport * as Toolkit from \"./Toolkit.js\"\n\ndescribe(\"Toolkit.toDescriptors\", () => {\n const GetWeatherInput = Schema.Struct({ city: Schema.String })\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"Look up the current temperature for a city.\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: ({ city }) => Effect.succeed({ city, tempC: 18 }),\n })\n\n it(\"renders the input schema as a JSON Schema document\", () => {\n const [desc] = Toolkit.toDescriptors(Toolkit.make([getWeather]))\n expect(desc?.name).toBe(\"get_weather\")\n expect(desc?.description).toBe(\"Look up the current temperature for a city.\")\n expect(desc?.inputSchema).toMatchObject({\n type: \"object\",\n properties: { city: { type: \"string\" } },\n required: [\"city\"],\n })\n })\n\n it(\"includes strict flag only when set on the tool\", () => {\n const strictTool = Tool.make({\n name: \"strict_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n strict: true,\n })\n const looseTool = Tool.make({\n name: \"loose_one\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(GetWeatherInput),\n run: () => Effect.succeed({}),\n })\n const [s, l] = Toolkit.toDescriptors(Toolkit.make([strictTool, looseTool]))\n expect(s?.strict).toBe(true)\n expect(l).not.toHaveProperty(\"strict\")\n })\n})\n\ndescribe(\"Toolkit.executeAll - tools with R requirements\", () => {\n // Two distinct services, modelling the \"typed per-tool context\" use case\n // (cf. AI SDK 7's `toolsContext`). In Effect each tool declares its R, the\n // compiler enforces it, and `executeAll` surfaces the union for the caller\n // to provide via Layer.\n type WeatherApiKeyShape = { readonly key: string }\n class WeatherApiKey extends Context.Service<WeatherApiKey, WeatherApiKeyShape>()(\n \"test/WeatherApiKey\",\n ) {}\n\n type GeoApiKeyShape = { readonly key: string }\n class GeoApiKey extends Context.Service<GeoApiKey, GeoApiKeyShape>()(\"test/GeoApiKey\") {}\n\n const Empty = Schema.Struct({})\n\n const getWeather = Tool.make({\n name: \"get_weather\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* WeatherApiKey\n return { source: \"weather\", key }\n }),\n })\n\n const getCoords = Tool.make({\n name: \"get_coords\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () =>\n Effect.gen(function* () {\n const { key } = yield* GeoApiKey\n return { source: \"geo\", key }\n }),\n })\n\n const call = (name: string, id: string): FunctionCall => ({\n type: \"function_call\",\n call_id: id,\n name,\n arguments: \"{}\",\n })\n\n it(\"propagates each tool's R into the resulting Stream's requirements\", () => {\n const stream = Toolkit.executeAll([getWeather, getCoords], [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, WeatherApiKey | GeoApiKey>\n >()\n })\n\n it(\"runs each tool with its own service injected\", async () => {\n const layer = Layer.mergeAll(\n Layer.succeed(WeatherApiKey, { key: \"weather-123\" }),\n Layer.succeed(GeoApiKey, { key: \"geo-456\" }),\n )\n\n const program = Toolkit.executeAll(\n [getWeather, getCoords],\n [call(\"get_weather\", \"c1\"), call(\"get_coords\", \"c2\")],\n ).pipe(Stream.runCollect, Effect.provide(layer))\n\n const events = await Effect.runPromise(program)\n const outputs = Array.from(events).filter(isOutput)\n const byCall = new Map(outputs.map((e) => [e.result.call_id, e.result]))\n\n const w = byCall.get(\"c1\")\n const g = byCall.get(\"c2\")\n expect(w !== undefined && isValue(w) && w.value).toEqual({\n source: \"weather\",\n key: \"weather-123\",\n })\n expect(g !== undefined && isValue(g) && g.value).toEqual({\n source: \"geo\",\n key: \"geo-456\",\n })\n })\n\n it(\"with no service-needing tools, R is never\", () => {\n const plain = Tool.make({\n name: \"plain\",\n description: \"\",\n inputSchema: Tool.fromEffectSchema(Empty),\n run: () => Effect.succeed(0),\n })\n const stream = Toolkit.executeAll([plain], [])\n expectTypeOf(stream).toEqualTypeOf<\n Stream.Stream<import(\"./ToolEvent.js\").ToolEvent, never, never>\n >()\n })\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { t as AiError } from "../AiError-csR8Bhxx.mjs";
|
|
2
|
+
import { a as AudioSource, r as AudioFormat } from "../Audio-BfCTGnH3.mjs";
|
|
3
|
+
import { TranscriptEvent, TranscriptResult } from "../domain/Transcript.mjs";
|
|
4
|
+
import { Context, Effect, Stream } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src/transcriber/Transcriber.d.ts
|
|
7
|
+
declare namespace Transcriber_d_exports {
|
|
8
|
+
export { CommonStreamTranscribeRequest, CommonTranscribeRequest, SttStreaming, Transcriber, TranscriberService, streamTranscriptionFrom, transcribe };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Cross-provider sync transcription request. Provider-specific
|
|
12
|
+
* extensions (Deepgram `keyterm[]`, ElevenLabs `diarize`, Google
|
|
13
|
+
* `adaptation`, …) live on each provider's typed request which extends
|
|
14
|
+
* this and narrows `model`.
|
|
15
|
+
*/
|
|
16
|
+
type CommonTranscribeRequest = {
|
|
17
|
+
readonly audio: AudioSource; /** Model identifier. Each provider narrows to its typed literal union. */
|
|
18
|
+
readonly model: string; /** ISO-639-1 / BCP-47. Omit for autodetection (where supported). */
|
|
19
|
+
readonly language?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Vocab biasing. Single-string covers OpenAI/Whisper-style prompts;
|
|
22
|
+
* `terms[]` covers Deepgram `keyterm`, Google adaptation phrases, AWS
|
|
23
|
+
* `vocabularyName`. Providers ignore what they don't support.
|
|
24
|
+
*/
|
|
25
|
+
readonly prompt?: string | {
|
|
26
|
+
readonly terms: ReadonlyArray<string>;
|
|
27
|
+
};
|
|
28
|
+
readonly diarization?: boolean;
|
|
29
|
+
readonly wordTimestamps?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Streaming-transcription request. `inputFormat` declares what the
|
|
33
|
+
* bytes in the input stream will look like — providers reject
|
|
34
|
+
* mismatches at stream startup with `AiError.InvalidRequest`.
|
|
35
|
+
*/
|
|
36
|
+
type CommonStreamTranscribeRequest = Omit<CommonTranscribeRequest, "audio"> & {
|
|
37
|
+
readonly inputFormat: AudioFormat;
|
|
38
|
+
readonly interimResults?: boolean;
|
|
39
|
+
readonly vadEvents?: boolean;
|
|
40
|
+
};
|
|
41
|
+
type TranscriberService = {
|
|
42
|
+
/**
|
|
43
|
+
* One-shot transcription. Universal — AWS Transcribe (which has no
|
|
44
|
+
* native sync endpoint) emulates this by draining a streaming session
|
|
45
|
+
* internally.
|
|
46
|
+
*/
|
|
47
|
+
readonly transcribe: (request: CommonTranscribeRequest) => Effect.Effect<TranscriptResult, AiError>;
|
|
48
|
+
/**
|
|
49
|
+
* Live transcription as a Stream transformer. Consumes audio bytes
|
|
50
|
+
* from `audioIn`; emits `TranscriptEvent`s as they arrive. The
|
|
51
|
+
* underlying WS / gRPC connection is acquired on first pull and
|
|
52
|
+
* released when the output stream is finalized (success, failure, or
|
|
53
|
+
* interruption) via `Stream.scoped` — no explicit Scope handling at
|
|
54
|
+
* the call site.
|
|
55
|
+
*
|
|
56
|
+
* Gated by the `SttStreaming` capability marker on the top-level
|
|
57
|
+
* helper — providers without streaming-STT support don't ship the
|
|
58
|
+
* marker, so calls fail at `Effect.provide` with a type error.
|
|
59
|
+
*/
|
|
60
|
+
readonly streamTranscriptionFrom: <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest) => Stream.Stream<TranscriptEvent, AiError | E, R>;
|
|
61
|
+
};
|
|
62
|
+
declare const Transcriber_base: Context.ServiceClass<Transcriber, "@betalyra/effect-uai/Transcriber", TranscriberService>;
|
|
63
|
+
declare class Transcriber extends Transcriber_base {}
|
|
64
|
+
declare const SttStreaming_base: Context.ServiceClass<SttStreaming, "@betalyra/effect-uai/capability/SttStreaming", void>;
|
|
65
|
+
/**
|
|
66
|
+
* Capability marker — provided by provider layers whose
|
|
67
|
+
* `streamTranscriptionFrom` is wired up at the wire level. Azure does
|
|
68
|
+
* not ship it (streaming-STT is SDK-internal). Calling
|
|
69
|
+
* `streamTranscriptionFrom` while only Azure's Layer is in scope fails
|
|
70
|
+
* at `Effect.provide` with a type error, not at runtime.
|
|
71
|
+
*
|
|
72
|
+
* Phantom — the value is `void`; providers register with
|
|
73
|
+
* `Layer.succeed(SttStreaming, undefined)`.
|
|
74
|
+
*/
|
|
75
|
+
declare class SttStreaming extends SttStreaming_base {}
|
|
76
|
+
/** One-shot transcription. */
|
|
77
|
+
declare const transcribe: (request: CommonTranscribeRequest) => Effect.Effect<TranscriptResult, AiError, Transcriber>;
|
|
78
|
+
/**
|
|
79
|
+
* Live transcription. Dual-arity: pipeable (data-last) and direct
|
|
80
|
+
* (data-first). Requires `SttStreaming` in R — providers without
|
|
81
|
+
* streaming support are a type error at provide time.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* // Pipeable — composes with other Stream operators
|
|
86
|
+
* mic.frames.pipe(
|
|
87
|
+
* Transcriber.streamTranscriptionFrom(req),
|
|
88
|
+
* Stream.filter((e) => e._tag === "final"),
|
|
89
|
+
* )
|
|
90
|
+
*
|
|
91
|
+
* // Direct
|
|
92
|
+
* Transcriber.streamTranscriptionFrom(mic.frames, req)
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
declare const streamTranscriptionFrom: {
|
|
96
|
+
(request: CommonStreamTranscribeRequest): <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>) => Stream.Stream<TranscriptEvent, AiError | E, R | Transcriber | SttStreaming>;
|
|
97
|
+
<E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest): Stream.Stream<TranscriptEvent, AiError | E, R | Transcriber | SttStreaming>;
|
|
98
|
+
};
|
|
99
|
+
//#endregion
|
|
100
|
+
export { CommonStreamTranscribeRequest, CommonTranscribeRequest, SttStreaming, Transcriber, TranscriberService, streamTranscriptionFrom, Transcriber_d_exports as t, transcribe };
|
|
101
|
+
//# sourceMappingURL=Transcriber.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transcriber.d.mts","names":[],"sources":["../../src/transcriber/Transcriber.ts"],"mappings":";;;;;;;;;;;;;;;KAWY,uBAAA;EAAA,SACD,KAAA,EAAO,WAAA;WAEP,KAAA;WAEA,QAAA;EALC;;;;;EAAA,SAWD,MAAA;IAAA,SAA6B,KAAA,EAAO,aAAA;EAAA;EAAA,SACpC,WAAA;EAAA,SACA,cAAA;AAAA;;;;;AAQX;KAAY,6BAAA,GAAgC,IAAA,CAAK,uBAAA;EAAA,SACtC,WAAA,EAAa,WAAA;EAAA,SACb,cAAA;EAAA,SACA,SAAA;AAAA;AAAA,KAGC,kBAAA;EALuB;;;;;EAAA,SAWxB,UAAA,GACP,OAAA,EAAS,uBAAA,KACN,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA;EAX5B;;;AAGX;;;;;;;;;EAHW,SAwBA,uBAAA,SACP,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,GACtC,OAAA,EAAS,6BAAA,KACN,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,OAAA,GAAkB,CAAA,EAAG,CAAA;AAAA;AAAA,cAC1D,gBAAA;cAEY,WAAA,SAAoB,gBAAA;AAAA,cAE7B,iBAAA;;;;;;;;;;;cAYS,YAAA,SAAqB,iBAAA;;cAKrB,UAAA,GACX,OAAA,EAAS,uBAAA,KACR,MAAA,CAAO,MAAA,CAAO,gBAAA,EAAkB,OAAA,EAAiB,WAAA;;;;;;;;;;;;;;;;;;cAoBvC,uBAAA;EAAA,CAET,OAAA,EAAS,6BAAA,UAET,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,MACnC,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,WAAA,GAAc,YAAA;EAAA,OAEzE,OAAA,EAAS,MAAA,CAAO,MAAA,CAAO,UAAA,EAAY,CAAA,EAAG,CAAA,GACtC,OAAA,EAAS,6BAAA,GACR,MAAA,CAAO,MAAA,CAAO,eAAA,EAAiB,OAAA,GAAkB,CAAA,EAAG,CAAA,GAAI,WAAA,GAAc,YAAA;AAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
|
|
2
|
+
import { Context, Effect, Function, Stream } from "effect";
|
|
3
|
+
//#region src/transcriber/Transcriber.ts
|
|
4
|
+
var Transcriber_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
SttStreaming: () => SttStreaming,
|
|
6
|
+
Transcriber: () => Transcriber,
|
|
7
|
+
streamTranscriptionFrom: () => streamTranscriptionFrom,
|
|
8
|
+
transcribe: () => transcribe
|
|
9
|
+
});
|
|
10
|
+
var Transcriber = class extends Context.Service()("@betalyra/effect-uai/Transcriber") {};
|
|
11
|
+
/**
|
|
12
|
+
* Capability marker — provided by provider layers whose
|
|
13
|
+
* `streamTranscriptionFrom` is wired up at the wire level. Azure does
|
|
14
|
+
* not ship it (streaming-STT is SDK-internal). Calling
|
|
15
|
+
* `streamTranscriptionFrom` while only Azure's Layer is in scope fails
|
|
16
|
+
* at `Effect.provide` with a type error, not at runtime.
|
|
17
|
+
*
|
|
18
|
+
* Phantom — the value is `void`; providers register with
|
|
19
|
+
* `Layer.succeed(SttStreaming, undefined)`.
|
|
20
|
+
*/
|
|
21
|
+
var SttStreaming = class extends Context.Service()("@betalyra/effect-uai/capability/SttStreaming") {};
|
|
22
|
+
/** One-shot transcription. */
|
|
23
|
+
const transcribe = (request) => Effect.flatMap(Transcriber.asEffect(), (t) => t.transcribe(request));
|
|
24
|
+
/**
|
|
25
|
+
* Live transcription. Dual-arity: pipeable (data-last) and direct
|
|
26
|
+
* (data-first). Requires `SttStreaming` in R — providers without
|
|
27
|
+
* streaming support are a type error at provide time.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* // Pipeable — composes with other Stream operators
|
|
32
|
+
* mic.frames.pipe(
|
|
33
|
+
* Transcriber.streamTranscriptionFrom(req),
|
|
34
|
+
* Stream.filter((e) => e._tag === "final"),
|
|
35
|
+
* )
|
|
36
|
+
*
|
|
37
|
+
* // Direct
|
|
38
|
+
* Transcriber.streamTranscriptionFrom(mic.frames, req)
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
const streamTranscriptionFrom = Function.dual(2, (audioIn, request) => Stream.unwrap(Effect.gen(function* () {
|
|
42
|
+
const t = yield* Transcriber.asEffect();
|
|
43
|
+
yield* SttStreaming.asEffect();
|
|
44
|
+
return t.streamTranscriptionFrom(audioIn, request);
|
|
45
|
+
})));
|
|
46
|
+
//#endregion
|
|
47
|
+
export { SttStreaming, Transcriber, streamTranscriptionFrom, Transcriber_exports as t, transcribe };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=Transcriber.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transcriber.mjs","names":[],"sources":["../../src/transcriber/Transcriber.ts"],"sourcesContent":["import { Context, Effect, Function, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { AudioFormat, AudioSource } from \"../domain/Audio.js\"\nimport type { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\n\n/**\n * Cross-provider sync transcription request. Provider-specific\n * extensions (Deepgram `keyterm[]`, ElevenLabs `diarize`, Google\n * `adaptation`, …) live on each provider's typed request which extends\n * this and narrows `model`.\n */\nexport type CommonTranscribeRequest = {\n readonly audio: AudioSource\n /** Model identifier. Each provider narrows to its typed literal union. */\n readonly model: string\n /** ISO-639-1 / BCP-47. Omit for autodetection (where supported). */\n readonly language?: string\n /**\n * Vocab biasing. Single-string covers OpenAI/Whisper-style prompts;\n * `terms[]` covers Deepgram `keyterm`, Google adaptation phrases, AWS\n * `vocabularyName`. Providers ignore what they don't support.\n */\n readonly prompt?: string | { readonly terms: ReadonlyArray<string> }\n readonly diarization?: boolean\n readonly wordTimestamps?: boolean\n}\n\n/**\n * Streaming-transcription request. `inputFormat` declares what the\n * bytes in the input stream will look like — providers reject\n * mismatches at stream startup with `AiError.InvalidRequest`.\n */\nexport type CommonStreamTranscribeRequest = Omit<CommonTranscribeRequest, \"audio\"> & {\n readonly inputFormat: AudioFormat\n readonly interimResults?: boolean\n readonly vadEvents?: boolean\n}\n\nexport type TranscriberService = {\n /**\n * One-shot transcription. Universal — AWS Transcribe (which has no\n * native sync endpoint) emulates this by draining a streaming session\n * internally.\n */\n readonly transcribe: (\n request: CommonTranscribeRequest,\n ) => Effect.Effect<TranscriptResult, AiError.AiError>\n /**\n * Live transcription as a Stream transformer. Consumes audio bytes\n * from `audioIn`; emits `TranscriptEvent`s as they arrive. The\n * underlying WS / gRPC connection is acquired on first pull and\n * released when the output stream is finalized (success, failure, or\n * interruption) via `Stream.scoped` — no explicit Scope handling at\n * the call site.\n *\n * Gated by the `SttStreaming` capability marker on the top-level\n * helper — providers without streaming-STT support don't ship the\n * marker, so calls fail at `Effect.provide` with a type error.\n */\n readonly streamTranscriptionFrom: <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R>\n}\n\nexport class Transcriber extends Context.Service<Transcriber, TranscriberService>()(\n \"@betalyra/effect-uai/Transcriber\",\n) {}\n\n/**\n * Capability marker — provided by provider layers whose\n * `streamTranscriptionFrom` is wired up at the wire level. Azure does\n * not ship it (streaming-STT is SDK-internal). Calling\n * `streamTranscriptionFrom` while only Azure's Layer is in scope fails\n * at `Effect.provide` with a type error, not at runtime.\n *\n * Phantom — the value is `void`; providers register with\n * `Layer.succeed(SttStreaming, undefined)`.\n */\nexport class SttStreaming extends Context.Service<SttStreaming, void>()(\n \"@betalyra/effect-uai/capability/SttStreaming\",\n) {}\n\n/** One-shot transcription. */\nexport const transcribe = (\n request: CommonTranscribeRequest,\n): Effect.Effect<TranscriptResult, AiError.AiError, Transcriber> =>\n Effect.flatMap(Transcriber.asEffect(), (t) => t.transcribe(request))\n\n/**\n * Live transcription. Dual-arity: pipeable (data-last) and direct\n * (data-first). Requires `SttStreaming` in R — providers without\n * streaming support are a type error at provide time.\n *\n * @example\n * ```ts\n * // Pipeable — composes with other Stream operators\n * mic.frames.pipe(\n * Transcriber.streamTranscriptionFrom(req),\n * Stream.filter((e) => e._tag === \"final\"),\n * )\n *\n * // Direct\n * Transcriber.streamTranscriptionFrom(mic.frames, req)\n * ```\n */\nexport const streamTranscriptionFrom: {\n (\n request: CommonStreamTranscribeRequest,\n ): <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n ) => Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n <E, R>(\n audioIn: Stream.Stream<Uint8Array, E, R>,\n request: CommonStreamTranscribeRequest,\n ): Stream.Stream<TranscriptEvent, AiError.AiError | E, R | Transcriber | SttStreaming>\n} = Function.dual(\n 2,\n <E, R>(audioIn: Stream.Stream<Uint8Array, E, R>, request: CommonStreamTranscribeRequest) =>\n Stream.unwrap(\n Effect.gen(function* () {\n const t = yield* Transcriber.asEffect()\n yield* SttStreaming.asEffect()\n return t.streamTranscriptionFrom(audioIn, request)\n }),\n ),\n)\n"],"mappings":";;;;;;;;;AAiEA,IAAa,cAAb,cAAiC,QAAQ,SAA0C,CACjF,mCACD,CAAC;;;;;;;;;;;AAYF,IAAa,eAAb,cAAkC,QAAQ,SAA6B,CACrE,+CACD,CAAC;;AAGF,MAAa,cACX,YAEA,OAAO,QAAQ,YAAY,UAAU,GAAG,MAAM,EAAE,WAAW,QAAQ,CAAC;;;;;;;;;;;;;;;;;;AAmBtE,MAAa,0BAUT,SAAS,KACX,IACO,SAA0C,YAC/C,OAAO,OACL,OAAO,IAAI,aAAa;CACtB,MAAM,IAAI,OAAO,YAAY,UAAU;AACvC,QAAO,aAAa,UAAU;AAC9B,QAAO,EAAE,wBAAwB,SAAS,QAAQ;EAClD,CACH,CACJ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { streamTranscriptionFrom, transcribe } from "./Transcriber.mjs";
|
|
2
|
+
import { i as it, n as globalExpect, r as describe, t as import_dist } from "../dist-DV5ISja1.mjs";
|
|
3
|
+
import { layer, layerSyncOnly } from "../testing/MockTranscriber.mjs";
|
|
4
|
+
import { Effect, Stream } from "effect";
|
|
5
|
+
//#region src/transcriber/Transcriber.test.ts
|
|
6
|
+
describe("Transcriber.transcribe", () => {
|
|
7
|
+
it("returns the scripted TranscriptResult", async () => {
|
|
8
|
+
const mock = layer({ transcripts: [{
|
|
9
|
+
text: "hello world",
|
|
10
|
+
durationSeconds: 1.23
|
|
11
|
+
}] });
|
|
12
|
+
const program = transcribe({
|
|
13
|
+
audio: {
|
|
14
|
+
_tag: "bytes",
|
|
15
|
+
bytes: new Uint8Array([0]),
|
|
16
|
+
mimeType: "audio/wav"
|
|
17
|
+
},
|
|
18
|
+
model: "mock-stt"
|
|
19
|
+
});
|
|
20
|
+
const result = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)));
|
|
21
|
+
globalExpect(result.text).toBe("hello world");
|
|
22
|
+
globalExpect(result.durationSeconds).toBe(1.23);
|
|
23
|
+
});
|
|
24
|
+
it("records each transcribe call", async () => {
|
|
25
|
+
const mock = layer({ transcripts: [{ text: "a" }, { text: "b" }] });
|
|
26
|
+
const program = Effect.gen(function* () {
|
|
27
|
+
yield* transcribe({
|
|
28
|
+
audio: {
|
|
29
|
+
_tag: "bytes",
|
|
30
|
+
bytes: new Uint8Array([1]),
|
|
31
|
+
mimeType: "audio/wav"
|
|
32
|
+
},
|
|
33
|
+
model: "m1"
|
|
34
|
+
});
|
|
35
|
+
yield* transcribe({
|
|
36
|
+
audio: {
|
|
37
|
+
_tag: "bytes",
|
|
38
|
+
bytes: new Uint8Array([2]),
|
|
39
|
+
mimeType: "audio/wav"
|
|
40
|
+
},
|
|
41
|
+
model: "m2"
|
|
42
|
+
});
|
|
43
|
+
return yield* mock.recorder;
|
|
44
|
+
});
|
|
45
|
+
globalExpect((await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))).transcribeCalls.map((c) => c.model)).toEqual(["m1", "m2"]);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("Transcriber capability marker (compile-time)", () => {
|
|
49
|
+
const sttReq = {
|
|
50
|
+
model: "mock-stt",
|
|
51
|
+
inputFormat: {
|
|
52
|
+
container: "raw",
|
|
53
|
+
encoding: "pcm_s16le",
|
|
54
|
+
sampleRate: 16e3
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
it("requires `SttStreaming` on the R channel of streamTranscriptionFrom", () => {
|
|
58
|
+
(0, import_dist.expectTypeOf)(Stream.fromIterable([new Uint8Array([0])]).pipe(streamTranscriptionFrom(sttReq))).toEqualTypeOf();
|
|
59
|
+
});
|
|
60
|
+
it("does NOT require `SttStreaming` for sync `transcribe`", () => {
|
|
61
|
+
(0, import_dist.expectTypeOf)(transcribe({
|
|
62
|
+
audio: {
|
|
63
|
+
_tag: "bytes",
|
|
64
|
+
bytes: new Uint8Array([0]),
|
|
65
|
+
mimeType: "audio/wav"
|
|
66
|
+
},
|
|
67
|
+
model: "m"
|
|
68
|
+
})).toEqualTypeOf();
|
|
69
|
+
});
|
|
70
|
+
it("a sync-only layer leaves `SttStreaming` unsatisfied in R", () => {
|
|
71
|
+
const syncOnly = layerSyncOnly({});
|
|
72
|
+
const events = Stream.fromIterable([new Uint8Array([0])]).pipe(streamTranscriptionFrom(sttReq));
|
|
73
|
+
(0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(syncOnly.layer))).toEqualTypeOf();
|
|
74
|
+
});
|
|
75
|
+
it("a full layer (with marker) clears R to never", () => {
|
|
76
|
+
const fullMock = layer({ streams: [[]] });
|
|
77
|
+
const events = Stream.fromIterable([new Uint8Array([0])]).pipe(streamTranscriptionFrom(sttReq));
|
|
78
|
+
(0, import_dist.expectTypeOf)(Stream.runDrain(events).pipe(Effect.provide(fullMock.layer))).toEqualTypeOf();
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("Transcriber.streamTranscriptionFrom", () => {
|
|
82
|
+
const sttReq = {
|
|
83
|
+
model: "mock-stt",
|
|
84
|
+
inputFormat: {
|
|
85
|
+
container: "raw",
|
|
86
|
+
encoding: "pcm_s16le",
|
|
87
|
+
sampleRate: 16e3
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
it("emits scripted events after draining the input audio stream", async () => {
|
|
91
|
+
const mock = layer({ streams: [[{
|
|
92
|
+
_tag: "partial",
|
|
93
|
+
text: "hello"
|
|
94
|
+
}, {
|
|
95
|
+
_tag: "final",
|
|
96
|
+
text: "hello world"
|
|
97
|
+
}]] });
|
|
98
|
+
const events = Stream.fromIterable([new Uint8Array([
|
|
99
|
+
0,
|
|
100
|
+
1,
|
|
101
|
+
2
|
|
102
|
+
]), new Uint8Array([
|
|
103
|
+
3,
|
|
104
|
+
4,
|
|
105
|
+
5
|
|
106
|
+
])]).pipe(streamTranscriptionFrom(sttReq));
|
|
107
|
+
globalExpect(await Effect.runPromise(Stream.runCollect(events).pipe(Effect.provide(mock.layer)))).toEqual([{
|
|
108
|
+
_tag: "partial",
|
|
109
|
+
text: "hello"
|
|
110
|
+
}, {
|
|
111
|
+
_tag: "final",
|
|
112
|
+
text: "hello world"
|
|
113
|
+
}]);
|
|
114
|
+
});
|
|
115
|
+
it("works data-first (direct call) as well as pipeable (data-last)", async () => {
|
|
116
|
+
const mock = layer({ streams: [[{
|
|
117
|
+
_tag: "final",
|
|
118
|
+
text: "x"
|
|
119
|
+
}]] });
|
|
120
|
+
const events = streamTranscriptionFrom(Stream.fromIterable([new Uint8Array([0])]), sttReq);
|
|
121
|
+
globalExpect(await Effect.runPromise(Stream.runCollect(events).pipe(Effect.provide(mock.layer)))).toEqual([{
|
|
122
|
+
_tag: "final",
|
|
123
|
+
text: "x"
|
|
124
|
+
}]);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
//#endregion
|
|
128
|
+
export {};
|
|
129
|
+
|
|
130
|
+
//# sourceMappingURL=Transcriber.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Transcriber.test.mjs","names":["MockTranscriber.layer","Transcriber.transcribe","Transcriber.streamTranscriptionFrom","MockTranscriber.layerSyncOnly"],"sources":["../../src/transcriber/Transcriber.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 { TranscriptEvent, TranscriptResult } from \"../domain/Transcript.js\"\nimport * as MockTranscriber from \"../testing/MockTranscriber.js\"\nimport * as Transcriber from \"./Transcriber.js\"\n\ndescribe(\"Transcriber.transcribe\", () => {\n it(\"returns the scripted TranscriptResult\", async () => {\n const mock = MockTranscriber.layer({\n transcripts: [{ text: \"hello world\", durationSeconds: 1.23 }],\n })\n const program = Transcriber.transcribe({\n audio: { _tag: \"bytes\", bytes: new Uint8Array([0]), mimeType: \"audio/wav\" },\n model: \"mock-stt\",\n })\n const result = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(result.text).toBe(\"hello world\")\n expect(result.durationSeconds).toBe(1.23)\n })\n\n it(\"records each transcribe call\", async () => {\n const mock = MockTranscriber.layer({\n transcripts: [{ text: \"a\" }, { text: \"b\" }],\n })\n const program = Effect.gen(function* () {\n yield* Transcriber.transcribe({\n audio: { _tag: \"bytes\", bytes: new Uint8Array([1]), mimeType: \"audio/wav\" },\n model: \"m1\",\n })\n yield* Transcriber.transcribe({\n audio: { _tag: \"bytes\", bytes: new Uint8Array([2]), mimeType: \"audio/wav\" },\n model: \"m2\",\n })\n return yield* mock.recorder\n })\n const rec = await Effect.runPromise(program.pipe(Effect.provide(mock.layer)))\n expect(rec.transcribeCalls.map((c) => c.model)).toEqual([\"m1\", \"m2\"])\n })\n})\n\ndescribe(\"Transcriber capability marker (compile-time)\", () => {\n const sttReq: Transcriber.CommonStreamTranscribeRequest = {\n model: \"mock-stt\",\n inputFormat: { container: \"raw\", encoding: \"pcm_s16le\", sampleRate: 16000 },\n }\n\n it(\"requires `SttStreaming` on the R channel of streamTranscriptionFrom\", () => {\n const audio: Stream.Stream<Uint8Array> = Stream.fromIterable([new Uint8Array([0])])\n const events = audio.pipe(Transcriber.streamTranscriptionFrom(sttReq))\n expectTypeOf(events).toEqualTypeOf<\n Stream.Stream<\n TranscriptEvent,\n AiError.AiError,\n Transcriber.Transcriber | Transcriber.SttStreaming\n >\n >()\n })\n\n it(\"does NOT require `SttStreaming` for sync `transcribe`\", () => {\n const eff = Transcriber.transcribe({\n audio: { _tag: \"bytes\", bytes: new Uint8Array([0]), mimeType: \"audio/wav\" },\n model: \"m\",\n })\n expectTypeOf(eff).toEqualTypeOf<\n Effect.Effect<TranscriptResult, AiError.AiError, Transcriber.Transcriber>\n >()\n })\n\n it(\"a sync-only layer leaves `SttStreaming` unsatisfied in R\", () => {\n const syncOnly = MockTranscriber.layerSyncOnly({})\n const audio: Stream.Stream<Uint8Array> = Stream.fromIterable([new Uint8Array([0])])\n const events = audio.pipe(Transcriber.streamTranscriptionFrom(sttReq))\n const program = Stream.runDrain(events).pipe(Effect.provide(syncOnly.layer))\n // `Transcriber` is provided by syncOnly.layer; `SttStreaming` is not.\n expectTypeOf(program).toEqualTypeOf<\n Effect.Effect<void, AiError.AiError, Transcriber.SttStreaming>\n >()\n })\n\n it(\"a full layer (with marker) clears R to never\", () => {\n const fullMock = MockTranscriber.layer({ streams: [[]] })\n const audio: Stream.Stream<Uint8Array> = Stream.fromIterable([new Uint8Array([0])])\n const events = audio.pipe(Transcriber.streamTranscriptionFrom(sttReq))\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(\"Transcriber.streamTranscriptionFrom\", () => {\n const sttReq: Transcriber.CommonStreamTranscribeRequest = {\n model: \"mock-stt\",\n inputFormat: { container: \"raw\", encoding: \"pcm_s16le\", sampleRate: 16000 },\n }\n\n it(\"emits scripted events after draining the input audio stream\", async () => {\n const mock = MockTranscriber.layer({\n streams: [\n [\n { _tag: \"partial\", text: \"hello\" },\n { _tag: \"final\", text: \"hello world\" },\n ],\n ],\n })\n const audio = Stream.fromIterable([new Uint8Array([0, 1, 2]), new Uint8Array([3, 4, 5])])\n const events = audio.pipe(Transcriber.streamTranscriptionFrom(sttReq))\n const collected = await Effect.runPromise(\n Stream.runCollect(events).pipe(Effect.provide(mock.layer)),\n )\n expect(collected).toEqual([\n { _tag: \"partial\", text: \"hello\" },\n { _tag: \"final\", text: \"hello world\" },\n ])\n })\n\n it(\"works data-first (direct call) as well as pipeable (data-last)\", async () => {\n const mock = MockTranscriber.layer({\n streams: [[{ _tag: \"final\", text: \"x\" }]],\n })\n const audio = Stream.fromIterable([new Uint8Array([0])])\n const events = Transcriber.streamTranscriptionFrom(audio, sttReq)\n const out = await Effect.runPromise(Stream.runCollect(events).pipe(Effect.provide(mock.layer)))\n expect(out).toEqual([{ _tag: \"final\", text: \"x\" }])\n })\n})\n"],"mappings":"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"}
|