@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.
Files changed (188) hide show
  1. package/Chat/package.json +6 -0
  2. package/EmbeddingModel/package.json +6 -0
  3. package/IdGenerator/package.json +6 -0
  4. package/LanguageModel/package.json +6 -0
  5. package/Model/package.json +6 -0
  6. package/Prompt/package.json +6 -0
  7. package/Response/package.json +6 -0
  8. package/Telemetry/package.json +6 -0
  9. package/Tool/package.json +6 -0
  10. package/Toolkit/package.json +6 -0
  11. package/dist/cjs/AiError.js +575 -11
  12. package/dist/cjs/AiError.js.map +1 -1
  13. package/dist/cjs/Chat.js +302 -0
  14. package/dist/cjs/Chat.js.map +1 -0
  15. package/dist/cjs/EmbeddingModel.js +184 -0
  16. package/dist/cjs/EmbeddingModel.js.map +1 -0
  17. package/dist/cjs/IdGenerator.js +255 -0
  18. package/dist/cjs/IdGenerator.js.map +1 -0
  19. package/dist/cjs/LanguageModel.js +584 -0
  20. package/dist/cjs/LanguageModel.js.map +1 -0
  21. package/dist/cjs/McpServer.js +12 -4
  22. package/dist/cjs/McpServer.js.map +1 -1
  23. package/dist/cjs/Model.js +118 -0
  24. package/dist/cjs/Model.js.map +1 -0
  25. package/dist/cjs/Prompt.js +649 -0
  26. package/dist/cjs/Prompt.js.map +1 -0
  27. package/dist/cjs/Response.js +635 -0
  28. package/dist/cjs/Response.js.map +1 -0
  29. package/dist/cjs/Telemetry.js +176 -0
  30. package/dist/cjs/Telemetry.js.map +1 -0
  31. package/dist/cjs/Tokenizer.js +87 -8
  32. package/dist/cjs/Tokenizer.js.map +1 -1
  33. package/dist/cjs/Tool.js +556 -0
  34. package/dist/cjs/Tool.js.map +1 -0
  35. package/dist/cjs/Toolkit.js +279 -0
  36. package/dist/cjs/Toolkit.js.map +1 -0
  37. package/dist/cjs/index.js +21 -19
  38. package/dist/dts/AiError.d.ts +577 -9
  39. package/dist/dts/AiError.d.ts.map +1 -1
  40. package/dist/dts/Chat.d.ts +356 -0
  41. package/dist/dts/Chat.d.ts.map +1 -0
  42. package/dist/dts/EmbeddingModel.d.ts +153 -0
  43. package/dist/dts/EmbeddingModel.d.ts.map +1 -0
  44. package/dist/dts/IdGenerator.d.ts +272 -0
  45. package/dist/dts/IdGenerator.d.ts.map +1 -0
  46. package/dist/dts/LanguageModel.d.ts +458 -0
  47. package/dist/dts/LanguageModel.d.ts.map +1 -0
  48. package/dist/dts/McpSchema.d.ts +25 -25
  49. package/dist/dts/McpServer.d.ts +6 -4
  50. package/dist/dts/McpServer.d.ts.map +1 -1
  51. package/dist/dts/Model.d.ts +124 -0
  52. package/dist/dts/Model.d.ts.map +1 -0
  53. package/dist/dts/Prompt.d.ts +1119 -0
  54. package/dist/dts/Prompt.d.ts.map +1 -0
  55. package/dist/dts/Response.d.ts +1519 -0
  56. package/dist/dts/Response.d.ts.map +1 -0
  57. package/dist/dts/Telemetry.d.ts +520 -0
  58. package/dist/dts/Telemetry.d.ts.map +1 -0
  59. package/dist/dts/Tokenizer.d.ts +131 -13
  60. package/dist/dts/Tokenizer.d.ts.map +1 -1
  61. package/dist/dts/Tool.d.ts +876 -0
  62. package/dist/dts/Tool.d.ts.map +1 -0
  63. package/dist/dts/Toolkit.d.ts +310 -0
  64. package/dist/dts/Toolkit.d.ts.map +1 -0
  65. package/dist/dts/index.d.ts +498 -13
  66. package/dist/dts/index.d.ts.map +1 -1
  67. package/dist/esm/AiError.js +570 -10
  68. package/dist/esm/AiError.js.map +1 -1
  69. package/dist/esm/Chat.js +291 -0
  70. package/dist/esm/Chat.js.map +1 -0
  71. package/dist/esm/EmbeddingModel.js +173 -0
  72. package/dist/esm/EmbeddingModel.js.map +1 -0
  73. package/dist/esm/IdGenerator.js +245 -0
  74. package/dist/esm/IdGenerator.js.map +1 -0
  75. package/dist/esm/LanguageModel.js +572 -0
  76. package/dist/esm/LanguageModel.js.map +1 -0
  77. package/dist/esm/McpServer.js +12 -4
  78. package/dist/esm/McpServer.js.map +1 -1
  79. package/dist/esm/Model.js +108 -0
  80. package/dist/esm/Model.js.map +1 -0
  81. package/dist/esm/Prompt.js +633 -0
  82. package/dist/esm/Prompt.js.map +1 -0
  83. package/dist/esm/Response.js +619 -0
  84. package/dist/esm/Response.js.map +1 -0
  85. package/dist/esm/Telemetry.js +166 -0
  86. package/dist/esm/Telemetry.js.map +1 -0
  87. package/dist/esm/Tokenizer.js +87 -8
  88. package/dist/esm/Tokenizer.js.map +1 -1
  89. package/dist/esm/Tool.js +534 -0
  90. package/dist/esm/Tool.js.map +1 -0
  91. package/dist/esm/Toolkit.js +269 -0
  92. package/dist/esm/Toolkit.js.map +1 -0
  93. package/dist/esm/index.js +498 -13
  94. package/dist/esm/index.js.map +1 -1
  95. package/package.json +76 -68
  96. package/src/AiError.ts +739 -9
  97. package/src/Chat.ts +546 -0
  98. package/src/EmbeddingModel.ts +311 -0
  99. package/src/IdGenerator.ts +320 -0
  100. package/src/LanguageModel.ts +1074 -0
  101. package/src/McpServer.ts +337 -194
  102. package/src/Model.ts +155 -0
  103. package/src/Prompt.ts +1616 -0
  104. package/src/Response.ts +2131 -0
  105. package/src/Telemetry.ts +655 -0
  106. package/src/Tokenizer.ts +145 -24
  107. package/src/Tool.ts +1267 -0
  108. package/src/Toolkit.ts +516 -0
  109. package/src/index.ts +499 -13
  110. package/AiChat/package.json +0 -6
  111. package/AiEmbeddingModel/package.json +0 -6
  112. package/AiInput/package.json +0 -6
  113. package/AiLanguageModel/package.json +0 -6
  114. package/AiModel/package.json +0 -6
  115. package/AiResponse/package.json +0 -6
  116. package/AiTelemetry/package.json +0 -6
  117. package/AiTool/package.json +0 -6
  118. package/AiToolkit/package.json +0 -6
  119. package/dist/cjs/AiChat.js +0 -122
  120. package/dist/cjs/AiChat.js.map +0 -1
  121. package/dist/cjs/AiEmbeddingModel.js +0 -109
  122. package/dist/cjs/AiEmbeddingModel.js.map +0 -1
  123. package/dist/cjs/AiInput.js +0 -458
  124. package/dist/cjs/AiInput.js.map +0 -1
  125. package/dist/cjs/AiLanguageModel.js +0 -351
  126. package/dist/cjs/AiLanguageModel.js.map +0 -1
  127. package/dist/cjs/AiModel.js +0 -37
  128. package/dist/cjs/AiModel.js.map +0 -1
  129. package/dist/cjs/AiResponse.js +0 -681
  130. package/dist/cjs/AiResponse.js.map +0 -1
  131. package/dist/cjs/AiTelemetry.js +0 -58
  132. package/dist/cjs/AiTelemetry.js.map +0 -1
  133. package/dist/cjs/AiTool.js +0 -150
  134. package/dist/cjs/AiTool.js.map +0 -1
  135. package/dist/cjs/AiToolkit.js +0 -157
  136. package/dist/cjs/AiToolkit.js.map +0 -1
  137. package/dist/cjs/internal/common.js +0 -21
  138. package/dist/cjs/internal/common.js.map +0 -1
  139. package/dist/dts/AiChat.d.ts +0 -101
  140. package/dist/dts/AiChat.d.ts.map +0 -1
  141. package/dist/dts/AiEmbeddingModel.d.ts +0 -65
  142. package/dist/dts/AiEmbeddingModel.d.ts.map +0 -1
  143. package/dist/dts/AiInput.d.ts +0 -590
  144. package/dist/dts/AiInput.d.ts.map +0 -1
  145. package/dist/dts/AiLanguageModel.d.ts +0 -302
  146. package/dist/dts/AiLanguageModel.d.ts.map +0 -1
  147. package/dist/dts/AiModel.d.ts +0 -25
  148. package/dist/dts/AiModel.d.ts.map +0 -1
  149. package/dist/dts/AiResponse.d.ts +0 -863
  150. package/dist/dts/AiResponse.d.ts.map +0 -1
  151. package/dist/dts/AiTelemetry.d.ts +0 -242
  152. package/dist/dts/AiTelemetry.d.ts.map +0 -1
  153. package/dist/dts/AiTool.d.ts +0 -334
  154. package/dist/dts/AiTool.d.ts.map +0 -1
  155. package/dist/dts/AiToolkit.d.ts +0 -96
  156. package/dist/dts/AiToolkit.d.ts.map +0 -1
  157. package/dist/dts/internal/common.d.ts +0 -2
  158. package/dist/dts/internal/common.d.ts.map +0 -1
  159. package/dist/esm/AiChat.js +0 -111
  160. package/dist/esm/AiChat.js.map +0 -1
  161. package/dist/esm/AiEmbeddingModel.js +0 -98
  162. package/dist/esm/AiEmbeddingModel.js.map +0 -1
  163. package/dist/esm/AiInput.js +0 -433
  164. package/dist/esm/AiInput.js.map +0 -1
  165. package/dist/esm/AiLanguageModel.js +0 -340
  166. package/dist/esm/AiLanguageModel.js.map +0 -1
  167. package/dist/esm/AiModel.js +0 -29
  168. package/dist/esm/AiModel.js.map +0 -1
  169. package/dist/esm/AiResponse.js +0 -657
  170. package/dist/esm/AiResponse.js.map +0 -1
  171. package/dist/esm/AiTelemetry.js +0 -48
  172. package/dist/esm/AiTelemetry.js.map +0 -1
  173. package/dist/esm/AiTool.js +0 -134
  174. package/dist/esm/AiTool.js.map +0 -1
  175. package/dist/esm/AiToolkit.js +0 -147
  176. package/dist/esm/AiToolkit.js.map +0 -1
  177. package/dist/esm/internal/common.js +0 -14
  178. package/dist/esm/internal/common.js.map +0 -1
  179. package/src/AiChat.ts +0 -251
  180. package/src/AiEmbeddingModel.ts +0 -169
  181. package/src/AiInput.ts +0 -602
  182. package/src/AiLanguageModel.ts +0 -685
  183. package/src/AiModel.ts +0 -53
  184. package/src/AiResponse.ts +0 -986
  185. package/src/AiTelemetry.ts +0 -333
  186. package/src/AiTool.ts +0 -579
  187. package/src/AiToolkit.ts +0 -265
  188. 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))