@effect/ai 0.26.0 → 0.27.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/Chat/package.json +6 -0
- package/EmbeddingModel/package.json +6 -0
- package/IdGenerator/package.json +6 -0
- package/LanguageModel/package.json +6 -0
- package/Model/package.json +6 -0
- package/Prompt/package.json +6 -0
- package/Response/package.json +6 -0
- package/Telemetry/package.json +6 -0
- package/Tool/package.json +6 -0
- package/Toolkit/package.json +6 -0
- package/dist/cjs/AiError.js +575 -11
- package/dist/cjs/AiError.js.map +1 -1
- package/dist/cjs/Chat.js +302 -0
- package/dist/cjs/Chat.js.map +1 -0
- package/dist/cjs/EmbeddingModel.js +184 -0
- package/dist/cjs/EmbeddingModel.js.map +1 -0
- package/dist/cjs/IdGenerator.js +255 -0
- package/dist/cjs/IdGenerator.js.map +1 -0
- package/dist/cjs/LanguageModel.js +584 -0
- package/dist/cjs/LanguageModel.js.map +1 -0
- package/dist/cjs/McpServer.js +12 -4
- package/dist/cjs/McpServer.js.map +1 -1
- package/dist/cjs/Model.js +118 -0
- package/dist/cjs/Model.js.map +1 -0
- package/dist/cjs/Prompt.js +649 -0
- package/dist/cjs/Prompt.js.map +1 -0
- package/dist/cjs/Response.js +635 -0
- package/dist/cjs/Response.js.map +1 -0
- package/dist/cjs/Telemetry.js +176 -0
- package/dist/cjs/Telemetry.js.map +1 -0
- package/dist/cjs/Tokenizer.js +87 -8
- package/dist/cjs/Tokenizer.js.map +1 -1
- package/dist/cjs/Tool.js +556 -0
- package/dist/cjs/Tool.js.map +1 -0
- package/dist/cjs/Toolkit.js +279 -0
- package/dist/cjs/Toolkit.js.map +1 -0
- package/dist/cjs/index.js +21 -19
- package/dist/dts/AiError.d.ts +577 -9
- package/dist/dts/AiError.d.ts.map +1 -1
- package/dist/dts/Chat.d.ts +356 -0
- package/dist/dts/Chat.d.ts.map +1 -0
- package/dist/dts/EmbeddingModel.d.ts +153 -0
- package/dist/dts/EmbeddingModel.d.ts.map +1 -0
- package/dist/dts/IdGenerator.d.ts +272 -0
- package/dist/dts/IdGenerator.d.ts.map +1 -0
- package/dist/dts/LanguageModel.d.ts +458 -0
- package/dist/dts/LanguageModel.d.ts.map +1 -0
- package/dist/dts/McpSchema.d.ts +25 -25
- package/dist/dts/McpServer.d.ts +6 -4
- package/dist/dts/McpServer.d.ts.map +1 -1
- package/dist/dts/Model.d.ts +124 -0
- package/dist/dts/Model.d.ts.map +1 -0
- package/dist/dts/Prompt.d.ts +1119 -0
- package/dist/dts/Prompt.d.ts.map +1 -0
- package/dist/dts/Response.d.ts +1519 -0
- package/dist/dts/Response.d.ts.map +1 -0
- package/dist/dts/Telemetry.d.ts +520 -0
- package/dist/dts/Telemetry.d.ts.map +1 -0
- package/dist/dts/Tokenizer.d.ts +131 -13
- package/dist/dts/Tokenizer.d.ts.map +1 -1
- package/dist/dts/Tool.d.ts +876 -0
- package/dist/dts/Tool.d.ts.map +1 -0
- package/dist/dts/Toolkit.d.ts +310 -0
- package/dist/dts/Toolkit.d.ts.map +1 -0
- package/dist/dts/index.d.ts +498 -13
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/AiError.js +570 -10
- package/dist/esm/AiError.js.map +1 -1
- package/dist/esm/Chat.js +291 -0
- package/dist/esm/Chat.js.map +1 -0
- package/dist/esm/EmbeddingModel.js +173 -0
- package/dist/esm/EmbeddingModel.js.map +1 -0
- package/dist/esm/IdGenerator.js +245 -0
- package/dist/esm/IdGenerator.js.map +1 -0
- package/dist/esm/LanguageModel.js +572 -0
- package/dist/esm/LanguageModel.js.map +1 -0
- package/dist/esm/McpServer.js +12 -4
- package/dist/esm/McpServer.js.map +1 -1
- package/dist/esm/Model.js +108 -0
- package/dist/esm/Model.js.map +1 -0
- package/dist/esm/Prompt.js +633 -0
- package/dist/esm/Prompt.js.map +1 -0
- package/dist/esm/Response.js +619 -0
- package/dist/esm/Response.js.map +1 -0
- package/dist/esm/Telemetry.js +166 -0
- package/dist/esm/Telemetry.js.map +1 -0
- package/dist/esm/Tokenizer.js +87 -8
- package/dist/esm/Tokenizer.js.map +1 -1
- package/dist/esm/Tool.js +534 -0
- package/dist/esm/Tool.js.map +1 -0
- package/dist/esm/Toolkit.js +269 -0
- package/dist/esm/Toolkit.js.map +1 -0
- package/dist/esm/index.js +498 -13
- package/dist/esm/index.js.map +1 -1
- package/package.json +76 -68
- package/src/AiError.ts +739 -9
- package/src/Chat.ts +546 -0
- package/src/EmbeddingModel.ts +311 -0
- package/src/IdGenerator.ts +320 -0
- package/src/LanguageModel.ts +1074 -0
- package/src/McpServer.ts +337 -194
- package/src/Model.ts +155 -0
- package/src/Prompt.ts +1616 -0
- package/src/Response.ts +2131 -0
- package/src/Telemetry.ts +655 -0
- package/src/Tokenizer.ts +145 -24
- package/src/Tool.ts +1267 -0
- package/src/Toolkit.ts +516 -0
- package/src/index.ts +499 -13
- package/AiChat/package.json +0 -6
- package/AiEmbeddingModel/package.json +0 -6
- package/AiInput/package.json +0 -6
- package/AiLanguageModel/package.json +0 -6
- package/AiModel/package.json +0 -6
- package/AiResponse/package.json +0 -6
- package/AiTelemetry/package.json +0 -6
- package/AiTool/package.json +0 -6
- package/AiToolkit/package.json +0 -6
- package/dist/cjs/AiChat.js +0 -122
- package/dist/cjs/AiChat.js.map +0 -1
- package/dist/cjs/AiEmbeddingModel.js +0 -109
- package/dist/cjs/AiEmbeddingModel.js.map +0 -1
- package/dist/cjs/AiInput.js +0 -458
- package/dist/cjs/AiInput.js.map +0 -1
- package/dist/cjs/AiLanguageModel.js +0 -351
- package/dist/cjs/AiLanguageModel.js.map +0 -1
- package/dist/cjs/AiModel.js +0 -37
- package/dist/cjs/AiModel.js.map +0 -1
- package/dist/cjs/AiResponse.js +0 -681
- package/dist/cjs/AiResponse.js.map +0 -1
- package/dist/cjs/AiTelemetry.js +0 -58
- package/dist/cjs/AiTelemetry.js.map +0 -1
- package/dist/cjs/AiTool.js +0 -150
- package/dist/cjs/AiTool.js.map +0 -1
- package/dist/cjs/AiToolkit.js +0 -157
- package/dist/cjs/AiToolkit.js.map +0 -1
- package/dist/cjs/internal/common.js +0 -21
- package/dist/cjs/internal/common.js.map +0 -1
- package/dist/dts/AiChat.d.ts +0 -101
- package/dist/dts/AiChat.d.ts.map +0 -1
- package/dist/dts/AiEmbeddingModel.d.ts +0 -65
- package/dist/dts/AiEmbeddingModel.d.ts.map +0 -1
- package/dist/dts/AiInput.d.ts +0 -590
- package/dist/dts/AiInput.d.ts.map +0 -1
- package/dist/dts/AiLanguageModel.d.ts +0 -302
- package/dist/dts/AiLanguageModel.d.ts.map +0 -1
- package/dist/dts/AiModel.d.ts +0 -25
- package/dist/dts/AiModel.d.ts.map +0 -1
- package/dist/dts/AiResponse.d.ts +0 -863
- package/dist/dts/AiResponse.d.ts.map +0 -1
- package/dist/dts/AiTelemetry.d.ts +0 -242
- package/dist/dts/AiTelemetry.d.ts.map +0 -1
- package/dist/dts/AiTool.d.ts +0 -334
- package/dist/dts/AiTool.d.ts.map +0 -1
- package/dist/dts/AiToolkit.d.ts +0 -96
- package/dist/dts/AiToolkit.d.ts.map +0 -1
- package/dist/dts/internal/common.d.ts +0 -2
- package/dist/dts/internal/common.d.ts.map +0 -1
- package/dist/esm/AiChat.js +0 -111
- package/dist/esm/AiChat.js.map +0 -1
- package/dist/esm/AiEmbeddingModel.js +0 -98
- package/dist/esm/AiEmbeddingModel.js.map +0 -1
- package/dist/esm/AiInput.js +0 -433
- package/dist/esm/AiInput.js.map +0 -1
- package/dist/esm/AiLanguageModel.js +0 -340
- package/dist/esm/AiLanguageModel.js.map +0 -1
- package/dist/esm/AiModel.js +0 -29
- package/dist/esm/AiModel.js.map +0 -1
- package/dist/esm/AiResponse.js +0 -657
- package/dist/esm/AiResponse.js.map +0 -1
- package/dist/esm/AiTelemetry.js +0 -48
- package/dist/esm/AiTelemetry.js.map +0 -1
- package/dist/esm/AiTool.js +0 -134
- package/dist/esm/AiTool.js.map +0 -1
- package/dist/esm/AiToolkit.js +0 -147
- package/dist/esm/AiToolkit.js.map +0 -1
- package/dist/esm/internal/common.js +0 -14
- package/dist/esm/internal/common.js.map +0 -1
- package/src/AiChat.ts +0 -251
- package/src/AiEmbeddingModel.ts +0 -169
- package/src/AiInput.ts +0 -602
- package/src/AiLanguageModel.ts +0 -685
- package/src/AiModel.ts +0 -53
- package/src/AiResponse.ts +0 -986
- package/src/AiTelemetry.ts +0 -333
- package/src/AiTool.ts +0 -579
- package/src/AiToolkit.ts +0 -265
- package/src/internal/common.ts +0 -12
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `EmbeddingModel` module provides vector embeddings for text using AI
|
|
3
|
+
* models.
|
|
4
|
+
*
|
|
5
|
+
* This module enables efficient conversion of text into high-dimensional vector
|
|
6
|
+
* representations that capture semantic meaning. It supports batching, caching,
|
|
7
|
+
* and request optimization for production use cases like semantic search,
|
|
8
|
+
* document similarity, and clustering.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { EmbeddingModel } from "@effect/ai"
|
|
13
|
+
* import { Effect } from "effect"
|
|
14
|
+
*
|
|
15
|
+
* // Basic embedding usage
|
|
16
|
+
* const program = Effect.gen(function* () {
|
|
17
|
+
* const embedding = yield* EmbeddingModel.EmbeddingModel
|
|
18
|
+
*
|
|
19
|
+
* const vector = yield* embedding.embed("Hello world!")
|
|
20
|
+
* console.log(vector) // [0.123, -0.456, 0.789, ...]
|
|
21
|
+
*
|
|
22
|
+
* return vector
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import { EmbeddingModel } from "@effect/ai"
|
|
29
|
+
* import { Effect, Duration } from "effect"
|
|
30
|
+
*
|
|
31
|
+
* declare const generateVectorFor: (text: string) => Array<number>
|
|
32
|
+
*
|
|
33
|
+
* // Create embedding service with batching and caching
|
|
34
|
+
* const embeddingService = EmbeddingModel.make({
|
|
35
|
+
* embedMany: (texts) => Effect.succeed(
|
|
36
|
+
* texts.map((text, index) => ({
|
|
37
|
+
* index,
|
|
38
|
+
* embeddings: generateVectorFor(text)
|
|
39
|
+
* }))
|
|
40
|
+
* ),
|
|
41
|
+
* maxBatchSize: 50,
|
|
42
|
+
* cache: {
|
|
43
|
+
* capacity: 1000,
|
|
44
|
+
* timeToLive: Duration.minutes(30)
|
|
45
|
+
* }
|
|
46
|
+
* })
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* @since 1.0.0
|
|
50
|
+
*/
|
|
51
|
+
import { dataLoader } from "@effect/experimental/RequestResolver"
|
|
52
|
+
import * as Context from "effect/Context"
|
|
53
|
+
import type * as Duration from "effect/Duration"
|
|
54
|
+
import * as Effect from "effect/Effect"
|
|
55
|
+
import { identity } from "effect/Function"
|
|
56
|
+
import * as Option from "effect/Option"
|
|
57
|
+
import * as Request from "effect/Request"
|
|
58
|
+
import * as RequestResolver from "effect/RequestResolver"
|
|
59
|
+
import * as Schema from "effect/Schema"
|
|
60
|
+
import type * as Types from "effect/Types"
|
|
61
|
+
import * as AiError from "./AiError.js"
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The `EmbeddingModel` service tag for dependency injection.
|
|
65
|
+
*
|
|
66
|
+
* This tag provides access to vector embedding functionality throughout your application,
|
|
67
|
+
* enabling conversion of text to high-dimensional vectors for semantic analysis.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* import { EmbeddingModel } from "@effect/ai"
|
|
72
|
+
* import { Effect } from "effect"
|
|
73
|
+
*
|
|
74
|
+
* const useEmbeddings = Effect.gen(function* () {
|
|
75
|
+
* const embedder = yield* EmbeddingModel
|
|
76
|
+
*
|
|
77
|
+
* const documentVector = yield* embedder.embed("This is a sample document")
|
|
78
|
+
* const queryVector = yield* embedder.embed("sample query")
|
|
79
|
+
*
|
|
80
|
+
* const similarity = cosineSimilarity(documentVector, queryVector)
|
|
81
|
+
* return similarity
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category Context
|
|
87
|
+
*/
|
|
88
|
+
export class EmbeddingModel extends Context.Tag("@effect/ai/EmbeddingModel")<
|
|
89
|
+
EmbeddingModel,
|
|
90
|
+
Service
|
|
91
|
+
>() {}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The service interface for vector embedding operations.
|
|
95
|
+
*
|
|
96
|
+
* Defines the contract that all embedding model implementations must fulfill.
|
|
97
|
+
* The service provides text-to-vector conversion functionality.
|
|
98
|
+
*
|
|
99
|
+
* @since 1.0.0
|
|
100
|
+
* @category Models
|
|
101
|
+
*/
|
|
102
|
+
export interface Service {
|
|
103
|
+
/**
|
|
104
|
+
* Converts a text string into a vector embedding.
|
|
105
|
+
*/
|
|
106
|
+
readonly embed: (input: string) => Effect.Effect<Array<number>, AiError.AiError>
|
|
107
|
+
/**
|
|
108
|
+
* Converts a batch of text strings into a chunk of vector embeddings.
|
|
109
|
+
*/
|
|
110
|
+
readonly embedMany: (input: ReadonlyArray<string>, options?: {
|
|
111
|
+
/**
|
|
112
|
+
* The concurrency level to use while batching requests.
|
|
113
|
+
*/
|
|
114
|
+
readonly concurrency?: Types.Concurrency | undefined
|
|
115
|
+
}) => Effect.Effect<Array<Array<number>>, AiError.AiError>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Represents the result of a batch embedding operation.
|
|
120
|
+
*
|
|
121
|
+
* Used internally by the batching system to associate embeddings with their
|
|
122
|
+
* original request positions in the batch.
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```ts
|
|
126
|
+
* import { EmbeddingModel } from "@effect/ai"
|
|
127
|
+
*
|
|
128
|
+
* const batchResults: EmbeddingModel.Result[] = [
|
|
129
|
+
* { index: 0, embeddings: [0.1, 0.2, 0.3] },
|
|
130
|
+
* { index: 1, embeddings: [0.4, 0.5, 0.6] },
|
|
131
|
+
* { index: 2, embeddings: [0.7, 0.8, 0.9] }
|
|
132
|
+
* ]
|
|
133
|
+
*
|
|
134
|
+
* // Results correspond to input texts at positions 0, 1, 2
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* @since 1.0.0
|
|
138
|
+
* @category Models
|
|
139
|
+
*/
|
|
140
|
+
export interface Result {
|
|
141
|
+
/**
|
|
142
|
+
* The position index of this result in the original batch request.
|
|
143
|
+
*/
|
|
144
|
+
readonly index: number
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The vector embedding for the text at this index.
|
|
148
|
+
*/
|
|
149
|
+
readonly embeddings: Array<number>
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
class EmbeddingRequest extends Schema.TaggedRequest<EmbeddingRequest>(
|
|
153
|
+
"@effect/ai/EmbeddingModel/Request"
|
|
154
|
+
)("EmbeddingRequest", {
|
|
155
|
+
failure: AiError.AiError,
|
|
156
|
+
success: Schema.mutable(Schema.Array(Schema.Number)),
|
|
157
|
+
payload: { input: Schema.String }
|
|
158
|
+
}) {}
|
|
159
|
+
|
|
160
|
+
const makeBatchedResolver = (
|
|
161
|
+
embedMany: (input: ReadonlyArray<string>) => Effect.Effect<Array<Result>, AiError.AiError>
|
|
162
|
+
) =>
|
|
163
|
+
RequestResolver.makeBatched(
|
|
164
|
+
(requests: ReadonlyArray<EmbeddingRequest>) =>
|
|
165
|
+
embedMany(requests.map((request) => request.input)).pipe(
|
|
166
|
+
Effect.flatMap(
|
|
167
|
+
Effect.forEach(
|
|
168
|
+
({ embeddings, index }) => Request.succeed(requests[index], embeddings),
|
|
169
|
+
{ discard: true }
|
|
170
|
+
)
|
|
171
|
+
),
|
|
172
|
+
Effect.catchAll((error) =>
|
|
173
|
+
Effect.forEach(
|
|
174
|
+
requests,
|
|
175
|
+
(request) => Request.fail(request, error),
|
|
176
|
+
{ discard: true }
|
|
177
|
+
)
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Creates an EmbeddingModel service with batching and caching capabilities.
|
|
184
|
+
*
|
|
185
|
+
* This is the primary constructor for creating embedding services. It supports
|
|
186
|
+
* automatic batching of requests for efficiency and optional caching to reduce
|
|
187
|
+
* redundant API calls.
|
|
188
|
+
*
|
|
189
|
+
* @since 1.0.0
|
|
190
|
+
* @category Constructors
|
|
191
|
+
*/
|
|
192
|
+
export const make = (options: {
|
|
193
|
+
/**
|
|
194
|
+
* A method which processes a batch of text inputs and returns embedding
|
|
195
|
+
* results.
|
|
196
|
+
*/
|
|
197
|
+
readonly embedMany: (input: ReadonlyArray<string>) => Effect.Effect<Array<Result>, AiError.AiError>
|
|
198
|
+
/**
|
|
199
|
+
* Optional maximum number of text inputs to process in one batch.
|
|
200
|
+
*/
|
|
201
|
+
readonly maxBatchSize?: number
|
|
202
|
+
/**
|
|
203
|
+
* Optional configuration to control how batch request results are cached.
|
|
204
|
+
*/
|
|
205
|
+
readonly cache?: {
|
|
206
|
+
/**
|
|
207
|
+
* The capacity of the cache.
|
|
208
|
+
*/
|
|
209
|
+
readonly capacity: number
|
|
210
|
+
/**
|
|
211
|
+
* The time-to-live for items in the cache.
|
|
212
|
+
*/
|
|
213
|
+
readonly timeToLive: Duration.DurationInput
|
|
214
|
+
}
|
|
215
|
+
}) =>
|
|
216
|
+
Effect.gen(function*() {
|
|
217
|
+
const cache = yield* Option.fromNullable(options.cache).pipe(
|
|
218
|
+
Effect.flatMap((config) => Request.makeCache(config)),
|
|
219
|
+
Effect.optionFromOptional
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
const resolver = makeBatchedResolver(options.embedMany).pipe(
|
|
223
|
+
options.maxBatchSize ? RequestResolver.batchN(options.maxBatchSize) : identity
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
const embed = (input: string) => {
|
|
227
|
+
const request = Effect.request(new EmbeddingRequest({ input }), resolver)
|
|
228
|
+
return Option.match(cache, {
|
|
229
|
+
onNone: () => request,
|
|
230
|
+
onSome: (cache) =>
|
|
231
|
+
request.pipe(
|
|
232
|
+
Effect.withRequestCaching(true),
|
|
233
|
+
Effect.withRequestCache(cache)
|
|
234
|
+
)
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const embedMany = (inputs: ReadonlyArray<string>, options?: {
|
|
239
|
+
readonly concurrency?: Types.Concurrency | undefined
|
|
240
|
+
}) =>
|
|
241
|
+
Effect.forEach(inputs, embed, {
|
|
242
|
+
batching: true,
|
|
243
|
+
concurrency: options?.concurrency
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
return EmbeddingModel.of({
|
|
247
|
+
embed: (input) =>
|
|
248
|
+
embed(input).pipe(
|
|
249
|
+
Effect.withSpan("EmbeddingModel.embed", { captureStackTrace: false })
|
|
250
|
+
),
|
|
251
|
+
embedMany: (inputs) =>
|
|
252
|
+
embedMany(inputs).pipe(
|
|
253
|
+
Effect.withSpan("EmbeddingModel.embedMany", { captureStackTrace: false })
|
|
254
|
+
)
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Creates an EmbeddingModel service with time-window based batching.
|
|
260
|
+
*
|
|
261
|
+
* This constructor creates a service that uses a data loader pattern to batch
|
|
262
|
+
* embedding requests within a specified time window. This is optimal for
|
|
263
|
+
* high-throughput scenarios where you want to automatically batch requests that
|
|
264
|
+
* arrive within a short time period.
|
|
265
|
+
*
|
|
266
|
+
* @since 1.0.0
|
|
267
|
+
* @category Constructors
|
|
268
|
+
*/
|
|
269
|
+
export const makeDataLoader = (options: {
|
|
270
|
+
/**
|
|
271
|
+
* A method which processes a batch of text inputs and returns embedding
|
|
272
|
+
* results.
|
|
273
|
+
*/
|
|
274
|
+
readonly embedMany: (input: ReadonlyArray<string>) => Effect.Effect<Array<Result>, AiError.AiError>
|
|
275
|
+
/**
|
|
276
|
+
* The duration between batch requests during which requests are collected and
|
|
277
|
+
* added to the current batch.
|
|
278
|
+
*/
|
|
279
|
+
readonly window: Duration.DurationInput
|
|
280
|
+
/**
|
|
281
|
+
* Optional maximum number of requests to add to the batch before a batch
|
|
282
|
+
* request must be sent.
|
|
283
|
+
*/
|
|
284
|
+
readonly maxBatchSize?: number
|
|
285
|
+
}) =>
|
|
286
|
+
Effect.gen(function*() {
|
|
287
|
+
const resolver = makeBatchedResolver(options.embedMany)
|
|
288
|
+
const resolverDelayed = yield* dataLoader(resolver, {
|
|
289
|
+
window: options.window,
|
|
290
|
+
maxBatchSize: options.maxBatchSize
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
function embed(input: string) {
|
|
294
|
+
return Effect.request(new EmbeddingRequest({ input }), resolverDelayed).pipe(
|
|
295
|
+
Effect.withSpan("EmbeddingModel.embed", { captureStackTrace: false })
|
|
296
|
+
)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function embedMany(inputs: ReadonlyArray<string>, options?: {
|
|
300
|
+
readonly concurrency?: Types.Concurrency | undefined
|
|
301
|
+
}) {
|
|
302
|
+
return Effect.forEach(inputs, embed, { batching: true, concurrency: options?.concurrency }).pipe(
|
|
303
|
+
Effect.withSpan("EmbeddingModel.embedMany", { captureStackTrace: false })
|
|
304
|
+
)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return EmbeddingModel.of({
|
|
308
|
+
embed,
|
|
309
|
+
embedMany
|
|
310
|
+
})
|
|
311
|
+
})
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `IdGenerator` module provides a pluggable system for generating unique identifiers
|
|
3
|
+
* for tool calls and other items in the Effect AI SDKs.
|
|
4
|
+
*
|
|
5
|
+
* This module offers a flexible and configurable approach to ID generation, supporting
|
|
6
|
+
* custom alphabets, prefixes, separators, and sizes.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { IdGenerator } from "@effect/ai"
|
|
11
|
+
* import { Effect, Layer } from "effect"
|
|
12
|
+
*
|
|
13
|
+
* // Using the default ID generator
|
|
14
|
+
* const program = Effect.gen(function* () {
|
|
15
|
+
* const idGen = yield* IdGenerator.IdGenerator
|
|
16
|
+
* const toolCallId = yield* idGen.generateId()
|
|
17
|
+
* console.log(toolCallId) // "id_A7xK9mP2qR5tY8uV"
|
|
18
|
+
* return toolCallId
|
|
19
|
+
* }).pipe(
|
|
20
|
+
* Effect.provide(Layer.succeed(
|
|
21
|
+
* IdGenerator.IdGenerator,
|
|
22
|
+
* IdGenerator.defaultIdGenerator
|
|
23
|
+
* ))
|
|
24
|
+
* )
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { IdGenerator } from "@effect/ai"
|
|
30
|
+
* import { Effect, Layer } from "effect"
|
|
31
|
+
*
|
|
32
|
+
* // Creating a custom ID generator for AI tool calls
|
|
33
|
+
* const customLayer = IdGenerator.layer({
|
|
34
|
+
* alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
|
35
|
+
* prefix: "tool_call",
|
|
36
|
+
* separator: "-",
|
|
37
|
+
* size: 12
|
|
38
|
+
* })
|
|
39
|
+
*
|
|
40
|
+
* const program = Effect.gen(function* () {
|
|
41
|
+
* const idGen = yield* IdGenerator.IdGenerator
|
|
42
|
+
* const id = yield* idGen.generateId()
|
|
43
|
+
* console.log(id) // "tool_call-A7XK9MP2QR5T"
|
|
44
|
+
* return id
|
|
45
|
+
* }).pipe(
|
|
46
|
+
* Effect.provide(customLayer)
|
|
47
|
+
* )
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @since 1.0.0
|
|
51
|
+
*/
|
|
52
|
+
import * as Cause from "effect/Cause"
|
|
53
|
+
import * as Context from "effect/Context"
|
|
54
|
+
import * as Effect from "effect/Effect"
|
|
55
|
+
import * as Layer from "effect/Layer"
|
|
56
|
+
import * as Predicate from "effect/Predicate"
|
|
57
|
+
import * as Random from "effect/Random"
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The `IdGenerator` service tag for dependency injection.
|
|
61
|
+
*
|
|
62
|
+
* This tag is used to provide and access ID generation functionality throughout
|
|
63
|
+
* the application. It follows Effect's standard service pattern for type-safe
|
|
64
|
+
* dependency injection.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { IdGenerator } from "@effect/ai"
|
|
69
|
+
* import { Effect } from "effect"
|
|
70
|
+
*
|
|
71
|
+
* const useIdGenerator = Effect.gen(function* () {
|
|
72
|
+
* const idGenerator = yield* IdGenerator.IdGenerator
|
|
73
|
+
* const newId = yield* idGenerator.generateId()
|
|
74
|
+
* return newId
|
|
75
|
+
* })
|
|
76
|
+
* ```
|
|
77
|
+
*
|
|
78
|
+
* @since 1.0.0
|
|
79
|
+
* @category Models
|
|
80
|
+
*/
|
|
81
|
+
export class IdGenerator extends Context.Tag("@effect/ai/IdGenerator")<
|
|
82
|
+
IdGenerator,
|
|
83
|
+
Service
|
|
84
|
+
>() {}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The service interface for ID generation.
|
|
88
|
+
*
|
|
89
|
+
* Defines the contract that all ID generator implementations must fulfill.
|
|
90
|
+
* The service provides a single method for generating unique identifiers
|
|
91
|
+
* in an effectful context.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* import { IdGenerator } from "@effect/ai"
|
|
96
|
+
* import { Effect } from "effect"
|
|
97
|
+
*
|
|
98
|
+
* // Custom implementation
|
|
99
|
+
* const customService: IdGenerator.Service = {
|
|
100
|
+
* generateId: () => Effect.succeed(`custom_${Date.now()}`)
|
|
101
|
+
* }
|
|
102
|
+
*
|
|
103
|
+
* const program = Effect.gen(function* () {
|
|
104
|
+
* const id = yield* customService.generateId()
|
|
105
|
+
* console.log(id) // "custom_1234567890"
|
|
106
|
+
* return id
|
|
107
|
+
* })
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* @since 1.0.0
|
|
111
|
+
* @category Models
|
|
112
|
+
*/
|
|
113
|
+
export interface Service {
|
|
114
|
+
readonly generateId: () => Effect.Effect<string>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Configuration options for creating custom ID generators.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import { IdGenerator } from "@effect/ai"
|
|
123
|
+
*
|
|
124
|
+
* // Configuration for tool call IDs
|
|
125
|
+
* const toolCallOptions: IdGenerator.MakeOptions = {
|
|
126
|
+
* alphabet: "0123456789ABCDEF",
|
|
127
|
+
* prefix: "tool",
|
|
128
|
+
* separator: "_",
|
|
129
|
+
* size: 8
|
|
130
|
+
* }
|
|
131
|
+
*
|
|
132
|
+
* // This will generate IDs like: "tool_A1B2C3D4"
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* @since 1.0.0
|
|
136
|
+
* @category Models
|
|
137
|
+
*/
|
|
138
|
+
export interface MakeOptions {
|
|
139
|
+
/**
|
|
140
|
+
* The character set to use for generating the random portion of IDs.
|
|
141
|
+
*/
|
|
142
|
+
readonly alphabet: string
|
|
143
|
+
/**
|
|
144
|
+
* Optional prefix to prepend to generated IDs.
|
|
145
|
+
*/
|
|
146
|
+
readonly prefix?: string | undefined
|
|
147
|
+
/**
|
|
148
|
+
* Character used to separate the prefix from the random portion.
|
|
149
|
+
*/
|
|
150
|
+
readonly separator: string
|
|
151
|
+
/**
|
|
152
|
+
* Length of the random portion of the generated ID.
|
|
153
|
+
*/
|
|
154
|
+
readonly size: number
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const DEFAULT_ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
158
|
+
const DEFAULT_SEPARATOR = "_"
|
|
159
|
+
const DEFAULT_SIZE = 16
|
|
160
|
+
|
|
161
|
+
const makeGenerator = ({
|
|
162
|
+
alphabet = DEFAULT_ALPHABET,
|
|
163
|
+
prefix,
|
|
164
|
+
separator = DEFAULT_SEPARATOR,
|
|
165
|
+
size = DEFAULT_SIZE
|
|
166
|
+
}: Partial<MakeOptions>) => {
|
|
167
|
+
const alphabetLength = alphabet.length
|
|
168
|
+
return Effect.fnUntraced(function*() {
|
|
169
|
+
const chars = new Array(size)
|
|
170
|
+
for (let i = 0; i < size; i++) {
|
|
171
|
+
chars[i] = alphabet[((yield* Random.next) * alphabetLength) | 0]
|
|
172
|
+
}
|
|
173
|
+
const identifier = chars.join("")
|
|
174
|
+
if (Predicate.isUndefined(prefix)) {
|
|
175
|
+
return identifier
|
|
176
|
+
}
|
|
177
|
+
return `${prefix}${separator}${identifier}`
|
|
178
|
+
})
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Default ID generator service implementation.
|
|
183
|
+
*
|
|
184
|
+
* Uses the standard configuration with "id" prefix and generates IDs in the
|
|
185
|
+
* format "id_XXXXXXXXXXXXXXXX" where X represents random alphanumeric
|
|
186
|
+
* characters.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import { IdGenerator } from "@effect/ai"
|
|
191
|
+
* import { Effect, Layer } from "effect"
|
|
192
|
+
*
|
|
193
|
+
* const program = Effect.gen(function* () {
|
|
194
|
+
* const id = yield* IdGenerator.defaultIdGenerator.generateId()
|
|
195
|
+
* console.log(id) // "id_A7xK9mP2qR5tY8uV"
|
|
196
|
+
* return id
|
|
197
|
+
* })
|
|
198
|
+
*
|
|
199
|
+
* // Or provide it as a service
|
|
200
|
+
* const withDefault = program.pipe(
|
|
201
|
+
* Effect.provideService(
|
|
202
|
+
* IdGenerator.IdGenerator,
|
|
203
|
+
* IdGenerator.defaultIdGenerator
|
|
204
|
+
* )
|
|
205
|
+
* )
|
|
206
|
+
* ```
|
|
207
|
+
*
|
|
208
|
+
* @since 1.0.0
|
|
209
|
+
* @category Constructors
|
|
210
|
+
*/
|
|
211
|
+
export const defaultIdGenerator: Service = {
|
|
212
|
+
generateId: makeGenerator({ prefix: "id" })
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Creates a custom ID generator service with the specified options.
|
|
217
|
+
*
|
|
218
|
+
* Validates the configuration to ensure the separator is not part of the
|
|
219
|
+
* alphabet, which would cause ambiguity in parsing generated IDs.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* import { IdGenerator } from "@effect/ai"
|
|
224
|
+
* import { Effect } from "effect"
|
|
225
|
+
*
|
|
226
|
+
* const program = Effect.gen(function* () {
|
|
227
|
+
* // Create a generator for AI assistant message IDs
|
|
228
|
+
* const messageIdGen = yield* IdGenerator.make({
|
|
229
|
+
* alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
230
|
+
* prefix: "msg",
|
|
231
|
+
* separator: "-",
|
|
232
|
+
* size: 10
|
|
233
|
+
* })
|
|
234
|
+
*
|
|
235
|
+
* const messageId = yield* messageIdGen.generateId()
|
|
236
|
+
* console.log(messageId) // "msg-A7X9K2M5P8"
|
|
237
|
+
* return messageId
|
|
238
|
+
* })
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```ts
|
|
243
|
+
* import { IdGenerator } from "@effect/ai"
|
|
244
|
+
* import { Effect } from "effect"
|
|
245
|
+
*
|
|
246
|
+
* // This will fail with IllegalArgumentException
|
|
247
|
+
* const invalidConfig = IdGenerator.make({
|
|
248
|
+
* alphabet: "ABC123",
|
|
249
|
+
* prefix: "test",
|
|
250
|
+
* separator: "A", // Error: separator is part of alphabet
|
|
251
|
+
* size: 8
|
|
252
|
+
* })
|
|
253
|
+
*
|
|
254
|
+
* const program = Effect.gen(function* () {
|
|
255
|
+
* const generator = yield* invalidConfig
|
|
256
|
+
* return generator
|
|
257
|
+
* }).pipe(
|
|
258
|
+
* Effect.catchAll(error =>
|
|
259
|
+
* Effect.succeed(`Configuration error: ${error.message}`)
|
|
260
|
+
* )
|
|
261
|
+
* )
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* @since 1.0.0
|
|
265
|
+
* @category Constructors
|
|
266
|
+
*/
|
|
267
|
+
export const make = Effect.fnUntraced(function*({
|
|
268
|
+
alphabet = DEFAULT_ALPHABET,
|
|
269
|
+
prefix,
|
|
270
|
+
separator = DEFAULT_SEPARATOR,
|
|
271
|
+
size = DEFAULT_SIZE
|
|
272
|
+
}: MakeOptions) {
|
|
273
|
+
if (alphabet.includes(separator)) {
|
|
274
|
+
const message = `The separator "${separator}" must not be part of the alphabet "${alphabet}".`
|
|
275
|
+
return yield* new Cause.IllegalArgumentException(message)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const generateId = makeGenerator({ alphabet, prefix, separator, size })
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
generateId
|
|
282
|
+
} as const
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Creates a Layer that provides the IdGenerator service with custom
|
|
287
|
+
* configuration.
|
|
288
|
+
*
|
|
289
|
+
* This is the recommended way to provide ID generation capabilities to your
|
|
290
|
+
* application. The layer will fail during construction if the configuration is
|
|
291
|
+
* invalid.
|
|
292
|
+
*
|
|
293
|
+
* @example
|
|
294
|
+
* ```ts
|
|
295
|
+
* import { IdGenerator } from "@effect/ai"
|
|
296
|
+
* import { Effect, Layer } from "effect"
|
|
297
|
+
*
|
|
298
|
+
* // Create a layer for generating AI tool call IDs
|
|
299
|
+
* const toolCallIdLayer = IdGenerator.layer({
|
|
300
|
+
* alphabet: "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
|
301
|
+
* prefix: "tool_call",
|
|
302
|
+
* separator: "_",
|
|
303
|
+
* size: 12
|
|
304
|
+
* })
|
|
305
|
+
*
|
|
306
|
+
* const program = Effect.gen(function* () {
|
|
307
|
+
* const idGen = yield* IdGenerator.IdGenerator
|
|
308
|
+
* const toolCallId = yield* idGen.generateId()
|
|
309
|
+
* console.log(toolCallId) // "tool_call_A7XK9MP2QR5T"
|
|
310
|
+
* return toolCallId
|
|
311
|
+
* }).pipe(
|
|
312
|
+
* Effect.provide(toolCallIdLayer)
|
|
313
|
+
* )
|
|
314
|
+
* ```
|
|
315
|
+
*
|
|
316
|
+
* @since 1.0.0
|
|
317
|
+
* @category Constructors
|
|
318
|
+
*/
|
|
319
|
+
export const layer = (options: MakeOptions): Layer.Layer<IdGenerator, Cause.IllegalArgumentException> =>
|
|
320
|
+
Layer.effect(IdGenerator, make(options))
|