@effect-uai/core 0.12.1 → 0.14.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 (97) hide show
  1. package/dist/{Audio-60lKKHxS.d.mts → Audio-_nGR4V0X.d.mts} +2 -2
  2. package/dist/{Audio-60lKKHxS.d.mts.map → Audio-_nGR4V0X.d.mts.map} +1 -1
  3. package/dist/Image-Cv6iBLTy.d.mts +99 -0
  4. package/dist/Image-Cv6iBLTy.d.mts.map +1 -0
  5. package/dist/{Items-SfUabNuB.d.mts → Items-C7MyrYPX.d.mts} +35 -4
  6. package/dist/Items-C7MyrYPX.d.mts.map +1 -0
  7. package/dist/Media-BbX7BAaO.d.mts +60 -0
  8. package/dist/Media-BbX7BAaO.d.mts.map +1 -0
  9. package/dist/{Tool-DE-rnvkw.d.mts → Tool-BF78QWtn.d.mts} +2 -2
  10. package/dist/{Tool-DE-rnvkw.d.mts.map → Tool-BF78QWtn.d.mts.map} +1 -1
  11. package/dist/{ToolEvent-D99MGQYH.d.mts → ToolEvent-Di4a723B.d.mts} +2 -2
  12. package/dist/{ToolEvent-D99MGQYH.d.mts.map → ToolEvent-Di4a723B.d.mts.map} +1 -1
  13. package/dist/{ToolResult-Cj8ChodL.d.mts → ToolResult-BsYQRjmW.d.mts} +2 -2
  14. package/dist/{ToolResult-Cj8ChodL.d.mts.map → ToolResult-BsYQRjmW.d.mts.map} +1 -1
  15. package/dist/{Toolkit-BEZrNPTI.d.mts → Toolkit-DAjJ9Tvl.d.mts} +5 -5
  16. package/dist/{Toolkit-BEZrNPTI.d.mts.map → Toolkit-DAjJ9Tvl.d.mts.map} +1 -1
  17. package/dist/browser/BrowserTool.d.mts +1 -1
  18. package/dist/capabilities/Capabilities.d.mts +26 -2
  19. package/dist/capabilities/Capabilities.d.mts.map +1 -1
  20. package/dist/capabilities/Capabilities.mjs +32 -1
  21. package/dist/capabilities/Capabilities.mjs.map +1 -1
  22. package/dist/chunker/Chunker.d.mts +32 -0
  23. package/dist/chunker/Chunker.d.mts.map +1 -0
  24. package/dist/chunker/Chunker.mjs +14 -0
  25. package/dist/chunker/Chunker.mjs.map +1 -0
  26. package/dist/domain/Audio.d.mts +1 -1
  27. package/dist/domain/Image.d.mts +3 -2
  28. package/dist/domain/Image.mjs +29 -3
  29. package/dist/domain/Image.mjs.map +1 -1
  30. package/dist/domain/Items.d.mts +2 -2
  31. package/dist/domain/Items.mjs +19 -1
  32. package/dist/domain/Items.mjs.map +1 -1
  33. package/dist/domain/Media.d.mts +2 -2
  34. package/dist/domain/Media.mjs +51 -2
  35. package/dist/domain/Media.mjs.map +1 -1
  36. package/dist/domain/Music.d.mts +3 -8
  37. package/dist/domain/Music.d.mts.map +1 -1
  38. package/dist/domain/Music.mjs.map +1 -1
  39. package/dist/domain/Research.d.mts +1 -1
  40. package/dist/domain/Turn.d.mts +73 -9
  41. package/dist/domain/Turn.d.mts.map +1 -1
  42. package/dist/domain/Turn.mjs +38 -2
  43. package/dist/domain/Turn.mjs.map +1 -1
  44. package/dist/embedding-model/Embedding.d.mts +1 -1
  45. package/dist/image-generator/ImageGenerator.d.mts +188 -0
  46. package/dist/image-generator/ImageGenerator.d.mts.map +1 -0
  47. package/dist/image-generator/ImageGenerator.mjs +50 -0
  48. package/dist/image-generator/ImageGenerator.mjs.map +1 -0
  49. package/dist/index.d.mts +14 -9
  50. package/dist/index.mjs +7 -2
  51. package/dist/language-model/LanguageModel.d.mts +2 -2
  52. package/dist/music-generator/MusicGenerator.d.mts +3 -2
  53. package/dist/music-generator/MusicGenerator.d.mts.map +1 -1
  54. package/dist/observability/Metrics.d.mts +1 -1
  55. package/dist/reranker/Reranker.d.mts +46 -0
  56. package/dist/reranker/Reranker.d.mts.map +1 -0
  57. package/dist/reranker/Reranker.mjs +14 -0
  58. package/dist/reranker/Reranker.mjs.map +1 -0
  59. package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +1 -1
  60. package/dist/streaming/Multipart.d.mts +21 -0
  61. package/dist/streaming/Multipart.d.mts.map +1 -0
  62. package/dist/streaming/Multipart.mjs +26 -0
  63. package/dist/streaming/Multipart.mjs.map +1 -0
  64. package/dist/testing/MockMusicGenerator.d.mts +1 -1
  65. package/dist/testing/MockProvider.d.mts +1 -1
  66. package/dist/testing/MockSpeechSynthesizer.d.mts +1 -1
  67. package/dist/tokenizer/Tokenizer.d.mts +22 -0
  68. package/dist/tokenizer/Tokenizer.d.mts.map +1 -0
  69. package/dist/tokenizer/Tokenizer.mjs +9 -0
  70. package/dist/tokenizer/Tokenizer.mjs.map +1 -0
  71. package/dist/tool/Approval.d.mts +3 -3
  72. package/dist/tool/HistoryCheck.d.mts +2 -2
  73. package/dist/tool/Tool.d.mts +1 -1
  74. package/dist/tool/ToolEvent.d.mts +1 -1
  75. package/dist/tool/ToolResult.d.mts +1 -1
  76. package/dist/tool/Toolkit.d.mts +1 -1
  77. package/dist/transcriber/Transcriber.d.mts +1 -1
  78. package/dist/web-read/WebReadTool.d.mts +1 -1
  79. package/dist/web-search/WebSearchTool.d.mts +1 -1
  80. package/package.json +21 -1
  81. package/src/capabilities/Capabilities.ts +38 -0
  82. package/src/chunker/Chunker.ts +34 -0
  83. package/src/domain/Image.ts +68 -2
  84. package/src/domain/Items.ts +18 -1
  85. package/src/domain/Media.ts +66 -0
  86. package/src/domain/Music.ts +4 -3
  87. package/src/domain/Turn.ts +56 -0
  88. package/src/image-generator/ImageGenerator.ts +165 -0
  89. package/src/index.ts +5 -0
  90. package/src/reranker/Reranker.ts +51 -0
  91. package/src/streaming/Multipart.ts +29 -0
  92. package/src/tokenizer/Tokenizer.ts +19 -0
  93. package/dist/Image-BK8-4H6s.d.mts +0 -59
  94. package/dist/Image-BK8-4H6s.d.mts.map +0 -1
  95. package/dist/Items-SfUabNuB.d.mts.map +0 -1
  96. package/dist/Media-DwgpifPD.d.mts +0 -57
  97. package/dist/Media-DwgpifPD.d.mts.map +0 -1
