@effect-uai/core 0.2.0 → 0.3.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-CBuPHVKA.d.mts} +1 -1
- package/dist/{AiError-CqmYjXyx.d.mts.map → AiError-CBuPHVKA.d.mts.map} +1 -1
- package/dist/Image-BZmKfIdq.d.mts +61 -0
- package/dist/Image-BZmKfIdq.d.mts.map +1 -0
- package/dist/{Items-D1C2686t.d.mts → Items-CB8Bo3FI.d.mts} +132 -80
- package/dist/Items-CB8Bo3FI.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-BWq5Hd1O.d.mts} +5 -5
- package/dist/StructuredFormat-BWq5Hd1O.d.mts.map +1 -0
- package/dist/{Tool-5wxOCuOh.d.mts → Tool-DjVufH7i.d.mts} +13 -13
- package/dist/Tool-DjVufH7i.d.mts.map +1 -0
- package/dist/{Turn-Bi83du4I.d.mts → Turn-OPaILVIB.d.mts} +5 -11
- package/dist/Turn-OPaILVIB.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 +1 -1
- package/dist/domain/AiError.mjs +1 -1
- 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/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 +15 -7
- package/dist/index.mjs +10 -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/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/streaming/JSONL.mjs +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/MockProvider.d.mts +6 -6
- package/dist/testing/MockProvider.d.mts.map +1 -1
- package/dist/testing/MockProvider.mjs.map +1 -1
- 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/package.json +29 -13
- package/src/domain/Image.ts +75 -0
- package/src/domain/Items.ts +18 -47
- package/src/domain/Media.ts +61 -0
- package/src/embedding-model/Embedding.ts +117 -0
- package/src/embedding-model/EmbeddingModel.ts +107 -0
- package/src/index.ts +9 -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/observability/Metrics.ts +1 -1
- package/src/streaming/SSE.ts +1 -1
- package/src/structured-format/StructuredFormat.ts +2 -2
- package/src/testing/MockProvider.ts +2 -2
- 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/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-CB8Bo3FI.mjs";
|
|
2
|
+
import { a as Tool, i as StreamingTool, o as ToolDescriptor, t as AnyKindTool } from "../Tool-DjVufH7i.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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Low-level primitives (loop, conversation, items, tools, streaming codecs) for building AI agents with Effect.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
"url": "https://github.com/betalyra/effect-uai",
|
|
21
21
|
"directory": "packages/core"
|
|
22
22
|
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"src",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
23
29
|
"type": "module",
|
|
24
30
|
"main": "./dist/index.mjs",
|
|
25
31
|
"types": "./dist/index.d.mts",
|
|
@@ -32,14 +38,30 @@
|
|
|
32
38
|
"types": "./dist/domain/AiError.d.mts",
|
|
33
39
|
"import": "./dist/domain/AiError.mjs"
|
|
34
40
|
},
|
|
41
|
+
"./Image": {
|
|
42
|
+
"types": "./dist/domain/Image.d.mts",
|
|
43
|
+
"import": "./dist/domain/Image.mjs"
|
|
44
|
+
},
|
|
35
45
|
"./Items": {
|
|
36
46
|
"types": "./dist/domain/Items.d.mts",
|
|
37
47
|
"import": "./dist/domain/Items.mjs"
|
|
38
48
|
},
|
|
49
|
+
"./Media": {
|
|
50
|
+
"types": "./dist/domain/Media.d.mts",
|
|
51
|
+
"import": "./dist/domain/Media.mjs"
|
|
52
|
+
},
|
|
39
53
|
"./Turn": {
|
|
40
54
|
"types": "./dist/domain/Turn.d.mts",
|
|
41
55
|
"import": "./dist/domain/Turn.mjs"
|
|
42
56
|
},
|
|
57
|
+
"./Embedding": {
|
|
58
|
+
"types": "./dist/embedding-model/Embedding.d.mts",
|
|
59
|
+
"import": "./dist/embedding-model/Embedding.mjs"
|
|
60
|
+
},
|
|
61
|
+
"./EmbeddingModel": {
|
|
62
|
+
"types": "./dist/embedding-model/EmbeddingModel.d.mts",
|
|
63
|
+
"import": "./dist/embedding-model/EmbeddingModel.mjs"
|
|
64
|
+
},
|
|
43
65
|
"./LanguageModel": {
|
|
44
66
|
"types": "./dist/language-model/LanguageModel.d.mts",
|
|
45
67
|
"import": "./dist/language-model/LanguageModel.mjs"
|
|
@@ -48,9 +70,9 @@
|
|
|
48
70
|
"types": "./dist/loop/Loop.d.mts",
|
|
49
71
|
"import": "./dist/loop/Loop.mjs"
|
|
50
72
|
},
|
|
51
|
-
"./
|
|
52
|
-
"types": "./dist/
|
|
53
|
-
"import": "./dist/
|
|
73
|
+
"./Vector": {
|
|
74
|
+
"types": "./dist/math/Vector.d.mts",
|
|
75
|
+
"import": "./dist/math/Vector.mjs"
|
|
54
76
|
},
|
|
55
77
|
"./Tool": {
|
|
56
78
|
"types": "./dist/tool/Tool.d.mts",
|
|
@@ -101,25 +123,19 @@
|
|
|
101
123
|
"import": "./dist/testing/MockProvider.mjs"
|
|
102
124
|
}
|
|
103
125
|
},
|
|
104
|
-
"files": [
|
|
105
|
-
"dist",
|
|
106
|
-
"src",
|
|
107
|
-
"README.md",
|
|
108
|
-
"LICENSE"
|
|
109
|
-
],
|
|
110
126
|
"publishConfig": {
|
|
111
127
|
"access": "public"
|
|
112
128
|
},
|
|
113
129
|
"dependencies": {
|
|
114
130
|
"@standard-schema/spec": "^1.1.0"
|
|
115
131
|
},
|
|
116
|
-
"peerDependencies": {
|
|
117
|
-
"effect": "4.0.0-beta.57"
|
|
118
|
-
},
|
|
119
132
|
"devDependencies": {
|
|
120
133
|
"effect": "4.0.0-beta.57",
|
|
121
134
|
"typescript": "^6.0.3"
|
|
122
135
|
},
|
|
136
|
+
"peerDependencies": {
|
|
137
|
+
"effect": "4.0.0-beta.57"
|
|
138
|
+
},
|
|
123
139
|
"scripts": {
|
|
124
140
|
"build": "tsdown",
|
|
125
141
|
"typecheck": "tsc --noEmit"
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Schema } from "effect"
|
|
2
|
+
import type { MediaBase64, MediaBytes, MediaSource, MediaUrl } from "./Media.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Image MIME types AI providers typically accept. The first four are the
|
|
6
|
+
* universal subset (Cohere v4, Voyage multimodal, Jina v4, Google
|
|
7
|
+
* `gemini-embedding-2`); HEIC / HEIF are Google-specific. The
|
|
8
|
+
* `(string & {})` tail keeps autocomplete on the literals while still
|
|
9
|
+
* accepting any string, so a newly-supported format works without an
|
|
10
|
+
* SDK update.
|
|
11
|
+
*/
|
|
12
|
+
export type ImageMimeType =
|
|
13
|
+
| "image/png"
|
|
14
|
+
| "image/jpeg"
|
|
15
|
+
| "image/webp"
|
|
16
|
+
| "image/gif"
|
|
17
|
+
| "image/heic"
|
|
18
|
+
| "image/heif"
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
20
|
+
| (string & {})
|
|
21
|
+
|
|
22
|
+
const ImageMimeTypeSchema = Schema.String as unknown as Schema.Schema<ImageMimeType>
|
|
23
|
+
|
|
24
|
+
export type ImageUrlSource = MediaUrl<ImageMimeType>
|
|
25
|
+
export type ImageBase64Source = MediaBase64<ImageMimeType>
|
|
26
|
+
export type ImageBytesSource = MediaBytes<ImageMimeType>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Where an image lives. Provider layers normalize across these:
|
|
30
|
+
* `bytes` becomes a base64 data URI for OpenAI / Anthropic, an
|
|
31
|
+
* `inlineData` part for Gemini, and a separate field for Cohere /
|
|
32
|
+
* Voyage. URL constraints (must be HTTPS, must be public, …) are
|
|
33
|
+
* provider-specific and validated at the layer, not in the type.
|
|
34
|
+
*/
|
|
35
|
+
export type ImageSource = MediaSource<ImageMimeType>
|
|
36
|
+
|
|
37
|
+
export const ImageUrlSource = Schema.TaggedStruct("url", {
|
|
38
|
+
url: Schema.String,
|
|
39
|
+
mimeType: Schema.optional(ImageMimeTypeSchema),
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
export const ImageBase64Source = Schema.TaggedStruct("base64", {
|
|
43
|
+
base64: Schema.String,
|
|
44
|
+
mimeType: ImageMimeTypeSchema,
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export const ImageBytesSource = Schema.TaggedStruct("bytes", {
|
|
48
|
+
bytes: Schema.Uint8Array,
|
|
49
|
+
mimeType: ImageMimeTypeSchema,
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export const ImageSource: Schema.Schema<ImageSource> = Schema.Union([
|
|
53
|
+
ImageUrlSource,
|
|
54
|
+
ImageBase64Source,
|
|
55
|
+
ImageBytesSource,
|
|
56
|
+
]) as unknown as Schema.Schema<ImageSource>
|
|
57
|
+
|
|
58
|
+
export const imageUrl = (url: string, mimeType?: ImageMimeType): ImageUrlSource =>
|
|
59
|
+
mimeType !== undefined ? { _tag: "url", url, mimeType } : { _tag: "url", url }
|
|
60
|
+
|
|
61
|
+
export const imageBase64 = (base64: string, mimeType: ImageMimeType): ImageBase64Source => ({
|
|
62
|
+
_tag: "base64",
|
|
63
|
+
base64,
|
|
64
|
+
mimeType,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
export const imageBytes = (bytes: Uint8Array, mimeType: ImageMimeType): ImageBytesSource => ({
|
|
68
|
+
_tag: "bytes",
|
|
69
|
+
bytes,
|
|
70
|
+
mimeType,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
export const isImageUrl = Schema.is(ImageUrlSource)
|
|
74
|
+
export const isImageBase64 = Schema.is(ImageBase64Source)
|
|
75
|
+
export const isImageBytes = Schema.is(ImageBytesSource)
|
package/src/domain/Items.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Schema } from "effect"
|
|
2
|
+
import { ImageSource } from "./Image.js"
|
|
2
3
|
|
|
3
4
|
// ---------------------------------------------------------------------------
|
|
4
5
|
// Content blocks (inside Message.content)
|
|
@@ -10,39 +11,13 @@ export const InputText = Schema.Struct({
|
|
|
10
11
|
})
|
|
11
12
|
export type InputText = typeof InputText.Type
|
|
12
13
|
|
|
13
|
-
/**
|
|
14
|
-
* Where an image lives. `url` covers HTTP(S) URLs (the model fetches
|
|
15
|
-
* them); `base64` covers inline bytes embedded in the request. Provider
|
|
16
|
-
* encoders dispatch on `_tag`. File-id / uploaded-asset references are
|
|
17
|
-
* provider-specific and stay out of this union for now.
|
|
18
|
-
*/
|
|
19
|
-
export const ImageUrlSource = Schema.Struct({
|
|
20
|
-
_tag: Schema.Literal("url"),
|
|
21
|
-
url: Schema.String,
|
|
22
|
-
})
|
|
23
|
-
export type ImageUrlSource = typeof ImageUrlSource.Type
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Inline image bytes. `data` is **already base64-encoded** (matches what
|
|
27
|
-
* the wire formats expect; no double-encoding needed downstream).
|
|
28
|
-
* `media_type` is the MIME type, e.g. `"image/png"`.
|
|
29
|
-
*/
|
|
30
|
-
export const ImageBase64Source = Schema.Struct({
|
|
31
|
-
_tag: Schema.Literal("base64"),
|
|
32
|
-
media_type: Schema.String,
|
|
33
|
-
data: Schema.String,
|
|
34
|
-
})
|
|
35
|
-
export type ImageBase64Source = typeof ImageBase64Source.Type
|
|
36
|
-
|
|
37
|
-
export const ImageSource = Schema.Union([ImageUrlSource, ImageBase64Source])
|
|
38
|
-
export type ImageSource = typeof ImageSource.Type
|
|
39
|
-
|
|
40
|
-
export const isImageUrlSource = (s: ImageSource): s is ImageUrlSource => s._tag === "url"
|
|
41
|
-
export const isImageBase64Source = (s: ImageSource): s is ImageBase64Source => s._tag === "base64"
|
|
42
|
-
|
|
43
14
|
/**
|
|
44
15
|
* User-provided image content block. Pair with `InputText` inside a
|
|
45
16
|
* `Message.content` array to ask "what's in this image?" style questions.
|
|
17
|
+
*
|
|
18
|
+
* `source` is the cross-modality `ImageSource` from `domain/Image.ts` -
|
|
19
|
+
* url, base64, or raw bytes. Provider codecs encode bytes to whatever
|
|
20
|
+
* wire format the provider wants.
|
|
46
21
|
*/
|
|
47
22
|
export const InputImage = Schema.Struct({
|
|
48
23
|
type: Schema.Literal("input_image"),
|
|
@@ -91,11 +66,10 @@ export type FilePath = typeof FilePath.Type
|
|
|
91
66
|
export const Annotation = Schema.Union([UrlCitation, FileCitation, ContainerFileCitation, FilePath])
|
|
92
67
|
export type Annotation = typeof Annotation.Type
|
|
93
68
|
|
|
94
|
-
export const isUrlCitation = (
|
|
95
|
-
export const isFileCitation = (
|
|
96
|
-
export const isContainerFileCitation = (
|
|
97
|
-
|
|
98
|
-
export const isFilePath = (a: Annotation): a is FilePath => a.type === "file_path"
|
|
69
|
+
export const isUrlCitation = Schema.is(UrlCitation)
|
|
70
|
+
export const isFileCitation = Schema.is(FileCitation)
|
|
71
|
+
export const isContainerFileCitation = Schema.is(ContainerFileCitation)
|
|
72
|
+
export const isFilePath = Schema.is(FilePath)
|
|
99
73
|
|
|
100
74
|
export const OutputText = Schema.Struct({
|
|
101
75
|
type: Schema.Literal("output_text"),
|
|
@@ -183,18 +157,15 @@ export type Item = typeof Item.Type
|
|
|
183
157
|
// Type guards
|
|
184
158
|
// ---------------------------------------------------------------------------
|
|
185
159
|
|
|
186
|
-
export const isInputText = (
|
|
187
|
-
export const isInputImage = (
|
|
188
|
-
|
|
189
|
-
export const
|
|
190
|
-
|
|
191
|
-
export const
|
|
192
|
-
|
|
193
|
-
export const
|
|
194
|
-
export const
|
|
195
|
-
export const isFunctionCallOutput = (item: Item): item is FunctionCallOutput =>
|
|
196
|
-
item.type === "function_call_output"
|
|
197
|
-
export const isReasoning = (item: Item): item is Reasoning => item.type === "reasoning"
|
|
160
|
+
export const isInputText = Schema.is(InputText)
|
|
161
|
+
export const isInputImage = Schema.is(InputImage)
|
|
162
|
+
export const isOutputText = Schema.is(OutputText)
|
|
163
|
+
export const isRefusal = Schema.is(Refusal)
|
|
164
|
+
|
|
165
|
+
export const isMessage = Schema.is(Message)
|
|
166
|
+
export const isFunctionCall = Schema.is(FunctionCall)
|
|
167
|
+
export const isFunctionCallOutput = Schema.is(FunctionCallOutput)
|
|
168
|
+
export const isReasoning = Schema.is(Reasoning)
|
|
198
169
|
|
|
199
170
|
// ---------------------------------------------------------------------------
|
|
200
171
|
// Usage and stop reason
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-modality media reference shape.
|
|
3
|
+
*
|
|
4
|
+
* Every "media at rest" reference - image, audio, video, document - is one
|
|
5
|
+
* of three variants:
|
|
6
|
+
*
|
|
7
|
+
* - `url` : a remote address (HTTP, GCS, etc.). The model fetches it.
|
|
8
|
+
* `mimeType` is optional - servers usually set Content-Type.
|
|
9
|
+
* Some providers (Gemini `fileData`) want it explicit.
|
|
10
|
+
*
|
|
11
|
+
* - `base64` : an inline base64-encoded payload. Always carries a
|
|
12
|
+
* `mimeType` so the consumer knows how to decode.
|
|
13
|
+
*
|
|
14
|
+
* - `bytes` : raw `Uint8Array`. Provider layers normalize to base64 or
|
|
15
|
+
* multipart upload at the wire boundary - users don't need
|
|
16
|
+
* to encode themselves.
|
|
17
|
+
*
|
|
18
|
+
* Per-modality files (`Image.ts`, future `Audio.ts` / `Video.ts` /
|
|
19
|
+
* `Document.ts`) instantiate this shape with their typed MIME union to
|
|
20
|
+
* get autocomplete on common formats while keeping the structural type
|
|
21
|
+
* uniform across modalities.
|
|
22
|
+
*
|
|
23
|
+
* Streaming media (live mic feed, streaming TTS playback) is *not*
|
|
24
|
+
* modeled here. Streams carry effect parameters (`Stream<A, E, R>`) and
|
|
25
|
+
* lifecycle (Scope, cancellation) that don't apply to media at rest. The
|
|
26
|
+
* complementary type lives alongside this one as `*Stream` in each
|
|
27
|
+
* per-modality file when those modalities land.
|
|
28
|
+
*
|
|
29
|
+
* Provider-uploaded asset references (OpenAI Files `file_id`, Gemini
|
|
30
|
+
* Files API URIs, Anthropic file IDs) are also out of scope here -
|
|
31
|
+
* they're a separate union (`FileRef`) added when needed.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export type MediaUrl<M extends string = string> = {
|
|
35
|
+
readonly _tag: "url"
|
|
36
|
+
readonly url: string
|
|
37
|
+
readonly mimeType?: M
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type MediaBase64<M extends string = string> = {
|
|
41
|
+
readonly _tag: "base64"
|
|
42
|
+
readonly base64: string
|
|
43
|
+
readonly mimeType: M
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type MediaBytes<M extends string = string> = {
|
|
47
|
+
readonly _tag: "bytes"
|
|
48
|
+
readonly bytes: Uint8Array
|
|
49
|
+
readonly mimeType: M
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type MediaSource<M extends string = string> = MediaUrl<M> | MediaBase64<M> | MediaBytes<M>
|
|
53
|
+
|
|
54
|
+
export const isMediaUrl = <M extends string>(s: MediaSource<M>): s is MediaUrl<M> =>
|
|
55
|
+
s._tag === "url"
|
|
56
|
+
|
|
57
|
+
export const isMediaBase64 = <M extends string>(s: MediaSource<M>): s is MediaBase64<M> =>
|
|
58
|
+
s._tag === "base64"
|
|
59
|
+
|
|
60
|
+
export const isMediaBytes = <M extends string>(s: MediaSource<M>): s is MediaBytes<M> =>
|
|
61
|
+
s._tag === "bytes"
|