@effect/ai 0.14.1 → 0.16.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 (134) hide show
  1. package/AiEmbeddingModel/package.json +6 -0
  2. package/AiLanguageModel/package.json +6 -0
  3. package/AiTool/package.json +6 -0
  4. package/dist/cjs/AiChat.js +65 -86
  5. package/dist/cjs/AiChat.js.map +1 -1
  6. package/dist/cjs/{Embeddings.js → AiEmbeddingModel.js} +12 -12
  7. package/dist/cjs/AiEmbeddingModel.js.map +1 -0
  8. package/dist/cjs/AiError.js +8 -1
  9. package/dist/cjs/AiError.js.map +1 -1
  10. package/dist/cjs/AiInput.js +335 -248
  11. package/dist/cjs/AiInput.js.map +1 -1
  12. package/dist/cjs/AiLanguageModel.js +311 -0
  13. package/dist/cjs/AiLanguageModel.js.map +1 -0
  14. package/dist/cjs/AiModel.js +11 -5
  15. package/dist/cjs/AiModel.js.map +1 -1
  16. package/dist/cjs/AiPlan.js +10 -3
  17. package/dist/cjs/AiPlan.js.map +1 -1
  18. package/dist/cjs/AiResponse.js +481 -165
  19. package/dist/cjs/AiResponse.js.map +1 -1
  20. package/dist/cjs/AiTelemetry.js +10 -3
  21. package/dist/cjs/AiTelemetry.js.map +1 -1
  22. package/dist/cjs/AiTool.js +93 -0
  23. package/dist/cjs/AiTool.js.map +1 -0
  24. package/dist/cjs/AiToolkit.js +121 -98
  25. package/dist/cjs/AiToolkit.js.map +1 -1
  26. package/dist/cjs/Tokenizer.js +14 -16
  27. package/dist/cjs/Tokenizer.js.map +1 -1
  28. package/dist/cjs/index.js +7 -9
  29. package/dist/cjs/internal/aiPlan.js +6 -9
  30. package/dist/cjs/internal/aiPlan.js.map +1 -1
  31. package/dist/cjs/internal/common.js +22 -0
  32. package/dist/cjs/internal/common.js.map +1 -0
  33. package/dist/dts/AiChat.d.ts +58 -44
  34. package/dist/dts/AiChat.d.ts.map +1 -1
  35. package/dist/dts/{Embeddings.d.ts → AiEmbeddingModel.d.ts} +13 -14
  36. package/dist/dts/AiEmbeddingModel.d.ts.map +1 -0
  37. package/dist/dts/AiError.d.ts +4 -3
  38. package/dist/dts/AiError.d.ts.map +1 -1
  39. package/dist/dts/AiInput.d.ts +441 -146
  40. package/dist/dts/AiInput.d.ts.map +1 -1
  41. package/dist/dts/AiLanguageModel.d.ts +263 -0
  42. package/dist/dts/AiLanguageModel.d.ts.map +1 -0
  43. package/dist/dts/AiModel.d.ts +21 -20
  44. package/dist/dts/AiModel.d.ts.map +1 -1
  45. package/dist/dts/AiPlan.d.ts +90 -26
  46. package/dist/dts/AiPlan.d.ts.map +1 -1
  47. package/dist/dts/AiResponse.d.ts +711 -100
  48. package/dist/dts/AiResponse.d.ts.map +1 -1
  49. package/dist/dts/AiTelemetry.d.ts +175 -157
  50. package/dist/dts/AiTelemetry.d.ts.map +1 -1
  51. package/dist/dts/AiTool.d.ts +288 -0
  52. package/dist/dts/AiTool.d.ts.map +1 -0
  53. package/dist/dts/AiToolkit.d.ts +50 -111
  54. package/dist/dts/AiToolkit.d.ts.map +1 -1
  55. package/dist/dts/Tokenizer.d.ts +8 -6
  56. package/dist/dts/Tokenizer.d.ts.map +1 -1
  57. package/dist/dts/index.d.ts +8 -12
  58. package/dist/dts/index.d.ts.map +1 -1
  59. package/dist/dts/internal/common.d.ts +2 -0
  60. package/dist/dts/internal/common.d.ts.map +1 -0
  61. package/dist/esm/AiChat.js +62 -83
  62. package/dist/esm/AiChat.js.map +1 -1
  63. package/dist/esm/{Embeddings.js → AiEmbeddingModel.js} +10 -10
  64. package/dist/esm/AiEmbeddingModel.js.map +1 -0
  65. package/dist/esm/AiError.js +8 -1
  66. package/dist/esm/AiError.js.map +1 -1
  67. package/dist/esm/AiInput.js +316 -238
  68. package/dist/esm/AiInput.js.map +1 -1
  69. package/dist/esm/AiLanguageModel.js +300 -0
  70. package/dist/esm/AiLanguageModel.js.map +1 -0
  71. package/dist/esm/AiModel.js +11 -5
  72. package/dist/esm/AiModel.js.map +1 -1
  73. package/dist/esm/AiPlan.js +8 -2
  74. package/dist/esm/AiPlan.js.map +1 -1
  75. package/dist/esm/AiResponse.js +467 -162
  76. package/dist/esm/AiResponse.js.map +1 -1
  77. package/dist/esm/AiTelemetry.js +8 -2
  78. package/dist/esm/AiTelemetry.js.map +1 -1
  79. package/dist/esm/AiTool.js +82 -0
  80. package/dist/esm/AiTool.js.map +1 -0
  81. package/dist/esm/AiToolkit.js +118 -96
  82. package/dist/esm/AiToolkit.js.map +1 -1
  83. package/dist/esm/Tokenizer.js +14 -16
  84. package/dist/esm/Tokenizer.js.map +1 -1
  85. package/dist/esm/index.js +8 -12
  86. package/dist/esm/index.js.map +1 -1
  87. package/dist/esm/internal/aiPlan.js +4 -7
  88. package/dist/esm/internal/aiPlan.js.map +1 -1
  89. package/dist/esm/internal/common.js +14 -0
  90. package/dist/esm/internal/common.js.map +1 -0
  91. package/package.json +28 -36
  92. package/src/AiChat.ts +182 -207
  93. package/src/{Embeddings.ts → AiEmbeddingModel.ts} +19 -18
  94. package/src/AiError.ts +8 -1
  95. package/src/AiInput.ts +434 -313
  96. package/src/AiLanguageModel.ts +569 -0
  97. package/src/AiModel.ts +47 -29
  98. package/src/AiPlan.ts +102 -30
  99. package/src/AiResponse.ts +743 -187
  100. package/src/AiTelemetry.ts +214 -197
  101. package/src/AiTool.ts +496 -0
  102. package/src/AiToolkit.ts +200 -240
  103. package/src/Tokenizer.ts +18 -22
  104. package/src/index.ts +9 -14
  105. package/src/internal/aiPlan.ts +12 -14
  106. package/src/internal/common.ts +12 -0
  107. package/AiModels/package.json +0 -6
  108. package/AiRole/package.json +0 -6
  109. package/Completions/package.json +0 -6
  110. package/Embeddings/package.json +0 -6
  111. package/dist/cjs/AiModels.js +0 -54
  112. package/dist/cjs/AiModels.js.map +0 -1
  113. package/dist/cjs/AiRole.js +0 -106
  114. package/dist/cjs/AiRole.js.map +0 -1
  115. package/dist/cjs/Completions.js +0 -256
  116. package/dist/cjs/Completions.js.map +0 -1
  117. package/dist/cjs/Embeddings.js.map +0 -1
  118. package/dist/dts/AiModels.d.ts +0 -34
  119. package/dist/dts/AiModels.d.ts.map +0 -1
  120. package/dist/dts/AiRole.d.ts +0 -111
  121. package/dist/dts/AiRole.d.ts.map +0 -1
  122. package/dist/dts/Completions.d.ts +0 -128
  123. package/dist/dts/Completions.d.ts.map +0 -1
  124. package/dist/dts/Embeddings.d.ts.map +0 -1
  125. package/dist/esm/AiModels.js +0 -44
  126. package/dist/esm/AiModels.js.map +0 -1
  127. package/dist/esm/AiRole.js +0 -93
  128. package/dist/esm/AiRole.js.map +0 -1
  129. package/dist/esm/Completions.js +0 -245
  130. package/dist/esm/Completions.js.map +0 -1
  131. package/dist/esm/Embeddings.js.map +0 -1
  132. package/src/AiModels.ts +0 -77
  133. package/src/AiRole.ts +0 -122
  134. package/src/Completions.ts +0 -434
