@effect/ai 0.8.1 → 0.8.2
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/AiTelemetry/package.json +6 -0
- package/Embeddings/package.json +6 -0
- package/dist/cjs/AiChat.js +90 -103
- package/dist/cjs/AiChat.js.map +1 -1
- package/dist/cjs/AiTelemetry.js +52 -0
- package/dist/cjs/AiTelemetry.js.map +1 -0
- package/dist/cjs/Completions.js +50 -46
- package/dist/cjs/Completions.js.map +1 -1
- package/dist/cjs/Embeddings.js +92 -0
- package/dist/cjs/Embeddings.js.map +1 -0
- package/dist/cjs/index.js +5 -1
- package/dist/dts/AiChat.d.ts +12 -1
- package/dist/dts/AiChat.d.ts.map +1 -1
- package/dist/dts/AiTelemetry.d.ts +205 -0
- package/dist/dts/AiTelemetry.d.ts.map +1 -0
- package/dist/dts/Completions.d.ts +29 -8
- package/dist/dts/Completions.d.ts.map +1 -1
- package/dist/dts/Embeddings.d.ts +59 -0
- package/dist/dts/Embeddings.d.ts.map +1 -0
- package/dist/dts/index.d.ts +8 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/AiChat.js +90 -102
- package/dist/esm/AiChat.js.map +1 -1
- package/dist/esm/AiTelemetry.js +42 -0
- package/dist/esm/AiTelemetry.js.map +1 -0
- package/dist/esm/Completions.js +50 -46
- package/dist/esm/Completions.js.map +1 -1
- package/dist/esm/Embeddings.js +80 -0
- package/dist/esm/Embeddings.js.map +1 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +20 -3
- package/src/AiChat.ts +160 -169
- package/src/AiTelemetry.ts +297 -0
- package/src/Completions.ts +140 -107
- package/src/Embeddings.ts +143 -0
- package/src/index.ts +10 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { dual } from "effect/Function"
|
|
6
|
+
import * as Predicate from "effect/Predicate"
|
|
7
|
+
import * as String from "effect/String"
|
|
8
|
+
import type { Span } from "effect/Tracer"
|
|
9
|
+
import type { Simplify } from "effect/Types"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The attributes used to describe telemetry in the context of Generative
|
|
13
|
+
* Artificial Intelligence (GenAI) Models requests and responses.
|
|
14
|
+
*
|
|
15
|
+
* {@see https://opentelemetry.io/docs/specs/semconv/attributes-registry/gen-ai/}
|
|
16
|
+
*
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category models
|
|
19
|
+
*/
|
|
20
|
+
export type GenAITelemetryAttributes = Simplify<
|
|
21
|
+
& GenAI.AttributesWithPrefix<GenAI.BaseAttributes, "gen_ai">
|
|
22
|
+
& GenAI.AttributesWithPrefix<GenAI.OperationAttributes, "gen_ai.operation">
|
|
23
|
+
& GenAI.AttributesWithPrefix<GenAI.TokenAttributes, "gen_ai.token">
|
|
24
|
+
& GenAI.AttributesWithPrefix<GenAI.UsageAttributes, "gen_ai.usage">
|
|
25
|
+
& GenAI.AttributesWithPrefix<GenAI.RequestAttributes, "gen_ai.request">
|
|
26
|
+
& GenAI.AttributesWithPrefix<GenAI.ResponseAttributes, "gen_ai.response">
|
|
27
|
+
>
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @since 1.0.0
|
|
31
|
+
* @category models
|
|
32
|
+
*/
|
|
33
|
+
export declare namespace GenAI {
|
|
34
|
+
/**
|
|
35
|
+
* All telemetry attributes which are part of the GenAI specification.
|
|
36
|
+
*
|
|
37
|
+
* @since 1.0.0
|
|
38
|
+
* @category models
|
|
39
|
+
*/
|
|
40
|
+
export type AllAttributes =
|
|
41
|
+
& BaseAttributes
|
|
42
|
+
& OperationAttributes
|
|
43
|
+
& TokenAttributes
|
|
44
|
+
& UsageAttributes
|
|
45
|
+
& RequestAttributes
|
|
46
|
+
& ResponseAttributes
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
50
|
+
* namespaced by `gen_ai`.
|
|
51
|
+
*
|
|
52
|
+
* @since 1.0.0
|
|
53
|
+
* @category models
|
|
54
|
+
*/
|
|
55
|
+
export interface BaseAttributes {
|
|
56
|
+
/**
|
|
57
|
+
* The Generative AI product as identified by the client or server
|
|
58
|
+
* instrumentation.
|
|
59
|
+
*/
|
|
60
|
+
readonly system?: (string & {}) | WellKnownSystem | null | undefined
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
65
|
+
* namespaced by `gen_ai.operation`.
|
|
66
|
+
*
|
|
67
|
+
* @since 1.0.0
|
|
68
|
+
* @category models
|
|
69
|
+
*/
|
|
70
|
+
export interface OperationAttributes {
|
|
71
|
+
readonly name?: (string & {}) | WellKnownOperationName | null | undefined
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
76
|
+
* namespaced by `gen_ai.token`.
|
|
77
|
+
*
|
|
78
|
+
* @since 1.0.0
|
|
79
|
+
* @category models
|
|
80
|
+
*/
|
|
81
|
+
export interface TokenAttributes {
|
|
82
|
+
readonly type?: string | null | undefined
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
87
|
+
* namespaced by `gen_ai.usage`.
|
|
88
|
+
*
|
|
89
|
+
* @since 1.0.0
|
|
90
|
+
* @category models
|
|
91
|
+
*/
|
|
92
|
+
export interface UsageAttributes {
|
|
93
|
+
readonly inputTokens?: number | null | undefined
|
|
94
|
+
readonly outputTokens?: number | null | undefined
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
99
|
+
* namespaced by `gen_ai.request`.
|
|
100
|
+
*
|
|
101
|
+
* @since 1.0.0
|
|
102
|
+
* @category models
|
|
103
|
+
*/
|
|
104
|
+
export interface RequestAttributes {
|
|
105
|
+
/**
|
|
106
|
+
* The name of the GenAI model a request is being made to.
|
|
107
|
+
*/
|
|
108
|
+
readonly model?: string | null | undefined
|
|
109
|
+
/**
|
|
110
|
+
* The temperature setting for the GenAI request.
|
|
111
|
+
*/
|
|
112
|
+
readonly temperature?: number | null | undefined
|
|
113
|
+
/**
|
|
114
|
+
* The temperature setting for the GenAI request.
|
|
115
|
+
*/
|
|
116
|
+
readonly topK?: number | null | undefined
|
|
117
|
+
/**
|
|
118
|
+
* The top_k sampling setting for the GenAI request.
|
|
119
|
+
*/
|
|
120
|
+
readonly topP?: number | null | undefined
|
|
121
|
+
/**
|
|
122
|
+
* The top_p sampling setting for the GenAI request.
|
|
123
|
+
*/
|
|
124
|
+
readonly maxTokens?: number | null | undefined
|
|
125
|
+
/**
|
|
126
|
+
* The encoding formats requested in an embeddings operation, if specified.
|
|
127
|
+
*/
|
|
128
|
+
readonly encodingFormats?: ReadonlyArray<string> | null | undefined
|
|
129
|
+
/**
|
|
130
|
+
* List of sequences that the model will use to stop generating further
|
|
131
|
+
* tokens.
|
|
132
|
+
*/
|
|
133
|
+
readonly stopSequences?: ReadonlyArray<string> | null | undefined
|
|
134
|
+
/**
|
|
135
|
+
* The frequency penalty setting for the GenAI request.
|
|
136
|
+
*/
|
|
137
|
+
readonly frequencyPenalty?: number | null | undefined
|
|
138
|
+
/**
|
|
139
|
+
* The presence penalty setting for the GenAI request.
|
|
140
|
+
*/
|
|
141
|
+
readonly presencePenalty?: number | null | undefined
|
|
142
|
+
/**
|
|
143
|
+
* The seed setting for the GenAI request. Requests with same seed value
|
|
144
|
+
* are more likely to return same result.
|
|
145
|
+
*/
|
|
146
|
+
readonly seed?: number | null | undefined
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Telemetry attributes which are part of the GenAI specification and are
|
|
151
|
+
* namespaced by `gen_ai.response`.
|
|
152
|
+
*
|
|
153
|
+
* @since 1.0.0
|
|
154
|
+
* @category models
|
|
155
|
+
*/
|
|
156
|
+
export interface ResponseAttributes {
|
|
157
|
+
/**
|
|
158
|
+
* The unique identifier for the completion.
|
|
159
|
+
*/
|
|
160
|
+
readonly id?: string | null | undefined
|
|
161
|
+
/**
|
|
162
|
+
* The name of the model that generated the response.
|
|
163
|
+
*/
|
|
164
|
+
readonly model?: string | null | undefined
|
|
165
|
+
/**
|
|
166
|
+
* Array of reasons the model stopped generating tokens, corresponding to
|
|
167
|
+
* each generation received.
|
|
168
|
+
*/
|
|
169
|
+
readonly finishReasons?: ReadonlyArray<string> | null | undefined
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* The `gen_ai.operation.name` attribute has the following list of well-known
|
|
174
|
+
* values.
|
|
175
|
+
*
|
|
176
|
+
* If one of them applies, then the respective value **MUST** be used;
|
|
177
|
+
* otherwise, a custom value **MAY** be used.
|
|
178
|
+
*
|
|
179
|
+
* @since 1.0.0
|
|
180
|
+
* @category models
|
|
181
|
+
*/
|
|
182
|
+
export type WellKnownOperationName = "chat" | "embeddings" | "text_completion"
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The `gen_ai.system` attribute has the following list of well-known values.
|
|
186
|
+
*
|
|
187
|
+
* If one of them applies, then the respective value **MUST** be used;
|
|
188
|
+
* otherwise, a custom value **MAY** be used.
|
|
189
|
+
*
|
|
190
|
+
* @since 1.0.0
|
|
191
|
+
* @category models
|
|
192
|
+
*/
|
|
193
|
+
export type WellKnownSystem =
|
|
194
|
+
| "anthropic"
|
|
195
|
+
| "aws.bedrock"
|
|
196
|
+
| "az.ai.inference"
|
|
197
|
+
| "az.ai.openai"
|
|
198
|
+
| "cohere"
|
|
199
|
+
| "deepseek"
|
|
200
|
+
| "gemini"
|
|
201
|
+
| "groq"
|
|
202
|
+
| "ibm.watsonx.ai"
|
|
203
|
+
| "mistral_ai"
|
|
204
|
+
| "openai"
|
|
205
|
+
| "perplexity"
|
|
206
|
+
| "vertex_ai"
|
|
207
|
+
| "xai"
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @since 1.0.0
|
|
211
|
+
* @category models
|
|
212
|
+
*/
|
|
213
|
+
export type AttributesWithPrefix<Attributes extends Record<string, any>, Prefix extends string> = {
|
|
214
|
+
[Name in keyof Attributes as `${Prefix}.${FormatAttributeName<Name>}`]: Attributes[Name]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @since 1.0.0
|
|
219
|
+
* @category models
|
|
220
|
+
*/
|
|
221
|
+
export type FormatAttributeName<T extends string | number | symbol> = T extends string ?
|
|
222
|
+
T extends `${infer First}${infer Rest}`
|
|
223
|
+
? `${First extends Uppercase<First> ? "_" : ""}${Lowercase<First>}${FormatAttributeName<Rest>}`
|
|
224
|
+
: T :
|
|
225
|
+
never
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* @since 1.0.0
|
|
230
|
+
* @category utilities
|
|
231
|
+
*/
|
|
232
|
+
export const addSpanAttributes = (
|
|
233
|
+
keyPrefix: string,
|
|
234
|
+
transformKey: (key: string) => string
|
|
235
|
+
) =>
|
|
236
|
+
<Attributes extends Record<string, any>>(span: Span, attributes: Attributes): void => {
|
|
237
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
238
|
+
if (Predicate.isNotNullable(value)) {
|
|
239
|
+
span.attribute(`${keyPrefix}.${transformKey(key)}`, value)
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const addSpanBaseAttributes = addSpanAttributes("gen_ai", String.camelToSnake)<GenAI.BaseAttributes>
|
|
245
|
+
const addSpanOperationAttributes = addSpanAttributes("gen_ai.operation", String.camelToSnake)<GenAI.OperationAttributes>
|
|
246
|
+
const addSpanRequestAttributes = addSpanAttributes("gen_ai.request", String.camelToSnake)<GenAI.RequestAttributes>
|
|
247
|
+
const addSpanResponseAttributes = addSpanAttributes("gen_ai.response", String.camelToSnake)<GenAI.ResponseAttributes>
|
|
248
|
+
const addSpanTokenAttributes = addSpanAttributes("gen_ai.token", String.camelToSnake)<GenAI.TokenAttributes>
|
|
249
|
+
const addSpanUsageAttributes = addSpanAttributes("gen_ai.usage", String.camelToSnake)<GenAI.UsageAttributes>
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* @since 1.0.0
|
|
253
|
+
* @since models
|
|
254
|
+
*/
|
|
255
|
+
export type GenAITelemetryAttributeOptions = GenAI.BaseAttributes & {
|
|
256
|
+
readonly operation?: GenAI.OperationAttributes | undefined
|
|
257
|
+
readonly request?: GenAI.RequestAttributes | undefined
|
|
258
|
+
readonly response?: GenAI.ResponseAttributes | undefined
|
|
259
|
+
readonly token?: GenAI.TokenAttributes | undefined
|
|
260
|
+
readonly usage?: GenAI.UsageAttributes | undefined
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Applies the specified GenAI telemetry attributes to the provided `Span`.
|
|
265
|
+
*
|
|
266
|
+
* **NOTE**: This method will mutate the `Span` **in-place**.
|
|
267
|
+
*
|
|
268
|
+
* @since 1.0.0
|
|
269
|
+
* @since utilities
|
|
270
|
+
*/
|
|
271
|
+
export const addGenAIAnnotations = dual<
|
|
272
|
+
/**
|
|
273
|
+
* Applies the specified GenAI telemetry attributes to the provided `Span`.
|
|
274
|
+
*
|
|
275
|
+
* **NOTE**: This method will mutate the `Span` **in-place**.
|
|
276
|
+
*
|
|
277
|
+
* @since 1.0.0
|
|
278
|
+
* @since utilities
|
|
279
|
+
*/
|
|
280
|
+
(options: GenAITelemetryAttributeOptions) => (span: Span) => void,
|
|
281
|
+
/**
|
|
282
|
+
* Applies the specified GenAI telemetry attributes to the provided `Span`.
|
|
283
|
+
*
|
|
284
|
+
* **NOTE**: This method will mutate the `Span` **in-place**.
|
|
285
|
+
*
|
|
286
|
+
* @since 1.0.0
|
|
287
|
+
* @since utilities
|
|
288
|
+
*/
|
|
289
|
+
(span: Span, options: GenAITelemetryAttributeOptions) => void
|
|
290
|
+
>(2, (span, options) => {
|
|
291
|
+
addSpanBaseAttributes(span, { system: options.system })
|
|
292
|
+
if (Predicate.isNotNullable(options.operation)) addSpanOperationAttributes(span, options.operation)
|
|
293
|
+
if (Predicate.isNotNullable(options.request)) addSpanRequestAttributes(span, options.request)
|
|
294
|
+
if (Predicate.isNotNullable(options.response)) addSpanResponseAttributes(span, options.response)
|
|
295
|
+
if (Predicate.isNotNullable(options.token)) addSpanTokenAttributes(span, options.token)
|
|
296
|
+
if (Predicate.isNotNullable(options.usage)) addSpanUsageAttributes(span, options.usage)
|
|
297
|
+
})
|
package/src/Completions.ts
CHANGED
|
@@ -10,6 +10,7 @@ import * as Option from "effect/Option"
|
|
|
10
10
|
import * as Schema from "effect/Schema"
|
|
11
11
|
import * as AST from "effect/SchemaAST"
|
|
12
12
|
import * as Stream from "effect/Stream"
|
|
13
|
+
import type { Span } from "effect/Tracer"
|
|
13
14
|
import type { Concurrency } from "effect/Types"
|
|
14
15
|
import { AiError } from "./AiError.js"
|
|
15
16
|
import type { Message } from "./AiInput.js"
|
|
@@ -34,26 +35,44 @@ export class Completions extends Context.Tag("@effect/ai/Completions")<
|
|
|
34
35
|
export declare namespace Completions {
|
|
35
36
|
/**
|
|
36
37
|
* @since 1.0.0
|
|
37
|
-
* @models
|
|
38
|
+
* @category models
|
|
38
39
|
*/
|
|
39
|
-
export
|
|
40
|
-
|
|
40
|
+
export type StructuredSchema<A, I, R> = TaggedSchema<A, I, R> | IdentifiedSchema<A, I, R>
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @since 1.0.0
|
|
44
|
+
* @category models
|
|
45
|
+
*/
|
|
46
|
+
export interface TaggedSchema<A, I, R> extends Schema.Schema<A, I, R> {
|
|
47
|
+
readonly _tag: string
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @since 1.0.0
|
|
52
|
+
* @category models
|
|
53
|
+
*/
|
|
54
|
+
export interface IdentifiedSchema<A, I, R> extends Schema.Schema<A, I, R> {
|
|
41
55
|
readonly identifier: string
|
|
42
56
|
}
|
|
43
57
|
|
|
44
58
|
/**
|
|
45
59
|
* @since 1.0.0
|
|
46
|
-
* @models
|
|
60
|
+
* @category models
|
|
47
61
|
*/
|
|
48
62
|
export interface Service {
|
|
49
63
|
readonly create: (input: AiInput.Input) => Effect.Effect<AiResponse, AiError>
|
|
50
64
|
readonly stream: (input: AiInput.Input) => Stream.Stream<AiResponse, AiError>
|
|
51
|
-
readonly structured:
|
|
52
|
-
options: {
|
|
65
|
+
readonly structured: {
|
|
66
|
+
<A, I, R>(options: {
|
|
53
67
|
readonly input: AiInput.Input
|
|
54
68
|
readonly schema: StructuredSchema<A, I, R>
|
|
55
|
-
}
|
|
56
|
-
|
|
69
|
+
}): Effect.Effect<WithResolved<A>, AiError, R>
|
|
70
|
+
<A, I, R>(options: {
|
|
71
|
+
readonly input: AiInput.Input
|
|
72
|
+
readonly schema: Schema.Schema<A, I, R>
|
|
73
|
+
readonly toolCallId: string
|
|
74
|
+
}): Effect.Effect<WithResolved<A>, AiError, R>
|
|
75
|
+
}
|
|
57
76
|
readonly toolkit: <Tools extends AiToolkit.Tool.AnySchema>(
|
|
58
77
|
options: {
|
|
59
78
|
readonly input: AiInput.Input
|
|
@@ -114,6 +133,7 @@ export const make = (options: {
|
|
|
114
133
|
readonly structured: boolean
|
|
115
134
|
}>
|
|
116
135
|
readonly required: boolean | string
|
|
136
|
+
readonly span: Span
|
|
117
137
|
}) => Effect.Effect<AiResponse, AiError>
|
|
118
138
|
readonly stream: (options: {
|
|
119
139
|
readonly system: Option.Option<string>
|
|
@@ -125,91 +145,103 @@ export const make = (options: {
|
|
|
125
145
|
readonly structured: boolean
|
|
126
146
|
}>
|
|
127
147
|
readonly required: boolean | string
|
|
148
|
+
readonly span: Span
|
|
128
149
|
}) => Stream.Stream<AiResponse, AiError>
|
|
129
150
|
}): Effect.Effect<Completions.Service> =>
|
|
130
151
|
Effect.map(Effect.serviceOption(AiInput.SystemInstruction), (parentSystem) => {
|
|
131
152
|
return Completions.of({
|
|
132
153
|
create(input) {
|
|
133
|
-
return Effect.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
return Effect.useSpan(
|
|
155
|
+
"Completions.create",
|
|
156
|
+
{ captureStackTrace: false },
|
|
157
|
+
(span) =>
|
|
158
|
+
Effect.serviceOption(AiInput.SystemInstruction).pipe(
|
|
159
|
+
Effect.flatMap((system) =>
|
|
160
|
+
options.create({
|
|
161
|
+
input: AiInput.make(input) as Chunk.NonEmptyChunk<Message>,
|
|
162
|
+
system: Option.orElse(system, () => parentSystem),
|
|
163
|
+
tools: [],
|
|
164
|
+
required: false,
|
|
165
|
+
span
|
|
166
|
+
})
|
|
167
|
+
)
|
|
168
|
+
)
|
|
143
169
|
)
|
|
144
170
|
},
|
|
145
171
|
stream(input_) {
|
|
146
172
|
const input = AiInput.make(input_)
|
|
147
|
-
return Effect.
|
|
148
|
-
Effect.
|
|
173
|
+
return Effect.makeSpanScoped("Completions.stream", { captureStackTrace: false }).pipe(
|
|
174
|
+
Effect.zip(Effect.serviceOption(AiInput.SystemInstruction)),
|
|
175
|
+
Effect.map(([span, system]) =>
|
|
149
176
|
options.stream({
|
|
150
177
|
input: input as Chunk.NonEmptyChunk<Message>,
|
|
151
178
|
system: Option.orElse(system, () => parentSystem),
|
|
152
179
|
tools: [],
|
|
153
|
-
required: false
|
|
180
|
+
required: false,
|
|
181
|
+
span
|
|
154
182
|
})
|
|
155
183
|
),
|
|
156
|
-
Stream.
|
|
157
|
-
Stream.withSpan("Completions.stream", { captureStackTrace: false })
|
|
184
|
+
Stream.unwrapScoped
|
|
158
185
|
)
|
|
159
186
|
},
|
|
160
187
|
structured(opts) {
|
|
161
188
|
const input = AiInput.make(opts.input)
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
description: `Failed to decode tool call '${toolId}' parameters`,
|
|
195
|
-
cause
|
|
196
|
-
}),
|
|
197
|
-
onSuccess: (resolved) =>
|
|
198
|
-
Effect.succeed(
|
|
199
|
-
new WithResolved({
|
|
200
|
-
response,
|
|
201
|
-
resolved: new Map([[toolCall.id, resolved]]),
|
|
202
|
-
encoded: new Map([[toolCall.id, toolCall.params]])
|
|
189
|
+
const decode = Schema.decodeUnknown(opts.schema)
|
|
190
|
+
const toolId = "toolCallId" in opts
|
|
191
|
+
? opts.toolCallId
|
|
192
|
+
: "_tag" in opts.schema
|
|
193
|
+
? opts.schema._tag
|
|
194
|
+
: opts.schema.identifier
|
|
195
|
+
return Effect.useSpan(
|
|
196
|
+
"Completions.structured",
|
|
197
|
+
{ attributes: { toolId }, captureStackTrace: false },
|
|
198
|
+
(span) =>
|
|
199
|
+
Effect.serviceOption(AiInput.SystemInstruction).pipe(
|
|
200
|
+
Effect.flatMap((system) =>
|
|
201
|
+
options.create({
|
|
202
|
+
input: input as Chunk.NonEmptyChunk<Message>,
|
|
203
|
+
system: Option.orElse(system, () => parentSystem),
|
|
204
|
+
tools: [convertTool(toolId, opts.schema, true)],
|
|
205
|
+
required: true,
|
|
206
|
+
span
|
|
207
|
+
})
|
|
208
|
+
),
|
|
209
|
+
Effect.flatMap((response) =>
|
|
210
|
+
Chunk.findFirst(
|
|
211
|
+
response.parts,
|
|
212
|
+
(part): part is ToolCallPart => part._tag === "ToolCall" && part.name === toolId
|
|
213
|
+
).pipe(
|
|
214
|
+
Option.match({
|
|
215
|
+
onNone: () =>
|
|
216
|
+
Effect.fail(
|
|
217
|
+
new AiError({
|
|
218
|
+
module: "Completions",
|
|
219
|
+
method: "structured",
|
|
220
|
+
description: `Tool call '${toolId}' not found in response`
|
|
203
221
|
})
|
|
204
|
-
)
|
|
222
|
+
),
|
|
223
|
+
onSome: (toolCall) =>
|
|
224
|
+
Effect.matchEffect(decode(toolCall.params), {
|
|
225
|
+
onFailure: (cause) =>
|
|
226
|
+
new AiError({
|
|
227
|
+
module: "Completions",
|
|
228
|
+
method: "structured",
|
|
229
|
+
description: `Failed to decode tool call '${toolId}' parameters`,
|
|
230
|
+
cause
|
|
231
|
+
}),
|
|
232
|
+
onSuccess: (resolved) =>
|
|
233
|
+
Effect.succeed(
|
|
234
|
+
new WithResolved({
|
|
235
|
+
response,
|
|
236
|
+
resolved: new Map([[toolCall.id, resolved]]),
|
|
237
|
+
encoded: new Map([[toolCall.id, toolCall.params]])
|
|
238
|
+
})
|
|
239
|
+
)
|
|
240
|
+
})
|
|
205
241
|
})
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
attributes: { tool: toolId },
|
|
209
|
-
captureStackTrace: false
|
|
210
|
-
})
|
|
242
|
+
)
|
|
243
|
+
)
|
|
211
244
|
)
|
|
212
|
-
)
|
|
213
245
|
)
|
|
214
246
|
},
|
|
215
247
|
toolkit({ concurrency, input: inputInput, required = false, tools }) {
|
|
@@ -221,26 +253,25 @@ export const make = (options: {
|
|
|
221
253
|
structured: boolean
|
|
222
254
|
}> = []
|
|
223
255
|
for (const [, tool] of tools.toolkit.tools) {
|
|
224
|
-
toolArr.push(convertTool(tool as any))
|
|
256
|
+
toolArr.push(convertTool(tool._tag, tool as any))
|
|
225
257
|
}
|
|
226
|
-
return Effect.
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
) as any
|
|
258
|
+
return Effect.useSpan(
|
|
259
|
+
"Completions.toolkit",
|
|
260
|
+
{ attributes: { concurrency, required }, captureStackTrace: false },
|
|
261
|
+
(span) =>
|
|
262
|
+
Effect.serviceOption(AiInput.SystemInstruction).pipe(
|
|
263
|
+
Effect.flatMap((system) =>
|
|
264
|
+
options.create({
|
|
265
|
+
input: input as Chunk.NonEmptyChunk<Message>,
|
|
266
|
+
system: Option.orElse(system, () => parentSystem),
|
|
267
|
+
tools: toolArr,
|
|
268
|
+
required: required as any,
|
|
269
|
+
span
|
|
270
|
+
})
|
|
271
|
+
),
|
|
272
|
+
Effect.flatMap((response) => resolveParts({ response, tools, concurrency, method: "toolkit" }))
|
|
273
|
+
) as any
|
|
274
|
+
)
|
|
244
275
|
},
|
|
245
276
|
toolkitStream({ concurrency, input, required = false, tools }) {
|
|
246
277
|
const toolArr: Array<{
|
|
@@ -250,38 +281,40 @@ export const make = (options: {
|
|
|
250
281
|
structured: boolean
|
|
251
282
|
}> = []
|
|
252
283
|
for (const [, tool] of tools.toolkit.tools) {
|
|
253
|
-
toolArr.push(convertTool(tool as any))
|
|
284
|
+
toolArr.push(convertTool(tool._tag, tool as any))
|
|
254
285
|
}
|
|
255
|
-
return Effect.
|
|
256
|
-
|
|
286
|
+
return Effect.makeSpanScoped("Completions.stream", {
|
|
287
|
+
captureStackTrace: false,
|
|
288
|
+
attributes: { required, concurrency }
|
|
289
|
+
}).pipe(
|
|
290
|
+
Effect.zip(Effect.serviceOption(AiInput.SystemInstruction)),
|
|
291
|
+
Effect.map(([span, system]) =>
|
|
257
292
|
options.stream({
|
|
258
293
|
input: AiInput.make(input) as Chunk.NonEmptyChunk<Message>,
|
|
259
294
|
system: Option.orElse(system, () => parentSystem),
|
|
260
295
|
tools: toolArr,
|
|
261
|
-
required: required as any
|
|
296
|
+
required: required as any,
|
|
297
|
+
span
|
|
262
298
|
})
|
|
263
299
|
),
|
|
264
|
-
Stream.
|
|
300
|
+
Stream.unwrapScoped,
|
|
265
301
|
Stream.mapEffect(
|
|
266
302
|
(chunk) => resolveParts({ response: chunk, tools, concurrency, method: "toolkitStream" }),
|
|
267
303
|
{ concurrency: "unbounded" }
|
|
268
|
-
)
|
|
269
|
-
Stream.withSpan("Completions.toolkitStream", {
|
|
270
|
-
captureStackTrace: false,
|
|
271
|
-
attributes: {
|
|
272
|
-
concurrency,
|
|
273
|
-
required
|
|
274
|
-
}
|
|
275
|
-
})
|
|
304
|
+
)
|
|
276
305
|
) as any
|
|
277
306
|
}
|
|
278
307
|
})
|
|
279
308
|
})
|
|
280
309
|
|
|
281
|
-
const convertTool = <A, I, R>(
|
|
282
|
-
name:
|
|
283
|
-
|
|
284
|
-
|
|
310
|
+
const convertTool = <A, I, R>(
|
|
311
|
+
name: string,
|
|
312
|
+
schema: Schema.Schema<A, I, R>,
|
|
313
|
+
structured = false
|
|
314
|
+
) => ({
|
|
315
|
+
name,
|
|
316
|
+
description: getDescription(schema.ast),
|
|
317
|
+
parameters: makeJsonSchema(schema.ast),
|
|
285
318
|
structured
|
|
286
319
|
})
|
|
287
320
|
|