@effect/ai-openai 4.0.0-beta.7 → 4.0.0-beta.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Generated.d.ts +66734 -37723
- package/dist/Generated.d.ts.map +1 -1
- package/dist/Generated.js +1 -1
- package/dist/Generated.js.map +1 -1
- package/dist/OpenAiClient.d.ts +167 -27
- package/dist/OpenAiClient.d.ts.map +1 -1
- package/dist/OpenAiClient.js +337 -44
- package/dist/OpenAiClient.js.map +1 -1
- package/dist/OpenAiClientGenerated.d.ts +91 -0
- package/dist/OpenAiClientGenerated.d.ts.map +1 -0
- package/dist/OpenAiClientGenerated.js +84 -0
- package/dist/OpenAiClientGenerated.js.map +1 -0
- package/dist/OpenAiConfig.d.ts +114 -10
- package/dist/OpenAiConfig.d.ts.map +1 -1
- package/dist/OpenAiConfig.js +68 -7
- package/dist/OpenAiConfig.js.map +1 -1
- package/dist/OpenAiEmbeddingModel.d.ts +213 -0
- package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
- package/dist/OpenAiEmbeddingModel.js +219 -0
- package/dist/OpenAiEmbeddingModel.js.map +1 -0
- package/dist/OpenAiError.d.ts +168 -35
- package/dist/OpenAiError.d.ts.map +1 -1
- package/dist/OpenAiError.js +1 -1
- package/dist/OpenAiLanguageModel.d.ts +384 -62
- package/dist/OpenAiLanguageModel.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.js +416 -166
- package/dist/OpenAiLanguageModel.js.map +1 -1
- package/dist/OpenAiSchema.d.ts +2298 -0
- package/dist/OpenAiSchema.d.ts.map +1 -0
- package/dist/OpenAiSchema.js +814 -0
- package/dist/OpenAiSchema.js.map +1 -0
- package/dist/OpenAiTelemetry.d.ts +59 -18
- package/dist/OpenAiTelemetry.d.ts.map +1 -1
- package/dist/OpenAiTelemetry.js +35 -8
- package/dist/OpenAiTelemetry.js.map +1 -1
- package/dist/OpenAiTool.d.ts +157 -62
- package/dist/OpenAiTool.d.ts.map +1 -1
- package/dist/OpenAiTool.js +134 -39
- package/dist/OpenAiTool.js.map +1 -1
- package/dist/index.d.ts +19 -33
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -33
- package/dist/index.js.map +1 -1
- package/dist/internal/errors.js +4 -4
- package/dist/internal/errors.js.map +1 -1
- package/package.json +3 -3
- package/src/Generated.ts +9858 -5044
- package/src/OpenAiClient.ts +513 -95
- package/src/OpenAiClientGenerated.ts +202 -0
- package/src/OpenAiConfig.ts +115 -11
- package/src/OpenAiEmbeddingModel.ts +357 -0
- package/src/OpenAiError.ts +170 -35
- package/src/OpenAiLanguageModel.ts +802 -167
- package/src/OpenAiSchema.ts +1289 -0
- package/src/OpenAiTelemetry.ts +81 -23
- package/src/OpenAiTool.ts +135 -40
- package/src/index.ts +22 -33
- package/src/internal/errors.ts +6 -4
|
@@ -1,28 +1,60 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OpenAI
|
|
2
|
+
* The `OpenAiLanguageModel` module provides the OpenAI Responses API
|
|
3
|
+
* implementation of Effect AI's `LanguageModel` service. It translates Effect
|
|
4
|
+
* AI prompts, files, tools, structured output requests, reasoning metadata, and
|
|
5
|
+
* provider options into OpenAI response requests, then converts OpenAI
|
|
6
|
+
* responses and streams back into Effect AI response parts.
|
|
3
7
|
*
|
|
4
|
-
*
|
|
5
|
-
* supporting text generation, structured output, tool calling, and streaming.
|
|
8
|
+
* **Mental model**
|
|
6
9
|
*
|
|
7
|
-
*
|
|
10
|
+
* `OpenAiClient` owns HTTP transport and provider calls. This module owns
|
|
11
|
+
* protocol translation: request assembly, tool choice conversion, structured
|
|
12
|
+
* output codecs, streaming event handling, response metadata, and GenAI
|
|
13
|
+
* telemetry annotations. {@link model}, {@link layer}, and {@link make} all
|
|
14
|
+
* build the same OpenAI-backed `LanguageModel.LanguageModel` service from a
|
|
15
|
+
* model id and optional request defaults.
|
|
16
|
+
*
|
|
17
|
+
* **Common tasks**
|
|
18
|
+
*
|
|
19
|
+
* - Provide an OpenAI-backed `LanguageModel.LanguageModel` from an existing
|
|
20
|
+
* `OpenAiClient`
|
|
21
|
+
* - Generate text or stream text through Effect AI's provider-neutral language
|
|
22
|
+
* model API
|
|
23
|
+
* - Use OpenAI provider metadata for files, reasoning items, tool calls, and
|
|
24
|
+
* response parts
|
|
25
|
+
* - Scope Responses API defaults with {@link Config} and
|
|
26
|
+
* {@link withConfigOverride}
|
|
27
|
+
*
|
|
28
|
+
* **Gotchas**
|
|
29
|
+
*
|
|
30
|
+
* - Some OpenAI model families receive system instructions as developer
|
|
31
|
+
* messages because the Responses API treats them differently.
|
|
32
|
+
* - File prompt parts are sent either as provider file ids or as base64
|
|
33
|
+
* content, depending on `fileIdPrefixes`.
|
|
34
|
+
* - Structured output and tool-call behavior depends on both Effect AI tool
|
|
35
|
+
* definitions and OpenAI model capabilities.
|
|
36
|
+
*
|
|
37
|
+
* @since 4.0.0
|
|
8
38
|
*/
|
|
39
|
+
import * as Context from "effect/Context"
|
|
9
40
|
import * as DateTime from "effect/DateTime"
|
|
10
41
|
import * as Effect from "effect/Effect"
|
|
11
42
|
import * as Encoding from "effect/Encoding"
|
|
12
43
|
import { dual } from "effect/Function"
|
|
13
44
|
import * as Layer from "effect/Layer"
|
|
45
|
+
import * as Option from "effect/Option"
|
|
14
46
|
import * as Predicate from "effect/Predicate"
|
|
15
47
|
import * as Redactable from "effect/Redactable"
|
|
16
48
|
import * as Schema from "effect/Schema"
|
|
17
49
|
import * as AST from "effect/SchemaAST"
|
|
18
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
19
50
|
import * as Stream from "effect/Stream"
|
|
20
51
|
import type { Span } from "effect/Tracer"
|
|
21
|
-
import type { DeepMutable, Simplify } from "effect/Types"
|
|
52
|
+
import type { DeepMutable, Mutable, Simplify } from "effect/Types"
|
|
22
53
|
import * as AiError from "effect/unstable/ai/AiError"
|
|
23
54
|
import * as IdGenerator from "effect/unstable/ai/IdGenerator"
|
|
24
55
|
import * as LanguageModel from "effect/unstable/ai/LanguageModel"
|
|
25
56
|
import * as AiModel from "effect/unstable/ai/Model"
|
|
57
|
+
import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput"
|
|
26
58
|
import type * as Prompt from "effect/unstable/ai/Prompt"
|
|
27
59
|
import type * as Response from "effect/unstable/ai/Response"
|
|
28
60
|
import * as Tool from "effect/unstable/ai/Tool"
|
|
@@ -31,6 +63,7 @@ import type * as HttpClientResponse from "effect/unstable/http/HttpClientRespons
|
|
|
31
63
|
import * as Generated from "./Generated.ts"
|
|
32
64
|
import * as InternalUtilities from "./internal/utilities.ts"
|
|
33
65
|
import { OpenAiClient } from "./OpenAiClient.ts"
|
|
66
|
+
import type * as OpenAiSchema from "./OpenAiSchema.ts"
|
|
34
67
|
import { addGenAIAnnotations } from "./OpenAiTelemetry.ts"
|
|
35
68
|
import type * as OpenAiTool from "./OpenAiTool.ts"
|
|
36
69
|
|
|
@@ -38,8 +71,10 @@ const ResponseModelIds = Generated.ModelIdsResponses.members[1]
|
|
|
38
71
|
const SharedModelIds = Generated.ModelIdsShared.members[1]
|
|
39
72
|
|
|
40
73
|
/**
|
|
41
|
-
*
|
|
74
|
+
* OpenAI model identifiers supported by the Responses API language model.
|
|
75
|
+
*
|
|
42
76
|
* @category models
|
|
77
|
+
* @since 4.0.0
|
|
43
78
|
*/
|
|
44
79
|
export type Model = typeof ResponseModelIds.Encoded | typeof SharedModelIds.Encoded
|
|
45
80
|
|
|
@@ -55,15 +90,27 @@ type ImageDetail = "auto" | "low" | "high"
|
|
|
55
90
|
/**
|
|
56
91
|
* Service definition for OpenAI language model configuration.
|
|
57
92
|
*
|
|
58
|
-
*
|
|
93
|
+
* **When to use**
|
|
94
|
+
*
|
|
95
|
+
* Use when you need to provide OpenAI Responses API request defaults through
|
|
96
|
+
* Effect context for language model operations.
|
|
97
|
+
*
|
|
98
|
+
* **Details**
|
|
99
|
+
*
|
|
100
|
+
* Config values are merged with the config object passed to `model`, `make`, or
|
|
101
|
+
* `layer`, with scoped context values taking precedence.
|
|
102
|
+
*
|
|
103
|
+
* @see {@link withConfigOverride} for scoping language model request overrides
|
|
104
|
+
*
|
|
59
105
|
* @category services
|
|
106
|
+
* @since 4.0.0
|
|
60
107
|
*/
|
|
61
|
-
export class Config extends
|
|
108
|
+
export class Config extends Context.Service<
|
|
62
109
|
Config,
|
|
63
110
|
Simplify<
|
|
64
111
|
& Partial<
|
|
65
112
|
Omit<
|
|
66
|
-
typeof
|
|
113
|
+
typeof OpenAiSchema.CreateResponse.Encoded,
|
|
67
114
|
"input" | "tools" | "tool_choice" | "stream" | "text"
|
|
68
115
|
>
|
|
69
116
|
>
|
|
@@ -105,7 +152,16 @@ export class Config extends ServiceMap.Service<
|
|
|
105
152
|
// =============================================================================
|
|
106
153
|
|
|
107
154
|
declare module "effect/unstable/ai/Prompt" {
|
|
155
|
+
/**
|
|
156
|
+
* OpenAI-specific options for file prompt parts.
|
|
157
|
+
*
|
|
158
|
+
* @category request
|
|
159
|
+
* @since 4.0.0
|
|
160
|
+
*/
|
|
108
161
|
export interface FilePartOptions extends ProviderOptions {
|
|
162
|
+
/**
|
|
163
|
+
* Provider-specific file options for the OpenAI Responses API.
|
|
164
|
+
*/
|
|
109
165
|
readonly openai?: {
|
|
110
166
|
/**
|
|
111
167
|
* The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.
|
|
@@ -114,7 +170,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
114
170
|
} | null
|
|
115
171
|
}
|
|
116
172
|
|
|
173
|
+
/**
|
|
174
|
+
* OpenAI-specific options for reasoning prompt parts.
|
|
175
|
+
*
|
|
176
|
+
* @category request
|
|
177
|
+
* @since 4.0.0
|
|
178
|
+
*/
|
|
117
179
|
export interface ReasoningPartOptions extends ProviderOptions {
|
|
180
|
+
/**
|
|
181
|
+
* Provider-specific reasoning options for the OpenAI Responses API.
|
|
182
|
+
*/
|
|
118
183
|
readonly openai?: {
|
|
119
184
|
/**
|
|
120
185
|
* The ID of the item to reference.
|
|
@@ -129,7 +194,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
129
194
|
} | null
|
|
130
195
|
}
|
|
131
196
|
|
|
197
|
+
/**
|
|
198
|
+
* OpenAI-specific options for assistant tool-call prompt parts.
|
|
199
|
+
*
|
|
200
|
+
* @category request
|
|
201
|
+
* @since 4.0.0
|
|
202
|
+
*/
|
|
132
203
|
export interface ToolCallPartOptions extends ProviderOptions {
|
|
204
|
+
/**
|
|
205
|
+
* Provider-specific tool-call options for the OpenAI Responses API.
|
|
206
|
+
*/
|
|
133
207
|
readonly openai?: {
|
|
134
208
|
/**
|
|
135
209
|
* The ID of the item to reference.
|
|
@@ -138,7 +212,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
138
212
|
/**
|
|
139
213
|
* The status of item.
|
|
140
214
|
*/
|
|
141
|
-
readonly status?: typeof
|
|
215
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
142
216
|
/**
|
|
143
217
|
* The ID of the approval request.
|
|
144
218
|
*/
|
|
@@ -146,7 +220,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
146
220
|
} | null
|
|
147
221
|
}
|
|
148
222
|
|
|
223
|
+
/**
|
|
224
|
+
* OpenAI-specific options for tool-result prompt parts.
|
|
225
|
+
*
|
|
226
|
+
* @category request
|
|
227
|
+
* @since 4.0.0
|
|
228
|
+
*/
|
|
149
229
|
export interface ToolResultPartOptions extends ProviderOptions {
|
|
230
|
+
/**
|
|
231
|
+
* Provider-specific tool-result options for the OpenAI Responses API.
|
|
232
|
+
*/
|
|
150
233
|
readonly openai?: {
|
|
151
234
|
/**
|
|
152
235
|
* The ID of the item to reference.
|
|
@@ -155,7 +238,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
155
238
|
/**
|
|
156
239
|
* The status of item.
|
|
157
240
|
*/
|
|
158
|
-
readonly status?: typeof
|
|
241
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
159
242
|
/**
|
|
160
243
|
* The ID of the approval request.
|
|
161
244
|
*/
|
|
@@ -163,7 +246,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
163
246
|
} | null
|
|
164
247
|
}
|
|
165
248
|
|
|
249
|
+
/**
|
|
250
|
+
* OpenAI-specific options for text prompt parts.
|
|
251
|
+
*
|
|
252
|
+
* @category request
|
|
253
|
+
* @since 4.0.0
|
|
254
|
+
*/
|
|
166
255
|
export interface TextPartOptions extends ProviderOptions {
|
|
256
|
+
/**
|
|
257
|
+
* Provider-specific text options for the OpenAI Responses API.
|
|
258
|
+
*/
|
|
167
259
|
readonly openai?: {
|
|
168
260
|
/**
|
|
169
261
|
* The ID of the item to reference.
|
|
@@ -172,18 +264,30 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
172
264
|
/**
|
|
173
265
|
* The status of item.
|
|
174
266
|
*/
|
|
175
|
-
readonly status?: typeof
|
|
267
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
176
268
|
/**
|
|
177
269
|
* A list of annotations that apply to the output text.
|
|
178
270
|
*/
|
|
179
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
271
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
180
272
|
} | null
|
|
181
273
|
}
|
|
182
274
|
}
|
|
183
275
|
|
|
184
276
|
declare module "effect/unstable/ai/Response" {
|
|
277
|
+
/**
|
|
278
|
+
* OpenAI metadata attached to a complete text response part.
|
|
279
|
+
*
|
|
280
|
+
* @category response
|
|
281
|
+
* @since 4.0.0
|
|
282
|
+
*/
|
|
185
283
|
export interface TextPartMetadata extends ProviderMetadata {
|
|
284
|
+
/**
|
|
285
|
+
* Provider-specific metadata returned for the text part.
|
|
286
|
+
*/
|
|
186
287
|
readonly openai?: {
|
|
288
|
+
/**
|
|
289
|
+
* The OpenAI item ID associated with the text part.
|
|
290
|
+
*/
|
|
187
291
|
readonly itemId?: string | null
|
|
188
292
|
/**
|
|
189
293
|
* If the model emits a refusal content part, the refusal explanation
|
|
@@ -194,63 +298,171 @@ declare module "effect/unstable/ai/Response" {
|
|
|
194
298
|
/**
|
|
195
299
|
* The status of item.
|
|
196
300
|
*/
|
|
197
|
-
readonly status?: typeof
|
|
301
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
198
302
|
/**
|
|
199
303
|
* The text content part annotations.
|
|
200
304
|
*/
|
|
201
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
305
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
202
306
|
}
|
|
203
307
|
}
|
|
204
308
|
|
|
309
|
+
/**
|
|
310
|
+
* OpenAI metadata emitted when a streamed text part starts.
|
|
311
|
+
*
|
|
312
|
+
* @category response
|
|
313
|
+
* @since 4.0.0
|
|
314
|
+
*/
|
|
205
315
|
export interface TextStartPartMetadata extends ProviderMetadata {
|
|
316
|
+
/**
|
|
317
|
+
* Provider-specific metadata returned for the streamed text start.
|
|
318
|
+
*/
|
|
206
319
|
readonly openai?: {
|
|
320
|
+
/**
|
|
321
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
322
|
+
*/
|
|
207
323
|
readonly itemId?: string | null
|
|
208
324
|
} | null
|
|
209
325
|
}
|
|
210
326
|
|
|
327
|
+
/**
|
|
328
|
+
* OpenAI metadata emitted when a streamed text part ends.
|
|
329
|
+
*
|
|
330
|
+
* @category response
|
|
331
|
+
* @since 4.0.0
|
|
332
|
+
*/
|
|
211
333
|
export interface TextEndPartMetadata extends ProviderMetadata {
|
|
334
|
+
/**
|
|
335
|
+
* Provider-specific metadata returned for the streamed text end.
|
|
336
|
+
*/
|
|
212
337
|
readonly openai?: {
|
|
338
|
+
/**
|
|
339
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
340
|
+
*/
|
|
213
341
|
readonly itemId?: string | null
|
|
214
|
-
|
|
342
|
+
/**
|
|
343
|
+
* The annotations collected for the completed streamed text part.
|
|
344
|
+
*/
|
|
345
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
215
346
|
} | null
|
|
216
347
|
}
|
|
217
348
|
|
|
349
|
+
/**
|
|
350
|
+
* OpenAI metadata attached to a complete reasoning response part.
|
|
351
|
+
*
|
|
352
|
+
* @category response
|
|
353
|
+
* @since 4.0.0
|
|
354
|
+
*/
|
|
218
355
|
export interface ReasoningPartMetadata extends ProviderMetadata {
|
|
356
|
+
/**
|
|
357
|
+
* Provider-specific metadata returned for the reasoning part.
|
|
358
|
+
*/
|
|
219
359
|
readonly openai?: {
|
|
360
|
+
/**
|
|
361
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
362
|
+
*/
|
|
220
363
|
readonly itemId?: string | null
|
|
364
|
+
/**
|
|
365
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
366
|
+
*/
|
|
221
367
|
readonly encryptedContent?: string | null
|
|
222
368
|
} | null
|
|
223
369
|
}
|
|
224
370
|
|
|
371
|
+
/**
|
|
372
|
+
* OpenAI metadata emitted when a streamed reasoning part starts.
|
|
373
|
+
*
|
|
374
|
+
* @category response
|
|
375
|
+
* @since 4.0.0
|
|
376
|
+
*/
|
|
225
377
|
export interface ReasoningStartPartMetadata extends ProviderMetadata {
|
|
378
|
+
/**
|
|
379
|
+
* Provider-specific metadata returned for the streamed reasoning start.
|
|
380
|
+
*/
|
|
226
381
|
readonly openai?: {
|
|
382
|
+
/**
|
|
383
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
384
|
+
*/
|
|
227
385
|
readonly itemId?: string | null
|
|
386
|
+
/**
|
|
387
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
388
|
+
*/
|
|
228
389
|
readonly encryptedContent?: string | null
|
|
229
390
|
} | null
|
|
230
391
|
}
|
|
231
392
|
|
|
393
|
+
/**
|
|
394
|
+
* OpenAI metadata emitted for a streamed reasoning delta.
|
|
395
|
+
*
|
|
396
|
+
* @category response
|
|
397
|
+
* @since 4.0.0
|
|
398
|
+
*/
|
|
232
399
|
export interface ReasoningDeltaPartMetadata extends ProviderMetadata {
|
|
400
|
+
/**
|
|
401
|
+
* Provider-specific metadata returned for the streamed reasoning delta.
|
|
402
|
+
*/
|
|
233
403
|
readonly openai?: {
|
|
404
|
+
/**
|
|
405
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
406
|
+
*/
|
|
234
407
|
readonly itemId?: string | null
|
|
235
408
|
} | null
|
|
236
409
|
}
|
|
237
410
|
|
|
411
|
+
/**
|
|
412
|
+
* OpenAI metadata emitted when a streamed reasoning part ends.
|
|
413
|
+
*
|
|
414
|
+
* @category response
|
|
415
|
+
* @since 4.0.0
|
|
416
|
+
*/
|
|
238
417
|
export interface ReasoningEndPartMetadata extends ProviderMetadata {
|
|
418
|
+
/**
|
|
419
|
+
* Provider-specific metadata returned for the streamed reasoning end.
|
|
420
|
+
*/
|
|
239
421
|
readonly openai?: {
|
|
422
|
+
/**
|
|
423
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
424
|
+
*/
|
|
240
425
|
readonly itemId?: string | null
|
|
426
|
+
/**
|
|
427
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
428
|
+
*/
|
|
241
429
|
readonly encryptedContent?: string
|
|
242
430
|
} | null
|
|
243
431
|
}
|
|
244
432
|
|
|
433
|
+
/**
|
|
434
|
+
* OpenAI metadata attached to tool-call response parts.
|
|
435
|
+
*
|
|
436
|
+
* @category response
|
|
437
|
+
* @since 4.0.0
|
|
438
|
+
*/
|
|
245
439
|
export interface ToolCallPartMetadata extends ProviderMetadata {
|
|
440
|
+
/**
|
|
441
|
+
* Provider-specific metadata returned for the tool call.
|
|
442
|
+
*/
|
|
246
443
|
readonly openai?: {
|
|
444
|
+
/**
|
|
445
|
+
* The OpenAI item ID associated with the tool call.
|
|
446
|
+
*/
|
|
247
447
|
readonly itemId?: string | null
|
|
248
448
|
} | null
|
|
249
449
|
}
|
|
250
450
|
|
|
451
|
+
/**
|
|
452
|
+
* OpenAI metadata attached to document source citations.
|
|
453
|
+
*
|
|
454
|
+
* @category response
|
|
455
|
+
* @since 4.0.0
|
|
456
|
+
*/
|
|
251
457
|
export interface DocumentSourcePartMetadata extends ProviderMetadata {
|
|
458
|
+
/**
|
|
459
|
+
* Provider-specific citation metadata for the OpenAI Responses API.
|
|
460
|
+
*/
|
|
252
461
|
readonly openai?:
|
|
253
462
|
| {
|
|
463
|
+
/**
|
|
464
|
+
* Identifies a citation to an uploaded file.
|
|
465
|
+
*/
|
|
254
466
|
readonly type: "file_citation"
|
|
255
467
|
/**
|
|
256
468
|
* The index of the file in the list of files.
|
|
@@ -262,6 +474,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
262
474
|
readonly fileId: string
|
|
263
475
|
}
|
|
264
476
|
| {
|
|
477
|
+
/**
|
|
478
|
+
* Identifies a citation to a generated file path.
|
|
479
|
+
*/
|
|
265
480
|
readonly type: "file_path"
|
|
266
481
|
/**
|
|
267
482
|
* The index of the file in the list of files.
|
|
@@ -273,6 +488,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
273
488
|
readonly fileId: string
|
|
274
489
|
}
|
|
275
490
|
| {
|
|
491
|
+
/**
|
|
492
|
+
* Identifies a citation to a file inside a container.
|
|
493
|
+
*/
|
|
276
494
|
readonly type: "container_file_citation"
|
|
277
495
|
/**
|
|
278
496
|
* The ID of the file.
|
|
@@ -286,8 +504,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
286
504
|
| null
|
|
287
505
|
}
|
|
288
506
|
|
|
507
|
+
/**
|
|
508
|
+
* OpenAI metadata attached to URL source citations.
|
|
509
|
+
*
|
|
510
|
+
* @category response
|
|
511
|
+
* @since 4.0.0
|
|
512
|
+
*/
|
|
289
513
|
export interface UrlSourcePartMetadata extends ProviderMetadata {
|
|
514
|
+
/**
|
|
515
|
+
* Provider-specific URL citation metadata for the OpenAI Responses API.
|
|
516
|
+
*/
|
|
290
517
|
readonly openai?: {
|
|
518
|
+
/**
|
|
519
|
+
* Identifies a citation to a URL.
|
|
520
|
+
*/
|
|
291
521
|
readonly type: "url_citation"
|
|
292
522
|
/**
|
|
293
523
|
* The index of the first character of the URL citation in the message.
|
|
@@ -300,8 +530,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
300
530
|
} | null
|
|
301
531
|
}
|
|
302
532
|
|
|
533
|
+
/**
|
|
534
|
+
* OpenAI metadata attached to finish response parts.
|
|
535
|
+
*
|
|
536
|
+
* @category response
|
|
537
|
+
* @since 4.0.0
|
|
538
|
+
*/
|
|
303
539
|
export interface FinishPartMetadata extends ProviderMetadata {
|
|
540
|
+
/**
|
|
541
|
+
* Provider-specific metadata returned when generation finishes.
|
|
542
|
+
*/
|
|
304
543
|
readonly openai?: {
|
|
544
|
+
/**
|
|
545
|
+
* The service tier reported by OpenAI for the response.
|
|
546
|
+
*/
|
|
305
547
|
readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
306
548
|
} | null
|
|
307
549
|
}
|
|
@@ -312,31 +554,58 @@ declare module "effect/unstable/ai/Response" {
|
|
|
312
554
|
// =============================================================================
|
|
313
555
|
|
|
314
556
|
/**
|
|
315
|
-
*
|
|
557
|
+
* Creates an OpenAI model descriptor that can be provided with
|
|
558
|
+
* `Effect.provide`.
|
|
559
|
+
*
|
|
560
|
+
* **When to use**
|
|
561
|
+
*
|
|
562
|
+
* Use when you want an OpenAI language model value that carries provider and
|
|
563
|
+
* model metadata and can be supplied directly to an Effect program.
|
|
564
|
+
*
|
|
565
|
+
* @see {@link layer} for creating a `LanguageModel.LanguageModel` layer directly
|
|
566
|
+
* @see {@link make} for constructing the language model service effectfully
|
|
567
|
+
*
|
|
316
568
|
* @category constructors
|
|
569
|
+
* @since 4.0.0
|
|
317
570
|
*/
|
|
318
571
|
export const model = (
|
|
319
572
|
model: (string & {}) | Model,
|
|
320
573
|
config?: Omit<typeof Config.Service, "model">
|
|
321
574
|
): AiModel.Model<"openai", LanguageModel.LanguageModel, OpenAiClient> =>
|
|
322
|
-
AiModel.make("openai", layer({ model, config }))
|
|
575
|
+
AiModel.make("openai", model, layer({ model, config }))
|
|
323
576
|
|
|
324
577
|
// TODO
|
|
325
578
|
// /**
|
|
326
|
-
// * @since
|
|
579
|
+
// * @since 4.0.0
|
|
327
580
|
// * @category constructors
|
|
328
581
|
// */
|
|
329
582
|
// export const modelWithTokenizer = (
|
|
330
583
|
// model: (string & {}) | Model,
|
|
331
584
|
// config?: Omit<typeof Config.Service, "model">
|
|
332
585
|
// ): AiModel.Model<"openai", LanguageModel.LanguageModel | Tokenizer.Tokenizer, OpenAiClient> =>
|
|
333
|
-
// AiModel.make("openai", layerWithTokenizer({ model, config }))
|
|
586
|
+
// AiModel.make("openai", model, layerWithTokenizer({ model, config }))
|
|
334
587
|
|
|
335
588
|
/**
|
|
336
|
-
* Creates an OpenAI
|
|
589
|
+
* Creates an OpenAI `LanguageModel` service from a model identifier and
|
|
590
|
+
* optional request defaults.
|
|
591
|
+
*
|
|
592
|
+
* **When to use**
|
|
593
|
+
*
|
|
594
|
+
* Use when an Effect needs to construct a `LanguageModel.Service` value backed
|
|
595
|
+
* by `OpenAiClient`.
|
|
596
|
+
*
|
|
597
|
+
* **Details**
|
|
598
|
+
*
|
|
599
|
+
* The returned effect requires `OpenAiClient`. Request defaults from the
|
|
600
|
+
* `config` option are merged with any `Config` service in the context, with
|
|
601
|
+
* context values taking precedence. The service supports both `generateText`
|
|
602
|
+
* and `streamText`.
|
|
603
|
+
*
|
|
604
|
+
* @see {@link layer} for providing the service as a `Layer`
|
|
605
|
+
* @see {@link model} for creating a model descriptor for `Effect.provide`
|
|
337
606
|
*
|
|
338
|
-
* @since 1.0.0
|
|
339
607
|
* @category constructors
|
|
608
|
+
* @since 4.0.0
|
|
340
609
|
*/
|
|
341
610
|
export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
|
|
342
611
|
readonly model: (string & {}) | Model
|
|
@@ -345,7 +614,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
345
614
|
const client = yield* OpenAiClient
|
|
346
615
|
|
|
347
616
|
const makeConfig = Effect.gen(function*() {
|
|
348
|
-
const services = yield* Effect.
|
|
617
|
+
const services = yield* Effect.context<never>()
|
|
349
618
|
return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
|
|
350
619
|
})
|
|
351
620
|
|
|
@@ -354,9 +623,9 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
354
623
|
readonly config: typeof Config.Service
|
|
355
624
|
readonly options: LanguageModel.ProviderOptions
|
|
356
625
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
357
|
-
}): Effect.fn.Return<typeof
|
|
358
|
-
const include = new Set<typeof
|
|
359
|
-
const capabilities = getModelCapabilities(config.model
|
|
626
|
+
}): Effect.fn.Return<typeof OpenAiSchema.CreateResponse.Encoded, AiError.AiError> {
|
|
627
|
+
const include = new Set<typeof OpenAiSchema.IncludeEnum.Encoded>()
|
|
628
|
+
const capabilities = getModelCapabilities(config.model as string)
|
|
360
629
|
const messages = yield* prepareMessages({
|
|
361
630
|
config,
|
|
362
631
|
options,
|
|
@@ -369,26 +638,29 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
369
638
|
options,
|
|
370
639
|
toolNameMapper
|
|
371
640
|
})
|
|
372
|
-
const responseFormat = prepareResponseFormat({
|
|
641
|
+
const responseFormat = yield* prepareResponseFormat({
|
|
373
642
|
config,
|
|
374
643
|
options
|
|
375
644
|
})
|
|
376
|
-
const
|
|
377
|
-
|
|
645
|
+
const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } = config
|
|
646
|
+
const request: Mutable<typeof OpenAiSchema.CreateResponse.Encoded> = {
|
|
647
|
+
...apiConfig,
|
|
378
648
|
input: messages,
|
|
379
|
-
include: include.size > 0 ? Array.from(include) :
|
|
649
|
+
include: include.size > 0 ? Array.from(include) : undefined,
|
|
380
650
|
text: {
|
|
381
|
-
verbosity: config.text?.verbosity ??
|
|
651
|
+
verbosity: config.text?.verbosity ?? undefined,
|
|
382
652
|
format: responseFormat
|
|
383
|
-
}
|
|
384
|
-
...(Predicate.isNotUndefined(tools) ? { tools } : undefined),
|
|
385
|
-
...(Predicate.isNotUndefined(toolChoice) ? { tool_choice: toolChoice } : undefined)
|
|
653
|
+
}
|
|
386
654
|
}
|
|
655
|
+
if (tools) request.tools = tools
|
|
656
|
+
if (toolChoice) request.tool_choice = toolChoice
|
|
657
|
+
if (options.previousResponseId) request.previous_response_id = options.previousResponseId
|
|
387
658
|
return request
|
|
388
659
|
}
|
|
389
660
|
)
|
|
390
661
|
|
|
391
662
|
return yield* LanguageModel.make({
|
|
663
|
+
codecTransformer: toCodecOpenAI,
|
|
392
664
|
generateText: Effect.fnUntraced(
|
|
393
665
|
function*(options) {
|
|
394
666
|
const config = yield* makeConfig
|
|
@@ -433,10 +705,27 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
433
705
|
})
|
|
434
706
|
|
|
435
707
|
/**
|
|
436
|
-
* Creates a layer
|
|
708
|
+
* Creates a layer that provides the OpenAI `LanguageModel.LanguageModel`
|
|
709
|
+
* service.
|
|
710
|
+
*
|
|
711
|
+
* **When to use**
|
|
712
|
+
*
|
|
713
|
+
* Use when composing application layers and you want OpenAI to satisfy
|
|
714
|
+
* `LanguageModel.LanguageModel` while supplying `OpenAiClient` from another
|
|
715
|
+
* layer.
|
|
716
|
+
*
|
|
717
|
+
* **Details**
|
|
718
|
+
*
|
|
719
|
+
* The `config` option supplies request defaults for the selected model. Scoped
|
|
720
|
+
* values from `withConfigOverride` are merged when each request is built and
|
|
721
|
+
* take precedence over these defaults.
|
|
722
|
+
*
|
|
723
|
+
* @see {@link make} for constructing the language model service effectfully
|
|
724
|
+
* @see {@link model} for creating a model descriptor for `Effect.provide`
|
|
725
|
+
* @see {@link withConfigOverride} for scoped request configuration overrides
|
|
437
726
|
*
|
|
438
|
-
* @since 1.0.0
|
|
439
727
|
* @category layers
|
|
728
|
+
* @since 4.0.0
|
|
440
729
|
*/
|
|
441
730
|
export const layer = (options: {
|
|
442
731
|
readonly model: (string & {}) | Model
|
|
@@ -445,39 +734,114 @@ export const layer = (options: {
|
|
|
445
734
|
Layer.effect(LanguageModel.LanguageModel, make(options))
|
|
446
735
|
|
|
447
736
|
/**
|
|
448
|
-
* Provides config overrides for OpenAI language model operations.
|
|
737
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
738
|
+
*
|
|
739
|
+
* **When to use**
|
|
740
|
+
*
|
|
741
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
742
|
+
* language model operations without changing the defaults passed to `model`,
|
|
743
|
+
* `make`, or `layer`.
|
|
744
|
+
*
|
|
745
|
+
* **Details**
|
|
746
|
+
*
|
|
747
|
+
* The override is dual, so it can be used in pipe form or as
|
|
748
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
749
|
+
* existing `Config` service in the current context, and the override values take
|
|
750
|
+
* precedence.
|
|
751
|
+
*
|
|
752
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
449
753
|
*
|
|
450
|
-
* @since 1.0.0
|
|
451
754
|
* @category configuration
|
|
755
|
+
* @since 4.0.0
|
|
452
756
|
*/
|
|
453
757
|
export const withConfigOverride: {
|
|
454
758
|
/**
|
|
455
|
-
* Provides config overrides for OpenAI language model operations.
|
|
759
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
760
|
+
*
|
|
761
|
+
* **When to use**
|
|
762
|
+
*
|
|
763
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
764
|
+
* language model operations without changing the defaults passed to `model`,
|
|
765
|
+
* `make`, or `layer`.
|
|
766
|
+
*
|
|
767
|
+
* **Details**
|
|
768
|
+
*
|
|
769
|
+
* The override is dual, so it can be used in pipe form or as
|
|
770
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
771
|
+
* existing `Config` service in the current context, and the override values take
|
|
772
|
+
* precedence.
|
|
773
|
+
*
|
|
774
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
456
775
|
*
|
|
457
|
-
* @since 1.0.0
|
|
458
776
|
* @category configuration
|
|
777
|
+
* @since 4.0.0
|
|
459
778
|
*/
|
|
460
779
|
(overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
461
780
|
/**
|
|
462
|
-
* Provides config overrides for OpenAI language model operations.
|
|
781
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
782
|
+
*
|
|
783
|
+
* **When to use**
|
|
784
|
+
*
|
|
785
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
786
|
+
* language model operations without changing the defaults passed to `model`,
|
|
787
|
+
* `make`, or `layer`.
|
|
788
|
+
*
|
|
789
|
+
* **Details**
|
|
790
|
+
*
|
|
791
|
+
* The override is dual, so it can be used in pipe form or as
|
|
792
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
793
|
+
* existing `Config` service in the current context, and the override values take
|
|
794
|
+
* precedence.
|
|
795
|
+
*
|
|
796
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
463
797
|
*
|
|
464
|
-
* @since 1.0.0
|
|
465
798
|
* @category configuration
|
|
799
|
+
* @since 4.0.0
|
|
466
800
|
*/
|
|
467
801
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
|
|
468
802
|
} = dual<
|
|
469
803
|
/**
|
|
470
|
-
* Provides config overrides for OpenAI language model operations.
|
|
804
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
805
|
+
*
|
|
806
|
+
* **When to use**
|
|
807
|
+
*
|
|
808
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
809
|
+
* language model operations without changing the defaults passed to `model`,
|
|
810
|
+
* `make`, or `layer`.
|
|
811
|
+
*
|
|
812
|
+
* **Details**
|
|
813
|
+
*
|
|
814
|
+
* The override is dual, so it can be used in pipe form or as
|
|
815
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
816
|
+
* existing `Config` service in the current context, and the override values take
|
|
817
|
+
* precedence.
|
|
818
|
+
*
|
|
819
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
471
820
|
*
|
|
472
|
-
* @since 1.0.0
|
|
473
821
|
* @category configuration
|
|
822
|
+
* @since 4.0.0
|
|
474
823
|
*/
|
|
475
824
|
(overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
|
|
476
825
|
/**
|
|
477
|
-
* Provides config overrides for OpenAI language model operations.
|
|
826
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
827
|
+
*
|
|
828
|
+
* **When to use**
|
|
829
|
+
*
|
|
830
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
831
|
+
* language model operations without changing the defaults passed to `model`,
|
|
832
|
+
* `make`, or `layer`.
|
|
833
|
+
*
|
|
834
|
+
* **Details**
|
|
835
|
+
*
|
|
836
|
+
* The override is dual, so it can be used in pipe form or as
|
|
837
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
838
|
+
* existing `Config` service in the current context, and the override values take
|
|
839
|
+
* precedence.
|
|
840
|
+
*
|
|
841
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
478
842
|
*
|
|
479
|
-
* @since 1.0.0
|
|
480
843
|
* @category configuration
|
|
844
|
+
* @since 4.0.0
|
|
481
845
|
*/
|
|
482
846
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
483
847
|
>(2, (self, overrides) =>
|
|
@@ -512,10 +876,10 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
512
876
|
}: {
|
|
513
877
|
readonly config: typeof Config.Service
|
|
514
878
|
readonly options: LanguageModel.ProviderOptions
|
|
515
|
-
readonly include: Set<typeof
|
|
879
|
+
readonly include: Set<typeof OpenAiSchema.IncludeEnum.Encoded>
|
|
516
880
|
readonly capabilities: ModelCapabilities
|
|
517
881
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
518
|
-
}): Effect.fn.Return<ReadonlyArray<typeof
|
|
882
|
+
}): Effect.fn.Return<ReadonlyArray<typeof OpenAiSchema.InputItem.Encoded>, AiError.AiError> {
|
|
519
883
|
const processedApprovalIds = new Set<string>()
|
|
520
884
|
|
|
521
885
|
const hasConversation = Predicate.isNotNullish(config.conversation)
|
|
@@ -547,27 +911,28 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
547
911
|
if (config.store === false && capabilities.isReasoningModel) {
|
|
548
912
|
include.add("reasoning.encrypted_content")
|
|
549
913
|
}
|
|
550
|
-
if (
|
|
914
|
+
if (codeInterpreterTool) {
|
|
551
915
|
include.add("code_interpreter_call.outputs")
|
|
552
916
|
}
|
|
553
|
-
if (
|
|
917
|
+
if (webSearchTool || webSearchPreviewTool) {
|
|
554
918
|
include.add("web_search_call.action.sources")
|
|
555
919
|
}
|
|
556
920
|
|
|
557
|
-
const messages: Array<typeof
|
|
921
|
+
const messages: Array<typeof OpenAiSchema.InputItem.Encoded> = []
|
|
922
|
+
const prompt = options.incrementalPrompt ?? options.prompt
|
|
558
923
|
|
|
559
|
-
for (const message of
|
|
924
|
+
for (const message of prompt.content) {
|
|
560
925
|
switch (message.role) {
|
|
561
926
|
case "system": {
|
|
562
927
|
messages.push({
|
|
563
|
-
role: getSystemMessageMode(config.model
|
|
928
|
+
role: getSystemMessageMode(config.model as string),
|
|
564
929
|
content: message.content
|
|
565
930
|
})
|
|
566
931
|
break
|
|
567
932
|
}
|
|
568
933
|
|
|
569
934
|
case "user": {
|
|
570
|
-
const content: Array<typeof
|
|
935
|
+
const content: Array<typeof OpenAiSchema.InputContent.Encoded> = []
|
|
571
936
|
|
|
572
937
|
for (let index = 0; index < message.content.length; index++) {
|
|
573
938
|
const part = message.content[index]
|
|
@@ -630,7 +995,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
630
995
|
}
|
|
631
996
|
|
|
632
997
|
case "assistant": {
|
|
633
|
-
const reasoningMessages: Record<string, DeepMutable<typeof
|
|
998
|
+
const reasoningMessages: Record<string, DeepMutable<typeof OpenAiSchema.ReasoningItem.Encoded>> = {}
|
|
634
999
|
|
|
635
1000
|
for (const part of message.content) {
|
|
636
1001
|
switch (part.type) {
|
|
@@ -689,7 +1054,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
689
1054
|
}
|
|
690
1055
|
}
|
|
691
1056
|
} else {
|
|
692
|
-
const summaryParts: Array<typeof
|
|
1057
|
+
const summaryParts: Array<typeof OpenAiSchema.SummaryTextContent.Encoded> = []
|
|
693
1058
|
|
|
694
1059
|
if (part.text.length > 0) {
|
|
695
1060
|
summaryParts.push({ type: "summary_text", text: part.text })
|
|
@@ -700,7 +1065,9 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
700
1065
|
type: "reasoning",
|
|
701
1066
|
id,
|
|
702
1067
|
summary: summaryParts,
|
|
703
|
-
|
|
1068
|
+
...(Predicate.isNotNull(encryptedContent)
|
|
1069
|
+
? { encrypted_content: encryptedContent }
|
|
1070
|
+
: undefined)
|
|
704
1071
|
}
|
|
705
1072
|
|
|
706
1073
|
messages.push(reasoningMessages[id])
|
|
@@ -921,7 +1288,7 @@ const buildHttpRequestDetails = (
|
|
|
921
1288
|
method: request.method,
|
|
922
1289
|
url: request.url,
|
|
923
1290
|
urlParams: Array.from(request.urlParams),
|
|
924
|
-
hash: request.hash,
|
|
1291
|
+
hash: Option.getOrUndefined(request.hash),
|
|
925
1292
|
headers: Redactable.redact(request.headers) as Record<string, string>
|
|
926
1293
|
})
|
|
927
1294
|
|
|
@@ -936,7 +1303,56 @@ const buildHttpResponseDetails = (
|
|
|
936
1303
|
// Response Conversion
|
|
937
1304
|
// =============================================================================
|
|
938
1305
|
|
|
939
|
-
type ResponseStreamEvent = typeof
|
|
1306
|
+
type ResponseStreamEvent = typeof OpenAiSchema.ResponseStreamEvent.Type
|
|
1307
|
+
|
|
1308
|
+
type KnownResponseStreamEventType =
|
|
1309
|
+
| "response.created"
|
|
1310
|
+
| "response.completed"
|
|
1311
|
+
| "response.incomplete"
|
|
1312
|
+
| "response.failed"
|
|
1313
|
+
| "response.output_item.added"
|
|
1314
|
+
| "response.output_item.done"
|
|
1315
|
+
| "response.output_text.delta"
|
|
1316
|
+
| "response.output_text.annotation.added"
|
|
1317
|
+
| "response.reasoning_summary_part.added"
|
|
1318
|
+
| "response.reasoning_summary_part.done"
|
|
1319
|
+
| "response.reasoning_summary_text.delta"
|
|
1320
|
+
| "response.function_call_arguments.delta"
|
|
1321
|
+
| "response.function_call_arguments.done"
|
|
1322
|
+
| "response.code_interpreter_call_code.delta"
|
|
1323
|
+
| "response.code_interpreter_call_code.done"
|
|
1324
|
+
| "response.apply_patch_call_operation_diff.delta"
|
|
1325
|
+
| "response.apply_patch_call_operation_diff.done"
|
|
1326
|
+
| "response.image_generation_call.partial_image"
|
|
1327
|
+
| "error"
|
|
1328
|
+
|
|
1329
|
+
type KnownResponseStreamEvent = Extract<ResponseStreamEvent, { readonly type: KnownResponseStreamEventType }>
|
|
1330
|
+
|
|
1331
|
+
const knownResponseStreamEventTypes = new Set<KnownResponseStreamEventType>([
|
|
1332
|
+
"response.created",
|
|
1333
|
+
"response.completed",
|
|
1334
|
+
"response.incomplete",
|
|
1335
|
+
"response.failed",
|
|
1336
|
+
"response.output_item.added",
|
|
1337
|
+
"response.output_item.done",
|
|
1338
|
+
"response.output_text.delta",
|
|
1339
|
+
"response.output_text.annotation.added",
|
|
1340
|
+
"response.reasoning_summary_part.added",
|
|
1341
|
+
"response.reasoning_summary_part.done",
|
|
1342
|
+
"response.reasoning_summary_text.delta",
|
|
1343
|
+
"response.function_call_arguments.delta",
|
|
1344
|
+
"response.function_call_arguments.done",
|
|
1345
|
+
"response.code_interpreter_call_code.delta",
|
|
1346
|
+
"response.code_interpreter_call_code.done",
|
|
1347
|
+
"response.apply_patch_call_operation_diff.delta",
|
|
1348
|
+
"response.apply_patch_call_operation_diff.done",
|
|
1349
|
+
"response.image_generation_call.partial_image",
|
|
1350
|
+
"error"
|
|
1351
|
+
])
|
|
1352
|
+
|
|
1353
|
+
const isKnownResponseStreamEvent = (
|
|
1354
|
+
event: ResponseStreamEvent
|
|
1355
|
+
): event is KnownResponseStreamEvent => knownResponseStreamEventTypes.has(event.type as KnownResponseStreamEventType)
|
|
940
1356
|
|
|
941
1357
|
const makeResponse = Effect.fnUntraced(
|
|
942
1358
|
function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
@@ -946,7 +1362,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
946
1362
|
toolNameMapper
|
|
947
1363
|
}: {
|
|
948
1364
|
readonly options: LanguageModel.ProviderOptions
|
|
949
|
-
readonly rawResponse:
|
|
1365
|
+
readonly rawResponse: OpenAiSchema.Response
|
|
950
1366
|
readonly response: HttpClientResponse.HttpClientResponse
|
|
951
1367
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
952
1368
|
}): Effect.fn.Return<
|
|
@@ -985,7 +1401,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
985
1401
|
id: part.call_id,
|
|
986
1402
|
name: toolName,
|
|
987
1403
|
params: { call_id: part.call_id, operation: part.operation },
|
|
988
|
-
metadata: { openai:
|
|
1404
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
989
1405
|
})
|
|
990
1406
|
break
|
|
991
1407
|
}
|
|
@@ -1036,10 +1452,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1036
1452
|
|
|
1037
1453
|
case "function_call": {
|
|
1038
1454
|
hasToolCalls = true
|
|
1455
|
+
|
|
1039
1456
|
const toolName = part.name
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
try: () => Tool.unsafeSecureJsonParse(
|
|
1457
|
+
|
|
1458
|
+
const toolParams = yield* Effect.try({
|
|
1459
|
+
try: () => Tool.unsafeSecureJsonParse(part.arguments),
|
|
1043
1460
|
catch: (cause) =>
|
|
1044
1461
|
AiError.make({
|
|
1045
1462
|
module: "OpenAiLanguageModel",
|
|
@@ -1051,12 +1468,15 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1051
1468
|
})
|
|
1052
1469
|
})
|
|
1053
1470
|
})
|
|
1471
|
+
|
|
1472
|
+
const params = yield* transformToolCallParams(options.tools, part.name, toolParams)
|
|
1473
|
+
|
|
1054
1474
|
parts.push({
|
|
1055
1475
|
type: "tool-call",
|
|
1056
1476
|
id: part.call_id,
|
|
1057
1477
|
name: toolName,
|
|
1058
1478
|
params,
|
|
1059
|
-
metadata: { openai:
|
|
1479
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1060
1480
|
})
|
|
1061
1481
|
break
|
|
1062
1482
|
}
|
|
@@ -1087,7 +1507,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1087
1507
|
id: part.call_id,
|
|
1088
1508
|
name: toolName,
|
|
1089
1509
|
params: { action: part.action },
|
|
1090
|
-
metadata: { openai:
|
|
1510
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1091
1511
|
})
|
|
1092
1512
|
break
|
|
1093
1513
|
}
|
|
@@ -1097,13 +1517,17 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1097
1517
|
? (approvalRequests.get(part.approval_request_id) ?? part.id)
|
|
1098
1518
|
: part.id
|
|
1099
1519
|
|
|
1100
|
-
const toolName =
|
|
1520
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1521
|
+
toolNameMapper,
|
|
1522
|
+
toolParams: part.arguments,
|
|
1523
|
+
method: "makeResponse"
|
|
1524
|
+
})
|
|
1101
1525
|
|
|
1102
1526
|
parts.push({
|
|
1103
1527
|
type: "tool-call",
|
|
1104
1528
|
id: toolId,
|
|
1105
1529
|
name: toolName,
|
|
1106
|
-
params
|
|
1530
|
+
params,
|
|
1107
1531
|
providerExecuted: true
|
|
1108
1532
|
})
|
|
1109
1533
|
|
|
@@ -1114,14 +1538,14 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1114
1538
|
isFailure: false,
|
|
1115
1539
|
providerExecuted: true,
|
|
1116
1540
|
result: {
|
|
1117
|
-
type: "
|
|
1541
|
+
type: "mcp_call",
|
|
1118
1542
|
name: part.name,
|
|
1119
1543
|
arguments: part.arguments,
|
|
1120
1544
|
server_label: part.server_label,
|
|
1121
1545
|
...(Predicate.isNotNullish(part.output) ? { output: part.output } : undefined),
|
|
1122
1546
|
...(Predicate.isNotNullish(part.error) ? { error: part.error } : undefined)
|
|
1123
1547
|
},
|
|
1124
|
-
metadata: { openai:
|
|
1548
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1125
1549
|
})
|
|
1126
1550
|
|
|
1127
1551
|
break
|
|
@@ -1135,20 +1559,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1135
1559
|
case "mcp_approval_request": {
|
|
1136
1560
|
const approvalRequestId = (part as any).approval_request_id ?? part.id
|
|
1137
1561
|
const toolId = yield* idGenerator.generateId()
|
|
1138
|
-
const toolName = `mcp.${part.name}`
|
|
1139
1562
|
|
|
1140
|
-
const params = yield*
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
module: "OpenAiLanguageModel",
|
|
1145
|
-
method: "makeResponse",
|
|
1146
|
-
reason: new AiError.ToolParameterValidationError({
|
|
1147
|
-
toolName,
|
|
1148
|
-
toolParams: {},
|
|
1149
|
-
description: `Failed securely JSON parse tool parameters: ${cause}`
|
|
1150
|
-
})
|
|
1151
|
-
})
|
|
1563
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1564
|
+
toolNameMapper,
|
|
1565
|
+
toolParams: part.arguments,
|
|
1566
|
+
method: "makeResponse"
|
|
1152
1567
|
})
|
|
1153
1568
|
|
|
1154
1569
|
parts.push({
|
|
@@ -1296,7 +1711,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1296
1711
|
id: part.call_id,
|
|
1297
1712
|
name: toolName,
|
|
1298
1713
|
params: { action: part.action },
|
|
1299
|
-
metadata: { openai:
|
|
1714
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1300
1715
|
})
|
|
1301
1716
|
break
|
|
1302
1717
|
}
|
|
@@ -1335,7 +1750,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1335
1750
|
reason: finishReason,
|
|
1336
1751
|
usage: getUsage(rawResponse.usage),
|
|
1337
1752
|
response: buildHttpResponseDetails(response),
|
|
1338
|
-
...(rawResponse.service_tier
|
|
1753
|
+
...toServiceTier(rawResponse.service_tier)
|
|
1339
1754
|
})
|
|
1340
1755
|
|
|
1341
1756
|
return parts
|
|
@@ -1368,18 +1783,44 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1368
1783
|
let hasToolCalls = false
|
|
1369
1784
|
|
|
1370
1785
|
// Track annotations for current message to include in text-end metadata
|
|
1371
|
-
const activeAnnotations: Array<typeof
|
|
1786
|
+
const activeAnnotations: Array<typeof OpenAiSchema.Annotation.Encoded> = []
|
|
1787
|
+
|
|
1788
|
+
type ReasoningSummaryPartStatus = "active" | "can-conclude" | "concluded"
|
|
1789
|
+
type ReasoningPart = {
|
|
1790
|
+
encryptedContent: string | undefined
|
|
1791
|
+
summaryParts: Record<number, ReasoningSummaryPartStatus>
|
|
1792
|
+
}
|
|
1372
1793
|
|
|
1373
1794
|
// Track active reasoning items with state machine for proper concluding logic
|
|
1374
|
-
const activeReasoning: Record<string, {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1795
|
+
const activeReasoning: Record<string, ReasoningPart> = {}
|
|
1796
|
+
|
|
1797
|
+
const getOrCreateReasoningPart = (
|
|
1798
|
+
itemId: string,
|
|
1799
|
+
encryptedContent?: string | null
|
|
1800
|
+
): ReasoningPart => {
|
|
1801
|
+
const activePart = activeReasoning[itemId]
|
|
1802
|
+
if (Predicate.isNotUndefined(activePart)) {
|
|
1803
|
+
if (Predicate.isNotNullish(encryptedContent)) {
|
|
1804
|
+
activePart.encryptedContent = encryptedContent
|
|
1805
|
+
}
|
|
1806
|
+
return activePart
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const reasoningPart: ReasoningPart = {
|
|
1810
|
+
encryptedContent: Predicate.isNotNullish(encryptedContent) ? encryptedContent : undefined,
|
|
1811
|
+
summaryParts: {}
|
|
1812
|
+
}
|
|
1813
|
+
activeReasoning[itemId] = reasoningPart
|
|
1814
|
+
return reasoningPart
|
|
1815
|
+
}
|
|
1378
1816
|
|
|
1379
1817
|
// Track active tool calls with optional provider-specific state
|
|
1380
1818
|
const activeToolCalls: Record<number, {
|
|
1381
1819
|
readonly id: string
|
|
1382
1820
|
readonly name: string
|
|
1821
|
+
readonly functionCall?: {
|
|
1822
|
+
emitted: boolean
|
|
1823
|
+
}
|
|
1383
1824
|
readonly applyPatch?: {
|
|
1384
1825
|
hasDiff: boolean
|
|
1385
1826
|
endEmitted: boolean
|
|
@@ -1399,6 +1840,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1399
1840
|
Stream.mapEffect(Effect.fnUntraced(function*(event) {
|
|
1400
1841
|
const parts: Array<Response.StreamPartEncoded> = []
|
|
1401
1842
|
|
|
1843
|
+
if (!isKnownResponseStreamEvent(event)) {
|
|
1844
|
+
return parts
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1402
1847
|
switch (event.type) {
|
|
1403
1848
|
case "response.created": {
|
|
1404
1849
|
const createdAt = new Date(event.response.created_at * 1000)
|
|
@@ -1428,7 +1873,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1428
1873
|
),
|
|
1429
1874
|
usage: getUsage(event.response.usage),
|
|
1430
1875
|
response: buildHttpResponseDetails(response),
|
|
1431
|
-
...(event.response.service_tier
|
|
1876
|
+
...toServiceTier(event.response.service_tier)
|
|
1432
1877
|
})
|
|
1433
1878
|
break
|
|
1434
1879
|
}
|
|
@@ -1529,7 +1974,8 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1529
1974
|
case "function_call": {
|
|
1530
1975
|
activeToolCalls[event.output_index] = {
|
|
1531
1976
|
id: event.item.call_id,
|
|
1532
|
-
name: event.item.name
|
|
1977
|
+
name: event.item.name,
|
|
1978
|
+
functionCall: { emitted: false }
|
|
1533
1979
|
}
|
|
1534
1980
|
parts.push({
|
|
1535
1981
|
type: "tool-params-start",
|
|
@@ -1566,34 +2012,33 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1566
2012
|
parts.push({
|
|
1567
2013
|
type: "text-start",
|
|
1568
2014
|
id: event.item.id,
|
|
1569
|
-
metadata: { openai:
|
|
2015
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1570
2016
|
})
|
|
1571
2017
|
break
|
|
1572
2018
|
}
|
|
1573
2019
|
|
|
1574
2020
|
case "reasoning": {
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
...makeEncryptedContentMetadata(event.item.encrypted_content)
|
|
2021
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
2022
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[0])) {
|
|
2023
|
+
reasoningPart.summaryParts[0] = "active"
|
|
2024
|
+
parts.push({
|
|
2025
|
+
type: "reasoning-start",
|
|
2026
|
+
id: `${event.item.id}:0`,
|
|
2027
|
+
metadata: {
|
|
2028
|
+
openai: {
|
|
2029
|
+
...makeItemIdMetadata(event.item.id),
|
|
2030
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2031
|
+
}
|
|
1587
2032
|
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
2033
|
+
})
|
|
2034
|
+
}
|
|
1590
2035
|
break
|
|
1591
2036
|
}
|
|
1592
2037
|
|
|
1593
2038
|
case "shell_call": {
|
|
1594
2039
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1595
2040
|
activeToolCalls[event.output_index] = {
|
|
1596
|
-
id: event.item.id,
|
|
2041
|
+
id: event.item.id ?? event.item.call_id,
|
|
1597
2042
|
name: toolName
|
|
1598
2043
|
}
|
|
1599
2044
|
break
|
|
@@ -1644,7 +2089,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1644
2089
|
parts.push({
|
|
1645
2090
|
type: "tool-params-delta",
|
|
1646
2091
|
id: toolCall.id,
|
|
1647
|
-
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff)
|
|
2092
|
+
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff ?? "")
|
|
1648
2093
|
})
|
|
1649
2094
|
}
|
|
1650
2095
|
parts.push({
|
|
@@ -1666,7 +2111,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1666
2111
|
id: toolCall.id,
|
|
1667
2112
|
name: toolName,
|
|
1668
2113
|
params: { call_id: event.item.call_id, operation: event.item.operation },
|
|
1669
|
-
metadata: { openai:
|
|
2114
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1670
2115
|
})
|
|
1671
2116
|
}
|
|
1672
2117
|
delete activeToolCalls[event.output_index]
|
|
@@ -1729,12 +2174,20 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1729
2174
|
}
|
|
1730
2175
|
|
|
1731
2176
|
case "function_call": {
|
|
2177
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2178
|
+
if (Predicate.isNotUndefined(toolCall?.functionCall?.emitted) && toolCall.functionCall.emitted) {
|
|
2179
|
+
delete activeToolCalls[event.output_index]
|
|
2180
|
+
break
|
|
2181
|
+
}
|
|
1732
2182
|
delete activeToolCalls[event.output_index]
|
|
2183
|
+
|
|
1733
2184
|
hasToolCalls = true
|
|
2185
|
+
|
|
1734
2186
|
const toolName = event.item.name
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
2187
|
+
const toolArgs = event.item.arguments
|
|
2188
|
+
|
|
2189
|
+
const toolParams = yield* Effect.try({
|
|
2190
|
+
try: () => Tool.unsafeSecureJsonParse(toolArgs),
|
|
1738
2191
|
catch: (cause) =>
|
|
1739
2192
|
AiError.make({
|
|
1740
2193
|
module: "OpenAiLanguageModel",
|
|
@@ -1746,17 +2199,22 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1746
2199
|
})
|
|
1747
2200
|
})
|
|
1748
2201
|
})
|
|
2202
|
+
|
|
2203
|
+
const params = yield* transformToolCallParams(options.tools, toolName, toolParams)
|
|
2204
|
+
|
|
1749
2205
|
parts.push({
|
|
1750
2206
|
type: "tool-params-end",
|
|
1751
2207
|
id: event.item.call_id
|
|
1752
2208
|
})
|
|
2209
|
+
|
|
1753
2210
|
parts.push({
|
|
1754
2211
|
type: "tool-call",
|
|
1755
2212
|
id: event.item.call_id,
|
|
1756
2213
|
name: toolName,
|
|
1757
2214
|
params,
|
|
1758
|
-
metadata: { openai:
|
|
2215
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1759
2216
|
})
|
|
2217
|
+
|
|
1760
2218
|
break
|
|
1761
2219
|
}
|
|
1762
2220
|
|
|
@@ -1780,7 +2238,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1780
2238
|
id: event.item.call_id,
|
|
1781
2239
|
name: toolName,
|
|
1782
2240
|
params: { action: event.item.action },
|
|
1783
|
-
metadata: { openai:
|
|
2241
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1784
2242
|
})
|
|
1785
2243
|
break
|
|
1786
2244
|
}
|
|
@@ -1794,13 +2252,17 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1794
2252
|
event.item.id)
|
|
1795
2253
|
: event.item.id
|
|
1796
2254
|
|
|
1797
|
-
const toolName =
|
|
2255
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2256
|
+
toolNameMapper,
|
|
2257
|
+
toolParams: event.item.arguments,
|
|
2258
|
+
method: "makeStreamResponse"
|
|
2259
|
+
})
|
|
1798
2260
|
|
|
1799
2261
|
parts.push({
|
|
1800
2262
|
type: "tool-call",
|
|
1801
2263
|
id: toolId,
|
|
1802
2264
|
name: toolName,
|
|
1803
|
-
params
|
|
2265
|
+
params,
|
|
1804
2266
|
providerExecuted: true
|
|
1805
2267
|
})
|
|
1806
2268
|
|
|
@@ -1811,14 +2273,14 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1811
2273
|
isFailure: false,
|
|
1812
2274
|
providerExecuted: true,
|
|
1813
2275
|
result: {
|
|
1814
|
-
type: "
|
|
2276
|
+
type: "mcp_call",
|
|
1815
2277
|
name: event.item.name,
|
|
1816
2278
|
arguments: event.item.arguments,
|
|
1817
2279
|
server_label: event.item.server_label,
|
|
1818
2280
|
...(Predicate.isNotNullish(event.item.output) ? { output: event.item.output } : undefined),
|
|
1819
2281
|
...(Predicate.isNotNullish(event.item.error) ? { error: event.item.error } : undefined)
|
|
1820
2282
|
},
|
|
1821
|
-
metadata: { openai:
|
|
2283
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1822
2284
|
})
|
|
1823
2285
|
|
|
1824
2286
|
break
|
|
@@ -1833,12 +2295,16 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1833
2295
|
const toolId = yield* idGenerator.generateId()
|
|
1834
2296
|
const approvalRequestId = (event.item as any).approval_request_id ?? event.item.id
|
|
1835
2297
|
streamApprovalRequests.set(approvalRequestId, toolId)
|
|
1836
|
-
const toolName =
|
|
2298
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2299
|
+
toolNameMapper,
|
|
2300
|
+
toolParams: event.item.arguments,
|
|
2301
|
+
method: "makeStreamResponse"
|
|
2302
|
+
})
|
|
1837
2303
|
parts.push({
|
|
1838
2304
|
type: "tool-call",
|
|
1839
2305
|
id: toolId,
|
|
1840
2306
|
name: toolName,
|
|
1841
|
-
params
|
|
2307
|
+
params,
|
|
1842
2308
|
providerExecuted: true
|
|
1843
2309
|
})
|
|
1844
2310
|
parts.push({
|
|
@@ -1862,7 +2328,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1862
2328
|
}
|
|
1863
2329
|
|
|
1864
2330
|
case "reasoning": {
|
|
1865
|
-
const reasoningPart =
|
|
2331
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
1866
2332
|
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
1867
2333
|
if (status === "active" || status === "can-conclude") {
|
|
1868
2334
|
parts.push({
|
|
@@ -1871,7 +2337,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1871
2337
|
metadata: {
|
|
1872
2338
|
openai: {
|
|
1873
2339
|
...makeItemIdMetadata(event.item.id),
|
|
1874
|
-
...makeEncryptedContentMetadata(
|
|
2340
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
1875
2341
|
}
|
|
1876
2342
|
}
|
|
1877
2343
|
})
|
|
@@ -1886,10 +2352,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1886
2352
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1887
2353
|
parts.push({
|
|
1888
2354
|
type: "tool-call",
|
|
1889
|
-
id: event.item.id,
|
|
2355
|
+
id: event.item.id ?? event.item.call_id,
|
|
1890
2356
|
name: toolName,
|
|
1891
2357
|
params: { action: event.item.action },
|
|
1892
|
-
metadata: { openai:
|
|
2358
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1893
2359
|
})
|
|
1894
2360
|
break
|
|
1895
2361
|
}
|
|
@@ -1924,7 +2390,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1924
2390
|
}
|
|
1925
2391
|
|
|
1926
2392
|
case "response.output_text.annotation.added": {
|
|
1927
|
-
const annotation = event.annotation as typeof
|
|
2393
|
+
const annotation = event.annotation as typeof OpenAiSchema.Annotation.Encoded
|
|
1928
2394
|
// Track annotation for text-end metadata
|
|
1929
2395
|
activeAnnotations.push(annotation)
|
|
1930
2396
|
if (annotation.type === "container_file_citation") {
|
|
@@ -2006,6 +2472,48 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2006
2472
|
break
|
|
2007
2473
|
}
|
|
2008
2474
|
|
|
2475
|
+
case "response.function_call_arguments.done": {
|
|
2476
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2477
|
+
if (
|
|
2478
|
+
Predicate.isNotUndefined(toolCall?.functionCall) &&
|
|
2479
|
+
!toolCall.functionCall.emitted
|
|
2480
|
+
) {
|
|
2481
|
+
hasToolCalls = true
|
|
2482
|
+
|
|
2483
|
+
const toolParams = yield* Effect.try({
|
|
2484
|
+
try: () => Tool.unsafeSecureJsonParse(event.arguments),
|
|
2485
|
+
catch: (cause) =>
|
|
2486
|
+
AiError.make({
|
|
2487
|
+
module: "OpenAiLanguageModel",
|
|
2488
|
+
method: "makeStreamResponse",
|
|
2489
|
+
reason: new AiError.ToolParameterValidationError({
|
|
2490
|
+
toolName: toolCall.name,
|
|
2491
|
+
toolParams: {},
|
|
2492
|
+
description: `Failed securely JSON parse tool parameters: ${cause}`
|
|
2493
|
+
})
|
|
2494
|
+
})
|
|
2495
|
+
})
|
|
2496
|
+
|
|
2497
|
+
const params = yield* transformToolCallParams(options.tools, toolCall.name, toolParams)
|
|
2498
|
+
|
|
2499
|
+
parts.push({
|
|
2500
|
+
type: "tool-params-end",
|
|
2501
|
+
id: toolCall.id
|
|
2502
|
+
})
|
|
2503
|
+
|
|
2504
|
+
parts.push({
|
|
2505
|
+
type: "tool-call",
|
|
2506
|
+
id: toolCall.id,
|
|
2507
|
+
name: toolCall.name,
|
|
2508
|
+
params,
|
|
2509
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2510
|
+
})
|
|
2511
|
+
|
|
2512
|
+
toolCall.functionCall.emitted = true
|
|
2513
|
+
}
|
|
2514
|
+
break
|
|
2515
|
+
}
|
|
2516
|
+
|
|
2009
2517
|
case "response.apply_patch_call_operation_diff.delta": {
|
|
2010
2518
|
const toolCall = activeToolCalls[event.output_index]
|
|
2011
2519
|
if (Predicate.isNotUndefined(toolCall?.applyPatch)) {
|
|
@@ -2095,28 +2603,28 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2095
2603
|
}
|
|
2096
2604
|
|
|
2097
2605
|
case "response.reasoning_summary_part.added": {
|
|
2098
|
-
|
|
2606
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2099
2607
|
if (event.summary_index > 0) {
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
...makeItemIdMetadata(event.item_id),
|
|
2111
|
-
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2112
|
-
}
|
|
2608
|
+
// Conclude all can-conclude parts before starting new one
|
|
2609
|
+
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
2610
|
+
if (status === "can-conclude") {
|
|
2611
|
+
parts.push({
|
|
2612
|
+
type: "reasoning-end",
|
|
2613
|
+
id: `${event.item_id}:${summaryIndex}`,
|
|
2614
|
+
metadata: {
|
|
2615
|
+
openai: {
|
|
2616
|
+
...makeItemIdMetadata(event.item_id),
|
|
2617
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2113
2618
|
}
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2619
|
+
}
|
|
2620
|
+
})
|
|
2621
|
+
reasoningPart.summaryParts[Number(summaryIndex)] = "concluded"
|
|
2117
2622
|
}
|
|
2118
|
-
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2119
2623
|
}
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[event.summary_index])) {
|
|
2627
|
+
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2120
2628
|
parts.push({
|
|
2121
2629
|
type: "reasoning-start",
|
|
2122
2630
|
id: `${event.item_id}:${event.summary_index}`,
|
|
@@ -2136,26 +2644,27 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2136
2644
|
type: "reasoning-delta",
|
|
2137
2645
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2138
2646
|
delta: event.delta,
|
|
2139
|
-
metadata: { openai:
|
|
2647
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2140
2648
|
})
|
|
2141
2649
|
break
|
|
2142
2650
|
}
|
|
2143
2651
|
|
|
2144
2652
|
case "response.reasoning_summary_part.done": {
|
|
2653
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2145
2654
|
// When OpenAI stores message data, we can immediately conclude the
|
|
2146
2655
|
// reasoning part given that we do not need the encrypted content
|
|
2147
2656
|
if (config.store === true) {
|
|
2148
2657
|
parts.push({
|
|
2149
2658
|
type: "reasoning-end",
|
|
2150
2659
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2151
|
-
metadata: { openai:
|
|
2660
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2152
2661
|
})
|
|
2153
2662
|
// Mark the summary part concluded
|
|
2154
|
-
|
|
2663
|
+
reasoningPart.summaryParts[event.summary_index] = "concluded"
|
|
2155
2664
|
} else {
|
|
2156
2665
|
// Mark the summary part as can-conclude given we still need a
|
|
2157
2666
|
// final summary part with the encrypted content
|
|
2158
|
-
|
|
2667
|
+
reasoningPart.summaryParts[event.summary_index] = "can-conclude"
|
|
2159
2668
|
}
|
|
2160
2669
|
break
|
|
2161
2670
|
}
|
|
@@ -2174,7 +2683,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2174
2683
|
|
|
2175
2684
|
const annotateRequest = (
|
|
2176
2685
|
span: Span,
|
|
2177
|
-
request: typeof
|
|
2686
|
+
request: typeof OpenAiSchema.CreateResponse.Encoded
|
|
2178
2687
|
): void => {
|
|
2179
2688
|
addGenAIAnnotations(span, {
|
|
2180
2689
|
system: "openai",
|
|
@@ -2194,7 +2703,7 @@ const annotateRequest = (
|
|
|
2194
2703
|
})
|
|
2195
2704
|
}
|
|
2196
2705
|
|
|
2197
|
-
const annotateResponse = (span: Span, response:
|
|
2706
|
+
const annotateResponse = (span: Span, response: OpenAiSchema.Response): void => {
|
|
2198
2707
|
const finishReason = response.incomplete_details?.reason as string | undefined
|
|
2199
2708
|
addGenAIAnnotations(span, {
|
|
2200
2709
|
response: {
|
|
@@ -2244,7 +2753,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) =>
|
|
|
2244
2753
|
// Tool Conversion
|
|
2245
2754
|
// =============================================================================
|
|
2246
2755
|
|
|
2247
|
-
type OpenAiToolChoice = typeof
|
|
2756
|
+
type OpenAiToolChoice = typeof OpenAiSchema.CreateResponse.Encoded["tool_choice"]
|
|
2248
2757
|
|
|
2249
2758
|
const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
2250
2759
|
config,
|
|
@@ -2255,7 +2764,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2255
2764
|
readonly options: LanguageModel.ProviderOptions
|
|
2256
2765
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
2257
2766
|
}): Effect.fn.Return<{
|
|
2258
|
-
readonly tools: ReadonlyArray<typeof
|
|
2767
|
+
readonly tools: ReadonlyArray<typeof OpenAiSchema.Tool.Encoded> | undefined
|
|
2259
2768
|
readonly toolChoice: OpenAiToolChoice | undefined
|
|
2260
2769
|
}, AiError.AiError> {
|
|
2261
2770
|
// Return immediately if no tools are in the toolkit
|
|
@@ -2263,7 +2772,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2263
2772
|
return { tools: undefined, toolChoice: undefined }
|
|
2264
2773
|
}
|
|
2265
2774
|
|
|
2266
|
-
const tools: Array<typeof
|
|
2775
|
+
const tools: Array<typeof OpenAiSchema.Tool.Encoded> = []
|
|
2267
2776
|
let toolChoice: OpenAiToolChoice | undefined = undefined
|
|
2268
2777
|
|
|
2269
2778
|
// Filter the incoming tools down to the set of allowed tools as indicated by
|
|
@@ -2279,14 +2788,16 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2279
2788
|
|
|
2280
2789
|
// Convert the tools in the toolkit to the provider-defined format
|
|
2281
2790
|
for (const tool of allowedTools) {
|
|
2282
|
-
if (Tool.isUserDefined(tool)) {
|
|
2791
|
+
if (Tool.isUserDefined(tool) || Tool.isDynamic(tool)) {
|
|
2283
2792
|
const strict = Tool.getStrictMode(tool) ?? config.strictJsonSchema ?? true
|
|
2793
|
+
const description = Tool.getDescription(tool)
|
|
2794
|
+
const parameters = yield* tryToolJsonSchema(tool, "prepareTools")
|
|
2284
2795
|
tools.push({
|
|
2285
2796
|
type: "function",
|
|
2286
2797
|
name: tool.name,
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2798
|
+
parameters,
|
|
2799
|
+
strict,
|
|
2800
|
+
...(Predicate.isNotUndefined(description) ? { description } : undefined)
|
|
2290
2801
|
})
|
|
2291
2802
|
}
|
|
2292
2803
|
|
|
@@ -2468,35 +2979,63 @@ const getStatus = (
|
|
|
2468
2979
|
| Prompt.TextPart
|
|
2469
2980
|
| Prompt.ToolCallPart
|
|
2470
2981
|
| Prompt.ToolResultPart
|
|
2471
|
-
): typeof
|
|
2982
|
+
): typeof OpenAiSchema.MessageStatus.Encoded | null => part.options.openai?.status ?? null
|
|
2472
2983
|
const getEncryptedContent = (
|
|
2473
2984
|
part: Prompt.ReasoningPart
|
|
2474
2985
|
): string | null => part.options.openai?.encryptedContent ?? null
|
|
2475
2986
|
|
|
2476
2987
|
const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options.openai?.imageDetail ?? "auto"
|
|
2477
2988
|
|
|
2478
|
-
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } :
|
|
2989
|
+
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } : {}
|
|
2479
2990
|
|
|
2480
2991
|
const makeEncryptedContentMetadata = (encryptedContent: string | null | undefined) =>
|
|
2481
2992
|
Predicate.isNotNullish(encryptedContent) ? { encryptedContent } : undefined
|
|
2482
2993
|
|
|
2483
|
-
const
|
|
2994
|
+
const unsupportedSchemaError = (error: unknown, method: string): AiError.AiError =>
|
|
2995
|
+
AiError.make({
|
|
2996
|
+
module: "OpenAiLanguageModel",
|
|
2997
|
+
method,
|
|
2998
|
+
reason: new AiError.UnsupportedSchemaError({
|
|
2999
|
+
description: error instanceof Error ? error.message : String(error)
|
|
3000
|
+
})
|
|
3001
|
+
})
|
|
3002
|
+
|
|
3003
|
+
const tryCodecTransform = <S extends Schema.Top>(schema: S, method: string) =>
|
|
3004
|
+
Effect.try({
|
|
3005
|
+
try: () => toCodecOpenAI(schema),
|
|
3006
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
3007
|
+
})
|
|
3008
|
+
|
|
3009
|
+
const tryJsonSchema = <S extends Schema.Top>(schema: S, method: string) =>
|
|
3010
|
+
Effect.try({
|
|
3011
|
+
try: () => Tool.getJsonSchemaFromSchema(schema, { transformer: toCodecOpenAI }),
|
|
3012
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
3013
|
+
})
|
|
3014
|
+
|
|
3015
|
+
const tryToolJsonSchema = <T extends Tool.Any>(tool: T, method: string) =>
|
|
3016
|
+
Effect.try({
|
|
3017
|
+
try: () => Tool.getJsonSchema(tool, { transformer: toCodecOpenAI }),
|
|
3018
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
3019
|
+
})
|
|
3020
|
+
|
|
3021
|
+
const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: {
|
|
2484
3022
|
readonly config: typeof Config.Service
|
|
2485
3023
|
readonly options: LanguageModel.ProviderOptions
|
|
2486
|
-
}): typeof
|
|
3024
|
+
}): Effect.fn.Return<typeof OpenAiSchema.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
|
|
2487
3025
|
if (options.responseFormat.type === "json") {
|
|
2488
3026
|
const name = options.responseFormat.objectName
|
|
2489
3027
|
const schema = options.responseFormat.schema
|
|
3028
|
+
const jsonSchema = yield* tryJsonSchema(schema, "prepareResponseFormat")
|
|
2490
3029
|
return {
|
|
2491
3030
|
type: "json_schema",
|
|
2492
3031
|
name,
|
|
2493
3032
|
description: AST.resolveDescription(schema.ast) ?? "Response with a JSON object",
|
|
2494
|
-
schema:
|
|
3033
|
+
schema: jsonSchema,
|
|
2495
3034
|
strict: config.strictJsonSchema ?? true
|
|
2496
3035
|
}
|
|
2497
3036
|
}
|
|
2498
3037
|
return { type: "text" }
|
|
2499
|
-
}
|
|
3038
|
+
})
|
|
2500
3039
|
|
|
2501
3040
|
interface ModelCapabilities {
|
|
2502
3041
|
readonly isReasoningModel: boolean
|
|
@@ -2570,7 +3109,42 @@ const getApprovalRequestIdMapping = (prompt: Prompt.Prompt): ReadonlyMap<string,
|
|
|
2570
3109
|
return mapping
|
|
2571
3110
|
}
|
|
2572
3111
|
|
|
2573
|
-
const
|
|
3112
|
+
const normalizeMcpToolCall = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
3113
|
+
toolNameMapper,
|
|
3114
|
+
toolParams,
|
|
3115
|
+
method
|
|
3116
|
+
}: {
|
|
3117
|
+
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
3118
|
+
readonly toolParams: unknown
|
|
3119
|
+
readonly method: string
|
|
3120
|
+
}): Effect.fn.Return<{
|
|
3121
|
+
readonly toolName: string
|
|
3122
|
+
readonly params: unknown
|
|
3123
|
+
}, AiError.AiError> {
|
|
3124
|
+
const toolName = toolNameMapper.getCustomName("mcp")
|
|
3125
|
+
|
|
3126
|
+
if (typeof toolParams !== "string") {
|
|
3127
|
+
return { toolName, params: toolParams }
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3130
|
+
const params = yield* Effect.try({
|
|
3131
|
+
try: () => Tool.unsafeSecureJsonParse(toolParams),
|
|
3132
|
+
catch: (cause) =>
|
|
3133
|
+
AiError.make({
|
|
3134
|
+
module: "OpenAiLanguageModel",
|
|
3135
|
+
method,
|
|
3136
|
+
reason: new AiError.ToolParameterValidationError({
|
|
3137
|
+
toolName,
|
|
3138
|
+
toolParams,
|
|
3139
|
+
description: `Failed to securely JSON parse tool parameters: ${cause}`
|
|
3140
|
+
})
|
|
3141
|
+
})
|
|
3142
|
+
})
|
|
3143
|
+
|
|
3144
|
+
return { toolName, params }
|
|
3145
|
+
})
|
|
3146
|
+
|
|
3147
|
+
const getUsage = (usage: OpenAiSchema.ResponseUsage | null | undefined): Response.Usage => {
|
|
2574
3148
|
if (Predicate.isNullish(usage)) {
|
|
2575
3149
|
return {
|
|
2576
3150
|
inputTokens: {
|
|
@@ -2589,8 +3163,8 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2589
3163
|
|
|
2590
3164
|
const inputTokens = usage.input_tokens
|
|
2591
3165
|
const outputTokens = usage.output_tokens
|
|
2592
|
-
const cachedTokens = usage.input_tokens_details
|
|
2593
|
-
const reasoningTokens = usage.output_tokens_details
|
|
3166
|
+
const cachedTokens = getUsageTokenDetail(usage.input_tokens_details, "cached_tokens")
|
|
3167
|
+
const reasoningTokens = getUsageTokenDetail(usage.output_tokens_details, "reasoning_tokens")
|
|
2594
3168
|
|
|
2595
3169
|
return {
|
|
2596
3170
|
inputTokens: {
|
|
@@ -2606,3 +3180,64 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2606
3180
|
}
|
|
2607
3181
|
}
|
|
2608
3182
|
}
|
|
3183
|
+
|
|
3184
|
+
type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
3185
|
+
|
|
3186
|
+
const toServiceTier = (value: string | undefined): {
|
|
3187
|
+
readonly metadata: {
|
|
3188
|
+
readonly openai: {
|
|
3189
|
+
readonly serviceTier: ServiceTier
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
} | undefined => {
|
|
3193
|
+
switch (value) {
|
|
3194
|
+
case "default":
|
|
3195
|
+
case "auto":
|
|
3196
|
+
case "flex":
|
|
3197
|
+
case "scale":
|
|
3198
|
+
case "priority":
|
|
3199
|
+
return { metadata: { openai: { serviceTier: value } } }
|
|
3200
|
+
default:
|
|
3201
|
+
return undefined
|
|
3202
|
+
}
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3205
|
+
const getUsageTokenDetail = (details: unknown, key: string): number =>
|
|
3206
|
+
Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : 0
|
|
3207
|
+
|
|
3208
|
+
const transformToolCallParams = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>(
|
|
3209
|
+
tools: Tools,
|
|
3210
|
+
toolName: string,
|
|
3211
|
+
toolParams: unknown
|
|
3212
|
+
): Effect.fn.Return<unknown, AiError.AiError> {
|
|
3213
|
+
const tool = tools.find((tool) => tool.name === toolName)
|
|
3214
|
+
|
|
3215
|
+
if (Predicate.isUndefined(tool)) {
|
|
3216
|
+
return yield* AiError.make({
|
|
3217
|
+
module: "OpenAiLanguageModel",
|
|
3218
|
+
method: "makeResponse",
|
|
3219
|
+
reason: new AiError.ToolNotFoundError({
|
|
3220
|
+
toolName,
|
|
3221
|
+
availableTools: tools.map((tool) => tool.name)
|
|
3222
|
+
})
|
|
3223
|
+
})
|
|
3224
|
+
}
|
|
3225
|
+
|
|
3226
|
+
const { codec } = yield* tryCodecTransform(tool.parametersSchema, "makeResponse")
|
|
3227
|
+
|
|
3228
|
+
const transform = Schema.decodeEffect(codec)
|
|
3229
|
+
|
|
3230
|
+
return yield* (
|
|
3231
|
+
transform(toolParams) as Effect.Effect<unknown, Schema.SchemaError>
|
|
3232
|
+
).pipe(Effect.mapError((error) =>
|
|
3233
|
+
AiError.make({
|
|
3234
|
+
module: "OpenAiLanguageModel",
|
|
3235
|
+
method: "makeResponse",
|
|
3236
|
+
reason: new AiError.ToolParameterValidationError({
|
|
3237
|
+
toolName,
|
|
3238
|
+
toolParams,
|
|
3239
|
+
description: error.issue.toString()
|
|
3240
|
+
})
|
|
3241
|
+
})
|
|
3242
|
+
))
|
|
3243
|
+
})
|