package/src/AiResponse.ts CHANGED
@@ -1,129 +1,542 @@
1
1
  /**
2
2
  * @since 1.0.0
3
3
  */
4
- import * as Chunk from "effect/Chunk"
5
- import * as Data from "effect/Data"
4
+ import type * as Context from "effect/Context"
6
5
  import * as Effect from "effect/Effect"
7
- import * as Iterable from "effect/Iterable"
6
+ import { dual } from "effect/Function"
8
7
  import * as Option from "effect/Option"
9
8
  import * as Predicate from "effect/Predicate"
10
9
  import * as Schema from "effect/Schema"
11
10
  import { AiError } from "./AiError.js"
12
- import * as AiRole from "./AiRole.js"
11
+ import type * as AiTool from "./AiTool.js"
12
+ import * as InternalCommon from "./internal/common.js"
13
+
14
+ const constDisableValidation = { disableValidation: true }
13
15
 
14
16
  /**
15
17
  * @since 1.0.0
16
- * @category type ids
18
+ * @category Type Ids
17
19
  */
18
- export const TypeId: unique symbol = Symbol("@effect/ai/AiResponse")
20
+ export const TypeId = Symbol.for("@effect/ai/AiResponse")
19
21
 
20
22
  /**
21
23
  * @since 1.0.0
22
- * @category type ids
24
+ * @category Type Ids
23
25
  */
24
26
  export type TypeId = typeof TypeId
25
27
 
28
+ /**
29
+ * Represents a response received from a large language model.
30
+ *
31
+ * @since 1.0.0
32
+ * @category Models
33
+ */
34
+ export class AiResponse extends Schema.Class<AiResponse>(
35
+ "@effect/ai/AiResponse"
36
+ )({
37
+ /**
38
+ * The parts of the response.
39
+ */
40
+ parts: Schema.Array(Schema.suspend(() => Part))
41
+ }) {
42
+ /**
43
+ * @since 1.0.0
44
+ */
45
+ readonly [TypeId]: TypeId = TypeId
46
+
47
+ /**
48
+ * Returns the generated text content of the response.
49
+ */
50
+ get text(): string {
51
+ let text = ""
52
+ let found = false
53
+ for (const part of this.parts) {
54
+ if (part._tag === "TextPart") {
55
+ text += found ? "\n\n" + part.text : part.text
56
+ found = true
57
+ }
58
+ }
59
+ return text
60
+ }
61
+
62
+ /**
63
+ * Returns the finish reason for the response, or `"unknown"` if the finish
64
+ * reason is not known.
65
+ */
66
+ get finishReason(): FinishReason {
67
+ const finishPart = this.parts.find((part) => part._tag === "FinishPart")
68
+ return finishPart?.reason ?? "unknown"
69
+ }
70
+
71
+ /**
72
+ * Attempts to retrieve provider-specific response metadata.
73
+ */
74
+ getProviderMetadata<I, S>(tag: Context.Tag<I, S>): Option.Option<S> {
75
+ const finishPart = this.parts.find((part) => part._tag === "FinishPart")
76
+ return Option.fromNullable(finishPart?.providerMetadata[tag.key] as S)
77
+ }
78
+ }
79
+
26
80
  /**
27
81
  * @since 1.0.0
28
- * @category parts
82
+ * @category Models
29
83
  */
