@effect/ai-openai 4.0.0-beta.7 → 4.0.0-beta.70
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 +81 -25
- package/dist/OpenAiClient.d.ts.map +1 -1
- package/dist/OpenAiClient.js +220 -39
- 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 +45 -10
- package/dist/OpenAiConfig.d.ts.map +1 -1
- package/dist/OpenAiConfig.js +31 -7
- package/dist/OpenAiConfig.js.map +1 -1
- package/dist/OpenAiEmbeddingModel.d.ts +89 -0
- package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
- package/dist/OpenAiEmbeddingModel.js +121 -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 +250 -57
- package/dist/OpenAiLanguageModel.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.js +311 -160
- package/dist/OpenAiLanguageModel.js.map +1 -1
- package/dist/OpenAiSchema.d.ts +2029 -0
- package/dist/OpenAiSchema.d.ts.map +1 -0
- package/dist/OpenAiSchema.js +591 -0
- package/dist/OpenAiSchema.js.map +1 -0
- package/dist/OpenAiTelemetry.d.ts +31 -18
- package/dist/OpenAiTelemetry.d.ts.map +1 -1
- package/dist/OpenAiTelemetry.js +6 -4
- package/dist/OpenAiTelemetry.js.map +1 -1
- package/dist/OpenAiTool.d.ts +56 -67
- package/dist/OpenAiTool.d.ts.map +1 -1
- package/dist/OpenAiTool.js +33 -44
- package/dist/OpenAiTool.js.map +1 -1
- package/dist/index.d.ts +42 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +42 -8
- 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 +396 -90
- package/src/OpenAiClientGenerated.ts +202 -0
- package/src/OpenAiConfig.ts +46 -11
- package/src/OpenAiEmbeddingModel.ts +207 -0
- package/src/OpenAiError.ts +170 -35
- package/src/OpenAiLanguageModel.ts +633 -157
- package/src/OpenAiSchema.ts +984 -0
- package/src/OpenAiTelemetry.ts +32 -19
- package/src/OpenAiTool.ts +34 -45
- package/src/index.ts +45 -8
- package/src/internal/errors.ts +6 -4
|
@@ -4,25 +4,27 @@
|
|
|
4
4
|
* Provides a LanguageModel implementation for OpenAI's responses API,
|
|
5
5
|
* supporting text generation, structured output, tool calling, and streaming.
|
|
6
6
|
*
|
|
7
|
-
* @since
|
|
7
|
+
* @since 4.0.0
|
|
8
8
|
*/
|
|
9
|
+
import * as Context from "effect/Context"
|
|
9
10
|
import * as DateTime from "effect/DateTime"
|
|
10
11
|
import * as Effect from "effect/Effect"
|
|
11
12
|
import * as Encoding from "effect/Encoding"
|
|
12
13
|
import { dual } from "effect/Function"
|
|
13
14
|
import * as Layer from "effect/Layer"
|
|
15
|
+
import * as Option from "effect/Option"
|
|
14
16
|
import * as Predicate from "effect/Predicate"
|
|
15
17
|
import * as Redactable from "effect/Redactable"
|
|
16
18
|
import * as Schema from "effect/Schema"
|
|
17
19
|
import * as AST from "effect/SchemaAST"
|
|
18
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
19
20
|
import * as Stream from "effect/Stream"
|
|
20
21
|
import type { Span } from "effect/Tracer"
|
|
21
|
-
import type { DeepMutable, Simplify } from "effect/Types"
|
|
22
|
+
import type { DeepMutable, Mutable, Simplify } from "effect/Types"
|
|
22
23
|
import * as AiError from "effect/unstable/ai/AiError"
|
|
23
24
|
import * as IdGenerator from "effect/unstable/ai/IdGenerator"
|
|
24
25
|
import * as LanguageModel from "effect/unstable/ai/LanguageModel"
|
|
25
26
|
import * as AiModel from "effect/unstable/ai/Model"
|
|
27
|
+
import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput"
|
|
26
28
|
import type * as Prompt from "effect/unstable/ai/Prompt"
|
|
27
29
|
import type * as Response from "effect/unstable/ai/Response"
|
|
28
30
|
import * as Tool from "effect/unstable/ai/Tool"
|
|
@@ -31,6 +33,7 @@ import type * as HttpClientResponse from "effect/unstable/http/HttpClientRespons
|
|
|
31
33
|
import * as Generated from "./Generated.ts"
|
|
32
34
|
import * as InternalUtilities from "./internal/utilities.ts"
|
|
33
35
|
import { OpenAiClient } from "./OpenAiClient.ts"
|
|
36
|
+
import type * as OpenAiSchema from "./OpenAiSchema.ts"
|
|
34
37
|
import { addGenAIAnnotations } from "./OpenAiTelemetry.ts"
|
|
35
38
|
import type * as OpenAiTool from "./OpenAiTool.ts"
|
|
36
39
|
|
|
@@ -38,8 +41,10 @@ const ResponseModelIds = Generated.ModelIdsResponses.members[1]
|
|
|
38
41
|
const SharedModelIds = Generated.ModelIdsShared.members[1]
|
|
39
42
|
|
|
40
43
|
/**
|
|
41
|
-
*
|
|
44
|
+
* OpenAI model identifiers supported by the Responses API language model.
|
|
45
|
+
*
|
|
42
46
|
* @category models
|
|
47
|
+
* @since 4.0.0
|
|
43
48
|
*/
|
|
44
49
|
export type Model = typeof ResponseModelIds.Encoded | typeof SharedModelIds.Encoded
|
|
45
50
|
|
|
@@ -55,15 +60,15 @@ type ImageDetail = "auto" | "low" | "high"
|
|
|
55
60
|
/**
|
|
56
61
|
* Service definition for OpenAI language model configuration.
|
|
57
62
|
*
|
|
58
|
-
* @since 1.0.0
|
|
59
63
|
* @category services
|
|
64
|
+
* @since 4.0.0
|
|
60
65
|
*/
|
|
61
|
-
export class Config extends
|
|
66
|
+
export class Config extends Context.Service<
|
|
62
67
|
Config,
|
|
63
68
|
Simplify<
|
|
64
69
|
& Partial<
|
|
65
70
|
Omit<
|
|
66
|
-
typeof
|
|
71
|
+
typeof OpenAiSchema.CreateResponse.Encoded,
|
|
67
72
|
"input" | "tools" | "tool_choice" | "stream" | "text"
|
|
68
73
|
>
|
|
69
74
|
>
|
|
@@ -105,7 +110,16 @@ export class Config extends ServiceMap.Service<
|
|
|
105
110
|
// =============================================================================
|
|
106
111
|
|
|
107
112
|
declare module "effect/unstable/ai/Prompt" {
|
|
113
|
+
/**
|
|
114
|
+
* OpenAI-specific options for file prompt parts.
|
|
115
|
+
*
|
|
116
|
+
* @category request
|
|
117
|
+
* @since 4.0.0
|
|
118
|
+
*/
|
|
108
119
|
export interface FilePartOptions extends ProviderOptions {
|
|
120
|
+
/**
|
|
121
|
+
* Provider-specific file options for the OpenAI Responses API.
|
|
122
|
+
*/
|
|
109
123
|
readonly openai?: {
|
|
110
124
|
/**
|
|
111
125
|
* The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.
|
|
@@ -114,7 +128,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
114
128
|
} | null
|
|
115
129
|
}
|
|
116
130
|
|
|
131
|
+
/**
|
|
132
|
+
* OpenAI-specific options for reasoning prompt parts.
|
|
133
|
+
*
|
|
134
|
+
* @category request
|
|
135
|
+
* @since 4.0.0
|
|
136
|
+
*/
|
|
117
137
|
export interface ReasoningPartOptions extends ProviderOptions {
|
|
138
|
+
/**
|
|
139
|
+
* Provider-specific reasoning options for the OpenAI Responses API.
|
|
140
|
+
*/
|
|
118
141
|
readonly openai?: {
|
|
119
142
|
/**
|
|
120
143
|
* The ID of the item to reference.
|
|
@@ -129,7 +152,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
129
152
|
} | null
|
|
130
153
|
}
|
|
131
154
|
|
|
155
|
+
/**
|
|
156
|
+
* OpenAI-specific options for assistant tool-call prompt parts.
|
|
157
|
+
*
|
|
158
|
+
* @category request
|
|
159
|
+
* @since 4.0.0
|
|
160
|
+
*/
|
|
132
161
|
export interface ToolCallPartOptions extends ProviderOptions {
|
|
162
|
+
/**
|
|
163
|
+
* Provider-specific tool-call options for the OpenAI Responses API.
|
|
164
|
+
*/
|
|
133
165
|
readonly openai?: {
|
|
134
166
|
/**
|
|
135
167
|
* The ID of the item to reference.
|
|
@@ -138,7 +170,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
138
170
|
/**
|
|
139
171
|
* The status of item.
|
|
140
172
|
*/
|
|
141
|
-
readonly status?: typeof
|
|
173
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
142
174
|
/**
|
|
143
175
|
* The ID of the approval request.
|
|
144
176
|
*/
|
|
@@ -146,7 +178,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
146
178
|
} | null
|
|
147
179
|
}
|
|
148
180
|
|
|
181
|
+
/**
|
|
182
|
+
* OpenAI-specific options for tool-result prompt parts.
|
|
183
|
+
*
|
|
184
|
+
* @category request
|
|
185
|
+
* @since 4.0.0
|
|
186
|
+
*/
|
|
149
187
|
export interface ToolResultPartOptions extends ProviderOptions {
|
|
188
|
+
/**
|
|
189
|
+
* Provider-specific tool-result options for the OpenAI Responses API.
|
|
190
|
+
*/
|
|
150
191
|
readonly openai?: {
|
|
151
192
|
/**
|
|
152
193
|
* The ID of the item to reference.
|
|
@@ -155,7 +196,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
155
196
|
/**
|
|
156
197
|
* The status of item.
|
|
157
198
|
*/
|
|
158
|
-
readonly status?: typeof
|
|
199
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
159
200
|
/**
|
|
160
201
|
* The ID of the approval request.
|
|
161
202
|
*/
|
|
@@ -163,7 +204,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
163
204
|
} | null
|
|
164
205
|
}
|
|
165
206
|
|
|
207
|
+
/**
|
|
208
|
+
* OpenAI-specific options for text prompt parts.
|
|
209
|
+
*
|
|
210
|
+
* @category request
|
|
211
|
+
* @since 4.0.0
|
|
212
|
+
*/
|
|
166
213
|
export interface TextPartOptions extends ProviderOptions {
|
|
214
|
+
/**
|
|
215
|
+
* Provider-specific text options for the OpenAI Responses API.
|
|
216
|
+
*/
|
|
167
217
|
readonly openai?: {
|
|
168
218
|
/**
|
|
169
219
|
* The ID of the item to reference.
|
|
@@ -172,18 +222,30 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
172
222
|
/**
|
|
173
223
|
* The status of item.
|
|
174
224
|
*/
|
|
175
|
-
readonly status?: typeof
|
|
225
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
176
226
|
/**
|
|
177
227
|
* A list of annotations that apply to the output text.
|
|
178
228
|
*/
|
|
179
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
229
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
180
230
|
} | null
|
|
181
231
|
}
|
|
182
232
|
}
|
|
183
233
|
|
|
184
234
|
declare module "effect/unstable/ai/Response" {
|
|
235
|
+
/**
|
|
236
|
+
* OpenAI metadata attached to a complete text response part.
|
|
237
|
+
*
|
|
238
|
+
* @category response
|
|
239
|
+
* @since 4.0.0
|
|
240
|
+
*/
|
|
185
241
|
export interface TextPartMetadata extends ProviderMetadata {
|
|
242
|
+
/**
|
|
243
|
+
* Provider-specific metadata returned for the text part.
|
|
244
|
+
*/
|
|
186
245
|
readonly openai?: {
|
|
246
|
+
/**
|
|
247
|
+
* The OpenAI item ID associated with the text part.
|
|
248
|
+
*/
|
|
187
249
|
readonly itemId?: string | null
|
|
188
250
|
/**
|
|
189
251
|
* If the model emits a refusal content part, the refusal explanation
|
|
@@ -194,63 +256,171 @@ declare module "effect/unstable/ai/Response" {
|
|
|
194
256
|
/**
|
|
195
257
|
* The status of item.
|
|
196
258
|
*/
|
|
197
|
-
readonly status?: typeof
|
|
259
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
198
260
|
/**
|
|
199
261
|
* The text content part annotations.
|
|
200
262
|
*/
|
|
201
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
263
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
202
264
|
}
|
|
203
265
|
}
|
|
204
266
|
|
|
267
|
+
/**
|
|
268
|
+
* OpenAI metadata emitted when a streamed text part starts.
|
|
269
|
+
*
|
|
270
|
+
* @category response
|
|
271
|
+
* @since 4.0.0
|
|
272
|
+
*/
|
|
205
273
|
export interface TextStartPartMetadata extends ProviderMetadata {
|
|
274
|
+
/**
|
|
275
|
+
* Provider-specific metadata returned for the streamed text start.
|
|
276
|
+
*/
|
|
206
277
|
readonly openai?: {
|
|
278
|
+
/**
|
|
279
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
280
|
+
*/
|
|
207
281
|
readonly itemId?: string | null
|
|
208
282
|
} | null
|
|
209
283
|
}
|
|
210
284
|
|
|
285
|
+
/**
|
|
286
|
+
* OpenAI metadata emitted when a streamed text part ends.
|
|
287
|
+
*
|
|
288
|
+
* @category response
|
|
289
|
+
* @since 4.0.0
|
|
290
|
+
*/
|
|
211
291
|
export interface TextEndPartMetadata extends ProviderMetadata {
|
|
292
|
+
/**
|
|
293
|
+
* Provider-specific metadata returned for the streamed text end.
|
|
294
|
+
*/
|
|
212
295
|
readonly openai?: {
|
|
296
|
+
/**
|
|
297
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
298
|
+
*/
|
|
213
299
|
readonly itemId?: string | null
|
|
214
|
-
|
|
300
|
+
/**
|
|
301
|
+
* The annotations collected for the completed streamed text part.
|
|
302
|
+
*/
|
|
303
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
215
304
|
} | null
|
|
216
305
|
}
|
|
217
306
|
|
|
307
|
+
/**
|
|
308
|
+
* OpenAI metadata attached to a complete reasoning response part.
|
|
309
|
+
*
|
|
310
|
+
* @category response
|
|
311
|
+
* @since 4.0.0
|
|
312
|
+
*/
|
|
218
313
|
export interface ReasoningPartMetadata extends ProviderMetadata {
|
|
314
|
+
/**
|
|
315
|
+
* Provider-specific metadata returned for the reasoning part.
|
|
316
|
+
*/
|
|
219
317
|
readonly openai?: {
|
|
318
|
+
/**
|
|
319
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
320
|
+
*/
|
|
220
321
|
readonly itemId?: string | null
|
|
322
|
+
/**
|
|
323
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
324
|
+
*/
|
|
221
325
|
readonly encryptedContent?: string | null
|
|
222
326
|
} | null
|
|
223
327
|
}
|
|
224
328
|
|
|
329
|
+
/**
|
|
330
|
+
* OpenAI metadata emitted when a streamed reasoning part starts.
|
|
331
|
+
*
|
|
332
|
+
* @category response
|
|
333
|
+
* @since 4.0.0
|
|
334
|
+
*/
|
|
225
335
|
export interface ReasoningStartPartMetadata extends ProviderMetadata {
|
|
336
|
+
/**
|
|
337
|
+
* Provider-specific metadata returned for the streamed reasoning start.
|
|
338
|
+
*/
|
|
226
339
|
readonly openai?: {
|
|
340
|
+
/**
|
|
341
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
342
|
+
*/
|
|
227
343
|
readonly itemId?: string | null
|
|
344
|
+
/**
|
|
345
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
346
|
+
*/
|
|
228
347
|
readonly encryptedContent?: string | null
|
|
229
348
|
} | null
|
|
230
349
|
}
|
|
231
350
|
|
|
351
|
+
/**
|
|
352
|
+
* OpenAI metadata emitted for a streamed reasoning delta.
|
|
353
|
+
*
|
|
354
|
+
* @category response
|
|
355
|
+
* @since 4.0.0
|
|
356
|
+
*/
|
|
232
357
|
export interface ReasoningDeltaPartMetadata extends ProviderMetadata {
|
|
358
|
+
/**
|
|
359
|
+
* Provider-specific metadata returned for the streamed reasoning delta.
|
|
360
|
+
*/
|
|
233
361
|
readonly openai?: {
|
|
362
|
+
/**
|
|
363
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
364
|
+
*/
|
|
234
365
|
readonly itemId?: string | null
|
|
235
366
|
} | null
|
|
236
367
|
}
|
|
237
368
|
|
|
369
|
+
/**
|
|
370
|
+
* OpenAI metadata emitted when a streamed reasoning part ends.
|
|
371
|
+
*
|
|
372
|
+
* @category response
|
|
373
|
+
* @since 4.0.0
|
|
374
|
+
*/
|
|
238
375
|
export interface ReasoningEndPartMetadata extends ProviderMetadata {
|
|
376
|
+
/**
|
|
377
|
+
* Provider-specific metadata returned for the streamed reasoning end.
|
|
378
|
+
*/
|
|
239
379
|
readonly openai?: {
|
|
380
|
+
/**
|
|
381
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
382
|
+
*/
|
|
240
383
|
readonly itemId?: string | null
|
|
384
|
+
/**
|
|
385
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
386
|
+
*/
|
|
241
387
|
readonly encryptedContent?: string
|
|
242
388
|
} | null
|
|
243
389
|
}
|
|
244
390
|
|
|
391
|
+
/**
|
|
392
|
+
* OpenAI metadata attached to tool-call response parts.
|
|
393
|
+
*
|
|
394
|
+
* @category response
|
|
395
|
+
* @since 4.0.0
|
|
396
|
+
*/
|
|
245
397
|
export interface ToolCallPartMetadata extends ProviderMetadata {
|
|
398
|
+
/**
|
|
399
|
+
* Provider-specific metadata returned for the tool call.
|
|
400
|
+
*/
|
|
246
401
|
readonly openai?: {
|
|
402
|
+
/**
|
|
403
|
+
* The OpenAI item ID associated with the tool call.
|
|
404
|
+
*/
|
|
247
405
|
readonly itemId?: string | null
|
|
248
406
|
} | null
|
|
249
407
|
}
|
|
250
408
|
|
|
409
|
+
/**
|
|
410
|
+
* OpenAI metadata attached to document source citations.
|
|
411
|
+
*
|
|
412
|
+
* @category response
|
|
413
|
+
* @since 4.0.0
|
|
414
|
+
*/
|
|
251
415
|
export interface DocumentSourcePartMetadata extends ProviderMetadata {
|
|
416
|
+
/**
|
|
417
|
+
* Provider-specific citation metadata for the OpenAI Responses API.
|
|
418
|
+
*/
|
|
252
419
|
readonly openai?:
|
|
253
420
|
| {
|
|
421
|
+
/**
|
|
422
|
+
* Identifies a citation to an uploaded file.
|
|
423
|
+
*/
|
|
254
424
|
readonly type: "file_citation"
|
|
255
425
|
/**
|
|
256
426
|
* The index of the file in the list of files.
|
|
@@ -262,6 +432,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
262
432
|
readonly fileId: string
|
|
263
433
|
}
|
|
264
434
|
| {
|
|
435
|
+
/**
|
|
436
|
+
* Identifies a citation to a generated file path.
|
|
437
|
+
*/
|
|
265
438
|
readonly type: "file_path"
|
|
266
439
|
/**
|
|
267
440
|
* The index of the file in the list of files.
|
|
@@ -273,6 +446,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
273
446
|
readonly fileId: string
|
|
274
447
|
}
|
|
275
448
|
| {
|
|
449
|
+
/**
|
|
450
|
+
* Identifies a citation to a file inside a container.
|
|
451
|
+
*/
|
|
276
452
|
readonly type: "container_file_citation"
|
|
277
453
|
/**
|
|
278
454
|
* The ID of the file.
|
|
@@ -286,8 +462,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
286
462
|
| null
|
|
287
463
|
}
|
|
288
464
|
|
|
465
|
+
/**
|
|
466
|
+
* OpenAI metadata attached to URL source citations.
|
|
467
|
+
*
|
|
468
|
+
* @category response
|
|
469
|
+
* @since 4.0.0
|
|
470
|
+
*/
|
|
289
471
|
export interface UrlSourcePartMetadata extends ProviderMetadata {
|
|
472
|
+
/**
|
|
473
|
+
* Provider-specific URL citation metadata for the OpenAI Responses API.
|
|
474
|
+
*/
|
|
290
475
|
readonly openai?: {
|
|
476
|
+
/**
|
|
477
|
+
* Identifies a citation to a URL.
|
|
478
|
+
*/
|
|
291
479
|
readonly type: "url_citation"
|
|
292
480
|
/**
|
|
293
481
|
* The index of the first character of the URL citation in the message.
|
|
@@ -300,8 +488,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
300
488
|
} | null
|
|
301
489
|
}
|
|
302
490
|
|
|
491
|
+
/**
|
|
492
|
+
* OpenAI metadata attached to finish response parts.
|
|
493
|
+
*
|
|
494
|
+
* @category response
|
|
495
|
+
* @since 4.0.0
|
|
496
|
+
*/
|
|
303
497
|
export interface FinishPartMetadata extends ProviderMetadata {
|
|
498
|
+
/**
|
|
499
|
+
* Provider-specific metadata returned when generation finishes.
|
|
500
|
+
*/
|
|
304
501
|
readonly openai?: {
|
|
502
|
+
/**
|
|
503
|
+
* The service tier reported by OpenAI for the response.
|
|
504
|
+
*/
|
|
305
505
|
readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
306
506
|
} | null
|
|
307
507
|
}
|
|
@@ -312,31 +512,33 @@ declare module "effect/unstable/ai/Response" {
|
|
|
312
512
|
// =============================================================================
|
|
313
513
|
|
|
314
514
|
/**
|
|
315
|
-
*
|
|
515
|
+
* Creates an OpenAI language model that can be used with `AiModel.provide`.
|
|
516
|
+
*
|
|
316
517
|
* @category constructors
|
|
518
|
+
* @since 4.0.0
|
|
317
519
|
*/
|
|
318
520
|
export const model = (
|
|
319
521
|
model: (string & {}) | Model,
|
|
320
522
|
config?: Omit<typeof Config.Service, "model">
|
|
321
523
|
): AiModel.Model<"openai", LanguageModel.LanguageModel, OpenAiClient> =>
|
|
322
|
-
AiModel.make("openai", layer({ model, config }))
|
|
524
|
+
AiModel.make("openai", model, layer({ model, config }))
|
|
323
525
|
|
|
324
526
|
// TODO
|
|
325
527
|
// /**
|
|
326
|
-
// * @since
|
|
528
|
+
// * @since 4.0.0
|
|
327
529
|
// * @category constructors
|
|
328
530
|
// */
|
|
329
531
|
// export const modelWithTokenizer = (
|
|
330
532
|
// model: (string & {}) | Model,
|
|
331
533
|
// config?: Omit<typeof Config.Service, "model">
|
|
332
534
|
// ): AiModel.Model<"openai", LanguageModel.LanguageModel | Tokenizer.Tokenizer, OpenAiClient> =>
|
|
333
|
-
// AiModel.make("openai", layerWithTokenizer({ model, config }))
|
|
535
|
+
// AiModel.make("openai", model, layerWithTokenizer({ model, config }))
|
|
334
536
|
|
|
335
537
|
/**
|
|
336
538
|
* Creates an OpenAI language model service.
|
|
337
539
|
*
|
|
338
|
-
* @since 1.0.0
|
|
339
540
|
* @category constructors
|
|
541
|
+
* @since 4.0.0
|
|
340
542
|
*/
|
|
341
543
|
export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
|
|
342
544
|
readonly model: (string & {}) | Model
|
|
@@ -345,7 +547,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
345
547
|
const client = yield* OpenAiClient
|
|
346
548
|
|
|
347
549
|
const makeConfig = Effect.gen(function*() {
|
|
348
|
-
const services = yield* Effect.
|
|
550
|
+
const services = yield* Effect.context<never>()
|
|
349
551
|
return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
|
|
350
552
|
})
|
|
351
553
|
|
|
@@ -354,9 +556,9 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
354
556
|
readonly config: typeof Config.Service
|
|
355
557
|
readonly options: LanguageModel.ProviderOptions
|
|
356
558
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
357
|
-
}): Effect.fn.Return<typeof
|
|
358
|
-
const include = new Set<typeof
|
|
359
|
-
const capabilities = getModelCapabilities(config.model
|
|
559
|
+
}): Effect.fn.Return<typeof OpenAiSchema.CreateResponse.Encoded, AiError.AiError> {
|
|
560
|
+
const include = new Set<typeof OpenAiSchema.IncludeEnum.Encoded>()
|
|
561
|
+
const capabilities = getModelCapabilities(config.model as string)
|
|
360
562
|
const messages = yield* prepareMessages({
|
|
361
563
|
config,
|
|
362
564
|
options,
|
|
@@ -369,26 +571,29 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
369
571
|
options,
|
|
370
572
|
toolNameMapper
|
|
371
573
|
})
|
|
372
|
-
const responseFormat = prepareResponseFormat({
|
|
574
|
+
const responseFormat = yield* prepareResponseFormat({
|
|
373
575
|
config,
|
|
374
576
|
options
|
|
375
577
|
})
|
|
376
|
-
const
|
|
377
|
-
|
|
578
|
+
const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } = config
|
|
579
|
+
const request: Mutable<typeof OpenAiSchema.CreateResponse.Encoded> = {
|
|
580
|
+
...apiConfig,
|
|
378
581
|
input: messages,
|
|
379
|
-
include: include.size > 0 ? Array.from(include) :
|
|
582
|
+
include: include.size > 0 ? Array.from(include) : undefined,
|
|
380
583
|
text: {
|
|
381
|
-
verbosity: config.text?.verbosity ??
|
|
584
|
+
verbosity: config.text?.verbosity ?? undefined,
|
|
382
585
|
format: responseFormat
|
|
383
|
-
}
|
|
384
|
-
...(Predicate.isNotUndefined(tools) ? { tools } : undefined),
|
|
385
|
-
...(Predicate.isNotUndefined(toolChoice) ? { tool_choice: toolChoice } : undefined)
|
|
586
|
+
}
|
|
386
587
|
}
|
|
588
|
+
if (tools) request.tools = tools
|
|
589
|
+
if (toolChoice) request.tool_choice = toolChoice
|
|
590
|
+
if (options.previousResponseId) request.previous_response_id = options.previousResponseId
|
|
387
591
|
return request
|
|
388
592
|
}
|
|
389
593
|
)
|
|
390
594
|
|
|
391
595
|
return yield* LanguageModel.make({
|
|
596
|
+
codecTransformer: toCodecOpenAI,
|
|
392
597
|
generateText: Effect.fnUntraced(
|
|
393
598
|
function*(options) {
|
|
394
599
|
const config = yield* makeConfig
|
|
@@ -435,8 +640,8 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
435
640
|
/**
|
|
436
641
|
* Creates a layer for the OpenAI language model.
|
|
437
642
|
*
|
|
438
|
-
* @since 1.0.0
|
|
439
643
|
* @category layers
|
|
644
|
+
* @since 4.0.0
|
|
440
645
|
*/
|
|
441
646
|
export const layer = (options: {
|
|
442
647
|
readonly model: (string & {}) | Model
|
|
@@ -447,37 +652,37 @@ export const layer = (options: {
|
|
|
447
652
|
/**
|
|
448
653
|
* Provides config overrides for OpenAI language model operations.
|
|
449
654
|
*
|
|
450
|
-
* @since 1.0.0
|
|
451
655
|
* @category configuration
|
|
656
|
+
* @since 4.0.0
|
|
452
657
|
*/
|
|
453
658
|
export const withConfigOverride: {
|
|
454
659
|
/**
|
|
455
660
|
* Provides config overrides for OpenAI language model operations.
|
|
456
661
|
*
|
|
457
|
-
* @since 1.0.0
|
|
458
662
|
* @category configuration
|
|
663
|
+
* @since 4.0.0
|
|
459
664
|
*/
|
|
460
665
|
(overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
461
666
|
/**
|
|
462
667
|
* Provides config overrides for OpenAI language model operations.
|
|
463
668
|
*
|
|
464
|
-
* @since 1.0.0
|
|
465
669
|
* @category configuration
|
|
670
|
+
* @since 4.0.0
|
|
466
671
|
*/
|
|
467
672
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
|
|
468
673
|
} = dual<
|
|
469
674
|
/**
|
|
470
675
|
* Provides config overrides for OpenAI language model operations.
|
|
471
676
|
*
|
|
472
|
-
* @since 1.0.0
|
|
473
677
|
* @category configuration
|
|
678
|
+
* @since 4.0.0
|
|
474
679
|
*/
|
|
475
680
|
(overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
|
|
476
681
|
/**
|
|
477
682
|
* Provides config overrides for OpenAI language model operations.
|
|
478
683
|
*
|
|
479
|
-
* @since 1.0.0
|
|
480
684
|
* @category configuration
|
|
685
|
+
* @since 4.0.0
|
|
481
686
|
*/
|
|
482
687
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
483
688
|
>(2, (self, overrides) =>
|
|
@@ -512,10 +717,10 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
512
717
|
}: {
|
|
513
718
|
readonly config: typeof Config.Service
|
|
514
719
|
readonly options: LanguageModel.ProviderOptions
|
|
515
|
-
readonly include: Set<typeof
|
|
720
|
+
readonly include: Set<typeof OpenAiSchema.IncludeEnum.Encoded>
|
|
516
721
|
readonly capabilities: ModelCapabilities
|
|
517
722
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
518
|
-
}): Effect.fn.Return<ReadonlyArray<typeof
|
|
723
|
+
}): Effect.fn.Return<ReadonlyArray<typeof OpenAiSchema.InputItem.Encoded>, AiError.AiError> {
|
|
519
724
|
const processedApprovalIds = new Set<string>()
|
|
520
725
|
|
|
521
726
|
const hasConversation = Predicate.isNotNullish(config.conversation)
|
|
@@ -547,27 +752,28 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
547
752
|
if (config.store === false && capabilities.isReasoningModel) {
|
|
548
753
|
include.add("reasoning.encrypted_content")
|
|
549
754
|
}
|
|
550
|
-
if (
|
|
755
|
+
if (codeInterpreterTool) {
|
|
551
756
|
include.add("code_interpreter_call.outputs")
|
|
552
757
|
}
|
|
553
|
-
if (
|
|
758
|
+
if (webSearchTool || webSearchPreviewTool) {
|
|
554
759
|
include.add("web_search_call.action.sources")
|
|
555
760
|
}
|
|
556
761
|
|
|
557
|
-
const messages: Array<typeof
|
|
762
|
+
const messages: Array<typeof OpenAiSchema.InputItem.Encoded> = []
|
|
763
|
+
const prompt = options.incrementalPrompt ?? options.prompt
|
|
558
764
|
|
|
559
|
-
for (const message of
|
|
765
|
+
for (const message of prompt.content) {
|
|
560
766
|
switch (message.role) {
|
|
561
767
|
case "system": {
|
|
562
768
|
messages.push({
|
|
563
|
-
role: getSystemMessageMode(config.model
|
|
769
|
+
role: getSystemMessageMode(config.model as string),
|
|
564
770
|
content: message.content
|
|
565
771
|
})
|
|
566
772
|
break
|
|
567
773
|
}
|
|
568
774
|
|
|
569
775
|
case "user": {
|
|
570
|
-
const content: Array<typeof
|
|
776
|
+
const content: Array<typeof OpenAiSchema.InputContent.Encoded> = []
|
|
571
777
|
|
|
572
778
|
for (let index = 0; index < message.content.length; index++) {
|
|
573
779
|
const part = message.content[index]
|
|
@@ -630,7 +836,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
630
836
|
}
|
|
631
837
|
|
|
632
838
|
case "assistant": {
|
|
633
|
-
const reasoningMessages: Record<string, DeepMutable<typeof
|
|
839
|
+
const reasoningMessages: Record<string, DeepMutable<typeof OpenAiSchema.ReasoningItem.Encoded>> = {}
|
|
634
840
|
|
|
635
841
|
for (const part of message.content) {
|
|
636
842
|
switch (part.type) {
|
|
@@ -689,7 +895,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
689
895
|
}
|
|
690
896
|
}
|
|
691
897
|
} else {
|
|
692
|
-
const summaryParts: Array<typeof
|
|
898
|
+
const summaryParts: Array<typeof OpenAiSchema.SummaryTextContent.Encoded> = []
|
|
693
899
|
|
|
694
900
|
if (part.text.length > 0) {
|
|
695
901
|
summaryParts.push({ type: "summary_text", text: part.text })
|
|
@@ -700,7 +906,9 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
700
906
|
type: "reasoning",
|
|
701
907
|
id,
|
|
702
908
|
summary: summaryParts,
|
|
703
|
-
|
|
909
|
+
...(Predicate.isNotNull(encryptedContent)
|
|
910
|
+
? { encrypted_content: encryptedContent }
|
|
911
|
+
: undefined)
|
|
704
912
|
}
|
|
705
913
|
|
|
706
914
|
messages.push(reasoningMessages[id])
|
|
@@ -921,7 +1129,7 @@ const buildHttpRequestDetails = (
|
|
|
921
1129
|
method: request.method,
|
|
922
1130
|
url: request.url,
|
|
923
1131
|
urlParams: Array.from(request.urlParams),
|
|
924
|
-
hash: request.hash,
|
|
1132
|
+
hash: Option.getOrUndefined(request.hash),
|
|
925
1133
|
headers: Redactable.redact(request.headers) as Record<string, string>
|
|
926
1134
|
})
|
|
927
1135
|
|
|
@@ -936,7 +1144,56 @@ const buildHttpResponseDetails = (
|
|
|
936
1144
|
// Response Conversion
|
|
937
1145
|
// =============================================================================
|
|
938
1146
|
|
|
939
|
-
type ResponseStreamEvent = typeof
|
|
1147
|
+
type ResponseStreamEvent = typeof OpenAiSchema.ResponseStreamEvent.Type
|
|
1148
|
+
|
|
1149
|
+
type KnownResponseStreamEventType =
|
|
1150
|
+
| "response.created"
|
|
1151
|
+
| "response.completed"
|
|
1152
|
+
| "response.incomplete"
|
|
1153
|
+
| "response.failed"
|
|
1154
|
+
| "response.output_item.added"
|
|
1155
|
+
| "response.output_item.done"
|
|
1156
|
+
| "response.output_text.delta"
|
|
1157
|
+
| "response.output_text.annotation.added"
|
|
1158
|
+
| "response.reasoning_summary_part.added"
|
|
1159
|
+
| "response.reasoning_summary_part.done"
|
|
1160
|
+
| "response.reasoning_summary_text.delta"
|
|
1161
|
+
| "response.function_call_arguments.delta"
|
|
1162
|
+
| "response.function_call_arguments.done"
|
|
1163
|
+
| "response.code_interpreter_call_code.delta"
|
|
1164
|
+
| "response.code_interpreter_call_code.done"
|
|
1165
|
+
| "response.apply_patch_call_operation_diff.delta"
|
|
1166
|
+
| "response.apply_patch_call_operation_diff.done"
|
|
1167
|
+
| "response.image_generation_call.partial_image"
|
|
1168
|
+
| "error"
|
|
1169
|
+
|
|
1170
|
+
type KnownResponseStreamEvent = Extract<ResponseStreamEvent, { readonly type: KnownResponseStreamEventType }>
|
|
1171
|
+
|
|
1172
|
+
const knownResponseStreamEventTypes = new Set<KnownResponseStreamEventType>([
|
|
1173
|
+
"response.created",
|
|
1174
|
+
"response.completed",
|
|
1175
|
+
"response.incomplete",
|
|
1176
|
+
"response.failed",
|
|
1177
|
+
"response.output_item.added",
|
|
1178
|
+
"response.output_item.done",
|
|
1179
|
+
"response.output_text.delta",
|
|
1180
|
+
"response.output_text.annotation.added",
|
|
1181
|
+
"response.reasoning_summary_part.added",
|
|
1182
|
+
"response.reasoning_summary_part.done",
|
|
1183
|
+
"response.reasoning_summary_text.delta",
|
|
1184
|
+
"response.function_call_arguments.delta",
|
|
1185
|
+
"response.function_call_arguments.done",
|
|
1186
|
+
"response.code_interpreter_call_code.delta",
|
|
1187
|
+
"response.code_interpreter_call_code.done",
|
|
1188
|
+
"response.apply_patch_call_operation_diff.delta",
|
|
1189
|
+
"response.apply_patch_call_operation_diff.done",
|
|
1190
|
+
"response.image_generation_call.partial_image",
|
|
1191
|
+
"error"
|
|
1192
|
+
])
|
|
1193
|
+
|
|
1194
|
+
const isKnownResponseStreamEvent = (
|
|
1195
|
+
event: ResponseStreamEvent
|
|
1196
|
+
): event is KnownResponseStreamEvent => knownResponseStreamEventTypes.has(event.type as KnownResponseStreamEventType)
|
|
940
1197
|
|
|
941
1198
|
const makeResponse = Effect.fnUntraced(
|
|
942
1199
|
function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
@@ -946,7 +1203,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
946
1203
|
toolNameMapper
|
|
947
1204
|
}: {
|
|
948
1205
|
readonly options: LanguageModel.ProviderOptions
|
|
949
|
-
readonly rawResponse:
|
|
1206
|
+
readonly rawResponse: OpenAiSchema.Response
|
|
950
1207
|
readonly response: HttpClientResponse.HttpClientResponse
|
|
951
1208
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
952
1209
|
}): Effect.fn.Return<
|
|
@@ -985,7 +1242,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
985
1242
|
id: part.call_id,
|
|
986
1243
|
name: toolName,
|
|
987
1244
|
params: { call_id: part.call_id, operation: part.operation },
|
|
988
|
-
metadata: { openai:
|
|
1245
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
989
1246
|
})
|
|
990
1247
|
break
|
|
991
1248
|
}
|
|
@@ -1036,10 +1293,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1036
1293
|
|
|
1037
1294
|
case "function_call": {
|
|
1038
1295
|
hasToolCalls = true
|
|
1296
|
+
|
|
1039
1297
|
const toolName = part.name
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
try: () => Tool.unsafeSecureJsonParse(
|
|
1298
|
+
|
|
1299
|
+
const toolParams = yield* Effect.try({
|
|
1300
|
+
try: () => Tool.unsafeSecureJsonParse(part.arguments),
|
|
1043
1301
|
catch: (cause) =>
|
|
1044
1302
|
AiError.make({
|
|
1045
1303
|
module: "OpenAiLanguageModel",
|
|
@@ -1051,12 +1309,15 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1051
1309
|
})
|
|
1052
1310
|
})
|
|
1053
1311
|
})
|
|
1312
|
+
|
|
1313
|
+
const params = yield* transformToolCallParams(options.tools, part.name, toolParams)
|
|
1314
|
+
|
|
1054
1315
|
parts.push({
|
|
1055
1316
|
type: "tool-call",
|
|
1056
1317
|
id: part.call_id,
|
|
1057
1318
|
name: toolName,
|
|
1058
1319
|
params,
|
|
1059
|
-
metadata: { openai:
|
|
1320
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1060
1321
|
})
|
|
1061
1322
|
break
|
|
1062
1323
|
}
|
|
@@ -1087,7 +1348,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1087
1348
|
id: part.call_id,
|
|
1088
1349
|
name: toolName,
|
|
1089
1350
|
params: { action: part.action },
|
|
1090
|
-
metadata: { openai:
|
|
1351
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1091
1352
|
})
|
|
1092
1353
|
break
|
|
1093
1354
|
}
|
|
@@ -1097,13 +1358,17 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1097
1358
|
? (approvalRequests.get(part.approval_request_id) ?? part.id)
|
|
1098
1359
|
: part.id
|
|
1099
1360
|
|
|
1100
|
-
const toolName =
|
|
1361
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1362
|
+
toolNameMapper,
|
|
1363
|
+
toolParams: part.arguments,
|
|
1364
|
+
method: "makeResponse"
|
|
1365
|
+
})
|
|
1101
1366
|
|
|
1102
1367
|
parts.push({
|
|
1103
1368
|
type: "tool-call",
|
|
1104
1369
|
id: toolId,
|
|
1105
1370
|
name: toolName,
|
|
1106
|
-
params
|
|
1371
|
+
params,
|
|
1107
1372
|
providerExecuted: true
|
|
1108
1373
|
})
|
|
1109
1374
|
|
|
@@ -1114,14 +1379,14 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1114
1379
|
isFailure: false,
|
|
1115
1380
|
providerExecuted: true,
|
|
1116
1381
|
result: {
|
|
1117
|
-
type: "
|
|
1382
|
+
type: "mcp_call",
|
|
1118
1383
|
name: part.name,
|
|
1119
1384
|
arguments: part.arguments,
|
|
1120
1385
|
server_label: part.server_label,
|
|
1121
1386
|
...(Predicate.isNotNullish(part.output) ? { output: part.output } : undefined),
|
|
1122
1387
|
...(Predicate.isNotNullish(part.error) ? { error: part.error } : undefined)
|
|
1123
1388
|
},
|
|
1124
|
-
metadata: { openai:
|
|
1389
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1125
1390
|
})
|
|
1126
1391
|
|
|
1127
1392
|
break
|
|
@@ -1135,20 +1400,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1135
1400
|
case "mcp_approval_request": {
|
|
1136
1401
|
const approvalRequestId = (part as any).approval_request_id ?? part.id
|
|
1137
1402
|
const toolId = yield* idGenerator.generateId()
|
|
1138
|
-
const toolName = `mcp.${part.name}`
|
|
1139
1403
|
|
|
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
|
-
})
|
|
1404
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1405
|
+
toolNameMapper,
|
|
1406
|
+
toolParams: part.arguments,
|
|
1407
|
+
method: "makeResponse"
|
|
1152
1408
|
})
|
|
1153
1409
|
|
|
1154
1410
|
parts.push({
|
|
@@ -1296,7 +1552,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1296
1552
|
id: part.call_id,
|
|
1297
1553
|
name: toolName,
|
|
1298
1554
|
params: { action: part.action },
|
|
1299
|
-
metadata: { openai:
|
|
1555
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1300
1556
|
})
|
|
1301
1557
|
break
|
|
1302
1558
|
}
|
|
@@ -1335,7 +1591,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1335
1591
|
reason: finishReason,
|
|
1336
1592
|
usage: getUsage(rawResponse.usage),
|
|
1337
1593
|
response: buildHttpResponseDetails(response),
|
|
1338
|
-
...(rawResponse.service_tier
|
|
1594
|
+
...toServiceTier(rawResponse.service_tier)
|
|
1339
1595
|
})
|
|
1340
1596
|
|
|
1341
1597
|
return parts
|
|
@@ -1368,18 +1624,44 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1368
1624
|
let hasToolCalls = false
|
|
1369
1625
|
|
|
1370
1626
|
// Track annotations for current message to include in text-end metadata
|
|
1371
|
-
const activeAnnotations: Array<typeof
|
|
1627
|
+
const activeAnnotations: Array<typeof OpenAiSchema.Annotation.Encoded> = []
|
|
1628
|
+
|
|
1629
|
+
type ReasoningSummaryPartStatus = "active" | "can-conclude" | "concluded"
|
|
1630
|
+
type ReasoningPart = {
|
|
1631
|
+
encryptedContent: string | undefined
|
|
1632
|
+
summaryParts: Record<number, ReasoningSummaryPartStatus>
|
|
1633
|
+
}
|
|
1372
1634
|
|
|
1373
1635
|
// Track active reasoning items with state machine for proper concluding logic
|
|
1374
|
-
const activeReasoning: Record<string, {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1636
|
+
const activeReasoning: Record<string, ReasoningPart> = {}
|
|
1637
|
+
|
|
1638
|
+
const getOrCreateReasoningPart = (
|
|
1639
|
+
itemId: string,
|
|
1640
|
+
encryptedContent?: string | null
|
|
1641
|
+
): ReasoningPart => {
|
|
1642
|
+
const activePart = activeReasoning[itemId]
|
|
1643
|
+
if (Predicate.isNotUndefined(activePart)) {
|
|
1644
|
+
if (Predicate.isNotNullish(encryptedContent)) {
|
|
1645
|
+
activePart.encryptedContent = encryptedContent
|
|
1646
|
+
}
|
|
1647
|
+
return activePart
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
const reasoningPart: ReasoningPart = {
|
|
1651
|
+
encryptedContent: Predicate.isNotNullish(encryptedContent) ? encryptedContent : undefined,
|
|
1652
|
+
summaryParts: {}
|
|
1653
|
+
}
|
|
1654
|
+
activeReasoning[itemId] = reasoningPart
|
|
1655
|
+
return reasoningPart
|
|
1656
|
+
}
|
|
1378
1657
|
|
|
1379
1658
|
// Track active tool calls with optional provider-specific state
|
|
1380
1659
|
const activeToolCalls: Record<number, {
|
|
1381
1660
|
readonly id: string
|
|
1382
1661
|
readonly name: string
|
|
1662
|
+
readonly functionCall?: {
|
|
1663
|
+
emitted: boolean
|
|
1664
|
+
}
|
|
1383
1665
|
readonly applyPatch?: {
|
|
1384
1666
|
hasDiff: boolean
|
|
1385
1667
|
endEmitted: boolean
|
|
@@ -1399,6 +1681,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1399
1681
|
Stream.mapEffect(Effect.fnUntraced(function*(event) {
|
|
1400
1682
|
const parts: Array<Response.StreamPartEncoded> = []
|
|
1401
1683
|
|
|
1684
|
+
if (!isKnownResponseStreamEvent(event)) {
|
|
1685
|
+
return parts
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1402
1688
|
switch (event.type) {
|
|
1403
1689
|
case "response.created": {
|
|
1404
1690
|
const createdAt = new Date(event.response.created_at * 1000)
|
|
@@ -1428,7 +1714,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1428
1714
|
),
|
|
1429
1715
|
usage: getUsage(event.response.usage),
|
|
1430
1716
|
response: buildHttpResponseDetails(response),
|
|
1431
|
-
...(event.response.service_tier
|
|
1717
|
+
...toServiceTier(event.response.service_tier)
|
|
1432
1718
|
})
|
|
1433
1719
|
break
|
|
1434
1720
|
}
|
|
@@ -1529,7 +1815,8 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1529
1815
|
case "function_call": {
|
|
1530
1816
|
activeToolCalls[event.output_index] = {
|
|
1531
1817
|
id: event.item.call_id,
|
|
1532
|
-
name: event.item.name
|
|
1818
|
+
name: event.item.name,
|
|
1819
|
+
functionCall: { emitted: false }
|
|
1533
1820
|
}
|
|
1534
1821
|
parts.push({
|
|
1535
1822
|
type: "tool-params-start",
|
|
@@ -1566,34 +1853,33 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1566
1853
|
parts.push({
|
|
1567
1854
|
type: "text-start",
|
|
1568
1855
|
id: event.item.id,
|
|
1569
|
-
metadata: { openai:
|
|
1856
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1570
1857
|
})
|
|
1571
1858
|
break
|
|
1572
1859
|
}
|
|
1573
1860
|
|
|
1574
1861
|
case "reasoning": {
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
...makeEncryptedContentMetadata(event.item.encrypted_content)
|
|
1862
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
1863
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[0])) {
|
|
1864
|
+
reasoningPart.summaryParts[0] = "active"
|
|
1865
|
+
parts.push({
|
|
1866
|
+
type: "reasoning-start",
|
|
1867
|
+
id: `${event.item.id}:0`,
|
|
1868
|
+
metadata: {
|
|
1869
|
+
openai: {
|
|
1870
|
+
...makeItemIdMetadata(event.item.id),
|
|
1871
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
1872
|
+
}
|
|
1587
1873
|
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1874
|
+
})
|
|
1875
|
+
}
|
|
1590
1876
|
break
|
|
1591
1877
|
}
|
|
1592
1878
|
|
|
1593
1879
|
case "shell_call": {
|
|
1594
1880
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1595
1881
|
activeToolCalls[event.output_index] = {
|
|
1596
|
-
id: event.item.id,
|
|
1882
|
+
id: event.item.id ?? event.item.call_id,
|
|
1597
1883
|
name: toolName
|
|
1598
1884
|
}
|
|
1599
1885
|
break
|
|
@@ -1644,7 +1930,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1644
1930
|
parts.push({
|
|
1645
1931
|
type: "tool-params-delta",
|
|
1646
1932
|
id: toolCall.id,
|
|
1647
|
-
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff)
|
|
1933
|
+
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff ?? "")
|
|
1648
1934
|
})
|
|
1649
1935
|
}
|
|
1650
1936
|
parts.push({
|
|
@@ -1666,7 +1952,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1666
1952
|
id: toolCall.id,
|
|
1667
1953
|
name: toolName,
|
|
1668
1954
|
params: { call_id: event.item.call_id, operation: event.item.operation },
|
|
1669
|
-
metadata: { openai:
|
|
1955
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1670
1956
|
})
|
|
1671
1957
|
}
|
|
1672
1958
|
delete activeToolCalls[event.output_index]
|
|
@@ -1729,12 +2015,20 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1729
2015
|
}
|
|
1730
2016
|
|
|
1731
2017
|
case "function_call": {
|
|
2018
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2019
|
+
if (Predicate.isNotUndefined(toolCall?.functionCall?.emitted) && toolCall.functionCall.emitted) {
|
|
2020
|
+
delete activeToolCalls[event.output_index]
|
|
2021
|
+
break
|
|
2022
|
+
}
|
|
1732
2023
|
delete activeToolCalls[event.output_index]
|
|
2024
|
+
|
|
1733
2025
|
hasToolCalls = true
|
|
2026
|
+
|
|
1734
2027
|
const toolName = event.item.name
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
2028
|
+
const toolArgs = event.item.arguments
|
|
2029
|
+
|
|
2030
|
+
const toolParams = yield* Effect.try({
|
|
2031
|
+
try: () => Tool.unsafeSecureJsonParse(toolArgs),
|
|
1738
2032
|
catch: (cause) =>
|
|
1739
2033
|
AiError.make({
|
|
1740
2034
|
module: "OpenAiLanguageModel",
|
|
@@ -1746,17 +2040,22 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1746
2040
|
})
|
|
1747
2041
|
})
|
|
1748
2042
|
})
|
|
2043
|
+
|
|
2044
|
+
const params = yield* transformToolCallParams(options.tools, toolName, toolParams)
|
|
2045
|
+
|
|
1749
2046
|
parts.push({
|
|
1750
2047
|
type: "tool-params-end",
|
|
1751
2048
|
id: event.item.call_id
|
|
1752
2049
|
})
|
|
2050
|
+
|
|
1753
2051
|
parts.push({
|
|
1754
2052
|
type: "tool-call",
|
|
1755
2053
|
id: event.item.call_id,
|
|
1756
2054
|
name: toolName,
|
|
1757
2055
|
params,
|
|
1758
|
-
metadata: { openai:
|
|
2056
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1759
2057
|
})
|
|
2058
|
+
|
|
1760
2059
|
break
|
|
1761
2060
|
}
|
|
1762
2061
|
|
|
@@ -1780,7 +2079,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1780
2079
|
id: event.item.call_id,
|
|
1781
2080
|
name: toolName,
|
|
1782
2081
|
params: { action: event.item.action },
|
|
1783
|
-
metadata: { openai:
|
|
2082
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1784
2083
|
})
|
|
1785
2084
|
break
|
|
1786
2085
|
}
|
|
@@ -1794,13 +2093,17 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1794
2093
|
event.item.id)
|
|
1795
2094
|
: event.item.id
|
|
1796
2095
|
|
|
1797
|
-
const toolName =
|
|
2096
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2097
|
+
toolNameMapper,
|
|
2098
|
+
toolParams: event.item.arguments,
|
|
2099
|
+
method: "makeStreamResponse"
|
|
2100
|
+
})
|
|
1798
2101
|
|
|
1799
2102
|
parts.push({
|
|
1800
2103
|
type: "tool-call",
|
|
1801
2104
|
id: toolId,
|
|
1802
2105
|
name: toolName,
|
|
1803
|
-
params
|
|
2106
|
+
params,
|
|
1804
2107
|
providerExecuted: true
|
|
1805
2108
|
})
|
|
1806
2109
|
|
|
@@ -1811,14 +2114,14 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1811
2114
|
isFailure: false,
|
|
1812
2115
|
providerExecuted: true,
|
|
1813
2116
|
result: {
|
|
1814
|
-
type: "
|
|
2117
|
+
type: "mcp_call",
|
|
1815
2118
|
name: event.item.name,
|
|
1816
2119
|
arguments: event.item.arguments,
|
|
1817
2120
|
server_label: event.item.server_label,
|
|
1818
2121
|
...(Predicate.isNotNullish(event.item.output) ? { output: event.item.output } : undefined),
|
|
1819
2122
|
...(Predicate.isNotNullish(event.item.error) ? { error: event.item.error } : undefined)
|
|
1820
2123
|
},
|
|
1821
|
-
metadata: { openai:
|
|
2124
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1822
2125
|
})
|
|
1823
2126
|
|
|
1824
2127
|
break
|
|
@@ -1833,12 +2136,16 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1833
2136
|
const toolId = yield* idGenerator.generateId()
|
|
1834
2137
|
const approvalRequestId = (event.item as any).approval_request_id ?? event.item.id
|
|
1835
2138
|
streamApprovalRequests.set(approvalRequestId, toolId)
|
|
1836
|
-
const toolName =
|
|
2139
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2140
|
+
toolNameMapper,
|
|
2141
|
+
toolParams: event.item.arguments,
|
|
2142
|
+
method: "makeStreamResponse"
|
|
2143
|
+
})
|
|
1837
2144
|
parts.push({
|
|
1838
2145
|
type: "tool-call",
|
|
1839
2146
|
id: toolId,
|
|
1840
2147
|
name: toolName,
|
|
1841
|
-
params
|
|
2148
|
+
params,
|
|
1842
2149
|
providerExecuted: true
|
|
1843
2150
|
})
|
|
1844
2151
|
parts.push({
|
|
@@ -1862,7 +2169,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1862
2169
|
}
|
|
1863
2170
|
|
|
1864
2171
|
case "reasoning": {
|
|
1865
|
-
const reasoningPart =
|
|
2172
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
1866
2173
|
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
1867
2174
|
if (status === "active" || status === "can-conclude") {
|
|
1868
2175
|
parts.push({
|
|
@@ -1871,7 +2178,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1871
2178
|
metadata: {
|
|
1872
2179
|
openai: {
|
|
1873
2180
|
...makeItemIdMetadata(event.item.id),
|
|
1874
|
-
...makeEncryptedContentMetadata(
|
|
2181
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
1875
2182
|
}
|
|
1876
2183
|
}
|
|
1877
2184
|
})
|
|
@@ -1886,10 +2193,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1886
2193
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1887
2194
|
parts.push({
|
|
1888
2195
|
type: "tool-call",
|
|
1889
|
-
id: event.item.id,
|
|
2196
|
+
id: event.item.id ?? event.item.call_id,
|
|
1890
2197
|
name: toolName,
|
|
1891
2198
|
params: { action: event.item.action },
|
|
1892
|
-
metadata: { openai:
|
|
2199
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1893
2200
|
})
|
|
1894
2201
|
break
|
|
1895
2202
|
}
|
|
@@ -1924,7 +2231,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1924
2231
|
}
|
|
1925
2232
|
|
|
1926
2233
|
case "response.output_text.annotation.added": {
|
|
1927
|
-
const annotation = event.annotation as typeof
|
|
2234
|
+
const annotation = event.annotation as typeof OpenAiSchema.Annotation.Encoded
|
|
1928
2235
|
// Track annotation for text-end metadata
|
|
1929
2236
|
activeAnnotations.push(annotation)
|
|
1930
2237
|
if (annotation.type === "container_file_citation") {
|
|
@@ -2006,6 +2313,48 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2006
2313
|
break
|
|
2007
2314
|
}
|
|
2008
2315
|
|
|
2316
|
+
case "response.function_call_arguments.done": {
|
|
2317
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2318
|
+
if (
|
|
2319
|
+
Predicate.isNotUndefined(toolCall?.functionCall) &&
|
|
2320
|
+
!toolCall.functionCall.emitted
|
|
2321
|
+
) {
|
|
2322
|
+
hasToolCalls = true
|
|
2323
|
+
|
|
2324
|
+
const toolParams = yield* Effect.try({
|
|
2325
|
+
try: () => Tool.unsafeSecureJsonParse(event.arguments),
|
|
2326
|
+
catch: (cause) =>
|
|
2327
|
+
AiError.make({
|
|
2328
|
+
module: "OpenAiLanguageModel",
|
|
2329
|
+
method: "makeStreamResponse",
|
|
2330
|
+
reason: new AiError.ToolParameterValidationError({
|
|
2331
|
+
toolName: toolCall.name,
|
|
2332
|
+
toolParams: {},
|
|
2333
|
+
description: `Failed securely JSON parse tool parameters: ${cause}`
|
|
2334
|
+
})
|
|
2335
|
+
})
|
|
2336
|
+
})
|
|
2337
|
+
|
|
2338
|
+
const params = yield* transformToolCallParams(options.tools, toolCall.name, toolParams)
|
|
2339
|
+
|
|
2340
|
+
parts.push({
|
|
2341
|
+
type: "tool-params-end",
|
|
2342
|
+
id: toolCall.id
|
|
2343
|
+
})
|
|
2344
|
+
|
|
2345
|
+
parts.push({
|
|
2346
|
+
type: "tool-call",
|
|
2347
|
+
id: toolCall.id,
|
|
2348
|
+
name: toolCall.name,
|
|
2349
|
+
params,
|
|
2350
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2351
|
+
})
|
|
2352
|
+
|
|
2353
|
+
toolCall.functionCall.emitted = true
|
|
2354
|
+
}
|
|
2355
|
+
break
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2009
2358
|
case "response.apply_patch_call_operation_diff.delta": {
|
|
2010
2359
|
const toolCall = activeToolCalls[event.output_index]
|
|
2011
2360
|
if (Predicate.isNotUndefined(toolCall?.applyPatch)) {
|
|
@@ -2095,28 +2444,28 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2095
2444
|
}
|
|
2096
2445
|
|
|
2097
2446
|
case "response.reasoning_summary_part.added": {
|
|
2098
|
-
|
|
2447
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2099
2448
|
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
|
-
}
|
|
2449
|
+
// Conclude all can-conclude parts before starting new one
|
|
2450
|
+
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
2451
|
+
if (status === "can-conclude") {
|
|
2452
|
+
parts.push({
|
|
2453
|
+
type: "reasoning-end",
|
|
2454
|
+
id: `${event.item_id}:${summaryIndex}`,
|
|
2455
|
+
metadata: {
|
|
2456
|
+
openai: {
|
|
2457
|
+
...makeItemIdMetadata(event.item_id),
|
|
2458
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2113
2459
|
}
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2460
|
+
}
|
|
2461
|
+
})
|
|
2462
|
+
reasoningPart.summaryParts[Number(summaryIndex)] = "concluded"
|
|
2117
2463
|
}
|
|
2118
|
-
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2119
2464
|
}
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[event.summary_index])) {
|
|
2468
|
+
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2120
2469
|
parts.push({
|
|
2121
2470
|
type: "reasoning-start",
|
|
2122
2471
|
id: `${event.item_id}:${event.summary_index}`,
|
|
@@ -2136,26 +2485,27 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2136
2485
|
type: "reasoning-delta",
|
|
2137
2486
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2138
2487
|
delta: event.delta,
|
|
2139
|
-
metadata: { openai:
|
|
2488
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2140
2489
|
})
|
|
2141
2490
|
break
|
|
2142
2491
|
}
|
|
2143
2492
|
|
|
2144
2493
|
case "response.reasoning_summary_part.done": {
|
|
2494
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2145
2495
|
// When OpenAI stores message data, we can immediately conclude the
|
|
2146
2496
|
// reasoning part given that we do not need the encrypted content
|
|
2147
2497
|
if (config.store === true) {
|
|
2148
2498
|
parts.push({
|
|
2149
2499
|
type: "reasoning-end",
|
|
2150
2500
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2151
|
-
metadata: { openai:
|
|
2501
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2152
2502
|
})
|
|
2153
2503
|
// Mark the summary part concluded
|
|
2154
|
-
|
|
2504
|
+
reasoningPart.summaryParts[event.summary_index] = "concluded"
|
|
2155
2505
|
} else {
|
|
2156
2506
|
// Mark the summary part as can-conclude given we still need a
|
|
2157
2507
|
// final summary part with the encrypted content
|
|
2158
|
-
|
|
2508
|
+
reasoningPart.summaryParts[event.summary_index] = "can-conclude"
|
|
2159
2509
|
}
|
|
2160
2510
|
break
|
|
2161
2511
|
}
|
|
@@ -2174,7 +2524,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2174
2524
|
|
|
2175
2525
|
const annotateRequest = (
|
|
2176
2526
|
span: Span,
|
|
2177
|
-
request: typeof
|
|
2527
|
+
request: typeof OpenAiSchema.CreateResponse.Encoded
|
|
2178
2528
|
): void => {
|
|
2179
2529
|
addGenAIAnnotations(span, {
|
|
2180
2530
|
system: "openai",
|
|
@@ -2194,7 +2544,7 @@ const annotateRequest = (
|
|
|
2194
2544
|
})
|
|
2195
2545
|
}
|
|
2196
2546
|
|
|
2197
|
-
const annotateResponse = (span: Span, response:
|
|
2547
|
+
const annotateResponse = (span: Span, response: OpenAiSchema.Response): void => {
|
|
2198
2548
|
const finishReason = response.incomplete_details?.reason as string | undefined
|
|
2199
2549
|
addGenAIAnnotations(span, {
|
|
2200
2550
|
response: {
|
|
@@ -2244,7 +2594,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) =>
|
|
|
2244
2594
|
// Tool Conversion
|
|
2245
2595
|
// =============================================================================
|
|
2246
2596
|
|
|
2247
|
-
type OpenAiToolChoice = typeof
|
|
2597
|
+
type OpenAiToolChoice = typeof OpenAiSchema.CreateResponse.Encoded["tool_choice"]
|
|
2248
2598
|
|
|
2249
2599
|
const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
2250
2600
|
config,
|
|
@@ -2255,7 +2605,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2255
2605
|
readonly options: LanguageModel.ProviderOptions
|
|
2256
2606
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
2257
2607
|
}): Effect.fn.Return<{
|
|
2258
|
-
readonly tools: ReadonlyArray<typeof
|
|
2608
|
+
readonly tools: ReadonlyArray<typeof OpenAiSchema.Tool.Encoded> | undefined
|
|
2259
2609
|
readonly toolChoice: OpenAiToolChoice | undefined
|
|
2260
2610
|
}, AiError.AiError> {
|
|
2261
2611
|
// Return immediately if no tools are in the toolkit
|
|
@@ -2263,7 +2613,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2263
2613
|
return { tools: undefined, toolChoice: undefined }
|
|
2264
2614
|
}
|
|
2265
2615
|
|
|
2266
|
-
const tools: Array<typeof
|
|
2616
|
+
const tools: Array<typeof OpenAiSchema.Tool.Encoded> = []
|
|
2267
2617
|
let toolChoice: OpenAiToolChoice | undefined = undefined
|
|
2268
2618
|
|
|
2269
2619
|
// Filter the incoming tools down to the set of allowed tools as indicated by
|
|
@@ -2279,14 +2629,16 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2279
2629
|
|
|
2280
2630
|
// Convert the tools in the toolkit to the provider-defined format
|
|
2281
2631
|
for (const tool of allowedTools) {
|
|
2282
|
-
if (Tool.isUserDefined(tool)) {
|
|
2632
|
+
if (Tool.isUserDefined(tool) || Tool.isDynamic(tool)) {
|
|
2283
2633
|
const strict = Tool.getStrictMode(tool) ?? config.strictJsonSchema ?? true
|
|
2634
|
+
const description = Tool.getDescription(tool)
|
|
2635
|
+
const parameters = yield* tryToolJsonSchema(tool, "prepareTools")
|
|
2284
2636
|
tools.push({
|
|
2285
2637
|
type: "function",
|
|
2286
2638
|
name: tool.name,
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2639
|
+
parameters,
|
|
2640
|
+
strict,
|
|
2641
|
+
...(Predicate.isNotUndefined(description) ? { description } : undefined)
|
|
2290
2642
|
})
|
|
2291
2643
|
}
|
|
2292
2644
|
|
|
@@ -2468,35 +2820,63 @@ const getStatus = (
|
|
|
2468
2820
|
| Prompt.TextPart
|
|
2469
2821
|
| Prompt.ToolCallPart
|
|
2470
2822
|
| Prompt.ToolResultPart
|
|
2471
|
-
): typeof
|
|
2823
|
+
): typeof OpenAiSchema.MessageStatus.Encoded | null => part.options.openai?.status ?? null
|
|
2472
2824
|
const getEncryptedContent = (
|
|
2473
2825
|
part: Prompt.ReasoningPart
|
|
2474
2826
|
): string | null => part.options.openai?.encryptedContent ?? null
|
|
2475
2827
|
|
|
2476
2828
|
const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options.openai?.imageDetail ?? "auto"
|
|
2477
2829
|
|
|
2478
|
-
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } :
|
|
2830
|
+
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } : {}
|
|
2479
2831
|
|
|
2480
2832
|
const makeEncryptedContentMetadata = (encryptedContent: string | null | undefined) =>
|
|
2481
2833
|
Predicate.isNotNullish(encryptedContent) ? { encryptedContent } : undefined
|
|
2482
2834
|
|
|
2483
|
-
const
|
|
2835
|
+
const unsupportedSchemaError = (error: unknown, method: string): AiError.AiError =>
|
|
2836
|
+
AiError.make({
|
|
2837
|
+
module: "OpenAiLanguageModel",
|
|
2838
|
+
method,
|
|
2839
|
+
reason: new AiError.UnsupportedSchemaError({
|
|
2840
|
+
description: error instanceof Error ? error.message : String(error)
|
|
2841
|
+
})
|
|
2842
|
+
})
|
|
2843
|
+
|
|
2844
|
+
const tryCodecTransform = <S extends Schema.Top>(schema: S, method: string) =>
|
|
2845
|
+
Effect.try({
|
|
2846
|
+
try: () => toCodecOpenAI(schema),
|
|
2847
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2848
|
+
})
|
|
2849
|
+
|
|
2850
|
+
const tryJsonSchema = <S extends Schema.Top>(schema: S, method: string) =>
|
|
2851
|
+
Effect.try({
|
|
2852
|
+
try: () => Tool.getJsonSchemaFromSchema(schema, { transformer: toCodecOpenAI }),
|
|
2853
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2854
|
+
})
|
|
2855
|
+
|
|
2856
|
+
const tryToolJsonSchema = <T extends Tool.Any>(tool: T, method: string) =>
|
|
2857
|
+
Effect.try({
|
|
2858
|
+
try: () => Tool.getJsonSchema(tool, { transformer: toCodecOpenAI }),
|
|
2859
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2860
|
+
})
|
|
2861
|
+
|
|
2862
|
+
const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: {
|
|
2484
2863
|
readonly config: typeof Config.Service
|
|
2485
2864
|
readonly options: LanguageModel.ProviderOptions
|
|
2486
|
-
}): typeof
|
|
2865
|
+
}): Effect.fn.Return<typeof OpenAiSchema.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
|
|
2487
2866
|
if (options.responseFormat.type === "json") {
|
|
2488
2867
|
const name = options.responseFormat.objectName
|
|
2489
2868
|
const schema = options.responseFormat.schema
|
|
2869
|
+
const jsonSchema = yield* tryJsonSchema(schema, "prepareResponseFormat")
|
|
2490
2870
|
return {
|
|
2491
2871
|
type: "json_schema",
|
|
2492
2872
|
name,
|
|
2493
2873
|
description: AST.resolveDescription(schema.ast) ?? "Response with a JSON object",
|
|
2494
|
-
schema:
|
|
2874
|
+
schema: jsonSchema,
|
|
2495
2875
|
strict: config.strictJsonSchema ?? true
|
|
2496
2876
|
}
|
|
2497
2877
|
}
|
|
2498
2878
|
return { type: "text" }
|
|
2499
|
-
}
|
|
2879
|
+
})
|
|
2500
2880
|
|
|
2501
2881
|
interface ModelCapabilities {
|
|
2502
2882
|
readonly isReasoningModel: boolean
|
|
@@ -2570,7 +2950,42 @@ const getApprovalRequestIdMapping = (prompt: Prompt.Prompt): ReadonlyMap<string,
|
|
|
2570
2950
|
return mapping
|
|
2571
2951
|
}
|
|
2572
2952
|
|
|
2573
|
-
const
|
|
2953
|
+
const normalizeMcpToolCall = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
2954
|
+
toolNameMapper,
|
|
2955
|
+
toolParams,
|
|
2956
|
+
method
|
|
2957
|
+
}: {
|
|
2958
|
+
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
2959
|
+
readonly toolParams: unknown
|
|
2960
|
+
readonly method: string
|
|
2961
|
+
}): Effect.fn.Return<{
|
|
2962
|
+
readonly toolName: string
|
|
2963
|
+
readonly params: unknown
|
|
2964
|
+
}, AiError.AiError> {
|
|
2965
|
+
const toolName = toolNameMapper.getCustomName("mcp")
|
|
2966
|
+
|
|
2967
|
+
if (typeof toolParams !== "string") {
|
|
2968
|
+
return { toolName, params: toolParams }
|
|
2969
|
+
}
|
|
2970
|
+
|
|
2971
|
+
const params = yield* Effect.try({
|
|
2972
|
+
try: () => Tool.unsafeSecureJsonParse(toolParams),
|
|
2973
|
+
catch: (cause) =>
|
|
2974
|
+
AiError.make({
|
|
2975
|
+
module: "OpenAiLanguageModel",
|
|
2976
|
+
method,
|
|
2977
|
+
reason: new AiError.ToolParameterValidationError({
|
|
2978
|
+
toolName,
|
|
2979
|
+
toolParams,
|
|
2980
|
+
description: `Failed to securely JSON parse tool parameters: ${cause}`
|
|
2981
|
+
})
|
|
2982
|
+
})
|
|
2983
|
+
})
|
|
2984
|
+
|
|
2985
|
+
return { toolName, params }
|
|
2986
|
+
})
|
|
2987
|
+
|
|
2988
|
+
const getUsage = (usage: OpenAiSchema.ResponseUsage | null | undefined): Response.Usage => {
|
|
2574
2989
|
if (Predicate.isNullish(usage)) {
|
|
2575
2990
|
return {
|
|
2576
2991
|
inputTokens: {
|
|
@@ -2589,8 +3004,8 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2589
3004
|
|
|
2590
3005
|
const inputTokens = usage.input_tokens
|
|
2591
3006
|
const outputTokens = usage.output_tokens
|
|
2592
|
-
const cachedTokens = usage.input_tokens_details
|
|
2593
|
-
const reasoningTokens = usage.output_tokens_details
|
|
3007
|
+
const cachedTokens = getUsageTokenDetail(usage.input_tokens_details, "cached_tokens")
|
|
3008
|
+
const reasoningTokens = getUsageTokenDetail(usage.output_tokens_details, "reasoning_tokens")
|
|
2594
3009
|
|
|
2595
3010
|
return {
|
|
2596
3011
|
inputTokens: {
|
|
@@ -2606,3 +3021,64 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2606
3021
|
}
|
|
2607
3022
|
}
|
|
2608
3023
|
}
|
|
3024
|
+
|
|
3025
|
+
type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
3026
|
+
|
|
3027
|
+
const toServiceTier = (value: string | undefined): {
|
|
3028
|
+
readonly metadata: {
|
|
3029
|
+
readonly openai: {
|
|
3030
|
+
readonly serviceTier: ServiceTier
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
} | undefined => {
|
|
3034
|
+
switch (value) {
|
|
3035
|
+
case "default":
|
|
3036
|
+
case "auto":
|
|
3037
|
+
case "flex":
|
|
3038
|
+
case "scale":
|
|
3039
|
+
case "priority":
|
|
3040
|
+
return { metadata: { openai: { serviceTier: value } } }
|
|
3041
|
+
default:
|
|
3042
|
+
return undefined
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
const getUsageTokenDetail = (details: unknown, key: string): number =>
|
|
3047
|
+
Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : 0
|
|
3048
|
+
|
|
3049
|
+
const transformToolCallParams = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>(
|
|
3050
|
+
tools: Tools,
|
|
3051
|
+
toolName: string,
|
|
3052
|
+
toolParams: unknown
|
|
3053
|
+
): Effect.fn.Return<unknown, AiError.AiError> {
|
|
3054
|
+
const tool = tools.find((tool) => tool.name === toolName)
|
|
3055
|
+
|
|
3056
|
+
if (Predicate.isUndefined(tool)) {
|
|
3057
|
+
return yield* AiError.make({
|
|
3058
|
+
module: "OpenAiLanguageModel",
|
|
3059
|
+
method: "makeResponse",
|
|
3060
|
+
reason: new AiError.ToolNotFoundError({
|
|
3061
|
+
toolName,
|
|
3062
|
+
availableTools: tools.map((tool) => tool.name)
|
|
3063
|
+
})
|
|
3064
|
+
})
|
|
3065
|
+
}
|
|
3066
|
+
|
|
3067
|
+
const { codec } = yield* tryCodecTransform(tool.parametersSchema, "makeResponse")
|
|
3068
|
+
|
|
3069
|
+
const transform = Schema.decodeEffect(codec)
|
|
3070
|
+
|
|
3071
|
+
return yield* (
|
|
3072
|
+
transform(toolParams) as Effect.Effect<unknown, Schema.SchemaError>
|
|
3073
|
+
).pipe(Effect.mapError((error) =>
|
|
3074
|
+
AiError.make({
|
|
3075
|
+
module: "OpenAiLanguageModel",
|
|
3076
|
+
method: "makeResponse",
|
|
3077
|
+
reason: new AiError.ToolParameterValidationError({
|
|
3078
|
+
toolName,
|
|
3079
|
+
toolParams,
|
|
3080
|
+
description: error.issue.toString()
|
|
3081
|
+
})
|
|
3082
|
+
})
|
|
3083
|
+
))
|
|
3084
|
+
})
|