@@ -0,0 +1,21 @@
1
+ import { Effect } from "effect";
2
+ import { HttpClientRequest } from "effect/unstable/http";
3
+ declare namespace Multipart_d_exports {
4
+ export { bodyMultipart };
5
+ }
6
+ /**
7
+ * Multipart body for an `HttpClientRequest`, encoded to bytes.
8
+ *
9
+ * Use this over `HttpClientRequest.bodyFormData`: the Undici client
10
+ * passes the `FormData` object straight to `dispatcher.request`, which
11
+ * cannot serialise it, so the request is never sent and the effect
12
+ * hangs with no error. Encoding here produces the same bytes every
13
+ * other client derives internally, so it works on all of them.
14
+ *
15
+ * The failure is whatever `Response.arrayBuffer` rejects with; map it
16
+ * to your provider's transport error at the call site.
17
+ */
18
+ declare const bodyMultipart: (form: FormData) => Effect.Effect<(request: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest, unknown>;
19
+ //#endregion
20
+ export { bodyMultipart, Multipart_d_exports as t };
21
+ //# sourceMappingURL=Multipart.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Multipart.d.mts","names":[],"sources":["../../src/streaming/Multipart.ts"],"mappings":";;;;;;;;;;;;;;;;;cAea,gBAAa,MAClB,aACL,OAAO,QACP,SAAS,kBAAkB,sBAAsB,kBAAkB"}
@@ -0,0 +1,26 @@
1
+ import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
+ import { Effect } from "effect";
3
+ import { HttpClientRequest } from "effect/unstable/http";
4
+ //#region src/streaming/Multipart.ts
5
+ var Multipart_exports = /* @__PURE__ */ __exportAll({ bodyMultipart: () => bodyMultipart });
6
+ /**
7
+ * Multipart body for an `HttpClientRequest`, encoded to bytes.
8
+ *
9
+ * Use this over `HttpClientRequest.bodyFormData`: the Undici client
10
+ * passes the `FormData` object straight to `dispatcher.request`, which
11
+ * cannot serialise it, so the request is never sent and the effect
12
+ * hangs with no error. Encoding here produces the same bytes every
13
+ * other client derives internally, so it works on all of them.
14
+ *
15
+ * The failure is whatever `Response.arrayBuffer` rejects with; map it
16
+ * to your provider's transport error at the call site.
17
+ */
18
+ const bodyMultipart = (form) => Effect.gen(function* () {
19
+ const encoded = new Response(form);
20
+ const buffer = yield* Effect.tryPromise(() => encoded.arrayBuffer());
21
+ return HttpClientRequest.bodyUint8Array(new Uint8Array(buffer), encoded.headers.get("content-type") ?? "multipart/form-data");
22
+ });
23
+ //#endregion
24
+ export { bodyMultipart, Multipart_exports as t };
25
+
26
+ //# sourceMappingURL=Multipart.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Multipart.mjs","names":[],"sources":["../../src/streaming/Multipart.ts"],"sourcesContent":["import { Effect } from \"effect\"\nimport { HttpClientRequest } from \"effect/unstable/http\"\n\n/**\n * Multipart body for an `HttpClientRequest`, encoded to bytes.\n *\n * Use this over `HttpClientRequest.bodyFormData`: the Undici client\n * passes the `FormData` object straight to `dispatcher.request`, which\n * cannot serialise it, so the request is never sent and the effect\n * hangs with no error. Encoding here produces the same bytes every\n * other client derives internally, so it works on all of them.\n *\n * The failure is whatever `Response.arrayBuffer` rejects with; map it\n * to your provider's transport error at the call site.\n */\nexport const bodyMultipart = (\n form: FormData,\n): Effect.Effect<\n (request: HttpClientRequest.HttpClientRequest) => HttpClientRequest.HttpClientRequest,\n unknown\n> =>\n Effect.gen(function* () {\n const encoded = new Response(form)\n const buffer = yield* Effect.tryPromise(() => encoded.arrayBuffer())\n return HttpClientRequest.bodyUint8Array(\n new Uint8Array(buffer),\n encoded.headers.get(\"content-type\") ?? \"multipart/form-data\",\n )\n })\n"],"mappings":";;;;;;;;;;;;;;;;;AAeA,MAAa,iBACX,SAKA,OAAO,IAAI,aAAa;CACtB,MAAM,UAAU,IAAI,SAAS,IAAI;CACjC,MAAM,SAAS,OAAO,OAAO,iBAAiB,QAAQ,YAAY,CAAC;CACnE,OAAO,kBAAkB,eACvB,IAAI,WAAW,MAAM,GACrB,QAAQ,QAAQ,IAAI,cAAc,KAAK,qBACzC;AACF,CAAC"}
@@ -1,4 +1,4 @@
1
- import { n as AudioChunk } from "../Audio-60lKKHxS.mjs";
1
+ import { n as AudioChunk } from "../Audio-_nGR4V0X.mjs";
2
2
  import { CommonGenerateMusicRequest, CommonStreamGenerateMusicRequest, GenerateResult, MusicStreamEvent, audioEvent } from "../domain/Music.mjs";
3
3
  import { MusicGenerator, MusicInteractiveSession } from "../music-generator/MusicGenerator.mjs";
4
4
  import { Effect, Layer } from "effect";
@@ -1,4 +1,4 @@
1
- import { o as HistoryItem } from "../Items-SfUabNuB.mjs";
1
+ import { o as HistoryItem } from "../Items-C7MyrYPX.mjs";
2
2
  import { Turn } from "../domain/Turn.mjs";
3
3
  import { LanguageModel, LanguageModelService } from "../language-model/LanguageModel.mjs";
4
4
  import { Duration, Effect, Layer } from "effect";
@@ -1,4 +1,4 @@
1
- import { n as AudioChunk, t as AudioBlob } from "../Audio-60lKKHxS.mjs";
1
+ import { n as AudioChunk, t as AudioBlob } from "../Audio-_nGR4V0X.mjs";
2
2
  import { CommonStreamSynthesizeRequest, CommonSynthesizeDialogueRequest, CommonSynthesizeRequest, MultiSpeakerTts, SpeechSynthesizer, TtsIncrementalText } from "../speech-synthesizer/SpeechSynthesizer.mjs";
3
3
  import { Effect, Layer } from "effect";
4
4
  //#region src/testing/MockSpeechSynthesizer.d.ts
@@ -0,0 +1,22 @@
1
+ import { Context } from "effect";
2
+ declare namespace Tokenizer_d_exports {
3
+ export { Tokenizer, TokenizerService };
4
+ }
5
+ /**
6
+ * Turning text into tokens, for sizing chunks, budgeting context, and
7
+ * estimating usage a provider does not report. Count with `encode(text).length`.
8
+ *
9
+ * Implementor contract: both methods are synchronous, so counting inside a loop
10
+ * never awaits; loading a vocabulary is the layer's job. `decode` inverts
11
+ * `encode`. Implement this only over a real vocabulary, never a heuristic:
12
+ * a character-count estimate is a plain function, not a service.
13
+ */
14
+ type TokenizerService = {
15
+ readonly encode: (text: string) => ReadonlyArray<number>;
16
+ readonly decode: (tokens: ReadonlyArray<number>) => string;
17
+ };
18
+ declare const Tokenizer_base: Context.ServiceClass<Tokenizer, "@betalyra/effect-uai/Tokenizer", TokenizerService>;
19
+ declare class Tokenizer extends Tokenizer_base {}
20
+ //#endregion
21
+ export { Tokenizer, TokenizerService, Tokenizer_d_exports as t };
22
+ //# sourceMappingURL=Tokenizer.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tokenizer.d.mts","names":[],"sources":["../../src/tokenizer/Tokenizer.ts"],"mappings":";;;;;;;;;;;;;KAWY;WACD,SAAS,iBAAiB;WAC1B,SAAS,QAAQ;;;cAGf,kBAAkB"}
@@ -0,0 +1,9 @@
1
+ import { n as __exportAll } from "../rolldown-runtime-EAsCxrDo.mjs";
2
+ import { Context } from "effect";
3
+ //#region src/tokenizer/Tokenizer.ts
4
+ var Tokenizer_exports = /* @__PURE__ */ __exportAll({ Tokenizer: () => Tokenizer });
5
+ var Tokenizer = class extends Context.Service()("@betalyra/effect-uai/Tokenizer") {};
6
+ //#endregion
7
+ export { Tokenizer, Tokenizer_exports as t };
8
+
9
+ //# sourceMappingURL=Tokenizer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Tokenizer.mjs","names":[],"sources":["../../src/tokenizer/Tokenizer.ts"],"sourcesContent":["import { Context } from \"effect\"\n\n/**\n * Turning text into tokens, for sizing chunks, budgeting context, and\n * estimating usage a provider does not report. Count with `encode(text).length`.\n *\n * Implementor contract: both methods are synchronous, so counting inside a loop\n * never awaits; loading a vocabulary is the layer's job. `decode` inverts\n * `encode`. Implement this only over a real vocabulary, never a heuristic:\n * a character-count estimate is a plain function, not a service.\n */\nexport type TokenizerService = {\n readonly encode: (text: string) => ReadonlyArray<number>\n readonly decode: (tokens: ReadonlyArray<number>) => string\n}\n\nexport class Tokenizer extends Context.Service<Tokenizer, TokenizerService>()(\n \"@betalyra/effect-uai/Tokenizer\",\n) {}\n"],"mappings":";;;;AAgBA,IAAa,YAAb,cAA+B,QAAQ,QAAqC,CAAC,CAC3E,gCACF,CAAC,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
- import { v as ToolCall } from "../Items-SfUabNuB.mjs";
2
- import { t as ToolResult } from "../ToolResult-Cj8ChodL.mjs";
3
- import { t as ToolEvent } from "../ToolEvent-D99MGQYH.mjs";
1
+ import { y as ToolCall } from "../Items-C7MyrYPX.mjs";
2
+ import { t as ToolResult } from "../ToolResult-BsYQRjmW.mjs";
3
+ import { t as ToolEvent } from "../ToolEvent-Di4a723B.mjs";
4
4
  import { Data, Duration, Effect, Queue, Scope, Stream } from "effect";
5
5
  declare namespace Approval_d_exports {
6
6
  export { ApprovalDecision, ApprovalMapEntry, FromQueueOptions, ToolCallPlan, Verdict, approvalRequested, approve, fromMap, fromQueue, reject, splitApprovalDecisions };
@@ -1,5 +1,5 @@
1
- import { o as HistoryItem, v as ToolCall } from "../Items-SfUabNuB.mjs";
2
- import { t as ToolResult } from "../ToolResult-Cj8ChodL.mjs";
1
+ import { o as HistoryItem, y as ToolCall } from "../Items-C7MyrYPX.mjs";
2
+ import { t as ToolResult } from "../ToolResult-BsYQRjmW.mjs";
3
3
  declare namespace HistoryCheck_d_exports {
4
4
  export { cancelAllPending, findUnansweredCalls, isReconciled };
5
5
  }
@@ -1,2 +1,2 @@
1
- import { A as providerToolsOf, C as fromEffectSchema, D as make, E as isProviderTool, M as toDescriptors, N as withName, O as noInput, P as withRun, S as fail, T as interaction, a as Emit, b as descriptorsOf, c as SignalTool, d as ToolE, f as ToolError, g as ToolValidationError, h as ToolR, i as DecodeResult, j as signal, k as provider, l as Tool, m as ToolInputSchema, n as AnyTool, o as InteractionTool, p as ToolFailed, r as DecodableTool, s as ProviderTool, t as AnyLocalTool, u as ToolDescriptor, v as decodeArgs, w as fromStandardSchema, x as execute, y as decodeCallInput } from "../Tool-DE-rnvkw.mjs";
1
+ import { A as providerToolsOf, C as fromEffectSchema, D as make, E as isProviderTool, M as toDescriptors, N as withName, O as noInput, P as withRun, S as fail, T as interaction, a as Emit, b as descriptorsOf, c as SignalTool, d as ToolE, f as ToolError, g as ToolValidationError, h as ToolR, i as DecodeResult, j as signal, k as provider, l as Tool, m as ToolInputSchema, n as AnyTool, o as InteractionTool, p as ToolFailed, r as DecodableTool, s as ProviderTool, t as AnyLocalTool, u as ToolDescriptor, v as decodeArgs, w as fromStandardSchema, x as execute, y as decodeCallInput } from "../Tool-BF78QWtn.mjs";
2
2
  export { AnyLocalTool, AnyTool, DecodableTool, DecodeResult, Emit, InteractionTool, ProviderTool, SignalTool, Tool, ToolDescriptor, ToolE, ToolError, ToolFailed, ToolInputSchema, ToolR, ToolValidationError, decodeArgs, decodeCallInput, descriptorsOf, execute, fail, fromEffectSchema, fromStandardSchema, interaction, isProviderTool, make, noInput, provider, providerToolsOf, signal, toDescriptors, withName, withRun };
@@ -1,2 +1,2 @@
1
- import { a as isProgress, i as isOutput, r as isApprovalRequested, t as ToolEvent } from "../ToolEvent-D99MGQYH.mjs";
1
+ import { a as isProgress, i as isOutput, r as isApprovalRequested, t as ToolEvent } from "../ToolEvent-Di4a723B.mjs";
2
2
  export { ToolEvent, isApprovalRequested, isOutput, isProgress };
@@ -1,2 +1,2 @@
1
- import { a as executionError, c as isOk, d as toToolCallOutput, f as validationError, i as denied, l as nonLocalTool, o as failed, r as cancelled, s as isFailure, t as ToolResult, u as serializeValue } from "../ToolResult-Cj8ChodL.mjs";
1
+ import { a as executionError, c as isOk, d as toToolCallOutput, f as validationError, i as denied, l as nonLocalTool, o as failed, r as cancelled, s as isFailure, t as ToolResult, u as serializeValue } from "../ToolResult-BsYQRjmW.mjs";
2
2
  export { ToolResult, cancelled, denied, executionError, failed, isFailure, isOk, nonLocalTool, serializeValue, toToolCallOutput, validationError };
@@ -1,2 +1,2 @@
1
- import { S as wrap, _ as fromArray, a as ToolMap, b as namespace, c as ToolkitR, d as appendToolResults, f as collectResults, g as descriptors, h as describeFailures, i as NoDuplicateNames, m as continueWithResults, n as ExecuteOptions, o as Toolkit, p as compose, r as Middleware, s as ToolkitE, t as DuplicateToolName, u as UniqueTools, v as make, x as run, y as makeNamespaced } from "../Toolkit-BEZrNPTI.mjs";
1
+ import { S as wrap, _ as fromArray, a as ToolMap, b as namespace, c as ToolkitR, d as appendToolResults, f as collectResults, g as descriptors, h as describeFailures, i as NoDuplicateNames, m as continueWithResults, n as ExecuteOptions, o as Toolkit, p as compose, r as Middleware, s as ToolkitE, t as DuplicateToolName, u as UniqueTools, v as make, x as run, y as makeNamespaced } from "../Toolkit-DAjJ9Tvl.mjs";
2
2
  export { DuplicateToolName, ExecuteOptions, Middleware, NoDuplicateNames, ToolMap, Toolkit, ToolkitE, ToolkitR, UniqueTools, appendToolResults, collectResults, compose, continueWithResults, describeFailures, descriptors, fromArray, make, makeNamespaced, namespace, run, wrap };
@@ -1,5 +1,5 @@
1
1
  import { t as AiError } from "../AiError-eLV9t1lU.mjs";
2
- import { a as AudioSource, r as AudioFormat } from "../Audio-60lKKHxS.mjs";
2
+ import { a as AudioSource, r as AudioFormat } from "../Audio-_nGR4V0X.mjs";
3
3
  import { TranscriptEvent, TranscriptResult } from "../domain/Transcript.mjs";
4
4
  import { Context, Effect, Stream } from "effect";
5
5
  declare namespace Transcriber_d_exports {
@@ -1,4 +1,4 @@
1
- import { l as Tool } from "../Tool-DE-rnvkw.mjs";
1
+ import { l as Tool } from "../Tool-BF78QWtn.mjs";
2
2
  import { t as AiError } from "../AiError-eLV9t1lU.mjs";
3
3
  import { ReadFormat, ReadResponse, WebRead } from "./WebRead.mjs";
4
4
  import { Schema } from "effect";
@@ -1,4 +1,4 @@
1
- import { l as Tool } from "../Tool-DE-rnvkw.mjs";
1
+ import { l as Tool } from "../Tool-BF78QWtn.mjs";
2
2
  import { t as AiError } from "../AiError-eLV9t1lU.mjs";
3
3
  import { SearchResult, WebSearch } from "./WebSearch.mjs";
4
4
  import { Schema } from "effect";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-uai/core",
3
- "version": "0.12.1",
3
+ "version": "0.14.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",
@@ -42,6 +42,10 @@
42
42
  "types": "./dist/domain/Audio.d.mts",
43
43
  "import": "./dist/domain/Audio.mjs"
44
44
  },
45
+ "./Chunker": {
46
+ "types": "./dist/chunker/Chunker.d.mts",
47
+ "import": "./dist/chunker/Chunker.mjs"
48
+ },
45
49
  "./Citation": {
46
50
  "types": "./dist/domain/Citation.d.mts",
47
51
  "import": "./dist/domain/Citation.mjs"
@@ -102,6 +106,10 @@
102
106
  "types": "./dist/embedding-model/EmbeddingModel.d.mts",
103
107
  "import": "./dist/embedding-model/EmbeddingModel.mjs"
104
108
  },
109
+ "./ImageGenerator": {
110
+ "types": "./dist/image-generator/ImageGenerator.d.mts",
111
+ "import": "./dist/image-generator/ImageGenerator.mjs"
112
+ },
105
113
  "./LanguageModel": {
106
114
  "types": "./dist/language-model/LanguageModel.d.mts",
107
115
  "import": "./dist/language-model/LanguageModel.mjs"
@@ -110,6 +118,10 @@
110
118
  "types": "./dist/music-generator/MusicGenerator.d.mts",
111
119
  "import": "./dist/music-generator/MusicGenerator.mjs"
112
120
  },
121
+ "./Reranker": {
122
+ "types": "./dist/reranker/Reranker.d.mts",
123
+ "import": "./dist/reranker/Reranker.mjs"
124
+ },
113
125
  "./Memory": {
114
126
  "types": "./dist/sandbox/Memory.d.mts",
115
127
  "import": "./dist/sandbox/Memory.mjs"
@@ -166,6 +178,10 @@
166
178
  "types": "./dist/math/Vector.d.mts",
167
179
  "import": "./dist/math/Vector.mjs"
168
180
  },
181
+ "./Tokenizer": {
182
+ "types": "./dist/tokenizer/Tokenizer.d.mts",
183
+ "import": "./dist/tokenizer/Tokenizer.mjs"
184
+ },
169
185
  "./Tool": {
170
186
  "types": "./dist/tool/Tool.d.mts",
171
187
  "import": "./dist/tool/Tool.mjs"
@@ -202,6 +218,10 @@
202
218
  "types": "./dist/streaming/Lines.d.mts",
203
219
  "import": "./dist/streaming/Lines.mjs"
204
220
  },
221
+ "./Multipart": {
222
+ "types": "./dist/streaming/Multipart.d.mts",
223
+ "import": "./dist/streaming/Multipart.mjs"
224
+ },
205
225
  "./SSE": {
206
226
  "types": "./dist/streaming/SSE.d.mts",
207
227
  "import": "./dist/streaming/SSE.mjs"
@@ -1,4 +1,5 @@
1
1
  import { Effect } from "effect"
2
+ import type { ContentBlock, HistoryItem } from "../domain/Items.js"
2
3
 
3
4
  /**
4
5
  * Structured event emitted when a provider adapter drops a Common
@@ -79,3 +80,40 @@ export const warnDroppedWhen = <T>(
79
80
  value: T | undefined,
80
81
  warning: Omit<CapabilityWarning, "_tag" | "value">,
81
82
  ): Effect.Effect<void> => (value === undefined ? Effect.void : warnDropped({ ...warning, value }))
83
+
84
+ /**
85
+ * Warn when history carries a content block this provider's wire has no
86
+ * place for, which its encoder therefore omits.
87
+ *
88
+ * History is provider-agnostic, so a turn one provider produced can be
89
+ * replayed into another. A block only one provider can express
90
+ * (`output_image`, which needs an assistant-role image part) then reaches
91
+ * an encoder with nothing to map it to. Omitting is right, since failing
92
+ * would break replaying a mixed history just to continue the text, but it
93
+ * should not be silent.
94
+ *
95
+ * Call once from the effectful request path; the block encoders stay pure.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * yield* warnDroppedBlocks(history, "output_image", {
100
+ * provider: "anthropic",
101
+ * capability: "output_image",
102
+ * reason: "Assistant messages carry no image here. Convert with `imagesAsInput` to send it as user content.",
103
+ * })
104
+ * ```
105
+ */
106
+ export const warnDroppedBlocks = (
107
+ history: ReadonlyArray<HistoryItem>,
108
+ type: ContentBlock["type"],
109
+ warning: Omit<CapabilityWarning, "_tag" | "field" | "value">,
110
+ ): Effect.Effect<void> => {
111
+ const count = history.reduce(
112
+ (n, item) =>
113
+ n + (item.type === "message" ? item.content.filter((b) => b.type === type).length : 0),
114
+ 0,
115
+ )
116
+ return count === 0
117
+ ? Effect.void
118
+ : warnDropped({ ...warning, field: type, value: `${count} block(s)` })
119
+ }
@@ -0,0 +1,34 @@
1
+ import { Context, Effect } from "effect"
2
+ import type * as AiError from "../domain/AiError.js"
3
+
4
+ /**
5
+ * A passage of a document, with the character offsets it came from.
6
+ *
7
+ * Implementor contract: `text` is the input's `[start, end)` slice verbatim,
8
+ * so a retrieved passage can be traced back to its source and enriched with
9
+ * what surrounds it. Chunks may overlap.
10
+ */
11
+ export type Chunk = {
12
+ readonly text: string
13
+ readonly start: number
14
+ readonly end: number
15
+ }
16
+
17
+ /**
18
+ * Splitting a document into retrievable passages. Strategy and sizing are
19
+ * fixed when the layer is built, so callers stay portable across a local
20
+ * splitter and a hosted chunking service.
21
+ */
22
+ export type ChunkerService = {
23
+ readonly chunk: (text: string) => Effect.Effect<ReadonlyArray<Chunk>, AiError.AiError>
24
+ }
25
+
26
+ export class Chunker extends Context.Service<Chunker, ChunkerService>()(
27
+ "@betalyra/effect-uai/Chunker",
28
+ ) {}
29
+
30
+ /** Split a document into passages. */
31
+ export const chunk = (
32
+ text: string,
33
+ ): Effect.Effect<ReadonlyArray<Chunk>, AiError.AiError, Chunker> =>
34
+ Effect.flatMap(Chunker, (chunker) => chunker.chunk(text))
@@ -1,5 +1,14 @@
1
- import { Schema } from "effect"
2
- import type { MediaBase64, MediaBytes, MediaSource, MediaUrl } from "./Media.js"
1
+ import { Result, Schema } from "effect"
2
+ import type {
3
+ AspectRatio,
4
+ Dimensions,
5
+ MediaBase64,
6
+ MediaBytes,
7
+ MediaSource,
8
+ MediaUrl,
9
+ Watermark,
10
+ } from "./Media.js"
11
+ import { parseAspectRatio, round16 } from "./Media.js"
3
12
 
4
13
  /**
5
14
  * Image MIME types AI providers typically accept. The first four are the
@@ -70,6 +79,63 @@ export const imageBytes = (bytes: Uint8Array, mimeType: ImageMimeType): ImageByt
70
79
  mimeType,
71
80
  })
72
81
 
82
+ /** Cross-modality; lives in `Media.ts` because video generation shares it. */
83
+ export type { AspectRatio, Dimensions } from "./Media.js"
84
+
85
+ /**
86
+ * Resolution tier, roughly the short edge in pixels. A tier rather than
87
+ * a pixel pair: adapters whose wire takes exact dimensions derive them
88
+ * from the tier and the aspect ratio. Image-typed on purpose, video
89
+ * models tier by scan height instead.
90
+ */
91
+ export type ImageResolution = "1K" | "2K" | "4K"
92
+
93
+ const SHORT_EDGE: Record<ImageResolution, number> = { "1K": 1024, "2K": 2048, "4K": 4096 }
94
+
95
+ /**
96
+ * Turn the portable ratio and tier into pixels: the tier is the short
97
+ * edge, the long edge follows the ratio. `undefined` when neither was
98
+ * asked for, which the caller sends as "no size" so the endpoint picks.
99
+ *
100
+ * Adapters whose wire wants `"WxH"` format the pair; those wanting an
101
+ * object send it as is. Whether the result is in range for a given model
102
+ * is the server's call: per-model limits churn and we do not table them.
103
+ */
104
+ export const pixelsOf = (request: {
105
+ readonly aspectRatio?: AspectRatio
106
+ readonly resolution?: ImageResolution
107
+ }): Result.Result<Dimensions | undefined, string> =>
108
+ request.aspectRatio === undefined && request.resolution === undefined
109
+ ? Result.succeed(undefined)
110
+ : Result.map(parseAspectRatio(request.aspectRatio ?? "1:1"), ([w, h]) => {
111
+ const short = SHORT_EDGE[request.resolution ?? "1K"]
112
+ return w >= h
113
+ ? { width: round16((short * w) / h), height: short }
114
+ : { width: short, height: round16((short * h) / w) }
115
+ })
116
+
117
+ /**
118
+ * Per-image extras a provider reported and this type has no field for:
119
+ * pixel dimensions, a file name, a revised prompt. Opaque here, and a
120
+ * shared slot, so a provider keys its data under its own name
121
+ * (`{ fal: … }`, `{ openai: … }`) and reads only that key. Each provider
122
+ * package ships a typed reader; the framework never interprets it.
123
+ *
124
+ * Not promoted to real fields because the providers disagree on what
125
+ * they report. Only some fal endpoints return dimensions, and neither
126
+ * OpenAI's Images API nor Gemini returns them at all.
127
+ */
128
+ export type ProviderData = unknown
129
+
130
+ /** One image off a generation call, plus what the provider stamped into it. */
131
+ export type GeneratedImage = {
132
+ readonly image: ImageSource
133
+ /** Set only when the provider applies one. */
134
+ readonly watermark?: Watermark
135
+ /** {@link ProviderData}: per-image extras, keyed by provider name. */
136
+ readonly providerData?: ProviderData
137
+ }
138
+
73
139
  export const isImageUrl = Schema.is(ImageUrlSource)
74
140
  export const isImageBase64 = Schema.is(ImageBase64Source)
75
141
  export const isImageBytes = Schema.is(ImageBytesSource)
@@ -88,6 +88,22 @@ export const OutputText = Schema.Struct({
88
88
  })
89
89
  export type OutputText = typeof OutputText.Type
90
90
 
91
+ /**
92
+ * Model-emitted image. The counterpart to {@link InputImage}: same
93
+ * `ImageSource`, so an image the model drew goes straight back into the
94
+ * next turn with no conversion.
95
+ *
96
+ * How the pixels were produced is not modelled here. A provider may draw
97
+ * them itself, as an image part of the turn, or reach a hosted tool that
98
+ * draws them; either way the fact is the same, and enabling it is
99
+ * provider-specific.
100
+ */
101
+ export const OutputImage = Schema.Struct({
102
+ type: Schema.Literal("output_image"),
103
+ source: ImageSource,
104
+ })
105
+ export type OutputImage = typeof OutputImage.Type
106
+
91
107
  /**
92
108
  * Model-emitted refusal. Distinct from `output_text`: the model declined
93
109
  * to answer rather than producing normal output. Pair with
@@ -100,7 +116,7 @@ export const Refusal = Schema.Struct({
100
116
  })
101
117
  export type Refusal = typeof Refusal.Type
102
118
 
103
- export const ContentBlock = Schema.Union([InputText, InputImage, OutputText, Refusal])
119
+ export const ContentBlock = Schema.Union([InputText, InputImage, OutputText, OutputImage, Refusal])
104
120
  export type ContentBlock = typeof ContentBlock.Type
105
121
 
106
122
  export const Role = Schema.Literals(["user", "assistant", "system"])
@@ -176,6 +192,7 @@ export type HistoryItem = typeof HistoryItem.Type
176
192
  export const isInputText = Schema.is(InputText)
177
193
  export const isInputImage = Schema.is(InputImage)
178
194
  export const isOutputText = Schema.is(OutputText)
195
+ export const isOutputImage = Schema.is(OutputImage)
179
196
  export const isRefusal = Schema.is(Refusal)
180
197
 
181
198
  export const isMessage = Schema.is(Message)
@@ -31,6 +31,72 @@
31
31
  * they're a separate union (`FileRef`) added when needed.
32
32
  */
33
33
 
34
+ import { Result } from "effect"
35
+
36
+ /**
37
+ * Watermark kind a provider embedded in generated media. Shared across
38
+ * modalities (audio, image). No provider exposes metadata beyond the
39
+ * kind, so a bare literal union is enough.
40
+ */
41
+ export type Watermark = "synthid" | "c2pa" | (string & {})
42
+
43
+ /**
44
+ * Requested shape of generated visual media, width to height. Shared by
45
+ * image and video generation: the listed ratios are the ones providers
46
+ * accept as literals, and the `(string & {})` tail keeps a
47
+ * newly-supported ratio working without an SDK update. Which ratios a
48
+ * given model accepts is not checked here; the provider rejects what it
49
+ * cannot render and the adapter translates that error.
50
+ *
51
+ * Resolution is NOT shared: image models tier by short edge
52
+ * (`ImageResolution`), video models by scan height. Each modality types
53
+ * its own.
54
+ */
55
+ export type AspectRatio =
56
+ | "1:1"
57
+ | "2:3"
58
+ | "3:2"
59
+ | "3:4"
60
+ | "4:3"
61
+ | "4:5"
62
+ | "5:4"
63
+ | "9:16"
64
+ | "16:9"
65
+ | "21:9"
66
+ | "1:4"
67
+ | "4:1"
68
+ | "1:8"
69
+ | "8:1"
70
+ | (string & {})
71
+
72
+ /** Exact pixels, for adapters whose wire takes dimensions rather than a ratio. */
73
+ export type Dimensions = {
74
+ readonly width: number
75
+ readonly height: number
76
+ }
77
+
78
+ const positive = (n: number | undefined): n is number =>
79
+ n !== undefined && Number.isFinite(n) && n > 0
80
+
81
+ /**
82
+ * Read `"W:H"` as a pair. Fails when the ratio is not arithmetic, which
83
+ * the `(string & {})` tail on {@link AspectRatio} lets through: a caller
84
+ * asking for `"cinematic"` gets a message rather than NaN pixels. The
85
+ * failure is a plain string so this file stays free of provider
86
+ * vocabulary; each adapter wraps it in its own error.
87
+ */
88
+ export const parseAspectRatio = (
89
+ ratio: AspectRatio,
90
+ ): Result.Result<readonly [number, number], string> => {
91
+ const [w, h, ...rest] = ratio.split(":").map(Number)
92
+ return positive(w) && positive(h) && rest.length === 0
93
+ ? Result.succeed([w, h] as const)
94
+ : Result.fail(`aspectRatio "${ratio}" must read "W:H" with positive numbers.`)
95
+ }
96
+
97
+ /** Several image endpoints require both edges to be multiples of 16. */
98
+ export const round16 = (n: number): number => Math.max(16, Math.round(n / 16) * 16)
99
+
34
100
  export type MediaUrl<M extends string = string> = {
35
101
  readonly _tag: "url"
36
102
  readonly url: string
@@ -1,5 +1,6 @@
1
1
  import type { Duration } from "effect"
2
2
  import type { AudioBlob, AudioChunk, AudioFormat } from "./Audio.js"
3
+ import type { Watermark } from "./Media.js"
3
4
 
4
5
  /**
5
6
  * Prompt fragment with a relative weight. Native to Lyria RealTime
@@ -95,10 +96,10 @@ export const controlInput = (action: MusicSessionControl): MusicSessionInput =>
95
96
 
96
97
  /**
97
98
  * Watermark kind on the response. SynthID is mandatory on Lyria; C2PA
98
- * is opt-in on ElevenLabs. No provider exposes additional metadata
99
- * about the watermark itself, so a bare literal-union is enough.
99
+ * is opt-in on ElevenLabs. Lives in `Media.ts` since image providers
100
+ * report the same kinds; re-exported here unchanged.
100
101
  */
101
- export type Watermark = "synthid" | "c2pa" | (string & {})
102
+ export type { Watermark } from "./Media.js"
102
103
 
103
104
  /**
104
105
  * Labelled section with absolute timing. Populated when the provider
@@ -1,5 +1,6 @@
1
1
  import { Data, Effect, Result, Schema, Stream, pipe } from "effect"
2
2
  import * as StructuredFormat from "../structured-format/StructuredFormat.js"
3
+ import type { ImageSource } from "./Image.js"
3
4
  import {
4
5
  type Annotation,
5
6
  HistoryItem,
@@ -9,6 +10,7 @@ import {
9
10
  ToolCall,
10
11
  ToolCallOutput,
11
12
  Usage,
13
+ isOutputImage,
12
14
  isOutputText,
13
15
  isReasoning,
14
16
  isRefusal,
@@ -49,6 +51,12 @@ export type Turn = typeof Turn.Type
49
51
  * `WebSearchCall`: lifecycle of a provider-executed web search within the
50
52
  * turn. Fires only when the turn grounds against search; absent otherwise.
51
53
  *
54
+ * `ImageOutput`: an image the model produced, whether it drew it as part
55
+ * of its turn or reached a hosted tool for it. `partialIndex` marks a
56
+ * preview frame from providers that stream them; the finished image
57
+ * arrives without one and is also on `TurnComplete.turn` as an
58
+ * `OutputImage` block.
59
+ *
52
60
  * `CitationAdded`: a citation attached to the answer, emitted incrementally
53
61
  * where the provider streams citations. Providers that bundle citations emit
54
62
  * none of these; their citations still arrive on `TurnComplete.turn`
@@ -66,6 +74,11 @@ export type TurnEvent = Data.TaggedEnum<{
66
74
  readonly query?: string
67
75
  readonly action?: "search" | "open_page" | "find_in_page"
68
76
  }
77
+ ImageOutput: {
78
+ readonly image: ImageSource
79
+ /** Set only on a preview frame, counting from 0. Absent means finished. */
80
+ readonly partialIndex?: number
81
+ }
69
82
  CitationAdded: { readonly annotation: Annotation }
70
83
  TurnComplete: { readonly turn: Turn }
71
84
  }>
@@ -109,6 +122,49 @@ export const assistantTexts = (turn: Turn): ReadonlyArray<string> =>
109
122
  */
110
123
  export const assistantText = (turn: Turn): string => assistantTexts(turn).join("")
111
124
 
125
+ /**
126
+ * Every image the model produced this turn, in order. The counterpart to
127
+ * {@link assistantTexts}: each is an `ImageSource`, so it can be written
128
+ * to disk or passed straight back as an `input_image` on the next turn.
129
+ */
130
+ export const assistantImages = (turn: Turn): ReadonlyArray<ImageSource> =>
131
+ assistantMessages(turn)
132
+ .flatMap((m) => m.content)
133
+ .filter(isOutputImage)
134
+ .map((b) => b.source)
135
+
136
+ /**
137
+ * Rewrite assistant `output_image` blocks into a user message carrying
138
+ * the same pictures as `input_image`, so another provider can look at
139
+ * what this one drew.
140
+ *
141
+ * Only Gemini's wire has an assistant-role image part; every other
142
+ * adapter omits an `output_image` on replay and says so. Moving it to a
143
+ * user message is the conversion that makes it portable, and it is
144
+ * yours to make rather than the adapter's: "the assistant drew this" and
145
+ * "here is an image, look at it" are different claims, and silently
146
+ * swapping one for the other behind your back would be the wrong kind of
147
+ * helpful.
148
+ *
149
+ * Items with no images pass through untouched, and the new user message
150
+ * follows the assistant message it came from.
151
+ */
152
+ export const imagesAsInput = (history: ReadonlyArray<HistoryItem>): ReadonlyArray<HistoryItem> =>
153
+ history.flatMap((item) => {
154
+ if (item.type !== "message" || item.role !== "assistant") return [item]
155
+ const images = item.content.filter(isOutputImage)
156
+ return images.length === 0
157
+ ? [item]
158
+ : [
159
+ item,
160
+ {
161
+ type: "message" as const,
162
+ role: "user" as const,
163
+ content: images.map((b) => ({ type: "input_image" as const, source: b.source })),
164
+ },
165
+ ]
166
+ })
167
+
112
168
  /**
113
169
  * Every citation annotation attached to the turn's assistant `output_text`
114
170
  * blocks, in order. The flat view of a grounded turn's sources, whether the