30
- export const PartTypeId: unique symbol = Symbol("@effect/ai/AiResponse/Part")
84
+ export const FromJson: Schema.transform<
85
+ Schema.SchemaClass<unknown, string, never>,
86
+ typeof AiResponse
87
+ > = Schema.parseJson(AiResponse)
31
88
 
32
89
  /**
33
90
  * @since 1.0.0
34
- * @category parts
91
+ * @category Type Ids
92
+ */
93
+ export const StructuredResponseTypeId = Symbol.for("@effect/ai/AiResponse/StructuredResponse")
94
+
95
+ /**
96
+ * @since 1.0.0
97
+ * @category Type Ids
98
+ */
99
+ export type StructuredResponseTypeId = typeof StructuredResponseTypeId
100
+
101
+ /**
102
+ * Represents a response generated by a large language model that includes
103
+ * structured output.
104
+ *
105
+ * @since 1.0.0
106
+ * @category Models
107
+ */
108
+ export class WithStructuredOutput<A> extends AiResponse {
109
+ /**
110
+ * @since 1.0.0
111
+ */
112
+ readonly [StructuredResponseTypeId]: StructuredResponseTypeId = StructuredResponseTypeId
113
+
114
+ /**
115
+ * The identifier of the tool which generated the structured output.
116
+ */
117
+ readonly id: ToolCallId
118
+
119
+ /**
120
+ * The name of the tool which generated the structured output.
121
+ */
122
+ readonly name: string
123
+
124
+ /**
125
+ * The structured output generated by the model.
126
+ */
127
+ readonly value: A
128
+
129
+ constructor(props: {
130
+ /**
131
+ * The identifier of the tool which generated the structured output.
132
+ */
133
+ readonly id: ToolCallId
134
+ /**
135
+ * The name of the tool which generated the structured output.
136
+ */
137
+ readonly name: string
138
+ /**
139
+ * The structured output generated by the model.
140
+ */
141
+ readonly value: A
142
+ /**
143
+ * The parts of the response.
144
+ */
145
+ readonly parts: ReadonlyArray<Part>
146
+ }, options?: Schema.MakeOptions) {
147
+ super({ parts: props.parts }, options)
148
+ this.id = props.id
149
+ this.name = props.name
150
+ this.value = props.value
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @since 1.0.0
156
+ * @category Type Ids
157
+ */
158
+ export const WithToolCallResultsTypeId = Symbol.for("@effect/ai/AiResponse/WithToolCallResults")
159
+
160
+ /**
161
+ * @since 1.0.0
162
+ * @category Type Ids
163
+ */
164
+ export type WithToolCallResultsTypeId = typeof WithToolCallResultsTypeId
165
+
166
+ /**
167
+ * Represents a response generated by a large language model that includes
168
+ * tool call results.
169
+ *
170
+ * @since 1.0.0
171
+ * @category Models
172
+ */
173
+ export class WithToolCallResults<Tools extends AiTool.Any> extends AiResponse {
174
+ /**
175
+ * @since 1.0.0
176
+ */
177
+ readonly [WithToolCallResultsTypeId]: WithToolCallResultsTypeId = WithToolCallResultsTypeId
178
+
179
+ /**
180
+ * The tool call results, represented as a mapping between the tool call
181
+ * identifier and the result of the tool call handler.
182
+ */
183
+ readonly results: ReadonlyMap<ToolCallId, AiTool.Success<Tools>>
184
+ /**
185
+ * The encoded tool call results, suitable for incorporation into subsequent
186
+ * requests to the large language model.
187
+ */
188
+ readonly encodedResults: ReadonlyMap<ToolCallId, unknown>
189
+
190
+ constructor(props: {
191
+ /**
192
+ * The tool call results, represented as a mapping between the tool call
193
+ * identifier and the result of the tool call handler.
194
+ */
195
+ readonly results: ReadonlyMap<ToolCallId, AiTool.Success<Tools>>
196
+ /**
197
+ * The encoded tool call results, suitable for incorporation into subsequent
198
+ * requests to the large language model.
199
+ */
200
+ readonly encodedResults: ReadonlyMap<ToolCallId, unknown>
201
+ /**
202
+ * The parts of the response.
203
+ */
204
+ readonly parts: ReadonlyArray<Part>
205
+ }, options?: Schema.MakeOptions) {
206
+ super({ parts: props.parts }, options)
207
+ this.results = props.results
208
+ this.encodedResults = props.encodedResults
209
+ }
210
+ }
211
+
212
+ // =============================================================================
213
+ // Part
214
+ // =============================================================================
215
+
216
+ /**
217
+ * @since 1.0.0
218
+ * @category Type Ids
219
+ */
220
+ export const PartTypeId = Symbol.for("@effect/ai/AiResponse/Part")
221
+
222
+ /**
223
+ * @since 1.0.0
224
+ * @category Type Ids
35
225
  */
36
226
  export type PartTypeId = typeof PartTypeId
37
227
 
38
- const constDisableValidation = { disableValidation: true } as const
228
+ /**
229
+ * Represents a content source that was used to generate a model response.
230
+ *
231
+ * @since 1.0.0
232
+ * @category Models
233
+ */
234
+ export class ContentSourceAnnotation extends Schema.TaggedClass<ContentSourceAnnotation>(
235
+ "@effect/ai/AiResponse/Annotation/ContentSourceAnnotation"
236
+ )("ContentSourceAnnotation", {
237
+ /**
238
+ * The identifier for the content source.
239
+ */
240
+ id: Schema.String,
241
+ /**
242
+ * The index of the content source in the list of sources provided in the
243
+ * model request parameters.
244
+ */
245
+ index: Schema.Int,
246
+ /**
247
+ * The provider-specific type of the file annotation.
248
+ *
249
+ * For example, when using Anthropic the type may be `char_location`,
250
+ * `page_location`, or `content_block_location`.
251
+ */
252
+ type: Schema.String,
253
+ /**
254
+ * The content used from the content source in the message generated by the
255
+ * model.
256
+ */
257
+ referencedContent: Schema.String,
258
+ /**
259
+ * The index of the first character of the content referenced by the content
260
+ * source in the message generated by the model.
261
+ */
262
+ startIndex: Schema.Int,
263
+ /**
264
+ * The index of the last character of the content referenced by the content
265
+ * source in the message generated by the model.
266
+ */
267
+ endIndex: Schema.Int
268
+ }) {}
269
+
270
+ /**
271
+ * Represents a file that was used to generate a model response.
272
+ *
273
+ * @since 1.0.0
274
+ * @category Models
275
+ */
276
+ export class FileAnnotation extends Schema.TaggedClass<FileAnnotation>(
277
+ "@effect/ai/AiResponse/Annotation/FileAnnotation"
278
+ )("FileAnnotation", {
279
+ /**
280
+ * The identifier for the file.
281
+ */
282
+ id: Schema.String,
283
+ /**
284
+ * The provider-specific type of the file annotation.
285
+ *
286
+ * For example, when using OpenAi the type may be `file_citation` or
287
+ * `file_path`.
288
+ */
289
+ type: Schema.String,
290
+ /**
291
+ * The index of the file in the list of files provided in the model request
292
+ * parameters.
293
+ */
294
+ index: Schema.Int
295
+ }) {}
296
+
297
+ /**
298
+ * Represents a web resource that was used to generate a model response.
299
+ *
300
+ * @since 1.0.0
301
+ * @category Models
302
+ */
303
+ export class UrlAnnotation extends Schema.TaggedClass<UrlAnnotation>(
304
+ "@effect/ai/AiResponse/Annotation/UrlAnnotation"
305
+ )("UrlAnnotation", {
306
+ /**
307
+ * The URL of the web resource.
308
+ */
309
+ url: Schema.String,
310
+ /**
311
+ * The title of the web resource.
312
+ */
313
+ title: Schema.String,
314
+ /**
315
+ * The index of the first character of the content referenced by the web
316
+ * resource in the message generated by the model.
317
+ */
318
+ startIndex: Schema.Int,
319
+ /**
320
+ * The index of the last character of the content referenced by the web
321
+ * resource in the message generated by the model.
322
+ */
323
+ endIndex: Schema.Int
324
+ }) {}
325
+
326
+ /**
327
+ * Represents annotations that were used to support the message generated by
328
+ * a model.
329
+ *
330
+ * @since 1.0.0
331
+ * @category Models
332
+ */
333
+ export const Annotation: Schema.Union<[
334
+ typeof ContentSourceAnnotation,
335
+ typeof FileAnnotation,
336
+ typeof UrlAnnotation
337
+ ]> = Schema.Union(
338
+ ContentSourceAnnotation,
339
+ FileAnnotation,
340
+ UrlAnnotation
341
+ )
342
+
343
+ /**
344
+ * @since 1.0.0
345
+ * @category Models
346
+ */
347
+ export type Annotation = typeof Annotation.Type
348
+
349
+ /**
350
+ * Represents part of the text generated by the model.
351
+ *
352
+ * @since 1.0.0
353
+ * @category Models
354
+ */
355
+ export class TextPart extends Schema.TaggedClass<TextPart>(
356
+ "@effect/ai/AiResponse/TextPart"
357
+ )("TextPart", {
358
+ /**
359
+ * The text content generated by the model.
360
+ */
361
+ text: Schema.String,
362
+ /**
363
+ * The annotations used to support the text generated by the model.
364
+ */
365
+ annotations: Schema.optionalWith(Schema.Array(Annotation), {
366
+ default: () => []
367
+ })
368
+ }) {
369
+ /**
370
+ * @since 1.0.0
371
+ */
372
+ readonly [PartTypeId]: PartTypeId = PartTypeId
373
+ }
39
374
 
40
375
  /**
376
+ * Represents part of the reasoning carried out by the model to generate a
377
+ * response.
378
+ *
41
379
  * @since 1.0.0
42
- * @category parts
380
+ * @category Models
43
381
  */
44
- export class TextPart extends Schema.TaggedClass<TextPart>("@effect/ai/AiResponse/TextPart")("Text", {
45
- content: Schema.String
382
+ export class ReasoningPart extends Schema.TaggedClass<ReasoningPart>(
383
+ "@effect/ai/AiResponse/ReasoningPart"
384
+ )("ReasoningPart", {
385
+ /**
386
+ * The reasoning content that the model used to return the output.
387
+ */
388
+ reasoningText: Schema.String,
389
+ /**
390
+ * An optional signature which verifies that the reasoning text was generated
391
+ * by the model.
392
+ */
393
+ signature: Schema.optional(Schema.String)
46
394
  }) {
47
395
  /**
48
396
  * @since 1.0.0
49
397
  */
50
398
  readonly [PartTypeId]: PartTypeId = PartTypeId
399
+ }
400
+
401
+ /**
402
+ * Represents part of the reasoning carried out by the model to generate a
403
+ * response which needed to be encrypted by the model provider for safety
404
+ * reasons.
405
+ *
406
+ * @since 1.0.0
407
+ * @category Models
408
+ */
409
+ export class RedactedReasoningPart extends Schema.TaggedClass<RedactedReasoningPart>(
410
+ "@effect/ai/AiResponse/RedactedReasoningPart"
411
+ )("RedactedReasoningPart", {
412
+ /**
413
+ * The content in the reasoning that was encrypted by the model provider for
414
+ * safety reasons.
415
+ */
416
+ redactedText: Schema.String
417
+ }) {
51
418
  /**
52
419
  * @since 1.0.0
53
420
  */
54
- static fromContent(content: string): TextPart {
55
- return new TextPart({ content }, constDisableValidation)
56
- }
421
+ readonly [PartTypeId]: PartTypeId = PartTypeId
57
422
  }
58
423
 
59
424
  /**
425
+ * Represents the identifier generated by a model when a tool call is requested.
426
+ *
60
427
  * @since 1.0.0
61
- * @category parts
428
+ * @category Models
62
429
  */
63
- export const ToolCallId = Schema.String.pipe(Schema.brand("ToolCallId"))
430
+ export const ToolCallId: Schema.brand<typeof Schema.String, "@effect/ai/ToolCallId"> = InternalCommon.ToolCallId
64
431
 
65
432
  /**
66
433
  * @since 1.0.0
67
- * @category parts
434
+ * @category Models
68
435
  */
69
436
  export type ToolCallId = typeof ToolCallId.Type
70
437
 
71
438
  /**
439
+ * Represents a request by a model to call a specific tool that it has been
440
+ * provided with.
441
+ *
72
442
  * @since 1.0.0
73
- * @category parts
443
+ * @category Models
74
444
  */
75
- export class ToolCallPart extends Schema.TaggedClass<ToolCallPart>("@effect/ai/AiResponse/ToolCallPart")("ToolCall", {
445
+ export class ToolCallPart extends Schema.TaggedClass<ToolCallPart>(
446
+ "@effect/ai/AiResponse/ToolCallPart"
447
+ )("ToolCallPart", {
448
+ /**
449
+ * The identifier generated by a model when requesting a tool call.
450
+ */
76
451
  id: ToolCallId,
452
+ /**
453
+ * The name of the tool to call.
454
+ */
77
455
  name: Schema.String,
456
+ /**
457
+ * The arguments to call the tool with as a JSON-serializable object that
458
+ * matches the tool call input schema.
459
+ */
78
460
  params: Schema.Unknown
79
461
  }) {
80
462
  /**
81
463
  * @since 1.0.0
82
464
  */
83
465
  readonly [PartTypeId]: PartTypeId = PartTypeId
466
+
84
467
  /**
468
+ * Converts a raw tool call into a `ToolCallPart` by parsing tool call
469
+ * parameters as a JSON string. If your tool call parameters are already
470
+ * parsed, use `ToolCallPart.fromUnknown`.
471
+ *
85
472
  * @since 1.0.0
86
473
  */
87
- static fromJson(
88
- { id, name, params }: {
89
- readonly id: string
90
- readonly name: string
91
- readonly params: string
92
- }
93
- ): Effect.Effect<ToolCallPart, AiError> {
474
+ static fromJson({ id, name, params }: {
475
+ readonly id: string
476
+ readonly name: string
477
+ readonly params: string
478
+ }): Effect.Effect<ToolCallPart, AiError> {
94
479
  return Effect.try({
95
480
  try() {
96
- return new ToolCallPart({ id: ToolCallId.make(id), name, params: JSON.parse(params) }, constDisableValidation)
481
+ return new ToolCallPart({
482
+ id: ToolCallId.make(id, constDisableValidation),
483
+ name,
484
+ params: JSON.parse(params)
485
+ }, constDisableValidation)
97
486
  },
98
487
  catch: (cause) =>
99
488
  new AiError({
100
489
  module: "AiResponse",
101
490
  method: "ToolCall.fromJson",
102
- description: "Failed to parse parameters",
491
+ description: `Failed to parse parameters from JSON:\n${params}`,
103
492
  cause
104
493
  })
105
494
  })
106
495
  }
496
+
107
497
  /**
498
+ * Converts a raw tool call into a `ToolCallPart` assuming that the tool call
499
+ * parameters have already been parsed. If your tool call parameters have not
500
+ * already been parsed, use `ToolCallPart.fromJson`.
501
+ *
108
502
  * @since 1.0.0
109
503
  */
110
- static fromUnknown(
111
- { id, name, params }: {
112
- readonly id: string
113
- readonly name: string
114
- readonly params: unknown
115
- }
116
- ): ToolCallPart {
117
- return new ToolCallPart({ id: ToolCallId.make(id), name, params }, constDisableValidation)
504
+ static fromUnknown({ id, name, params }: {
505
+ readonly id: string
506
+ readonly name: string
507
+ readonly params: unknown
508
+ }): ToolCallPart {
509
+ return new ToolCallPart({
510
+ id: ToolCallId.make(id, constDisableValidation),
511
+ name,
512
+ params
513
+ }, constDisableValidation)
118
514
  }
119
515
  }
120
516
 
121
517
  /**
518
+ * Represents the initial response metadata generated by a model when responding
519
+ * to a request.
520
+ *
122
521
  * @since 1.0.0
123
- * @category parts
522
+ * @categor Models
124
523
  */
125
- export class ImageUrlPart extends Schema.TaggedClass<ImageUrlPart>("@effect/ai/AiResponse/ImageUrlPart")("ImageUrl", {
126
- url: Schema.String
524
+ export class MetadataPart extends Schema.TaggedClass<MetadataPart>(
525
+ "@effect/ai/AiResponse/MetadataPart"
526
+ )("MetadataPart", {
527
+ /**
528
+ * The unique identifier for the response. Each chunk of the response should
529
+ * have the same identifier.
530
+ */
531
+ id: Schema.optional(Schema.String),
532
+ /**
533
+ * The model that was used to generate the response.
534
+ */
535
+ model: Schema.String,
536
+ /**
537
+ * The Unix timestamp of when the model began generated the response.
538
+ */
539
+ timestamp: Schema.optional(Schema.DateFromNumber)
127
540
  }) {
128
541
  /**
129
542
  * @since 1.0.0
@@ -132,212 +545,355 @@ export class ImageUrlPart extends Schema.TaggedClass<ImageUrlPart>("@effect/ai/A
132
545
  }
133
546
 
134
547
  /**
548
+ * Represents the reason why a model finished generation of a response.
549
+ *
550
+ * Possible finish reasons:
551
+ * - `"stop"`: The model generated a stop sequence.
552
+ * - `"length"`: The model exceeded its token budget.
553
+ * - `"content-filter"`: The model generated content which violated a content filter.
554
+ * - `"tool-calls"`: The model triggered a tool call.
555
+ * - `"error"`: The model encountered an error.
556
+ * - `"other"`: The model stopped for a reason not supported by this protocol.
557
+ * - `"unknown"`: The model did not specify a finish reason.
558
+ *
135
559
  * @since 1.0.0
136
- * @category parts
560
+ * @category Models
137
561
  */
138
- export type Part = TextPart | ToolCallPart | ImageUrlPart
562
+ export const FinishReason: Schema.Literal<[
563
+ "stop",
564
+ "length",
565
+ "content-filter",
566
+ "tool-calls",
567
+ "error",
568
+ "other",
569
+ "unknown"
570
+ ]> = Schema.Literal(
571
+ "stop",
572
+ "length",
573
+ "content-filter",
574
+ "tool-calls",
575
+ "error",
576
+ "other",
577
+ "unknown"
578
+ )
139
579
 
140
580
  /**
141
581
  * @since 1.0.0
142
- * @category parts
582
+ * @category Models
143
583
  */
144
- export const Part: Schema.Union<[
145
- typeof TextPart,
146
- typeof ToolCallPart,
147
- typeof ImageUrlPart
148
- ]> = Schema.Union(TextPart, ToolCallPart, ImageUrlPart)
584
+ export type FinishReason = typeof FinishReason.Type
149
585
 
150
586
  /**
587
+ * Represents information about the number of tokens used by the model to
588
+ * generate a response.
589
+ *
151
590
  * @since 1.0.0
152
- * @category models
591
+ * @category Models
153
592
  */
154
- export class AiResponse extends Schema.Class<AiResponse>("@effect/ai/AiResponse")({
155
- role: AiRole.AiRole,
156
- parts: Schema.Chunk(Part)
157
- }) {
593
+ export class Usage extends Schema.Class<Usage>(
594
+ "@effect/ai/AiResponse/Usage"
595
+ )({
158
596
  /**
159
- * @since 1.0.0
597
+ * The number of tokens sent in the request to the model.
160
598
  */
161
- readonly [TypeId]: TypeId = TypeId
599
+ inputTokens: Schema.Number,
162
600
  /**
163
- * @since 1.0.0
601
+ * The number of tokens that the model generated for the request.
164
602
  */
165
- static is(u: unknown): u is AiResponse {
166
- return Predicate.hasProperty(u, TypeId)
167
- }
603
+ outputTokens: Schema.Number,
168
604
  /**
169
- * @since 1.0.0
605
+ * The total of number of input tokens and output tokens generated by the
606
+ * model.
170
607
  */
171
- static readonly empty = new AiResponse({
172
- role: AiRole.model,
173
- parts: Chunk.empty()
174
- })
608
+ totalTokens: Schema.Number,
175
609
  /**
176
- * @since 1.0.0
610
+ * The number of reasoning tokens that the model used to generate the output
611
+ * for the request.
177
612
  */
178
- static fromText(options: { role: AiRole.AiRole; content: string }): AiResponse {
179
- return new AiResponse({
180
- role: options.role,
181
- parts: Chunk.of(TextPart.fromContent(options.content))
182
- }, constDisableValidation)
183
- }
613
+ reasoningTokens: Schema.Number,
184
614
  /**
185
- * @since 1.0.0
615
+ * The number of input tokens read from the prompt cache for the request.
186
616
  */
187
- get text(): string {
188
- let text = ""
189
- let found = false
190
- for (const part of this.parts) {
191
- if (part._tag === "Text") {
192
- text += found ? "\n\n" + part.content : part.content
193
- found = true
194
- }
195
- }
196
- return text
197
- }
617
+ cacheReadInputTokens: Schema.Number,
198
618
  /**
199
- * @since 1.0.0
619
+ * The number of input tokens written to the prompt cache for the request.
200
620
  */
201
- get imageUrl(): Option.Option<string> {
202
- for (const part of this.parts) {
203
- if (part._tag === "ImageUrl") {
204
- return Option.some(part.url)
205
- }
206
- }
207
- return Option.none()
208
- }
621
+ cacheWriteInputTokens: Schema.Number
622
+ }) {}
623
+
624
+ /**
625
+ * Represents additional provider-specific metadata that was returned by the
626
+ * model. Specific providers will use module augmentation to add their own
627
+ * specific provider metadata.
628
+ *
629
+ * The outer record is keyed by provider name, while the inner record is keyed
630
+ * by the names of the provider-specific metadata properties.
631
+ *
632
+ * For example:
633
+ *
634
+ * ```ts
635
+ * const providerMeta = {
636
+ * "amazon-bedrock": {
637
+ * // Provider specific metadata
638
+ * }
639
+ * }
640
+ * ```
641
+ *
642
+ * @since 1.0.0
643
+ * @category Models
644
+ */
645
+ export interface ProviderMetadata {}
646
+
647
+ /**
648
+ * Represents the final part of a response generated by a large language model.
649
+ *
650
+ * Contains useful information such as tokens used as part of the interaction
651
+ * with the model.
652
+ *
653
+ * @since 1.0.0
654
+ * @category Models
655
+ */
656
+ export class FinishPart extends Schema.TaggedClass<FinishPart>(
657
+ "@effect/ai/AiResponse/FinishPart"
658
+ )("FinishPart", {
209
659
  /**
210
- * @since 1.0.0
660
+ * The usage information for the response.
211
661
  */
212
- withToolCallsJson(
213
- calls: Iterable<{
214
- readonly id: string
215
- readonly name: string
216
- readonly params: string
217
- }>
218
- ): Effect.Effect<AiResponse, AiError> {
219
- return Effect.forEach(calls, (call): Effect.Effect<Part, AiError> => ToolCallPart.fromJson(call)).pipe(
220
- Effect.map((parts) =>
221
- new AiResponse({
222
- role: this.role,
223
- parts: Chunk.appendAll(this.parts, Chunk.unsafeFromArray(parts))
224
- }, constDisableValidation)
225
- )
226
- )
227
- }
662
+ usage: Usage,
228
663
  /**
229
- * @since 1.0.0
664
+ * The reason the model finished generating a response.
230
665
  */
231
- withToolCallsUnknown(
232
- calls: Iterable<{
233
- readonly id: string
234
- readonly name: string
235
- readonly params: unknown
236
- }>
237
- ): AiResponse {
238
- return new AiResponse({
239
- role: this.role,
240
- parts: Chunk.fromIterable(calls).pipe(
241
- Chunk.map((part) => ToolCallPart.fromUnknown(part))
242
- )
243
- }, constDisableValidation)
244
- }
666
+ reason: FinishReason,
667
+ /**
668
+ * Provider-specific metadata associated with the response.
669
+ */
670
+ providerMetadata: Schema.optionalWith(
671
+ Schema.Record({
672
+ key: Schema.String,
673
+ value: Schema.Record({ key: Schema.String, value: Schema.Unknown })
674
+ }),
675
+ { default: () => ({}) }
676
+ )
677
+ }) {
245
678
  /**
246
679
  * @since 1.0.0
247
680
  */
248
- concat(that: AiResponse): AiResponse {
249
- if (Chunk.isEmpty(that.parts)) {
250
- return this
251
- }
252
- const lastPart = Chunk.last(this.parts)
253
- if (Option.isNone(lastPart)) {
254
- return that
255
- }
256
- const newParts: Array<Part> = []
257
- let content = lastPart.value._tag === "Text" ? lastPart.value.content : ""
258
- for (const part of that.parts) {
259
- if (part._tag === "Text") {
260
- content += part.content
261
- }
262
- }
263
- if (content.length > 0) {
264
- newParts.push(TextPart.fromContent(content))
265
- }
266
- return newParts.length === 0 ? this : new AiResponse({
267
- role: that.role,
268
- parts: Chunk.appendAll(
269
- Chunk.dropRight(this.parts, 1),
270
- Chunk.unsafeFromArray(newParts)
271
- )
272
- }, constDisableValidation)
273
- }
681
+ readonly [PartTypeId]: PartTypeId = PartTypeId
274
682
  }
275
683
 
684
+ /**
685
+ * Represents an single part of a response received from a large language model.
686
+ *
687
+ * @since 1.0.0
688
+ * @category Models
689
+ */
690
+ export const Part: Schema.Union<[
691
+ typeof TextPart,
692
+ typeof ReasoningPart,
693
+ typeof RedactedReasoningPart,
694
+ typeof ToolCallPart,
695
+ typeof MetadataPart,
696
+ typeof FinishPart
697
+ ]> = Schema.Union(
698
+ TextPart,
699
+ ReasoningPart,
700
+ RedactedReasoningPart,
701
+ ToolCallPart,
702
+ MetadataPart,
703
+ FinishPart
704
+ )
705
+
706
+ /**
707
+ * @since 1.0.0
708
+ * @category Models
709
+ */
710
+ export type Part = typeof Part.Type
711
+
276
712
  /**
277
713
  * @since 1.0.0
278
- * @category tools
714
+ * @category Guards
279
715
  */
280
- export const WithResolvedTypeId: unique symbol = Symbol("@effect/ai/AiResponse/WithResolved")
716
+ export const is = (u: unknown): u is AiResponse => Predicate.hasProperty(u, TypeId)
281
717
 
282
718
  /**
283
719
  * @since 1.0.0
284
- * @category tools
720
+ * @category Guards
285
721
  */
286
- export type WithResolvedTypeId = typeof WithResolvedTypeId
722
+ export const isPart = (u: unknown): u is Part => Predicate.hasProperty(u, PartTypeId)
287
723
 
288
724
  /**
289
725
  * @since 1.0.0
290
- * @category tools
726
+ * @category Guards
291
727
  */
292
- export class WithResolved<A> extends Data.Class<{
293
- readonly response: AiResponse
294
- readonly resolved: ReadonlyMap<ToolCallId, A>
295
- readonly encoded: ReadonlyMap<ToolCallId, unknown>
296
- }> {
728
+ export const isStructured = (u: unknown): u is WithStructuredOutput<any> =>
729
+ Predicate.hasProperty(u, StructuredResponseTypeId)
730
+
731
+ /**
732
+ * @since 1.0.0
733
+ * @category Guards
734
+ */
735
+ export const hasToolCallResults = (u: unknown): u is WithToolCallResults<any> =>
736
+ Predicate.hasProperty(u, WithToolCallResultsTypeId)
737
+
738
+ /**
739
+ * @since 1.0.0
740
+ * @category Constructors
741
+ */
742
+ export const empty: AiResponse = new AiResponse(
743
+ { parts: [] },
744
+ constDisableValidation
745
+ )
746
+
747
+ /**
748
+ * Combines two responses into a single response.
749
+ *
750
+ * @since 1.0.0
751
+ * @category Combination
752
+ */
753
+ export const merge: {
297
754
  /**
755
+ * Combines two responses into a single response.
756
+ *
298
757
  * @since 1.0.0
758
+ * @category Combination
299
759
  */
300
- readonly [WithResolvedTypeId]: WithResolvedTypeId = WithResolvedTypeId
760
+ (other: AiResponse): (self: AiResponse) => AiResponse
301
761
  /**
762
+ * Combines two responses into a single response.
763
+ *
302
764
  * @since 1.0.0
765
+ * @category Combination
303
766
  */
304
- static is<A>(u: unknown): u is WithResolved<A> {
305
- return Predicate.hasProperty(u, WithResolvedTypeId)
306
- }
767
+ (self: AiResponse, other: AiResponse): AiResponse
768
+ } = dual<
307
769
  /**
770
+ * Combines two responses into a single response.
771
+ *
308
772
  * @since 1.0.0
773
+ * @category Combination
309
774
  */
310
- static readonly empty = new WithResolved<never>({
311
- response: AiResponse.empty,
312
- resolved: new Map<never, never>(),
313
- encoded: new Map<never, never>()
314
- })
775
+ (other: AiResponse) => (self: AiResponse) => AiResponse,
315
776
  /**
777
+ * Combines two responses into a single response.
778
+ *
316
779
  * @since 1.0.0
780
+ * @category Combination
317
781
  */
318
- get values(): Array<A> {
319
- return Array.from(this.resolved.values())
782
+ (self: AiResponse, other: AiResponse) => AiResponse
783
+ >(2, (self, other) => {
784
+ if (other.parts.length === 0) {
785
+ return new AiResponse({
786
+ parts: self.parts
787
+ }, constDisableValidation)
320
788
  }
789
+ if (self.parts.length === 0) {
790
+ return new AiResponse({
791
+ parts: other.parts
792
+ }, constDisableValidation)
793
+ }
794
+ const lastPart = self.parts[self.parts.length - 1]
795
+ const newParts: Array<Part> = []
796
+ let text = lastPart._tag === "TextPart" ? lastPart.text : ""
797
+ for (const part of other.parts) {
798
+ if (part._tag === "TextPart") {
799
+ text += part.text
800
+ }
801
+ }
802
+ if (text.length > 0) {
803
+ newParts.push(new TextPart({ text }, constDisableValidation))
804
+ }
805
+ return newParts.length === 0 ? self : new AiResponse({
806
+ parts: [...self.parts.slice(0, self.parts.length - 1), ...newParts]
807
+ }, constDisableValidation)
808
+ })
809
+
810
+ /**
811
+ * Adds the specified tool calls to the provided `AiResponse`.
812
+ *
813
+ * **NOTE**: With this method, the tool call parameters will be parsed as a
814
+ * JSON string. If your tool call parameters are already parsed, use
815
+ * `AiResponse.withToolCallsUnknown`.
816
+ *
817
+ * @since 1.0.0
818
+ * @category Combination
819
+ */
820
+ export const withToolCallsJson: {
321
821
  /**
822
+ * Adds the specified tool calls to the provided `AiResponse`.
823
+ *
824
+ * **NOTE**: With this method, the tool call parameters will be parsed as a
825
+ * JSON string. If your tool call parameters are already parsed, use
826
+ * `AiResponse.withToolCallsUnknown`.
827
+ *
322
828
  * @since 1.0.0
829
+ * @category Combination
323
830
  */
324
- get value(): Option.Option<A> {
325
- return Iterable.head(this.resolved.values())
326
- }
831
+ (
832
+ toolCalls: Iterable<{
833
+ readonly id: string
834
+ readonly name: string
835
+ readonly params: string
836
+ }>
837
+ ): (self: AiResponse) => Effect.Effect<AiResponse, AiError>
327
838
  /**
839
+ * Adds the specified tool calls to the provided `AiResponse`.
840
+ *
841
+ * **NOTE**: With this method, the tool call parameters will be parsed as a
842
+ * JSON string. If your tool call parameters are already parsed, use
843
+ * `AiResponse.withToolCallsUnknown`.
844
+ *
328
845
  * @since 1.0.0
846
+ * @category Combination
329
847
  */
330
- get unsafeValue(): A {
331
- return Iterable.unsafeHead(this.resolved.values())
332
- }
848
+ (
849
+ self: AiResponse,
850
+ toolCalls: Iterable<{
851
+ readonly id: string
852
+ readonly name: string
853
+ readonly params: string
854
+ }>
855
+ ): Effect.Effect<AiResponse, AiError>
856
+ } = dual<
333
857
  /**
858
+ * Adds the specified tool calls to the provided `AiResponse`.
859
+ *
860
+ * **NOTE**: With this method, the tool call parameters will be parsed as a
861
+ * JSON string. If your tool call parameters are already parsed, use
862
+ * `AiResponse.withToolCallsUnknown`.
863
+ *
334
864
  * @since 1.0.0
865
+ * @category Combination
335
866
  */
336
- concat<B>(that: WithResolved<B>): WithResolved<A | B> {
337
- return new WithResolved({
338
- response: this.response.concat(that.response),
339
- resolved: that.resolved.size === 0 ? this.resolved : new Map([...this.resolved, ...that.resolved] as any),
340
- encoded: that.encoded.size === 0 ? this.encoded : new Map([...this.encoded, ...that.encoded] as any)
341
- })
342
- }
343
- }
867
+ (
868
+ toolCalls: Iterable<{
869
+ readonly id: string
870
+ readonly name: string
871
+ readonly params: string
872
+ }>
873
+ ) => (self: AiResponse) => Effect.Effect<AiResponse, AiError>,
874
+ /**
875
+ * Adds the specified tool calls to the provided `AiResponse`.
876
+ *
877
+ * **NOTE**: With this method, the tool call parameters will be parsed as a
878
+ * JSON string. If your tool call parameters are already parsed, use
879
+ * `AiResponse.withToolCallsUnknown`.
880
+ *
881
+ * @since 1.0.0
882
+ * @category Combination
883
+ */
884
+ (
885
+ self: AiResponse,
886
+ toolCalls: Iterable<{
887
+ readonly id: string
888
+ readonly name: string
889
+ readonly params: string
890
+ }>
891
+ ) => Effect.Effect<AiResponse, AiError>
892
+ >(2, (self, toolCalls) =>
893
+ Effect.forEach(toolCalls, (toolCall) => ToolCallPart.fromJson(toolCall)).pipe(
894
+ Effect.map((parts) =>
895
+ new AiResponse({
896
+ parts: [...self.parts, ...parts]
897
+ }, constDisableValidation)
898
+ )
899
+ ))