@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.
Files changed (145) hide show
  1. package/README.md +1 -1
  2. package/dist/{AiError-CqmYjXyx.d.mts → AiError-CBuPHVKA.d.mts} +1 -1
  3. package/dist/{AiError-CqmYjXyx.d.mts.map → AiError-CBuPHVKA.d.mts.map} +1 -1
  4. package/dist/Image-BZmKfIdq.d.mts +61 -0
  5. package/dist/Image-BZmKfIdq.d.mts.map +1 -0
  6. package/dist/{Items-D1C2686t.d.mts → Items-CB8Bo3FI.d.mts} +132 -80
  7. package/dist/Items-CB8Bo3FI.d.mts.map +1 -0
  8. package/dist/Media-D_CpcM1Z.d.mts +57 -0
  9. package/dist/Media-D_CpcM1Z.d.mts.map +1 -0
  10. package/dist/{StructuredFormat-B5ueioNr.d.mts → StructuredFormat-BWq5Hd1O.d.mts} +5 -5
  11. package/dist/StructuredFormat-BWq5Hd1O.d.mts.map +1 -0
  12. package/dist/{Tool-5wxOCuOh.d.mts → Tool-DjVufH7i.d.mts} +13 -13
  13. package/dist/Tool-DjVufH7i.d.mts.map +1 -0
  14. package/dist/{Turn-Bi83du4I.d.mts → Turn-OPaILVIB.d.mts} +5 -11
  15. package/dist/Turn-OPaILVIB.d.mts.map +1 -0
  16. package/dist/{chunk-CfYAbeIz.mjs → chunk-uyGKjUfl.mjs} +2 -1
  17. package/dist/dist-DV5ISja1.mjs +13782 -0
  18. package/dist/dist-DV5ISja1.mjs.map +1 -0
  19. package/dist/domain/AiError.d.mts +1 -1
  20. package/dist/domain/AiError.mjs +1 -1
  21. package/dist/domain/Image.d.mts +2 -0
  22. package/dist/domain/Image.mjs +58 -0
  23. package/dist/domain/Image.mjs.map +1 -0
  24. package/dist/domain/Items.d.mts +2 -2
  25. package/dist/domain/Items.mjs +19 -42
  26. package/dist/domain/Items.mjs.map +1 -1
  27. package/dist/domain/Media.d.mts +2 -0
  28. package/dist/domain/Media.mjs +14 -0
  29. package/dist/domain/Media.mjs.map +1 -0
  30. package/dist/domain/Turn.d.mts +1 -1
  31. package/dist/domain/Turn.mjs +1 -1
  32. package/dist/embedding-model/Embedding.d.mts +107 -0
  33. package/dist/embedding-model/Embedding.d.mts.map +1 -0
  34. package/dist/embedding-model/Embedding.mjs +18 -0
  35. package/dist/embedding-model/Embedding.mjs.map +1 -0
  36. package/dist/embedding-model/EmbeddingModel.d.mts +97 -0
  37. package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -0
  38. package/dist/embedding-model/EmbeddingModel.mjs +17 -0
  39. package/dist/embedding-model/EmbeddingModel.mjs.map +1 -0
  40. package/dist/index.d.mts +15 -7
  41. package/dist/index.mjs +10 -2
  42. package/dist/language-model/LanguageModel.d.mts +12 -20
  43. package/dist/language-model/LanguageModel.d.mts.map +1 -1
  44. package/dist/language-model/LanguageModel.mjs +3 -20
  45. package/dist/language-model/LanguageModel.mjs.map +1 -1
  46. package/dist/loop/Loop.d.mts +31 -7
  47. package/dist/loop/Loop.d.mts.map +1 -1
  48. package/dist/loop/Loop.mjs +39 -6
  49. package/dist/loop/Loop.mjs.map +1 -1
  50. package/dist/loop/Loop.test.d.mts +1 -0
  51. package/dist/loop/Loop.test.mjs +411 -0
  52. package/dist/loop/Loop.test.mjs.map +1 -0
  53. package/dist/magic-string.es-BgIV5Mu3.mjs +1013 -0
  54. package/dist/magic-string.es-BgIV5Mu3.mjs.map +1 -0
  55. package/dist/math/Vector.d.mts +47 -0
  56. package/dist/math/Vector.d.mts.map +1 -0
  57. package/dist/math/Vector.mjs +117 -0
  58. package/dist/math/Vector.mjs.map +1 -0
  59. package/dist/observability/Metrics.d.mts +2 -2
  60. package/dist/observability/Metrics.d.mts.map +1 -1
  61. package/dist/observability/Metrics.mjs +1 -1
  62. package/dist/observability/Metrics.mjs.map +1 -1
  63. package/dist/streaming/JSONL.mjs +1 -1
  64. package/dist/streaming/JSONL.test.d.mts +1 -0
  65. package/dist/streaming/JSONL.test.mjs +70 -0
  66. package/dist/streaming/JSONL.test.mjs.map +1 -0
  67. package/dist/streaming/Lines.mjs +1 -1
  68. package/dist/streaming/SSE.d.mts +2 -2
  69. package/dist/streaming/SSE.d.mts.map +1 -1
  70. package/dist/streaming/SSE.mjs +1 -1
  71. package/dist/streaming/SSE.mjs.map +1 -1
  72. package/dist/streaming/SSE.test.d.mts +1 -0
  73. package/dist/streaming/SSE.test.mjs +72 -0
  74. package/dist/streaming/SSE.test.mjs.map +1 -0
  75. package/dist/structured-format/StructuredFormat.d.mts +1 -1
  76. package/dist/structured-format/StructuredFormat.mjs +1 -1
  77. package/dist/structured-format/StructuredFormat.mjs.map +1 -1
  78. package/dist/testing/MockProvider.d.mts +6 -6
  79. package/dist/testing/MockProvider.d.mts.map +1 -1
  80. package/dist/testing/MockProvider.mjs.map +1 -1
  81. package/dist/tool/HistoryCheck.d.mts +6 -3
  82. package/dist/tool/HistoryCheck.d.mts.map +1 -1
  83. package/dist/tool/HistoryCheck.mjs +7 -1
  84. package/dist/tool/HistoryCheck.mjs.map +1 -1
  85. package/dist/tool/Outcome.d.mts +138 -2
  86. package/dist/tool/Outcome.d.mts.map +1 -0
  87. package/dist/tool/Outcome.mjs +32 -10
  88. package/dist/tool/Outcome.mjs.map +1 -1
  89. package/dist/tool/Resolvers.d.mts +11 -8
  90. package/dist/tool/Resolvers.d.mts.map +1 -1
  91. package/dist/tool/Resolvers.mjs +10 -1
  92. package/dist/tool/Resolvers.mjs.map +1 -1
  93. package/dist/tool/Resolvers.test.d.mts +1 -0
  94. package/dist/tool/Resolvers.test.mjs +317 -0
  95. package/dist/tool/Resolvers.test.mjs.map +1 -0
  96. package/dist/tool/Tool.d.mts +1 -1
  97. package/dist/tool/Tool.mjs +1 -1
  98. package/dist/tool/Tool.mjs.map +1 -1
  99. package/dist/tool/ToolEvent.d.mts +151 -2
  100. package/dist/tool/ToolEvent.d.mts.map +1 -0
  101. package/dist/tool/ToolEvent.mjs +30 -4
  102. package/dist/tool/ToolEvent.mjs.map +1 -1
  103. package/dist/tool/Toolkit.d.mts +19 -10
  104. package/dist/tool/Toolkit.d.mts.map +1 -1
  105. package/dist/tool/Toolkit.mjs +5 -5
  106. package/dist/tool/Toolkit.mjs.map +1 -1
  107. package/dist/tool/Toolkit.test.d.mts +1 -0
  108. package/dist/tool/Toolkit.test.mjs +113 -0
  109. package/dist/tool/Toolkit.test.mjs.map +1 -0
  110. package/package.json +29 -13
  111. package/src/domain/Image.ts +75 -0
  112. package/src/domain/Items.ts +18 -47
  113. package/src/domain/Media.ts +61 -0
  114. package/src/embedding-model/Embedding.ts +117 -0
  115. package/src/embedding-model/EmbeddingModel.ts +107 -0
  116. package/src/index.ts +9 -1
  117. package/src/language-model/LanguageModel.ts +2 -22
  118. package/src/loop/Loop.test.ts +114 -2
  119. package/src/loop/Loop.ts +69 -5
  120. package/src/math/Vector.ts +138 -0
  121. package/src/observability/Metrics.ts +1 -1
  122. package/src/streaming/SSE.ts +1 -1
  123. package/src/structured-format/StructuredFormat.ts +2 -2
  124. package/src/testing/MockProvider.ts +2 -2
  125. package/src/tool/HistoryCheck.ts +2 -5
  126. package/src/tool/Outcome.ts +36 -36
  127. package/src/tool/Resolvers.test.ts +11 -35
  128. package/src/tool/Resolvers.ts +5 -14
  129. package/src/tool/Tool.ts +9 -9
  130. package/src/tool/ToolEvent.ts +28 -24
  131. package/src/tool/Toolkit.test.ts +97 -2
  132. package/src/tool/Toolkit.ts +57 -33
  133. package/dist/Items-D1C2686t.d.mts.map +0 -1
  134. package/dist/Outcome-GiaNvt7i.d.mts +0 -32
  135. package/dist/Outcome-GiaNvt7i.d.mts.map +0 -1
  136. package/dist/StructuredFormat-B5ueioNr.d.mts.map +0 -1
  137. package/dist/Tool-5wxOCuOh.d.mts.map +0 -1
  138. package/dist/ToolEvent-wTMgb2GO.d.mts +0 -29
  139. package/dist/ToolEvent-wTMgb2GO.d.mts.map +0 -1
  140. package/dist/Turn-Bi83du4I.d.mts.map +0 -1
  141. package/dist/match/Match.d.mts +0 -16
  142. package/dist/match/Match.d.mts.map +0 -1
  143. package/dist/match/Match.mjs +0 -15
  144. package/dist/match/Match.mjs.map +0 -1
  145. 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 `nextStateFrom` and apply\n * `toFunctionCallOutput` when appending to history.\n */\nimport type { ToolResult } from \"./Outcome.js\"\n\nexport type ToolEvent =\n | {\n readonly _tag: \"ApprovalRequested\"\n readonly call_id: string\n readonly tool: string\n readonly arguments: string\n }\n | {\n readonly _tag: \"Intermediate\"\n readonly call_id: string\n readonly tool: string\n readonly data: unknown\n }\n | { readonly _tag: \"Output\"; readonly result: ToolResult }\n\nexport const isApprovalRequested = (\n e: ToolEvent,\n): e is Extract<ToolEvent, { _tag: \"ApprovalRequested\" }> => e._tag === \"ApprovalRequested\"\n\nexport const isIntermediate = (\n e: ToolEvent,\n): e is Extract<ToolEvent, { _tag: \"Intermediate\" }> => e._tag === \"Intermediate\"\n\nexport const isOutput = (e: ToolEvent): e is Extract<ToolEvent, { _tag: \"Output\" }> =>\n e._tag === \"Output\"\n"],"mappings":";AA2BA,MAAa,uBACX,MAC2D,EAAE,SAAS;AAExE,MAAa,kBACX,MACsD,EAAE,SAAS;AAEnE,MAAa,YAAY,MACvB,EAAE,SAAS"}
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"}
@@ -1,19 +1,25 @@
1
- import { o as FunctionCall } from "../Items-D1C2686t.mjs";
2
- import { a as Tool, o as ToolDescriptor, t as AnyKindTool } from "../Tool-5wxOCuOh.mjs";
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 { t as ToolResult } from "../Outcome-GiaNvt7i.mjs";
5
- import { t as ToolEvent } from "../ToolEvent-wTMgb2GO.mjs";
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, nextStateFrom, outputEvent, outputEvents, toDescriptors };
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
- interface ExecuteOptions {
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: (tools: ReadonlyArray<AnyKindTool>, calls: ReadonlyArray<FunctionCall>, options?: ExecuteOptions) => Stream.Stream<ToolEvent>;
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 nextStateFrom: <S>(stream: Stream.Stream<ToolEvent>, build: (results: ReadonlyArray<ToolResult>) => S) => Stream.Stream<Event<ToolEvent, S>>;
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, nextStateFrom, outputEvent, outputEvents, Toolkit_d_exports as t, toDescriptors };
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":";;;;;;;;;;;KAmBY,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;AAAA,cAE1C,IAAA,uBAA4B,aAAA,CAAc,OAAA,GAAU,KAAA,EAAO,KAAA,KAAQ,OAAA,CAAQ,KAAA;;;;;;cAS3E,aAAA,iBAA+B,aAAA,CAAc,OAAA,GACxD,OAAA,EAAS,OAAA,CAAQ,KAAA,MAChB,aAAA,CAAc,cAAA;AAAA,UAgBA,cAAA;EAAA,SACN,WAAA;AAAA;AArCX;AAAA,cAyCa,UAAA,GACX,KAAA,EAAO,aAAA,CAAc,WAAA,GACrB,KAAA,EAAO,aAAA,CAAc,YAAA,GACrB,OAAA,GAAU,cAAA,KACT,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,cAOJ,WAAA,GAAe,MAAA,EAAQ,UAAA,KAAa,SAAA;AAAA,cAEpC,YAAA,GACX,OAAA,EAAS,aAAA,CAAc,UAAA,MACtB,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,cAqHJ,aAAA,MACX,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,SAAA,GACtB,KAAA,GAAQ,OAAA,EAAS,aAAA,CAAc,UAAA,MAAgB,CAAA,KAC9C,MAAA,CAAO,MAAA,CAAO,KAAA,CAAW,SAAA,EAAW,CAAA"}
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"}
@@ -1,14 +1,14 @@
1
- import { t as __exportAll } from "../chunk-CfYAbeIz.mjs";
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 nextStateFrom = (stream, build) => nextAfterFold(stream, [], (acc, e) => isOutput(e) ? Array.append(acc, e.result) : acc, build);
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, nextStateFrom, outputEvent, outputEvents, Toolkit_exports as t, toDescriptors };
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":";;;;;;;AAQA,SAAS,+BAA+B;CACtC,MAAM,kBAAkB,OAAO,OAAO,EAAE,MAAM,OAAO,QAAQ,CAAC;CAE9D,MAAM,aAAaA,KAAU;EAC3B,MAAM;EACN,aAAa;EACb,aAAaC,iBAAsB,gBAAgB;EACnD,MAAM,EAAE,WAAW,OAAO,QAAQ;GAAE;GAAM,OAAO;GAAI,CAAC;EACvD,CAAC;AAEF,IAAG,4DAA4D;EAC7D,MAAM,CAAC,QAAQC,cAAsBC,OAAa,CAAC,WAAW,CAAC,CAAC;AAChE,eAAO,MAAM,KAAK,CAAC,KAAK,cAAc;AACtC,eAAO,MAAM,YAAY,CAAC,KAAK,8CAA8C;AAC7E,eAAO,MAAM,YAAY,CAAC,cAAc;GACtC,MAAM;GACN,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,EAAE;GACxC,UAAU,CAAC,OAAO;GACnB,CAAC;GACF;AAEF,IAAG,wDAAwD;EAczD,MAAM,CAAC,GAAG,KAAKD,cAAsBC,OAAa,CAb/BH,KAAU;GAC3B,MAAM;GACN,aAAa;GACb,aAAaC,iBAAsB,gBAAgB;GACnD,WAAW,OAAO,QAAQ,EAAE,CAAC;GAC7B,QAAQ;GACT,CAOkD,EANjCD,KAAU;GAC1B,MAAM;GACN,aAAa;GACb,aAAaC,iBAAsB,gBAAgB;GACnD,WAAW,OAAO,QAAQ,EAAE,CAAC;GAC9B,CAC8D,CAAU,CAAC,CAAC;AAC3E,eAAO,GAAG,OAAO,CAAC,KAAK,KAAK;AAC5B,eAAO,EAAE,CAAC,IAAI,eAAe,SAAS;GACtC;EACF;AAEF,SAAS,wDAAwD;CAM/D,MAAM,sBAAsB,QAAQ,SAA4C,CAC9E,qBACD,CAAC;CAGF,MAAM,kBAAkB,QAAQ,SAAoC,CAAC,iBAAiB,CAAC;CAEvF,MAAM,QAAQ,OAAO,OAAO,EAAE,CAAC;CAE/B,MAAM,aAAaD,KAAU;EAC3B,MAAM;EACN,aAAa;EACb,aAAaC,iBAAsB,MAAM;EACzC,WACE,OAAO,IAAI,aAAa;GACtB,MAAM,EAAE,QAAQ,OAAO;AACvB,UAAO;IAAE,QAAQ;IAAW;IAAK;IACjC;EACL,CAAC;CAEF,MAAM,YAAYD,KAAU;EAC1B,MAAM;EACN,aAAa;EACb,aAAaC,iBAAsB,MAAM;EACzC,WACE,OAAO,IAAI,aAAa;GACtB,MAAM,EAAE,QAAQ,OAAO;AACvB,UAAO;IAAE,QAAQ;IAAO;IAAK;IAC7B;EACL,CAAC;CAEF,MAAM,QAAQ,MAAc,QAA8B;EACxD,MAAM;EACN,SAAS;EACT;EACA,WAAW;EACZ;AAED,IAAG,2EAA2E;AAE5E,GAAA,GAAA,YAAA,cADeG,WAAmB,CAAC,YAAY,UAAU,EAAE,EAAE,CAC1C,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,gDAAgD,YAAY;EAC7D,MAAM,QAAQ,MAAM,SAClB,MAAM,QAAQ,eAAe,EAAE,KAAK,eAAe,CAAC,EACpD,MAAM,QAAQ,WAAW,EAAE,KAAK,WAAW,CAAC,CAC7C;EAED,MAAM,UAAUA,WACd,CAAC,YAAY,UAAU,EACvB,CAAC,KAAK,eAAe,KAAK,EAAE,KAAK,cAAc,KAAK,CAAC,CACtD,CAAC,KAAK,OAAO,YAAY,OAAO,QAAQ,MAAM,CAAC;EAEhD,MAAM,SAAS,MAAM,OAAO,WAAW,QAAQ;EAC/C,MAAM,UAAU,MAAM,KAAK,OAAO,CAAC,OAAO,SAAS;EACnD,MAAM,SAAS,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC,EAAE,OAAO,SAAS,EAAE,OAAO,CAAC,CAAC;EAExE,MAAM,IAAI,OAAO,IAAI,KAAK;EAC1B,MAAM,IAAI,OAAO,IAAI,KAAK;AAC1B,eAAO,MAAM,KAAA,KAAa,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ;GACvD,QAAQ;GACR,KAAK;GACN,CAAC;AACF,eAAO,MAAM,KAAA,KAAa,QAAQ,EAAE,IAAI,EAAE,MAAM,CAAC,QAAQ;GACvD,QAAQ;GACR,KAAK;GACN,CAAC;GACF;AAEF,IAAG,mDAAmD;EACpD,MAAM,QAAQJ,KAAU;GACtB,MAAM;GACN,aAAa;GACb,aAAaC,iBAAsB,MAAM;GACzC,WAAW,OAAO,QAAQ,EAAE;GAC7B,CAAC;AAEF,GAAA,GAAA,YAAA,cADeG,WAAmB,CAAC,MAAM,EAAE,EAAE,CAC1B,CAAC,CAAC,eAElB;GACH;EACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/core",
3
- "version": "0.2.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
- "./Match": {
52
- "types": "./dist/match/Match.d.mts",
53
- "import": "./dist/match/Match.mjs"
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)
@@ -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 = (a: Annotation): a is UrlCitation => a.type === "url_citation"
95
- export const isFileCitation = (a: Annotation): a is FileCitation => a.type === "file_citation"
96
- export const isContainerFileCitation = (a: Annotation): a is ContainerFileCitation =>
97
- a.type === "container_file_citation"
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 = (block: ContentBlock): block is InputText => block.type === "input_text"
187
- export const isInputImage = (block: ContentBlock): block is InputImage =>
188
- block.type === "input_image"
189
- export const isOutputText = (block: ContentBlock): block is OutputText =>
190
- block.type === "output_text"
191
- export const isRefusal = (block: ContentBlock): block is Refusal => block.type === "refusal"
192
-
193
- export const isMessage = (item: Item): item is Message => item.type === "message"
194
- export const isFunctionCall = (item: Item): item is FunctionCall => item.type === "function_call"
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"