@effect-uai/core 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{AiError-CBuPHVKA.d.mts → AiError-CAX_48RU.d.mts} +27 -5
- package/dist/{AiError-CBuPHVKA.d.mts.map → AiError-CAX_48RU.d.mts.map} +1 -1
- package/dist/Audio-BfCTGnH3.d.mts +61 -0
- package/dist/Audio-BfCTGnH3.d.mts.map +1 -0
- package/dist/{Image-BZmKfIdq.d.mts → Image-HNmMpMTh.d.mts} +1 -1
- package/dist/{Image-BZmKfIdq.d.mts.map → Image-HNmMpMTh.d.mts.map} +1 -1
- package/dist/{Items-CB8Bo3FI.d.mts → Items-DqbaJoz7.d.mts} +5 -5
- package/dist/{Items-CB8Bo3FI.d.mts.map → Items-DqbaJoz7.d.mts.map} +1 -1
- package/dist/{StructuredFormat-BWq5Hd1O.d.mts → StructuredFormat-BbN4dosH.d.mts} +11 -4
- package/dist/StructuredFormat-BbN4dosH.d.mts.map +1 -0
- package/dist/{Tool-DjVufH7i.d.mts → Tool-Y0__Py1H.d.mts} +20 -4
- package/dist/Tool-Y0__Py1H.d.mts.map +1 -0
- package/dist/Turn-ChbL2foc.d.mts +388 -0
- package/dist/Turn-ChbL2foc.d.mts.map +1 -0
- package/dist/domain/AiError.d.mts +2 -2
- package/dist/domain/AiError.mjs +19 -3
- package/dist/domain/AiError.mjs.map +1 -1
- package/dist/domain/Audio.d.mts +2 -0
- package/dist/domain/Audio.mjs +14 -0
- package/dist/domain/Audio.mjs.map +1 -0
- package/dist/domain/Image.d.mts +1 -1
- package/dist/domain/Items.d.mts +1 -1
- package/dist/domain/Items.mjs +1 -1
- package/dist/domain/Items.mjs.map +1 -1
- package/dist/domain/Music.d.mts +116 -0
- package/dist/domain/Music.d.mts.map +1 -0
- package/dist/domain/Music.mjs +29 -0
- package/dist/domain/Music.mjs.map +1 -0
- package/dist/domain/Transcript.d.mts +95 -0
- package/dist/domain/Transcript.d.mts.map +1 -0
- package/dist/domain/Transcript.mjs +22 -0
- package/dist/domain/Transcript.mjs.map +1 -0
- package/dist/domain/Turn.d.mts +2 -2
- package/dist/domain/Turn.mjs +22 -4
- package/dist/domain/Turn.mjs.map +1 -1
- package/dist/domain/Turn.test.d.mts +1 -0
- package/dist/domain/Turn.test.mjs +136 -0
- package/dist/domain/Turn.test.mjs.map +1 -0
- package/dist/embedding-model/Embedding.d.mts +15 -3
- package/dist/embedding-model/Embedding.d.mts.map +1 -1
- package/dist/embedding-model/Embedding.mjs.map +1 -1
- package/dist/embedding-model/EmbeddingModel.d.mts +33 -17
- package/dist/embedding-model/EmbeddingModel.d.mts.map +1 -1
- package/dist/embedding-model/EmbeddingModel.mjs.map +1 -1
- package/dist/embedding-model/EmbeddingModel.test.d.mts +1 -0
- package/dist/embedding-model/EmbeddingModel.test.mjs +59 -0
- package/dist/embedding-model/EmbeddingModel.test.mjs.map +1 -0
- package/dist/index.d.mts +13 -7
- package/dist/index.mjs +7 -1
- package/dist/language-model/LanguageModel.d.mts +30 -8
- package/dist/language-model/LanguageModel.d.mts.map +1 -1
- package/dist/language-model/LanguageModel.mjs +33 -3
- package/dist/language-model/LanguageModel.mjs.map +1 -1
- package/dist/language-model/LanguageModel.test.d.mts +1 -0
- package/dist/language-model/LanguageModel.test.mjs +143 -0
- package/dist/language-model/LanguageModel.test.mjs.map +1 -0
- package/dist/loop/Loop.d.mts +94 -11
- package/dist/loop/Loop.d.mts.map +1 -1
- package/dist/loop/Loop.mjs +92 -26
- package/dist/loop/Loop.mjs.map +1 -1
- package/dist/loop/Loop.test.mjs +171 -3
- package/dist/loop/Loop.test.mjs.map +1 -1
- package/dist/music-generator/MusicGenerator.d.mts +77 -0
- package/dist/music-generator/MusicGenerator.d.mts.map +1 -0
- package/dist/music-generator/MusicGenerator.mjs +51 -0
- package/dist/music-generator/MusicGenerator.mjs.map +1 -0
- package/dist/music-generator/MusicGenerator.test.d.mts +1 -0
- package/dist/music-generator/MusicGenerator.test.mjs +154 -0
- package/dist/music-generator/MusicGenerator.test.mjs.map +1 -0
- package/dist/observability/Metrics.d.mts +1 -1
- package/dist/observability/Metrics.mjs +1 -1
- package/dist/observability/Metrics.mjs.map +1 -1
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts +96 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.d.mts.map +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs +48 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.mjs.map +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.d.mts +1 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs +112 -0
- package/dist/speech-synthesizer/SpeechSynthesizer.test.mjs.map +1 -0
- package/dist/streaming/JSONL.d.mts +10 -3
- package/dist/streaming/JSONL.d.mts.map +1 -1
- package/dist/streaming/JSONL.mjs +15 -9
- package/dist/streaming/JSONL.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.d.mts +2 -2
- package/dist/structured-format/StructuredFormat.mjs +9 -1
- package/dist/structured-format/StructuredFormat.mjs.map +1 -1
- package/dist/structured-format/StructuredFormat.test.d.mts +1 -0
- package/dist/structured-format/StructuredFormat.test.mjs +70 -0
- package/dist/structured-format/StructuredFormat.test.mjs.map +1 -0
- package/dist/testing/MockMusicGenerator.d.mts +39 -0
- package/dist/testing/MockMusicGenerator.d.mts.map +1 -0
- package/dist/testing/MockMusicGenerator.mjs +96 -0
- package/dist/testing/MockMusicGenerator.mjs.map +1 -0
- package/dist/testing/MockProvider.d.mts +23 -18
- package/dist/testing/MockProvider.d.mts.map +1 -1
- package/dist/testing/MockProvider.mjs +56 -72
- package/dist/testing/MockProvider.mjs.map +1 -1
- package/dist/testing/MockSpeechSynthesizer.d.mts +37 -0
- package/dist/testing/MockSpeechSynthesizer.d.mts.map +1 -0
- package/dist/testing/MockSpeechSynthesizer.mjs +95 -0
- package/dist/testing/MockSpeechSynthesizer.mjs.map +1 -0
- package/dist/testing/MockTranscriber.d.mts +37 -0
- package/dist/testing/MockTranscriber.d.mts.map +1 -0
- package/dist/testing/MockTranscriber.mjs +77 -0
- package/dist/testing/MockTranscriber.mjs.map +1 -0
- package/dist/tool/HistoryCheck.d.mts +1 -1
- package/dist/tool/Outcome.d.mts +1 -1
- package/dist/tool/Resolvers.d.mts +65 -8
- package/dist/tool/Resolvers.d.mts.map +1 -1
- package/dist/tool/Resolvers.mjs +8 -12
- package/dist/tool/Resolvers.mjs.map +1 -1
- package/dist/tool/Resolvers.test.mjs +6 -5
- package/dist/tool/Resolvers.test.mjs.map +1 -1
- package/dist/tool/Tool.d.mts +2 -2
- package/dist/tool/Tool.mjs +18 -1
- package/dist/tool/Tool.mjs.map +1 -1
- package/dist/tool/Tool.test.d.mts +1 -0
- package/dist/tool/Tool.test.mjs +66 -0
- package/dist/tool/Tool.test.mjs.map +1 -0
- package/dist/tool/Toolkit.d.mts +4 -6
- package/dist/tool/Toolkit.d.mts.map +1 -1
- package/dist/tool/Toolkit.mjs +14 -43
- package/dist/tool/Toolkit.mjs.map +1 -1
- package/dist/transcriber/Transcriber.d.mts +101 -0
- package/dist/transcriber/Transcriber.d.mts.map +1 -0
- package/dist/transcriber/Transcriber.mjs +49 -0
- package/dist/transcriber/Transcriber.mjs.map +1 -0
- package/dist/transcriber/Transcriber.test.d.mts +1 -0
- package/dist/transcriber/Transcriber.test.mjs +130 -0
- package/dist/transcriber/Transcriber.test.mjs.map +1 -0
- package/package.json +37 -1
- package/src/domain/AiError.ts +22 -1
- package/src/domain/Audio.ts +88 -0
- package/src/domain/Items.ts +1 -1
- package/src/domain/Music.ts +121 -0
- package/src/domain/Transcript.ts +83 -0
- package/src/domain/Turn.test.ts +141 -0
- package/src/domain/Turn.ts +50 -43
- package/src/embedding-model/Embedding.ts +23 -0
- package/src/embedding-model/EmbeddingModel.test.ts +92 -0
- package/src/embedding-model/EmbeddingModel.ts +30 -20
- package/src/index.ts +6 -0
- package/src/language-model/LanguageModel.test.ts +170 -0
- package/src/language-model/LanguageModel.ts +64 -1
- package/src/loop/Loop.test.ts +256 -3
- package/src/loop/Loop.ts +225 -49
- package/src/music-generator/MusicGenerator.test.ts +170 -0
- package/src/music-generator/MusicGenerator.ts +123 -0
- package/src/observability/Metrics.ts +1 -1
- package/src/speech-synthesizer/SpeechSynthesizer.test.ts +141 -0
- package/src/speech-synthesizer/SpeechSynthesizer.ts +131 -0
- package/src/streaming/JSONL.ts +16 -13
- package/src/structured-format/StructuredFormat.test.ts +105 -0
- package/src/structured-format/StructuredFormat.ts +14 -1
- package/src/testing/MockMusicGenerator.ts +168 -0
- package/src/testing/MockProvider.ts +126 -105
- package/src/testing/MockSpeechSynthesizer.ts +163 -0
- package/src/testing/MockTranscriber.ts +137 -0
- package/src/tool/Resolvers.test.ts +8 -5
- package/src/tool/Resolvers.ts +17 -19
- package/src/tool/Tool.test.ts +105 -0
- package/src/tool/Tool.ts +20 -0
- package/src/tool/Toolkit.ts +49 -50
- package/src/transcriber/Transcriber.test.ts +125 -0
- package/src/transcriber/Transcriber.ts +127 -0
- package/dist/StructuredFormat-BWq5Hd1O.d.mts.map +0 -1
- package/dist/Tool-DjVufH7i.d.mts.map +0 -1
- package/dist/Turn-OPaILVIB.d.mts +0 -194
- package/dist/Turn-OPaILVIB.d.mts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Embedding.mjs","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"sourcesContent":["import type { ImageSource } from \"../domain/Image.js\"\n\n/**\n * One part of a mixed text+image input. Used inside `EmbedInput.content[]`\n * for providers that accept interleaved modalities in a single embed call\n * (Cohere v4, Voyage multimodal, Jina v4, Google `gemini-embedding-2`).\n */\nexport type EmbedContentPart = { readonly text: string } | { readonly image: ImageSource }\n\n/**\n * What you embed. The `string` shorthand covers the common text-only case;\n * structured variants exist for image-only and mixed-modality inputs.\n *\n * Not every provider accepts every variant: text-only providers (OpenAI,\n * Mixedbread today) handle `string` and `{ text }`; multimodal providers\n * (Google, Jina v4, Voyage multimodal, Cohere v4) handle all four. A\n * provider layer rejects shapes it can't encode as `AiError.InvalidRequest`.\n */\nexport type EmbedInput =\n | string\n | { readonly text: string }\n | { readonly image: ImageSource }\n | { readonly content: ReadonlyArray<EmbedContentPart> }\n\n// ---------------------------------------------------------------------------\n// Embedding representations\n//\n// The `_tag` reflects the wire form the provider returned, *not* what the\n// consumer asked for - request `encoding: \"int8\"` and you get back an\n// `Int8Embedding`. Math primitives are typed against the named interfaces\n// (see `Vector.ts`) so e.g. `sparseCosine` only accepts `SparseEmbedding`.\n// ---------------------------------------------------------------------------\n\n/** Dense float32 vector. The default representation across all providers. */\nexport type Float32Embedding = {\n readonly _tag: \"float32\"\n readonly vector: Float32Array\n}\n\n/**\n * Dense int8-quantized vector. ~4x smaller than float32 with minimal\n * recall loss on most benchmarks.\n */\nexport type Int8Embedding = {\n readonly _tag: \"int8\"\n readonly vector: Int8Array\n}\n\n/**\n * Dense binary-quantized vector. One bit per dimension, packed into bytes.\n * ~32x smaller than float32; meaningful recall loss but useful for hot\n * indexes paired with a float32 reranker pass.\n */\nexport type BinaryEmbedding = {\n readonly _tag: \"binary\"\n readonly vector: Uint8Array\n}\n\n/**\n * Sparse vector. Token-keyed weights for hybrid search (dense + lexical-\n * style sparse). The single hosted producer today is Jina's `elser-v2`\n * model, which returns subword tokens (e.g. `\"bread\"`, `\"##ing\"`) with\n * their relevance weights.\n *\n * The shape is `Record<string, number>` rather than `(indices, values)`\n * because real hosted learned-sparse encoders (ELSER, SPLADE) emit token\n * strings with no shared vocabulary index. Converting to integer indices\n * would either need a vocabulary table the model doesn't expose, or\n * lose the cross-vector matching semantics. If a provider ever exposes\n * index-valued sparse vectors (Pinecone-style, where you bring your own\n * vocab), add an `IndexSparseEmbedding` sibling arm with `_tag:\n * \"sparse-indexed\"`.\n *\n * Score with `Vector.sparseCosine` — dot product over the intersection\n * of keys, normalized by the L2 norms of both maps.\n */\nexport type SparseEmbedding = {\n readonly _tag: \"sparse\"\n readonly weights: Readonly<Record<string, number>>\n}\n\n/**\n * Multivector / late-interaction output: one float32 vector per token.\n * Score documents with `Vector.maxSim` (ColBERT-style: per query vector,\n * max dot product across doc vectors, summed). Typically ~50-500 vectors\n * per document, each shorter than a single-vector embedding (~128 dim\n * vs ~1024).\n *\n * Quantized multivector forms aren't modeled for the same reason as\n * sparse - nothing on hosted APIs ships them yet.\n */\nexport type MultivectorEmbedding = {\n readonly _tag: \"multivector\"\n readonly vectors: ReadonlyArray<Float32Array>\n}\n\nexport type Embedding =\n | Float32Embedding\n | Int8Embedding\n | BinaryEmbedding\n | SparseEmbedding\n | MultivectorEmbedding\n\nexport const isFloat32 = (e: Embedding): e is Float32Embedding => e._tag === \"float32\"\nexport const isInt8 = (e: Embedding): e is Int8Embedding => e._tag === \"int8\"\nexport const isBinary = (e: Embedding): e is BinaryEmbedding => e._tag === \"binary\"\nexport const isSparse = (e: Embedding): e is SparseEmbedding => e._tag === \"sparse\"\nexport const isMultivector = (e: Embedding): e is MultivectorEmbedding => e._tag === \"multivector\"\n\n/**\n * Token usage for one embed / embedMany call. One value per HTTP request,\n * not per input vector. Most providers populate `inputTokens`; the field\n * is optional for those that don't (or for mock layers in tests).\n */\nexport type Usage = {\n readonly inputTokens?: number\n}\n"],"mappings":";;;;;;;;;AAuGA,MAAa,aAAa,MAAwC,EAAE,SAAS;AAC7E,MAAa,UAAU,MAAqC,EAAE,SAAS;AACvE,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,iBAAiB,MAA4C,EAAE,SAAS"}
|
|
1
|
+
{"version":3,"file":"Embedding.mjs","names":[],"sources":["../../src/embedding-model/Embedding.ts"],"sourcesContent":["import type { ImageSource } from \"../domain/Image.js\"\n\n/**\n * One part of a mixed text+image input. Used inside `EmbedInput.content[]`\n * for providers that accept interleaved modalities in a single embed call\n * (Cohere v4, Voyage multimodal, Jina v4, Google `gemini-embedding-2`).\n */\nexport type EmbedContentPart = { readonly text: string } | { readonly image: ImageSource }\n\n/**\n * What you embed. The `string` shorthand covers the common text-only case;\n * structured variants exist for image-only and mixed-modality inputs.\n *\n * Not every provider accepts every variant: text-only providers (OpenAI,\n * Mixedbread today) handle `string` and `{ text }`; multimodal providers\n * (Google, Jina v4, Voyage multimodal, Cohere v4) handle all four. A\n * provider layer rejects shapes it can't encode as `AiError.InvalidRequest`.\n */\nexport type EmbedInput =\n | string\n | { readonly text: string }\n | { readonly image: ImageSource }\n | { readonly content: ReadonlyArray<EmbedContentPart> }\n\n// ---------------------------------------------------------------------------\n// Embedding representations\n//\n// The `_tag` reflects the wire form the provider returned, *not* what the\n// consumer asked for - request `encoding: \"int8\"` and you get back an\n// `Int8Embedding`. Math primitives are typed against the named interfaces\n// (see `Vector.ts`) so e.g. `sparseCosine` only accepts `SparseEmbedding`.\n// ---------------------------------------------------------------------------\n\n/** Dense float32 vector. The default representation across all providers. */\nexport type Float32Embedding = {\n readonly _tag: \"float32\"\n readonly vector: Float32Array\n}\n\n/**\n * Dense int8-quantized vector. ~4x smaller than float32 with minimal\n * recall loss on most benchmarks.\n */\nexport type Int8Embedding = {\n readonly _tag: \"int8\"\n readonly vector: Int8Array\n}\n\n/**\n * Dense binary-quantized vector. One bit per dimension, packed into bytes.\n * ~32x smaller than float32; meaningful recall loss but useful for hot\n * indexes paired with a float32 reranker pass.\n */\nexport type BinaryEmbedding = {\n readonly _tag: \"binary\"\n readonly vector: Uint8Array\n}\n\n/**\n * Sparse vector. Token-keyed weights for hybrid search (dense + lexical-\n * style sparse). The single hosted producer today is Jina's `elser-v2`\n * model, which returns subword tokens (e.g. `\"bread\"`, `\"##ing\"`) with\n * their relevance weights.\n *\n * The shape is `Record<string, number>` rather than `(indices, values)`\n * because real hosted learned-sparse encoders (ELSER, SPLADE) emit token\n * strings with no shared vocabulary index. Converting to integer indices\n * would either need a vocabulary table the model doesn't expose, or\n * lose the cross-vector matching semantics. If a provider ever exposes\n * index-valued sparse vectors (Pinecone-style, where you bring your own\n * vocab), add an `IndexSparseEmbedding` sibling arm with `_tag:\n * \"sparse-indexed\"`.\n *\n * Score with `Vector.sparseCosine` — dot product over the intersection\n * of keys, normalized by the L2 norms of both maps.\n */\nexport type SparseEmbedding = {\n readonly _tag: \"sparse\"\n readonly weights: Readonly<Record<string, number>>\n}\n\n/**\n * Multivector / late-interaction output: one float32 vector per token.\n * Score documents with `Vector.maxSim` (ColBERT-style: per query vector,\n * max dot product across doc vectors, summed). Typically ~50-500 vectors\n * per document, each shorter than a single-vector embedding (~128 dim\n * vs ~1024).\n *\n * Quantized multivector forms aren't modeled for the same reason as\n * sparse - nothing on hosted APIs ships them yet.\n */\nexport type MultivectorEmbedding = {\n readonly _tag: \"multivector\"\n readonly vectors: ReadonlyArray<Float32Array>\n}\n\nexport type Embedding =\n | Float32Embedding\n | Int8Embedding\n | BinaryEmbedding\n | SparseEmbedding\n | MultivectorEmbedding\n\nexport const isFloat32 = (e: Embedding): e is Float32Embedding => e._tag === \"float32\"\nexport const isInt8 = (e: Embedding): e is Int8Embedding => e._tag === \"int8\"\nexport const isBinary = (e: Embedding): e is BinaryEmbedding => e._tag === \"binary\"\nexport const isSparse = (e: Embedding): e is SparseEmbedding => e._tag === \"sparse\"\nexport const isMultivector = (e: Embedding): e is MultivectorEmbedding => e._tag === \"multivector\"\n\n/**\n * Maps an `encoding` request field to the corresponding response embedding\n * variant. `undefined` (no encoding requested) defaults to `Float32Embedding`,\n * which is what every provider returns when the caller doesn't ask for\n * anything else. Widened `E` falls back to the full `Embedding` union — the\n * caller has to narrow at use site, which honestly reflects what they know\n * at compile time.\n *\n * Used by `EmbedResponse<E>` / `EmbedManyResponse<E>` to give callers a\n * precise embedding type without a runtime narrowing helper.\n */\nexport type EmbeddingFor<E> = [E] extends [undefined | \"float32\"]\n ? Float32Embedding\n : [E] extends [\"int8\"]\n ? Int8Embedding\n : [E] extends [\"binary\"]\n ? BinaryEmbedding\n : [E] extends [\"sparse\"]\n ? SparseEmbedding\n : [E] extends [\"multivector\"]\n ? MultivectorEmbedding\n : Embedding\n\n/**\n * Token usage for one embed / embedMany call. One value per HTTP request,\n * not per input vector. Most providers populate `inputTokens`; the field\n * is optional for those that don't (or for mock layers in tests).\n */\nexport type Usage = {\n readonly inputTokens?: number\n}\n"],"mappings":";;;;;;;;;AAuGA,MAAa,aAAa,MAAwC,EAAE,SAAS;AAC7E,MAAa,UAAU,MAAqC,EAAE,SAAS;AACvE,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,YAAY,MAAuC,EAAE,SAAS;AAC3E,MAAa,iBAAiB,MAA4C,EAAE,SAAS"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { t as AiError } from "../AiError-
|
|
2
|
-
import { EmbedInput,
|
|
1
|
+
import { t as AiError } from "../AiError-CAX_48RU.mjs";
|
|
2
|
+
import { EmbedInput, EmbeddingFor, Usage } from "./Embedding.mjs";
|
|
3
3
|
import { Context, Effect } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/embedding-model/EmbeddingModel.d.ts
|
|
6
6
|
declare namespace EmbeddingModel_d_exports {
|
|
7
|
-
export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService,
|
|
7
|
+
export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedEncoding, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, embed, embedMany };
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Output representation requested from the provider.
|
|
@@ -22,12 +22,12 @@ declare namespace EmbeddingModel_d_exports {
|
|
|
22
22
|
* style) scoring via `Vector.maxSim`. Currently Jina v4 only.
|
|
23
23
|
*
|
|
24
24
|
* Each provider's typed request narrows this to its supported set at
|
|
25
|
-
* compile time (e.g. `
|
|
25
|
+
* compile time (e.g. `JinaEmbedEncoding = "float32" | "binary" | "sparse" |
|
|
26
26
|
* "multivector"`). On the generic `EmbeddingModel` path, callers can
|
|
27
|
-
* pass any `
|
|
27
|
+
* pass any `EmbedEncoding` and the provider's API will reject mismatches at
|
|
28
28
|
* runtime.
|
|
29
29
|
*/
|
|
30
|
-
type
|
|
30
|
+
type EmbedEncoding = "float32" | "int8" | "binary" | "sparse" | "multivector";
|
|
31
31
|
/**
|
|
32
32
|
* Cross-provider single-embed request. Mirrors the shape of
|
|
33
33
|
* `LanguageModel.CommonRequest`: cross-cutting fields here, vendor
|
|
@@ -60,11 +60,11 @@ type CommonEmbedRequest = {
|
|
|
60
60
|
*/
|
|
61
61
|
readonly dimensions?: number;
|
|
62
62
|
/**
|
|
63
|
-
* Output representation - see {@link
|
|
63
|
+
* Output representation - see {@link EmbedEncoding}. Dense float32 is the
|
|
64
64
|
* default; provider layers reject unsupported values up front with
|
|
65
65
|
* `InvalidRequest`.
|
|
66
66
|
*/
|
|
67
|
-
readonly encoding?:
|
|
67
|
+
readonly encoding?: EmbedEncoding;
|
|
68
68
|
};
|
|
69
69
|
/**
|
|
70
70
|
* Cross-provider batch-embed request. One `task` for the whole batch -
|
|
@@ -74,24 +74,40 @@ type CommonEmbedRequest = {
|
|
|
74
74
|
type CommonEmbedManyRequest = Omit<CommonEmbedRequest, "input"> & {
|
|
75
75
|
readonly inputs: ReadonlyArray<EmbedInput>;
|
|
76
76
|
};
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
/**
|
|
78
|
+
* Single-embed response. The `embedding` type is determined by the
|
|
79
|
+
* request's `encoding` field via `EmbeddingFor<E>` — callers that don't
|
|
80
|
+
* specify an encoding get a `Float32Embedding` directly with no runtime
|
|
81
|
+
* narrowing. Defaults to `undefined` for back-compat with consumers that
|
|
82
|
+
* use the bare `EmbedResponse` name.
|
|
83
|
+
*/
|
|
84
|
+
type EmbedResponse<E extends EmbedEncoding | undefined = undefined> = {
|
|
85
|
+
readonly embedding: EmbeddingFor<E>;
|
|
79
86
|
readonly usage: Usage;
|
|
80
87
|
};
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
/** Batch-embed response. Same encoding rule as {@link EmbedResponse}. */
|
|
89
|
+
type EmbedManyResponse<E extends EmbedEncoding | undefined = undefined> = {
|
|
90
|
+
readonly embeddings: ReadonlyArray<EmbeddingFor<E>>;
|
|
83
91
|
readonly usage: Usage;
|
|
84
92
|
};
|
|
85
93
|
type EmbeddingModelService = {
|
|
86
|
-
readonly embed: (request: CommonEmbedRequest
|
|
87
|
-
|
|
94
|
+
readonly embed: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedRequest, "encoding"> & {
|
|
95
|
+
readonly encoding?: E;
|
|
96
|
+
}) => Effect.Effect<EmbedResponse<E>, AiError>;
|
|
97
|
+
readonly embedMany: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedManyRequest, "encoding"> & {
|
|
98
|
+
readonly encoding?: E;
|
|
99
|
+
}) => Effect.Effect<EmbedManyResponse<E>, AiError>;
|
|
88
100
|
};
|
|
89
101
|
declare const EmbeddingModel_base: Context.ServiceClass<EmbeddingModel, "@betalyra/effect-uai/EmbeddingModel", EmbeddingModelService>;
|
|
90
102
|
declare class EmbeddingModel extends EmbeddingModel_base {}
|
|
91
103
|
/** Embed a single input. */
|
|
92
|
-
declare const embed: (request: CommonEmbedRequest
|
|
104
|
+
declare const embed: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedRequest, "encoding"> & {
|
|
105
|
+
readonly encoding?: E;
|
|
106
|
+
}) => Effect.Effect<EmbedResponse<E>, AiError, EmbeddingModel>;
|
|
93
107
|
/** Embed a batch in one provider call. Same `task` for every input. */
|
|
94
|
-
declare const embedMany: (request: CommonEmbedManyRequest
|
|
108
|
+
declare const embedMany: <E extends EmbedEncoding | undefined = undefined>(request: Omit<CommonEmbedManyRequest, "encoding"> & {
|
|
109
|
+
readonly encoding?: E;
|
|
110
|
+
}) => Effect.Effect<EmbedManyResponse<E>, AiError, EmbeddingModel>;
|
|
95
111
|
//#endregion
|
|
96
|
-
export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService,
|
|
112
|
+
export { CommonEmbedManyRequest, CommonEmbedRequest, EmbedEncoding, EmbedManyResponse, EmbedResponse, EmbeddingModel, EmbeddingModelService, embed, embedMany, EmbeddingModel_d_exports as t };
|
|
97
113
|
//# sourceMappingURL=EmbeddingModel.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmbeddingModel.d.mts","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;;AAYA;;KAZY,
|
|
1
|
+
{"version":3,"file":"EmbeddingModel.d.mts","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAyBA;;;;;AAYA;;KAZY,aAAA;;;;;;;;;;;KAYA,kBAAA;EAAA,SACD,KAAA,EAAO,UAAA;;;;;WAKP,KAAA;EA6BqB;;;;;;;EAAA,SArBrB,IAAA;EAqBgC;AAU3C;;;;EAV2C,SAfhC,UAAA;EA0BW;;;;;EAAA,SApBX,QAAA,GAAW,aAAA;AAAA;;;;;;KAQV,sBAAA,GAAyB,IAAA,CAAK,kBAAA;EAAA,SAC/B,MAAA,EAAQ,aAAA,CAAc,UAAA;AAAA;;;;;;;;KAUrB,aAAA,WAAwB,aAAA;EAAA,SACzB,SAAA,EAAW,YAAA,CAAa,CAAA;EAAA,SACxB,KAAA,EAAO,KAAA;AAAA;;KAIN,iBAAA,WAA4B,aAAA;EAAA,SAC7B,UAAA,EAAY,aAAA,CAAc,YAAA,CAAa,CAAA;EAAA,SACvC,KAAA,EAAO,KAAA;AAAA;AAAA,KAGN,qBAAA;EAAA,SACD,KAAA,aAAkB,aAAA,0BACzB,OAAA,EAAS,IAAA,CAAK,kBAAA;IAAA,SAA6C,QAAA,GAAW,CAAA;EAAA,MACnE,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,CAAA,GAAI,OAAA;EAAA,SAC5B,SAAA,aAAsB,aAAA,0BAC7B,OAAA,EAAS,IAAA,CAAK,sBAAA;IAAA,SAAiD,QAAA,GAAW,CAAA;EAAA,MACvE,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,CAAA,GAAI,OAAA;AAAA;AAAA,cAC1C,mBAAA;cAEY,cAAA,SAAuB,mBAAA;;cAKvB,KAAA,aAAmB,aAAA,0BAC9B,OAAA,EAAS,IAAA,CAAK,kBAAA;EAAA,SAA6C,QAAA,GAAW,CAAA;AAAA,MACrE,MAAA,CAAO,MAAA,CAAO,aAAA,CAAc,CAAA,GAAI,OAAA,EAAiB,cAAA;;cAIvC,SAAA,aAAuB,aAAA,0BAClC,OAAA,EAAS,IAAA,CAAK,sBAAA;EAAA,SAAiD,QAAA,GAAW,CAAA;AAAA,MACzE,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,CAAA,GAAI,OAAA,EAAiB,cAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmbeddingModel.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"sourcesContent":["import { Context, Effect } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type {
|
|
1
|
+
{"version":3,"file":"EmbeddingModel.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.ts"],"sourcesContent":["import { Context, Effect } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { EmbeddingFor, EmbedInput, Usage } from \"./Embedding.js\"\n\n/**\n * Output representation requested from the provider.\n *\n * Dense quantizations - same vector at different storage cost:\n * - `float32` — universal default.\n * - `int8` — ~4x smaller; minimal recall loss on most benchmarks.\n * - `binary` — ~32x smaller; meaningful recall loss but pairs well with\n * a float32 reranker pass over a small candidate set.\n *\n * Non-dense representations:\n * - `sparse` — learned sparse vector for hybrid (dense + lexical) search.\n * Currently Jina ELSER only on hosted APIs.\n * - `multivector` — one vector per token for late-interaction (ColBERT-\n * style) scoring via `Vector.maxSim`. Currently Jina v4 only.\n *\n * Each provider's typed request narrows this to its supported set at\n * compile time (e.g. `JinaEmbedEncoding = \"float32\" | \"binary\" | \"sparse\" |\n * \"multivector\"`). On the generic `EmbeddingModel` path, callers can\n * pass any `EmbedEncoding` and the provider's API will reject mismatches at\n * runtime.\n */\nexport type EmbedEncoding = \"float32\" | \"int8\" | \"binary\" | \"sparse\" | \"multivector\"\n\n/**\n * Cross-provider single-embed request. Mirrors the shape of\n * `LanguageModel.CommonRequest`: cross-cutting fields here, vendor\n * specifics in the provider's typed request.\n *\n * Provider-specific extensions (Cohere widened `task` enum, Jina LoRA\n * tasks, Mixedbread free-form `prompt`, etc.) live in that provider's own\n * request interface, which extends this and narrows `model` / widens\n * `task`.\n */\nexport type CommonEmbedRequest = {\n readonly input: EmbedInput\n /**\n * Model identifier. Each provider narrows this to its typed literal\n * union, so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n /**\n * Retrieval-task hint. Applies to the input. OpenAI ignores this;\n * Mixedbread doesn't have it; Cohere v3+ requires it on the wire (typed\n * as required in `CohereEmbedRequest`). Provider-specific task enums\n * (classification, clustering, code retrieval, …) live on the\n * provider's own request type.\n */\n readonly task?: \"query\" | \"document\"\n /**\n * Matryoshka truncation. Default: provider's native dimension.\n * Discrete-value providers (Cohere, Vertex `multimodalembedding@001`)\n * narrow this to a literal union in their typed request.\n */\n readonly dimensions?: number\n /**\n * Output representation - see {@link EmbedEncoding}. Dense float32 is the\n * default; provider layers reject unsupported values up front with\n * `InvalidRequest`.\n */\n readonly encoding?: EmbedEncoding\n}\n\n/**\n * Cross-provider batch-embed request. One `task` for the whole batch -\n * mixed-task batches aren't a real provider feature (rerankers exist for\n * that).\n */\nexport type CommonEmbedManyRequest = Omit<CommonEmbedRequest, \"input\"> & {\n readonly inputs: ReadonlyArray<EmbedInput>\n}\n\n/**\n * Single-embed response. The `embedding` type is determined by the\n * request's `encoding` field via `EmbeddingFor<E>` — callers that don't\n * specify an encoding get a `Float32Embedding` directly with no runtime\n * narrowing. Defaults to `undefined` for back-compat with consumers that\n * use the bare `EmbedResponse` name.\n */\nexport type EmbedResponse<E extends EmbedEncoding | undefined = undefined> = {\n readonly embedding: EmbeddingFor<E>\n readonly usage: Usage\n}\n\n/** Batch-embed response. Same encoding rule as {@link EmbedResponse}. */\nexport type EmbedManyResponse<E extends EmbedEncoding | undefined = undefined> = {\n readonly embeddings: ReadonlyArray<EmbeddingFor<E>>\n readonly usage: Usage\n}\n\nexport type EmbeddingModelService = {\n readonly embed: <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedResponse<E>, AiError.AiError>\n readonly embedMany: <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n ) => Effect.Effect<EmbedManyResponse<E>, AiError.AiError>\n}\n\nexport class EmbeddingModel extends Context.Service<EmbeddingModel, EmbeddingModelService>()(\n \"@betalyra/effect-uai/EmbeddingModel\",\n) {}\n\n/** Embed a single input. */\nexport const embed = <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedRequest, \"encoding\"> & { readonly encoding?: E },\n): Effect.Effect<EmbedResponse<E>, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embed(request))\n\n/** Embed a batch in one provider call. Same `task` for every input. */\nexport const embedMany = <E extends EmbedEncoding | undefined = undefined>(\n request: Omit<CommonEmbedManyRequest, \"encoding\"> & { readonly encoding?: E },\n): Effect.Effect<EmbedManyResponse<E>, AiError.AiError, EmbeddingModel> =>\n Effect.flatMap(EmbeddingModel.asEffect(), (m) => m.embedMany(request))\n"],"mappings":";;;;;;;;AAsGA,IAAa,iBAAb,cAAoC,QAAQ,SAAgD,CAC1F,sCACD,CAAC;;AAGF,MAAa,SACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,MAAM,QAAQ,CAAC;;AAGpE,MAAa,aACX,YAEA,OAAO,QAAQ,eAAe,UAAU,GAAG,MAAM,EAAE,UAAU,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { embed, embedMany } from "./EmbeddingModel.mjs";
|
|
2
|
+
import { i as it, r as describe, t as import_dist } from "../dist-DV5ISja1.mjs";
|
|
3
|
+
//#region src/embedding-model/EmbeddingModel.test.ts
|
|
4
|
+
describe("EmbedResponse<E> conditional narrowing", () => {
|
|
5
|
+
it("defaults to Float32Embedding when E is undefined", () => {
|
|
6
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
7
|
+
});
|
|
8
|
+
it("maps each EmbedEncoding literal to the matching Embedding variant", () => {
|
|
9
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
10
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
11
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
12
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
13
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
14
|
+
});
|
|
15
|
+
it("falls back to the open Embedding union when E is the full EmbedEncoding", () => {
|
|
16
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe("EmbedManyResponse<E> mirrors EmbedResponse<E>", () => {
|
|
20
|
+
it("defaults to ReadonlyArray<Float32Embedding>", () => {
|
|
21
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
22
|
+
});
|
|
23
|
+
it("narrows per encoding", () => {
|
|
24
|
+
(0, import_dist.expectTypeOf)().toEqualTypeOf();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("embed / embedMany free exports preserve E in their return type", () => {
|
|
28
|
+
it("embed with no encoding returns EmbedResponse<undefined> = Float32", () => {
|
|
29
|
+
(0, import_dist.expectTypeOf)(embed({
|
|
30
|
+
input: "x",
|
|
31
|
+
model: "m"
|
|
32
|
+
})).toEqualTypeOf();
|
|
33
|
+
});
|
|
34
|
+
it("embed with encoding: int8 returns EmbedResponse<int8>", () => {
|
|
35
|
+
(0, import_dist.expectTypeOf)(embed({
|
|
36
|
+
input: "x",
|
|
37
|
+
model: "m",
|
|
38
|
+
encoding: "int8"
|
|
39
|
+
})).toEqualTypeOf();
|
|
40
|
+
});
|
|
41
|
+
it("embedMany with encoding: multivector returns EmbedManyResponse<multivector>", () => {
|
|
42
|
+
(0, import_dist.expectTypeOf)(embedMany({
|
|
43
|
+
inputs: ["x"],
|
|
44
|
+
model: "m",
|
|
45
|
+
encoding: "multivector"
|
|
46
|
+
})).toEqualTypeOf();
|
|
47
|
+
});
|
|
48
|
+
it("when the request is widened to CommonEmbedRequest, the response is the open union", () => {
|
|
49
|
+
(0, import_dist.expectTypeOf)(embed({
|
|
50
|
+
input: "x",
|
|
51
|
+
model: "m",
|
|
52
|
+
encoding: "int8"
|
|
53
|
+
})).toEqualTypeOf();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
//#endregion
|
|
57
|
+
export {};
|
|
58
|
+
|
|
59
|
+
//# sourceMappingURL=EmbeddingModel.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmbeddingModel.test.mjs","names":[],"sources":["../../src/embedding-model/EmbeddingModel.test.ts"],"sourcesContent":["import { Effect } from \"effect\"\nimport { describe, expectTypeOf, it } from \"vitest\"\nimport type * as AiError from \"../domain/AiError.js\"\nimport type {\n BinaryEmbedding,\n Float32Embedding,\n Int8Embedding,\n MultivectorEmbedding,\n SparseEmbedding,\n} from \"./Embedding.js\"\nimport {\n type EmbedEncoding,\n embed,\n embedMany,\n type EmbedManyResponse,\n type EmbedResponse,\n EmbeddingModel,\n} from \"./EmbeddingModel.js\"\n\n// Type-level tests only. The actual narrowing happens at the type system\n// boundary — the runtime impl is exercised in provider-specific test files.\ndescribe(\"EmbedResponse<E> conditional narrowing\", () => {\n it(\"defaults to Float32Embedding when E is undefined\", () => {\n expectTypeOf<EmbedResponse>().toEqualTypeOf<{\n readonly embedding: Float32Embedding\n readonly usage: import(\"./Embedding.js\").Usage\n }>()\n })\n\n it(\"maps each EmbedEncoding literal to the matching Embedding variant\", () => {\n expectTypeOf<EmbedResponse<\"float32\">[\"embedding\"]>().toEqualTypeOf<Float32Embedding>()\n expectTypeOf<EmbedResponse<\"int8\">[\"embedding\"]>().toEqualTypeOf<Int8Embedding>()\n expectTypeOf<EmbedResponse<\"binary\">[\"embedding\"]>().toEqualTypeOf<BinaryEmbedding>()\n expectTypeOf<EmbedResponse<\"sparse\">[\"embedding\"]>().toEqualTypeOf<SparseEmbedding>()\n expectTypeOf<EmbedResponse<\"multivector\">[\"embedding\"]>().toEqualTypeOf<MultivectorEmbedding>()\n })\n\n it(\"falls back to the open Embedding union when E is the full EmbedEncoding\", () => {\n expectTypeOf<EmbedResponse<EmbedEncoding>[\"embedding\"]>().toEqualTypeOf<\n Float32Embedding | Int8Embedding | BinaryEmbedding | SparseEmbedding | MultivectorEmbedding\n >()\n })\n})\n\ndescribe(\"EmbedManyResponse<E> mirrors EmbedResponse<E>\", () => {\n it(\"defaults to ReadonlyArray<Float32Embedding>\", () => {\n expectTypeOf<EmbedManyResponse[\"embeddings\"]>().toEqualTypeOf<ReadonlyArray<Float32Embedding>>()\n })\n\n it(\"narrows per encoding\", () => {\n expectTypeOf<EmbedManyResponse<\"int8\">[\"embeddings\"]>().toEqualTypeOf<\n ReadonlyArray<Int8Embedding>\n >()\n })\n})\n\ndescribe(\"embed / embedMany free exports preserve E in their return type\", () => {\n it(\"embed with no encoding returns EmbedResponse<undefined> = Float32\", () => {\n const result = embed({ input: \"x\", model: \"m\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<undefined>, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"embed with encoding: int8 returns EmbedResponse<int8>\", () => {\n const result = embed({ input: \"x\", model: \"m\", encoding: \"int8\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<\"int8\">, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"embedMany with encoding: multivector returns EmbedManyResponse<multivector>\", () => {\n const result = embedMany({ inputs: [\"x\"], model: \"m\", encoding: \"multivector\" })\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedManyResponse<\"multivector\">, AiError.AiError, EmbeddingModel>\n >()\n })\n\n it(\"when the request is widened to CommonEmbedRequest, the response is the open union\", () => {\n // Demonstrates the documented case: annotating the request type erases\n // the literal `encoding` and the response falls back to `Embedding`.\n const req: { input: string; model: string; encoding?: EmbedEncoding } = {\n input: \"x\",\n model: \"m\",\n encoding: \"int8\",\n }\n const result = embed(req)\n expectTypeOf(result).toEqualTypeOf<\n Effect.Effect<EmbedResponse<EmbedEncoding>, AiError.AiError, EmbeddingModel>\n >()\n })\n})\n"],"mappings":";;;AAqBA,SAAS,gDAAgD;AACvD,IAAG,0DAA0D;AAC3D,GAAA,GAAA,YAAA,eAA6B,CAAC,eAG1B;GACJ;AAEF,IAAG,2EAA2E;AAC5E,GAAA,GAAA,YAAA,eAAqD,CAAC,eAAiC;AACvF,GAAA,GAAA,YAAA,eAAkD,CAAC,eAA8B;AACjF,GAAA,GAAA,YAAA,eAAoD,CAAC,eAAgC;AACrF,GAAA,GAAA,YAAA,eAAoD,CAAC,eAAgC;AACrF,GAAA,GAAA,YAAA,eAAyD,CAAC,eAAqC;GAC/F;AAEF,IAAG,iFAAiF;AAClF,GAAA,GAAA,YAAA,eAAyD,CAAC,eAEvD;GACH;EACF;AAEF,SAAS,uDAAuD;AAC9D,IAAG,qDAAqD;AACtD,GAAA,GAAA,YAAA,eAA+C,CAAC,eAAgD;GAChG;AAEF,IAAG,8BAA8B;AAC/B,GAAA,GAAA,YAAA,eAAuD,CAAC,eAErD;GACH;EACF;AAEF,SAAS,wEAAwE;AAC/E,IAAG,2EAA2E;AAE5E,GAAA,GAAA,YAAA,cADe,MAAM;GAAE,OAAO;GAAK,OAAO;GAAK,CAC5B,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,+DAA+D;AAEhE,GAAA,GAAA,YAAA,cADe,MAAM;GAAE,OAAO;GAAK,OAAO;GAAK,UAAU;GAAQ,CAC9C,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,qFAAqF;AAEtF,GAAA,GAAA,YAAA,cADe,UAAU;GAAE,QAAQ,CAAC,IAAI;GAAE,OAAO;GAAK,UAAU;GAAe,CAC5D,CAAC,CAAC,eAElB;GACH;AAEF,IAAG,2FAA2F;AAS5F,GAAA,GAAA,YAAA,cADe,MAAM;GAJnB,OAAO;GACP,OAAO;GACP,UAAU;GAEY,CACL,CAAC,CAAC,eAElB;GACH;EACF"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import { n as AiError_d_exports } from "./AiError-
|
|
1
|
+
import { n as AiError_d_exports } from "./AiError-CAX_48RU.mjs";
|
|
2
2
|
import { a as Media_d_exports } from "./Media-D_CpcM1Z.mjs";
|
|
3
|
-
import { o as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
3
|
+
import { o as Audio_d_exports } from "./Audio-BfCTGnH3.mjs";
|
|
4
|
+
import { o as Image_d_exports } from "./Image-HNmMpMTh.mjs";
|
|
5
|
+
import { f as Items_d_exports } from "./Items-DqbaJoz7.mjs";
|
|
6
|
+
import { t as Music_d_exports } from "./domain/Music.mjs";
|
|
7
|
+
import { t as Transcript_d_exports } from "./domain/Transcript.mjs";
|
|
8
|
+
import { a as StructuredFormat_d_exports } from "./StructuredFormat-BbN4dosH.mjs";
|
|
9
|
+
import { a as Turn_d_exports } from "./Turn-ChbL2foc.mjs";
|
|
7
10
|
import { t as Embedding_d_exports } from "./embedding-model/Embedding.mjs";
|
|
8
11
|
import { t as EmbeddingModel_d_exports } from "./embedding-model/EmbeddingModel.mjs";
|
|
9
|
-
import { l as Tool_d_exports } from "./Tool-
|
|
12
|
+
import { l as Tool_d_exports } from "./Tool-Y0__Py1H.mjs";
|
|
10
13
|
import { t as LanguageModel_d_exports } from "./language-model/LanguageModel.mjs";
|
|
14
|
+
import { t as MusicGenerator_d_exports } from "./music-generator/MusicGenerator.mjs";
|
|
15
|
+
import { t as SpeechSynthesizer_d_exports } from "./speech-synthesizer/SpeechSynthesizer.mjs";
|
|
16
|
+
import { t as Transcriber_d_exports } from "./transcriber/Transcriber.mjs";
|
|
11
17
|
import { t as Vector_d_exports } from "./math/Vector.mjs";
|
|
12
18
|
import { t as Loop_d_exports } from "./loop/Loop.mjs";
|
|
13
19
|
import { t as Outcome_d_exports } from "./tool/Outcome.mjs";
|
|
@@ -19,4 +25,4 @@ import { t as JSONL_d_exports } from "./streaming/JSONL.mjs";
|
|
|
19
25
|
import { t as Lines_d_exports } from "./streaming/Lines.mjs";
|
|
20
26
|
import { t as SSE_d_exports } from "./streaming/SSE.mjs";
|
|
21
27
|
import { t as Metrics_d_exports } from "./observability/Metrics.mjs";
|
|
22
|
-
export { AiError_d_exports as AiError, Embedding_d_exports as Embedding, EmbeddingModel_d_exports as EmbeddingModel, HistoryCheck_d_exports as HistoryCheck, Image_d_exports as Image, Items_d_exports as Items, JSONL_d_exports as JSONL, LanguageModel_d_exports as LanguageModel, Lines_d_exports as Lines, Loop_d_exports as Loop, Media_d_exports as Media, Metrics_d_exports as Metrics, Outcome_d_exports as Outcome, Resolvers_d_exports as Resolvers, SSE_d_exports as SSE, StructuredFormat_d_exports as StructuredFormat, Tool_d_exports as Tool, ToolEvent_d_exports as ToolEvent, Toolkit_d_exports as Toolkit, Turn_d_exports as Turn, Vector_d_exports as Vector };
|
|
28
|
+
export { AiError_d_exports as AiError, Audio_d_exports as Audio, Embedding_d_exports as Embedding, EmbeddingModel_d_exports as EmbeddingModel, HistoryCheck_d_exports as HistoryCheck, Image_d_exports as Image, Items_d_exports as Items, JSONL_d_exports as JSONL, LanguageModel_d_exports as LanguageModel, Lines_d_exports as Lines, Loop_d_exports as Loop, Media_d_exports as Media, Metrics_d_exports as Metrics, Music_d_exports as Music, MusicGenerator_d_exports as MusicGenerator, Outcome_d_exports as Outcome, Resolvers_d_exports as Resolvers, SSE_d_exports as SSE, SpeechSynthesizer_d_exports as SpeechSynthesizer, StructuredFormat_d_exports as StructuredFormat, Tool_d_exports as Tool, ToolEvent_d_exports as ToolEvent, Toolkit_d_exports as Toolkit, Transcriber_d_exports as Transcriber, Transcript_d_exports as Transcript, Turn_d_exports as Turn, Vector_d_exports as Vector };
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import { t as AiError_exports } from "./domain/AiError.mjs";
|
|
2
|
+
import { t as Audio_exports } from "./domain/Audio.mjs";
|
|
2
3
|
import { t as Image_exports } from "./domain/Image.mjs";
|
|
3
4
|
import { t as Items_exports } from "./domain/Items.mjs";
|
|
4
5
|
import { t as Media_exports } from "./domain/Media.mjs";
|
|
6
|
+
import { t as Music_exports } from "./domain/Music.mjs";
|
|
7
|
+
import { t as Transcript_exports } from "./domain/Transcript.mjs";
|
|
5
8
|
import { t as StructuredFormat_exports } from "./structured-format/StructuredFormat.mjs";
|
|
6
9
|
import { t as Turn_exports } from "./domain/Turn.mjs";
|
|
7
10
|
import { t as Embedding_exports } from "./embedding-model/Embedding.mjs";
|
|
8
11
|
import { t as EmbeddingModel_exports } from "./embedding-model/EmbeddingModel.mjs";
|
|
9
12
|
import { t as LanguageModel_exports } from "./language-model/LanguageModel.mjs";
|
|
13
|
+
import { t as MusicGenerator_exports } from "./music-generator/MusicGenerator.mjs";
|
|
14
|
+
import { t as SpeechSynthesizer_exports } from "./speech-synthesizer/SpeechSynthesizer.mjs";
|
|
15
|
+
import { t as Transcriber_exports } from "./transcriber/Transcriber.mjs";
|
|
10
16
|
import { t as Vector_exports } from "./math/Vector.mjs";
|
|
11
17
|
import { t as Loop_exports } from "./loop/Loop.mjs";
|
|
12
18
|
import { t as Tool_exports } from "./tool/Tool.mjs";
|
|
@@ -19,4 +25,4 @@ import { t as JSONL_exports } from "./streaming/JSONL.mjs";
|
|
|
19
25
|
import { t as Lines_exports } from "./streaming/Lines.mjs";
|
|
20
26
|
import { t as SSE_exports } from "./streaming/SSE.mjs";
|
|
21
27
|
import { t as Metrics_exports } from "./observability/Metrics.mjs";
|
|
22
|
-
export { AiError_exports as AiError, Embedding_exports as Embedding, EmbeddingModel_exports as EmbeddingModel, HistoryCheck_exports as HistoryCheck, Image_exports as Image, Items_exports as Items, JSONL_exports as JSONL, LanguageModel_exports as LanguageModel, Lines_exports as Lines, Loop_exports as Loop, Media_exports as Media, Metrics_exports as Metrics, Outcome_exports as Outcome, Resolvers_exports as Resolvers, SSE_exports as SSE, StructuredFormat_exports as StructuredFormat, Tool_exports as Tool, ToolEvent_exports as ToolEvent, Toolkit_exports as Toolkit, Turn_exports as Turn, Vector_exports as Vector };
|
|
28
|
+
export { AiError_exports as AiError, Audio_exports as Audio, Embedding_exports as Embedding, EmbeddingModel_exports as EmbeddingModel, HistoryCheck_exports as HistoryCheck, Image_exports as Image, Items_exports as Items, JSONL_exports as JSONL, LanguageModel_exports as LanguageModel, Lines_exports as Lines, Loop_exports as Loop, Media_exports as Media, Metrics_exports as Metrics, Music_exports as Music, MusicGenerator_exports as MusicGenerator, Outcome_exports as Outcome, Resolvers_exports as Resolvers, SSE_exports as SSE, SpeechSynthesizer_exports as SpeechSynthesizer, StructuredFormat_exports as StructuredFormat, Tool_exports as Tool, ToolEvent_exports as ToolEvent, Toolkit_exports as Toolkit, Transcriber_exports as Transcriber, Transcript_exports as Transcript, Turn_exports as Turn, Vector_exports as Vector };
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { t as AiError } from "../AiError-
|
|
2
|
-
import { d as Item } from "../Items-
|
|
3
|
-
import { i as StructuredFormat } from "../StructuredFormat-
|
|
4
|
-
import { i as TurnEvent } from "../Turn-
|
|
5
|
-
import { o as ToolDescriptor } from "../Tool-
|
|
6
|
-
import { Context, Stream } from "effect";
|
|
1
|
+
import { d as RateLimited, l as IncompleteTurn, m as Unavailable, p as Timeout, t as AiError } from "../AiError-CAX_48RU.mjs";
|
|
2
|
+
import { d as Item } from "../Items-DqbaJoz7.mjs";
|
|
3
|
+
import { i as StructuredFormat } from "../StructuredFormat-BbN4dosH.mjs";
|
|
4
|
+
import { i as TurnEvent, r as Turn } from "../Turn-ChbL2foc.mjs";
|
|
5
|
+
import { o as ToolDescriptor } from "../Tool-Y0__Py1H.mjs";
|
|
6
|
+
import { Context, Effect, Schedule, Stream } from "effect";
|
|
7
|
+
import * as _$effect_Types0 from "effect/Types";
|
|
8
|
+
import * as _$effect_Cause0 from "effect/Cause";
|
|
7
9
|
|
|
8
10
|
//#region src/language-model/LanguageModel.d.ts
|
|
9
11
|
declare namespace LanguageModel_d_exports {
|
|
10
|
-
export { CommonRequest, LanguageModel, LanguageModelService, streamTurn };
|
|
12
|
+
export { CommonRequest, LanguageModel, LanguageModelService, Retryable, retry, streamTurn, turn };
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Cross-provider request shape. Every call carries its own `history` and
|
|
@@ -47,6 +49,26 @@ declare class LanguageModel extends LanguageModel_base {}
|
|
|
47
49
|
* Stream the deltas of a single turn.
|
|
48
50
|
*/
|
|
49
51
|
declare const streamTurn: (request: CommonRequest) => Stream.Stream<TurnEvent, AiError, LanguageModel>;
|
|
52
|
+
/**
|
|
53
|
+
* Drain `streamTurn` and return the assembled `Turn` from the terminal
|
|
54
|
+
* `TurnComplete` event. Fails with `IncompleteTurn` if the stream ends
|
|
55
|
+
* without one. Derived from `streamTurn`; providers get it for free.
|
|
56
|
+
*/
|
|
57
|
+
declare const turn: (request: CommonRequest) => Effect.Effect<Turn, AiError | IncompleteTurn, LanguageModel>;
|
|
58
|
+
declare const Retryable_base: new <A extends Record<string, any> = {}>(args: _$effect_Types0.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>) => _$effect_Cause0.YieldableError & {
|
|
59
|
+
readonly _tag: "RetryableAi";
|
|
60
|
+
} & Readonly<A>;
|
|
61
|
+
/** Internal wrapper around the retryable subset of `AiError`. */
|
|
62
|
+
declare class Retryable extends Retryable_base<{
|
|
63
|
+
readonly cause: RateLimited | Unavailable | Timeout;
|
|
64
|
+
}> {}
|
|
65
|
+
/**
|
|
66
|
+
* Retry a stream of `AiError` on the retryable subset
|
|
67
|
+
* (`RateLimited | Unavailable | Timeout`). Other failures bypass the
|
|
68
|
+
* schedule and propagate unchanged. Like all `Stream.retry`, the entire
|
|
69
|
+
* stream re-runs — deltas before the failure replay on the next attempt.
|
|
70
|
+
*/
|
|
71
|
+
declare const retry: <Out>(schedule: Schedule.Schedule<Out, Retryable>) => <A, R>(stream: Stream.Stream<A, AiError, R>) => Stream.Stream<A, AiError, R>;
|
|
50
72
|
//#endregion
|
|
51
|
-
export { CommonRequest, LanguageModel, LanguageModelService, streamTurn, LanguageModel_d_exports as t };
|
|
73
|
+
export { CommonRequest, LanguageModel, LanguageModelService, Retryable, retry, streamTurn, LanguageModel_d_exports as t, turn };
|
|
52
74
|
//# sourceMappingURL=LanguageModel.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageModel.d.mts","names":[],"sources":["../../src/language-model/LanguageModel.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"LanguageModel.d.mts","names":[],"sources":["../../src/language-model/LanguageModel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;KAaY,aAAA;EAAA,SACD,OAAA,EAAS,aAAA,CAAc,IAAA;;;;;WAKvB,KAAA;EAAA,SACA,KAAA,GAAQ,aAAA,CAAc,cAAA;EAAA,SACtB,UAAA;IAAA,SAIM,IAAA;IAAA,SAA2B,IAAA;EAAA;EAAA,SACjC,WAAA;EAAA,SACA,IAAA;EAAA,SACA,eAAA;EAOa;;;;;;EAAA,SAAb,UAAA,GAAa,gBAAA;AAAA;AAAA,KAGZ,oBAAA;EAAA,SACD,UAAA,GAAa,OAAA,EAAS,aAAA,KAAkB,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA;AAAA;AAAA,cAC3E,kBAAA;cAEY,aAAA,SAAsB,kBAAA;;;;cAOtB,UAAA,GACX,OAAA,EAAS,aAAA,KACR,MAAA,CAAO,MAAA,CAAO,SAAA,EAAW,OAAA,EAAiB,aAAA;;;;AAb7C;;cAqBa,IAAA,GACX,OAAA,EAAS,aAAA,KACR,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,OAAA,GAAkB,cAAA,EAAwB,aAAA;AAAA,cAW9D,cAAA;;;;cAOU,SAAA,SAAkB,cAAA;EAAA,SACpB,KAAA,EAAO,WAAA,GAAsB,WAAA,GAAsB,OAAA;AAAA;;;;;;;cAoBjD,KAAA,QACL,QAAA,EAAU,QAAA,CAAS,QAAA,CAAS,GAAA,EAAK,SAAA,aAChC,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,OAAA,EAAiB,CAAA,MAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,OAAA,EAAiB,CAAA"}
|
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import { n as __exportAll } from "../chunk-uyGKjUfl.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { IncompleteTurn } from "../domain/AiError.mjs";
|
|
3
|
+
import { isTurnComplete } from "../domain/Turn.mjs";
|
|
4
|
+
import { Array, Context, Data, Effect, Option, Stream } from "effect";
|
|
3
5
|
//#region src/language-model/LanguageModel.ts
|
|
4
6
|
var LanguageModel_exports = /* @__PURE__ */ __exportAll({
|
|
5
7
|
LanguageModel: () => LanguageModel,
|
|
6
|
-
|
|
8
|
+
Retryable: () => Retryable,
|
|
9
|
+
retry: () => retry,
|
|
10
|
+
streamTurn: () => streamTurn,
|
|
11
|
+
turn: () => turn
|
|
7
12
|
});
|
|
8
13
|
var LanguageModel = class extends Context.Service()("@betalyra/effect-uai/LanguageModel") {};
|
|
9
14
|
/**
|
|
10
15
|
* Stream the deltas of a single turn.
|
|
11
16
|
*/
|
|
12
17
|
const streamTurn = (request) => Stream.unwrap(Effect.map(LanguageModel.asEffect(), (m) => m.streamTurn(request)));
|
|
18
|
+
/**
|
|
19
|
+
* Drain `streamTurn` and return the assembled `Turn` from the terminal
|
|
20
|
+
* `TurnComplete` event. Fails with `IncompleteTurn` if the stream ends
|
|
21
|
+
* without one. Derived from `streamTurn`; providers get it for free.
|
|
22
|
+
*/
|
|
23
|
+
const turn = (request) => streamTurn(request).pipe(Stream.runCollect, Effect.flatMap((events) => Array.findLast(events, isTurnComplete).pipe(Option.match({
|
|
24
|
+
onNone: () => Effect.fail(new IncompleteTurn({})),
|
|
25
|
+
onSome: (e) => Effect.succeed(e.turn)
|
|
26
|
+
}))));
|
|
27
|
+
/** Internal wrapper around the retryable subset of `AiError`. */
|
|
28
|
+
var Retryable = class extends Data.TaggedError("RetryableAi") {};
|
|
29
|
+
const isRetryable = (e) => e._tag === "RateLimited" || e._tag === "Unavailable" || e._tag === "Timeout";
|
|
30
|
+
/**
|
|
31
|
+
* Retry a stream of `AiError` on the retryable subset
|
|
32
|
+
* (`RateLimited | Unavailable | Timeout`). Other failures bypass the
|
|
33
|
+
* schedule and propagate unchanged. Like all `Stream.retry`, the entire
|
|
34
|
+
* stream re-runs — deltas before the failure replay on the next attempt.
|
|
35
|
+
*/
|
|
36
|
+
const retry = (schedule) => (stream) => stream.pipe(Stream.map((value) => ({
|
|
37
|
+
_tag: "Item",
|
|
38
|
+
value
|
|
39
|
+
})), Stream.catchIf(isRetryable, (cause) => Stream.fail(new Retryable({ cause })), (cause) => Stream.succeed({
|
|
40
|
+
_tag: "Terminal",
|
|
41
|
+
cause
|
|
42
|
+
})), Stream.retry(schedule), Stream.catchTag("RetryableAi", (e) => Stream.fail(e.cause)), Stream.flatMap((item) => item._tag === "Item" ? Stream.succeed(item.value) : Stream.fail(item.cause)));
|
|
13
43
|
//#endregion
|
|
14
|
-
export { LanguageModel, streamTurn, LanguageModel_exports as t };
|
|
44
|
+
export { LanguageModel, Retryable, retry, streamTurn, LanguageModel_exports as t, turn };
|
|
15
45
|
|
|
16
46
|
//# sourceMappingURL=LanguageModel.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LanguageModel.mjs","names":[],"sources":["../../src/language-model/LanguageModel.ts"],"sourcesContent":["import { Context, Effect, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { Item } from \"../domain/Items.js\"\nimport type * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport type { ToolDescriptor } from \"../tool/Tool.js\"\nimport { isTurnComplete, type Turn, type TurnEvent } from \"../domain/Turn.js\"\n\n/**\n * Cross-provider request shape. Every call carries its own `history` and\n * `model` - models are not bound at layer construction. Anything specific\n * to a single provider (reasoning effort, prompt caching, store flags,\n * ...) lives in that provider's own request interface, which extends this.\n */\nexport type CommonRequest = {\n readonly history: ReadonlyArray<Item>\n /**\n * Model identifier. Each provider narrows this to its typed literal union,\n * so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n readonly tools?: ReadonlyArray<ToolDescriptor>\n readonly toolChoice?:\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n readonly temperature?: number\n readonly topP?: number\n readonly maxOutputTokens?: number\n /**\n * Schema-bound JSON output. The provider constrains the wire to match the\n * schema; pair with `Turn.toStructured` for runtime validation. Supported\n * across all current providers (OpenAI Responses json_schema, Anthropic\n * `output_config`, Gemini `responseJsonSchema`).\n */\n readonly structured?: StructuredFormat.StructuredFormat<unknown>\n}\n\nexport type LanguageModelService = {\n readonly streamTurn: (request: CommonRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n}\n\nexport class LanguageModel extends Context.Service<LanguageModel, LanguageModelService>()(\n \"@betalyra/effect-uai/LanguageModel\",\n) {}\n\n/**\n * Stream the deltas of a single turn.\n */\nexport const streamTurn = (\n request: CommonRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, LanguageModel> =>\n Stream.unwrap(Effect.map(LanguageModel.asEffect(), (m) => m.streamTurn(request)))\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"LanguageModel.mjs","names":["Arr","AiError.IncompleteTurn"],"sources":["../../src/language-model/LanguageModel.ts"],"sourcesContent":["import { Array as Arr, Context, Data, Effect, Option, type Schedule, Stream } from \"effect\"\nimport * as AiError from \"../domain/AiError.js\"\nimport type { Item } from \"../domain/Items.js\"\nimport type * as StructuredFormat from \"../structured-format/StructuredFormat.js\"\nimport type { ToolDescriptor } from \"../tool/Tool.js\"\nimport { isTurnComplete, type Turn, type TurnEvent } from \"../domain/Turn.js\"\n\n/**\n * Cross-provider request shape. Every call carries its own `history` and\n * `model` - models are not bound at layer construction. Anything specific\n * to a single provider (reasoning effort, prompt caching, store flags,\n * ...) lives in that provider's own request interface, which extends this.\n */\nexport type CommonRequest = {\n readonly history: ReadonlyArray<Item>\n /**\n * Model identifier. Each provider narrows this to its typed literal union,\n * so code that yields a typed provider tag gets autocompletion.\n */\n readonly model: string\n readonly tools?: ReadonlyArray<ToolDescriptor>\n readonly toolChoice?:\n | \"auto\"\n | \"required\"\n | \"none\"\n | { readonly type: \"function\"; readonly name: string }\n readonly temperature?: number\n readonly topP?: number\n readonly maxOutputTokens?: number\n /**\n * Schema-bound JSON output. The provider constrains the wire to match the\n * schema; pair with `Turn.toStructured` for runtime validation. Supported\n * across all current providers (OpenAI Responses json_schema, Anthropic\n * `output_config`, Gemini `responseJsonSchema`).\n */\n readonly structured?: StructuredFormat.StructuredFormat<unknown>\n}\n\nexport type LanguageModelService = {\n readonly streamTurn: (request: CommonRequest) => Stream.Stream<TurnEvent, AiError.AiError>\n}\n\nexport class LanguageModel extends Context.Service<LanguageModel, LanguageModelService>()(\n \"@betalyra/effect-uai/LanguageModel\",\n) {}\n\n/**\n * Stream the deltas of a single turn.\n */\nexport const streamTurn = (\n request: CommonRequest,\n): Stream.Stream<TurnEvent, AiError.AiError, LanguageModel> =>\n Stream.unwrap(Effect.map(LanguageModel.asEffect(), (m) => m.streamTurn(request)))\n\n/**\n * Drain `streamTurn` and return the assembled `Turn` from the terminal\n * `TurnComplete` event. Fails with `IncompleteTurn` if the stream ends\n * without one. Derived from `streamTurn`; providers get it for free.\n */\nexport const turn = (\n request: CommonRequest,\n): Effect.Effect<Turn, AiError.AiError | AiError.IncompleteTurn, LanguageModel> =>\n streamTurn(request).pipe(\n Stream.runCollect,\n Effect.flatMap((events) =>\n Arr.findLast(events, isTurnComplete).pipe(\n Option.match({\n onNone: () => Effect.fail(new AiError.IncompleteTurn({})),\n onSome: (e) => Effect.succeed(e.turn),\n }),\n ),\n ),\n )\n\n// ---------------------------------------------------------------------------\n// retry — retry the retryable subset of AiError, let other failures escape\n// ---------------------------------------------------------------------------\n\n/** Internal wrapper around the retryable subset of `AiError`. */\nexport class Retryable extends Data.TaggedError(\"RetryableAi\")<{\n readonly cause: AiError.RateLimited | AiError.Unavailable | AiError.Timeout\n}> {}\n\nconst isRetryable = (\n e: AiError.AiError,\n): e is AiError.RateLimited | AiError.Unavailable | AiError.Timeout =>\n e._tag === \"RateLimited\" || e._tag === \"Unavailable\" || e._tag === \"Timeout\"\n\n// Lift events to Items, non-retryable failures to Terminal values (escape\n// retry), retryable failures to wrapped errors (only thing retry sees).\ntype Lifted<A> =\n | { readonly _tag: \"Item\"; readonly value: A }\n | { readonly _tag: \"Terminal\"; readonly cause: AiError.AiError }\n\n/**\n * Retry a stream of `AiError` on the retryable subset\n * (`RateLimited | Unavailable | Timeout`). Other failures bypass the\n * schedule and propagate unchanged. Like all `Stream.retry`, the entire\n * stream re-runs — deltas before the failure replay on the next attempt.\n */\nexport const retry =\n <Out>(schedule: Schedule.Schedule<Out, Retryable>) =>\n <A, R>(stream: Stream.Stream<A, AiError.AiError, R>): Stream.Stream<A, AiError.AiError, R> =>\n stream.pipe(\n Stream.map((value): Lifted<A> => ({ _tag: \"Item\", value })),\n Stream.catchIf(\n isRetryable,\n (cause) => Stream.fail(new Retryable({ cause })),\n (cause) => Stream.succeed<Lifted<A>>({ _tag: \"Terminal\", cause }),\n ),\n Stream.retry(schedule),\n Stream.catchTag(\"RetryableAi\", (e) => Stream.fail<AiError.AiError>(e.cause)),\n Stream.flatMap((item) =>\n item._tag === \"Item\" ? Stream.succeed(item.value) : Stream.fail(item.cause),\n ),\n )\n"],"mappings":";;;;;;;;;;;;AA0CA,IAAa,gBAAb,cAAmC,QAAQ,SAA8C,CACvF,qCACD,CAAC;;;;AAKF,MAAa,cACX,YAEA,OAAO,OAAO,OAAO,IAAI,cAAc,UAAU,GAAG,MAAM,EAAE,WAAW,QAAQ,CAAC,CAAC;;;;;;AAOnF,MAAa,QACX,YAEA,WAAW,QAAQ,CAAC,KAClB,OAAO,YACP,OAAO,SAAS,WACdA,MAAI,SAAS,QAAQ,eAAe,CAAC,KACnC,OAAO,MAAM;CACX,cAAc,OAAO,KAAK,IAAIC,eAAuB,EAAE,CAAC,CAAC;CACzD,SAAS,MAAM,OAAO,QAAQ,EAAE,KAAK;CACtC,CAAC,CACH,CACF,CACF;;AAOH,IAAa,YAAb,cAA+B,KAAK,YAAY,cAAc,CAE3D;AAEH,MAAM,eACJ,MAEA,EAAE,SAAS,iBAAiB,EAAE,SAAS,iBAAiB,EAAE,SAAS;;;;;;;AAcrE,MAAa,SACL,cACC,WACL,OAAO,KACL,OAAO,KAAK,WAAsB;CAAE,MAAM;CAAQ;CAAO,EAAE,EAC3D,OAAO,QACL,cACC,UAAU,OAAO,KAAK,IAAI,UAAU,EAAE,OAAO,CAAC,CAAC,GAC/C,UAAU,OAAO,QAAmB;CAAE,MAAM;CAAY;CAAO,CAAC,CAClE,EACD,OAAO,MAAM,SAAS,EACtB,OAAO,SAAS,gBAAgB,MAAM,OAAO,KAAsB,EAAE,MAAM,CAAC,EAC5E,OAAO,SAAS,SACd,KAAK,SAAS,SAAS,OAAO,QAAQ,KAAK,MAAM,GAAG,OAAO,KAAK,KAAK,MAAM,CAC5E,CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { ContentFiltered, IncompleteTurn, RateLimited, Timeout, Unavailable } from "../domain/AiError.mjs";
|
|
2
|
+
import { assistantText, userText } from "../domain/Items.mjs";
|
|
3
|
+
import { TurnEvent } from "../domain/Turn.mjs";
|
|
4
|
+
import { LanguageModel, retry, turn } from "./LanguageModel.mjs";
|
|
5
|
+
import { i as it, n as globalExpect, r as describe } from "../dist-DV5ISja1.mjs";
|
|
6
|
+
import { layer } from "../testing/MockProvider.mjs";
|
|
7
|
+
import { Cause, Effect, Exit, Layer, Option, Ref, Schedule, Stream } from "effect";
|
|
8
|
+
//#region src/language-model/LanguageModel.test.ts
|
|
9
|
+
const oneTextTurn = (text) => ({
|
|
10
|
+
items: [assistantText(text)],
|
|
11
|
+
usage: {
|
|
12
|
+
input_tokens: 1,
|
|
13
|
+
output_tokens: 1
|
|
14
|
+
},
|
|
15
|
+
stop_reason: "stop"
|
|
16
|
+
});
|
|
17
|
+
describe("LanguageModel.turn", () => {
|
|
18
|
+
it("returns the assembled Turn from the terminal TurnComplete event", async () => {
|
|
19
|
+
const expected = oneTextTurn("hello world");
|
|
20
|
+
const program = turn({
|
|
21
|
+
history: [userText("hi")],
|
|
22
|
+
model: "mock"
|
|
23
|
+
});
|
|
24
|
+
globalExpect(await Effect.runPromise(program.pipe(Effect.provide(layer([expected]))))).toEqual(expected);
|
|
25
|
+
});
|
|
26
|
+
it("fails with IncompleteTurn when the stream ends without TurnComplete", async () => {
|
|
27
|
+
const broken = Layer.succeed(LanguageModel, { streamTurn: () => Stream.fromIterable([TurnEvent.TextDelta({ text: "partial" })]) });
|
|
28
|
+
const program = turn({
|
|
29
|
+
history: [userText("hi")],
|
|
30
|
+
model: "mock"
|
|
31
|
+
});
|
|
32
|
+
const exit = await Effect.runPromise(Effect.exit(program.pipe(Effect.provide(broken))));
|
|
33
|
+
globalExpect(Exit.isFailure(exit)).toBe(true);
|
|
34
|
+
if (Exit.isFailure(exit)) {
|
|
35
|
+
const failure = Cause.findErrorOption(exit.cause);
|
|
36
|
+
globalExpect(Option.isSome(failure)).toBe(true);
|
|
37
|
+
if (Option.isSome(failure)) globalExpect(failure.value).toBeInstanceOf(IncompleteTurn);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
it("propagates an AiError raised by streamTurn", async () => {
|
|
41
|
+
const rateLimited = new RateLimited({
|
|
42
|
+
provider: "mock",
|
|
43
|
+
raw: null
|
|
44
|
+
});
|
|
45
|
+
const failing = Layer.succeed(LanguageModel, { streamTurn: () => Stream.fail(rateLimited) });
|
|
46
|
+
const program = turn({
|
|
47
|
+
history: [],
|
|
48
|
+
model: "mock"
|
|
49
|
+
});
|
|
50
|
+
const exit = await Effect.runPromise(Effect.exit(program.pipe(Effect.provide(failing))));
|
|
51
|
+
globalExpect(Exit.isFailure(exit)).toBe(true);
|
|
52
|
+
if (Exit.isFailure(exit)) globalExpect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(rateLimited));
|
|
53
|
+
});
|
|
54
|
+
it("returns the LAST TurnComplete when the stream contains multiple (defensive)", async () => {
|
|
55
|
+
const first = oneTextTurn("first");
|
|
56
|
+
const second = oneTextTurn("second");
|
|
57
|
+
const weird = Layer.succeed(LanguageModel, { streamTurn: () => Stream.fromIterable([TurnEvent.TurnComplete({ turn: first }), TurnEvent.TurnComplete({ turn: second })]) });
|
|
58
|
+
const program = turn({
|
|
59
|
+
history: [],
|
|
60
|
+
model: "mock"
|
|
61
|
+
});
|
|
62
|
+
globalExpect(await Effect.runPromise(program.pipe(Effect.provide(weird)))).toEqual(second);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe("LanguageModel.retry", () => {
|
|
66
|
+
const textDelta = (text) => TurnEvent.TextDelta({ text });
|
|
67
|
+
const textTurn = (text) => ({
|
|
68
|
+
items: [assistantText(text)],
|
|
69
|
+
usage: {
|
|
70
|
+
input_tokens: 0,
|
|
71
|
+
output_tokens: 0
|
|
72
|
+
},
|
|
73
|
+
stop_reason: "stop"
|
|
74
|
+
});
|
|
75
|
+
const completeEvent = (text) => TurnEvent.TurnComplete({ turn: textTurn(text) });
|
|
76
|
+
const attemptStream = (attempts, plan) => Stream.unwrap(Ref.getAndUpdate(attempts, (n) => n + 1).pipe(Effect.map((n) => plan[Math.min(n, plan.length - 1)])));
|
|
77
|
+
it("retries on RateLimited and yields the success on the next attempt", async () => {
|
|
78
|
+
const program = Effect.gen(function* () {
|
|
79
|
+
const attempts = yield* Ref.make(0);
|
|
80
|
+
const stream = attemptStream(attempts, [Stream.fail(new RateLimited({
|
|
81
|
+
provider: "mock",
|
|
82
|
+
raw: null
|
|
83
|
+
})), Stream.fromIterable([textDelta("ok"), completeEvent("ok")])]).pipe(retry(Schedule.recurs(3)));
|
|
84
|
+
const events = yield* Stream.runCollect(stream);
|
|
85
|
+
const count = yield* Ref.get(attempts);
|
|
86
|
+
return {
|
|
87
|
+
events: Array.from(events),
|
|
88
|
+
count
|
|
89
|
+
};
|
|
90
|
+
});
|
|
91
|
+
const { events, count } = await Effect.runPromise(program);
|
|
92
|
+
globalExpect(count).toBe(2);
|
|
93
|
+
globalExpect(events.map((e) => e._tag)).toEqual(["TextDelta", "TurnComplete"]);
|
|
94
|
+
});
|
|
95
|
+
it("surfaces the underlying retryable failure when retries are exhausted", async () => {
|
|
96
|
+
const cause = new Unavailable({
|
|
97
|
+
provider: "mock",
|
|
98
|
+
raw: null
|
|
99
|
+
});
|
|
100
|
+
const stream = Stream.fail(cause).pipe(retry(Schedule.recurs(2)));
|
|
101
|
+
const exit = await Effect.runPromise(Effect.exit(Stream.runCollect(stream)));
|
|
102
|
+
globalExpect(Exit.isFailure(exit)).toBe(true);
|
|
103
|
+
if (Exit.isFailure(exit)) globalExpect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(cause));
|
|
104
|
+
});
|
|
105
|
+
it("bypasses retry for non-retryable AiError (ContentFiltered)", async () => {
|
|
106
|
+
const program = Effect.gen(function* () {
|
|
107
|
+
const attempts = yield* Ref.make(0);
|
|
108
|
+
const cause = new ContentFiltered({
|
|
109
|
+
provider: "mock",
|
|
110
|
+
raw: null
|
|
111
|
+
});
|
|
112
|
+
const stream = attemptStream(attempts, [Stream.fail(cause)]).pipe(retry(Schedule.recurs(5)));
|
|
113
|
+
return {
|
|
114
|
+
exit: yield* Effect.exit(Stream.runCollect(stream)),
|
|
115
|
+
count: yield* Ref.get(attempts),
|
|
116
|
+
cause
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
const { exit, count, cause } = await Effect.runPromise(program);
|
|
120
|
+
globalExpect(count).toBe(1);
|
|
121
|
+
globalExpect(Exit.isFailure(exit)).toBe(true);
|
|
122
|
+
if (Exit.isFailure(exit)) globalExpect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(cause));
|
|
123
|
+
});
|
|
124
|
+
it("preserves deltas emitted before a retryable failure (and replays on retry)", async () => {
|
|
125
|
+
const program = Effect.gen(function* () {
|
|
126
|
+
const stream = attemptStream(yield* Ref.make(0), [Stream.concat(Stream.succeed(textDelta("partial")), Stream.fail(new Timeout({
|
|
127
|
+
provider: "mock",
|
|
128
|
+
raw: null
|
|
129
|
+
}))), Stream.fromIterable([textDelta("partial"), completeEvent("done")])]).pipe(retry(Schedule.recurs(1)));
|
|
130
|
+
const events = yield* Stream.runCollect(stream);
|
|
131
|
+
return Array.from(events);
|
|
132
|
+
});
|
|
133
|
+
globalExpect((await Effect.runPromise(program)).map((e) => e._tag)).toEqual([
|
|
134
|
+
"TextDelta",
|
|
135
|
+
"TextDelta",
|
|
136
|
+
"TurnComplete"
|
|
137
|
+
]);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
//#endregion
|
|
141
|
+
export {};
|
|
142
|
+
|
|
143
|
+
//# sourceMappingURL=LanguageModel.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LanguageModel.test.mjs","names":["Items.assistantText","Items.userText","MockProvider.layer","AiError.IncompleteTurn","AiError.RateLimited","AiError.Unavailable","AiError.ContentFiltered","AiError.Timeout"],"sources":["../../src/language-model/LanguageModel.test.ts"],"sourcesContent":["import { Cause, Effect, Exit, Layer, Option, Ref, Schedule, Stream } from \"effect\"\nimport { describe, expect, it } from \"vitest\"\nimport * as AiError from \"../domain/AiError.js\"\nimport * as Items from \"../domain/Items.js\"\nimport { type Turn, TurnEvent } from \"../domain/Turn.js\"\nimport { LanguageModel, retry, turn } from \"./LanguageModel.js\"\nimport * as MockProvider from \"../testing/MockProvider.js\"\n\nconst oneTextTurn = (text: string): Turn => ({\n items: [Items.assistantText(text)],\n usage: { input_tokens: 1, output_tokens: 1 },\n stop_reason: \"stop\",\n})\n\ndescribe(\"LanguageModel.turn\", () => {\n it(\"returns the assembled Turn from the terminal TurnComplete event\", async () => {\n const expected = oneTextTurn(\"hello world\")\n const program = turn({ history: [Items.userText(\"hi\")], model: \"mock\" })\n\n const result = await Effect.runPromise(\n program.pipe(Effect.provide(MockProvider.layer([expected]))),\n )\n\n expect(result).toEqual(expected)\n })\n\n it(\"fails with IncompleteTurn when the stream ends without TurnComplete\", async () => {\n // Custom service whose stream emits a single TextDelta and then ends.\n const broken = Layer.succeed(LanguageModel, {\n streamTurn: () => Stream.fromIterable<TurnEvent>([TurnEvent.TextDelta({ text: \"partial\" })]),\n })\n\n const program = turn({ history: [Items.userText(\"hi\")], model: \"mock\" })\n const exit = await Effect.runPromise(Effect.exit(program.pipe(Effect.provide(broken))))\n\n expect(Exit.isFailure(exit)).toBe(true)\n if (Exit.isFailure(exit)) {\n const failure = Cause.findErrorOption(exit.cause)\n expect(Option.isSome(failure)).toBe(true)\n if (Option.isSome(failure)) {\n expect(failure.value).toBeInstanceOf(AiError.IncompleteTurn)\n }\n }\n })\n\n it(\"propagates an AiError raised by streamTurn\", async () => {\n const rateLimited = new AiError.RateLimited({ provider: \"mock\", raw: null })\n const failing = Layer.succeed(LanguageModel, {\n streamTurn: () => Stream.fail<AiError.AiError>(rateLimited),\n })\n\n const program = turn({ history: [], model: \"mock\" })\n const exit = await Effect.runPromise(Effect.exit(program.pipe(Effect.provide(failing))))\n\n expect(Exit.isFailure(exit)).toBe(true)\n if (Exit.isFailure(exit)) {\n expect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(rateLimited))\n }\n })\n\n it(\"returns the LAST TurnComplete when the stream contains multiple (defensive)\", async () => {\n // A misbehaving provider might emit two TurnComplete events; turn\n // should pick the last one (the most recent assembled Turn).\n const first = oneTextTurn(\"first\")\n const second = oneTextTurn(\"second\")\n const weird = Layer.succeed(LanguageModel, {\n streamTurn: () =>\n Stream.fromIterable<TurnEvent>([\n TurnEvent.TurnComplete({ turn: first }),\n TurnEvent.TurnComplete({ turn: second }),\n ]),\n })\n\n const program = turn({ history: [], model: \"mock\" })\n const result = await Effect.runPromise(program.pipe(Effect.provide(weird)))\n\n expect(result).toEqual(second)\n })\n})\n\ndescribe(\"LanguageModel.retry\", () => {\n const textDelta = (text: string): TurnEvent => TurnEvent.TextDelta({ text })\n const textTurn = (text: string): Turn => ({\n items: [Items.assistantText(text)],\n usage: { input_tokens: 0, output_tokens: 0 },\n stop_reason: \"stop\",\n })\n const completeEvent = (text: string): TurnEvent =>\n TurnEvent.TurnComplete({ turn: textTurn(text) })\n\n // Builds a stream that emits a failure or success based on attempt counter.\n // Each call to the returned Effect produces a fresh attempt stream.\n const attemptStream = (\n attempts: Ref.Ref<number>,\n plan: ReadonlyArray<Stream.Stream<TurnEvent, AiError.AiError>>,\n ): Stream.Stream<TurnEvent, AiError.AiError> =>\n Stream.unwrap(\n Ref.getAndUpdate(attempts, (n) => n + 1).pipe(\n Effect.map((n) => plan[Math.min(n, plan.length - 1)]!),\n ),\n )\n\n it(\"retries on RateLimited and yields the success on the next attempt\", async () => {\n const program = Effect.gen(function* () {\n const attempts = yield* Ref.make(0)\n const stream = attemptStream(attempts, [\n Stream.fail(new AiError.RateLimited({ provider: \"mock\", raw: null })),\n Stream.fromIterable([textDelta(\"ok\"), completeEvent(\"ok\")]),\n ]).pipe(retry(Schedule.recurs(3)))\n const events = yield* Stream.runCollect(stream)\n const count = yield* Ref.get(attempts)\n return { events: Array.from(events), count }\n })\n\n const { events, count } = await Effect.runPromise(program)\n expect(count).toBe(2)\n expect(events.map((e) => e._tag)).toEqual([\"TextDelta\", \"TurnComplete\"])\n })\n\n it(\"surfaces the underlying retryable failure when retries are exhausted\", async () => {\n const cause = new AiError.Unavailable({ provider: \"mock\", raw: null })\n const stream = Stream.fail<AiError.AiError>(cause).pipe(retry(Schedule.recurs(2)))\n\n const exit = await Effect.runPromise(Effect.exit(Stream.runCollect(stream)))\n expect(Exit.isFailure(exit)).toBe(true)\n if (Exit.isFailure(exit)) {\n expect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(cause))\n }\n })\n\n it(\"bypasses retry for non-retryable AiError (ContentFiltered)\", async () => {\n const program = Effect.gen(function* () {\n const attempts = yield* Ref.make(0)\n const cause = new AiError.ContentFiltered({ provider: \"mock\", raw: null })\n const stream = attemptStream(attempts, [Stream.fail(cause)]).pipe(retry(Schedule.recurs(5)))\n const exit = yield* Effect.exit(Stream.runCollect(stream))\n const count = yield* Ref.get(attempts)\n return { exit, count, cause }\n })\n\n const { exit, count, cause } = await Effect.runPromise(program)\n expect(count).toBe(1) // no retry happened\n expect(Exit.isFailure(exit)).toBe(true)\n if (Exit.isFailure(exit)) {\n expect(Cause.findErrorOption(exit.cause)).toEqual(Option.some(cause))\n }\n })\n\n it(\"preserves deltas emitted before a retryable failure (and replays on retry)\", async () => {\n // Documents the 'replays on retry' caveat in the JSDoc — first attempt\n // emits a delta then fails; second attempt is a clean success. Consumer\n // sees the first delta twice (once from the failed attempt, once from\n // the replay).\n const program = Effect.gen(function* () {\n const attempts = yield* Ref.make(0)\n const stream = attemptStream(attempts, [\n Stream.concat(\n Stream.succeed<TurnEvent>(textDelta(\"partial\")),\n Stream.fail(new AiError.Timeout({ provider: \"mock\", raw: null })),\n ),\n Stream.fromIterable([textDelta(\"partial\"), completeEvent(\"done\")]),\n ]).pipe(retry(Schedule.recurs(1)))\n const events = yield* Stream.runCollect(stream)\n return Array.from(events)\n })\n\n const events = await Effect.runPromise(program)\n expect(events.map((e) => e._tag)).toEqual([\"TextDelta\", \"TextDelta\", \"TurnComplete\"])\n })\n})\n"],"mappings":"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"}
|