@effect/ai-openai 4.0.0-beta.9 → 4.0.0-beta.90
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 +66675 -37672
- 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 +170 -29
- package/dist/OpenAiClient.d.ts.map +1 -1
- package/dist/OpenAiClient.js +321 -46
- 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 +88 -10
- package/dist/OpenAiConfig.d.ts.map +1 -1
- package/dist/OpenAiConfig.js +42 -7
- package/dist/OpenAiConfig.js.map +1 -1
- package/dist/OpenAiEmbeddingModel.d.ts +188 -0
- package/dist/OpenAiEmbeddingModel.d.ts.map +1 -0
- package/dist/OpenAiEmbeddingModel.js +194 -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 +357 -63
- package/dist/OpenAiLanguageModel.d.ts.map +1 -1
- package/dist/OpenAiLanguageModel.js +390 -168
- package/dist/OpenAiLanguageModel.js.map +1 -1
- package/dist/OpenAiSchema.d.ts +2325 -0
- package/dist/OpenAiSchema.d.ts.map +1 -0
- package/dist/OpenAiSchema.js +811 -0
- package/dist/OpenAiSchema.js.map +1 -0
- package/dist/OpenAiTelemetry.d.ts +63 -22
- package/dist/OpenAiTelemetry.d.ts.map +1 -1
- package/dist/OpenAiTelemetry.js +20 -10
- package/dist/OpenAiTelemetry.js.map +1 -1
- package/dist/OpenAiTool.d.ts +157 -67
- package/dist/OpenAiTool.d.ts.map +1 -1
- package/dist/OpenAiTool.js +125 -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/dist/internal/utilities.js +0 -4
- package/dist/internal/utilities.js.map +1 -1
- package/package.json +3 -3
- package/src/Generated.ts +9720 -4890
- package/src/OpenAiClient.ts +499 -98
- package/src/OpenAiClientGenerated.ts +202 -0
- package/src/OpenAiConfig.ts +89 -11
- package/src/OpenAiEmbeddingModel.ts +332 -0
- package/src/OpenAiError.ts +170 -35
- package/src/OpenAiLanguageModel.ts +776 -169
- package/src/OpenAiSchema.ts +1286 -0
- package/src/OpenAiTelemetry.ts +69 -28
- package/src/OpenAiTool.ts +126 -40
- package/src/index.ts +22 -33
- package/src/internal/errors.ts +6 -4
- package/src/internal/utilities.ts +0 -6
|
@@ -1,28 +1,32 @@
|
|
|
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
|
+
* non-streaming or streaming response results back into Effect AI response
|
|
7
|
+
* content and metadata.
|
|
3
8
|
*
|
|
4
|
-
*
|
|
5
|
-
* supporting text generation, structured output, tool calling, and streaming.
|
|
6
|
-
*
|
|
7
|
-
* @since 1.0.0
|
|
9
|
+
* @since 4.0.0
|
|
8
10
|
*/
|
|
11
|
+
import * as Context from "effect/Context"
|
|
9
12
|
import * as DateTime from "effect/DateTime"
|
|
10
13
|
import * as Effect from "effect/Effect"
|
|
11
14
|
import * as Encoding from "effect/Encoding"
|
|
12
15
|
import { dual } from "effect/Function"
|
|
13
16
|
import * as Layer from "effect/Layer"
|
|
17
|
+
import * as Option from "effect/Option"
|
|
14
18
|
import * as Predicate from "effect/Predicate"
|
|
15
19
|
import * as Redactable from "effect/Redactable"
|
|
16
20
|
import * as Schema from "effect/Schema"
|
|
17
21
|
import * as AST from "effect/SchemaAST"
|
|
18
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
19
22
|
import * as Stream from "effect/Stream"
|
|
20
23
|
import type { Span } from "effect/Tracer"
|
|
21
|
-
import type { DeepMutable, Simplify } from "effect/Types"
|
|
24
|
+
import type { DeepMutable, Mutable, Simplify } from "effect/Types"
|
|
22
25
|
import * as AiError from "effect/unstable/ai/AiError"
|
|
23
26
|
import * as IdGenerator from "effect/unstable/ai/IdGenerator"
|
|
24
27
|
import * as LanguageModel from "effect/unstable/ai/LanguageModel"
|
|
25
28
|
import * as AiModel from "effect/unstable/ai/Model"
|
|
29
|
+
import { toCodecOpenAI } from "effect/unstable/ai/OpenAiStructuredOutput"
|
|
26
30
|
import type * as Prompt from "effect/unstable/ai/Prompt"
|
|
27
31
|
import type * as Response from "effect/unstable/ai/Response"
|
|
28
32
|
import * as Tool from "effect/unstable/ai/Tool"
|
|
@@ -31,6 +35,7 @@ import type * as HttpClientResponse from "effect/unstable/http/HttpClientRespons
|
|
|
31
35
|
import * as Generated from "./Generated.ts"
|
|
32
36
|
import * as InternalUtilities from "./internal/utilities.ts"
|
|
33
37
|
import { OpenAiClient } from "./OpenAiClient.ts"
|
|
38
|
+
import type * as OpenAiSchema from "./OpenAiSchema.ts"
|
|
34
39
|
import { addGenAIAnnotations } from "./OpenAiTelemetry.ts"
|
|
35
40
|
import type * as OpenAiTool from "./OpenAiTool.ts"
|
|
36
41
|
|
|
@@ -38,8 +43,10 @@ const ResponseModelIds = Generated.ModelIdsResponses.members[1]
|
|
|
38
43
|
const SharedModelIds = Generated.ModelIdsShared.members[1]
|
|
39
44
|
|
|
40
45
|
/**
|
|
41
|
-
*
|
|
46
|
+
* OpenAI model identifiers supported by the Responses API language model.
|
|
47
|
+
*
|
|
42
48
|
* @category models
|
|
49
|
+
* @since 4.0.0
|
|
43
50
|
*/
|
|
44
51
|
export type Model = typeof ResponseModelIds.Encoded | typeof SharedModelIds.Encoded
|
|
45
52
|
|
|
@@ -53,17 +60,29 @@ type ImageDetail = "auto" | "low" | "high"
|
|
|
53
60
|
// =============================================================================
|
|
54
61
|
|
|
55
62
|
/**
|
|
56
|
-
*
|
|
63
|
+
* Context service for OpenAI language model configuration.
|
|
64
|
+
*
|
|
65
|
+
* **When to use**
|
|
66
|
+
*
|
|
67
|
+
* Use when you need to provide OpenAI Responses API request defaults through
|
|
68
|
+
* Effect context for language model operations.
|
|
69
|
+
*
|
|
70
|
+
* **Details**
|
|
71
|
+
*
|
|
72
|
+
* Config values are merged with the config object passed to `model`, `make`, or
|
|
73
|
+
* `layer`, with scoped context values taking precedence.
|
|
74
|
+
*
|
|
75
|
+
* @see {@link withConfigOverride} for scoping language model request overrides
|
|
57
76
|
*
|
|
58
|
-
* @since 1.0.0
|
|
59
77
|
* @category services
|
|
78
|
+
* @since 4.0.0
|
|
60
79
|
*/
|
|
61
|
-
export class Config extends
|
|
80
|
+
export class Config extends Context.Service<
|
|
62
81
|
Config,
|
|
63
82
|
Simplify<
|
|
64
83
|
& Partial<
|
|
65
84
|
Omit<
|
|
66
|
-
typeof
|
|
85
|
+
typeof OpenAiSchema.CreateResponse.Encoded,
|
|
67
86
|
"input" | "tools" | "tool_choice" | "stream" | "text"
|
|
68
87
|
>
|
|
69
88
|
>
|
|
@@ -105,7 +124,16 @@ export class Config extends ServiceMap.Service<
|
|
|
105
124
|
// =============================================================================
|
|
106
125
|
|
|
107
126
|
declare module "effect/unstable/ai/Prompt" {
|
|
127
|
+
/**
|
|
128
|
+
* OpenAI-specific options for file prompt parts.
|
|
129
|
+
*
|
|
130
|
+
* @category request
|
|
131
|
+
* @since 4.0.0
|
|
132
|
+
*/
|
|
108
133
|
export interface FilePartOptions extends ProviderOptions {
|
|
134
|
+
/**
|
|
135
|
+
* Provider-specific file options for the OpenAI Responses API.
|
|
136
|
+
*/
|
|
109
137
|
readonly openai?: {
|
|
110
138
|
/**
|
|
111
139
|
* The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. Defaults to `auto`.
|
|
@@ -114,7 +142,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
114
142
|
} | null
|
|
115
143
|
}
|
|
116
144
|
|
|
145
|
+
/**
|
|
146
|
+
* OpenAI-specific options for reasoning prompt parts.
|
|
147
|
+
*
|
|
148
|
+
* @category request
|
|
149
|
+
* @since 4.0.0
|
|
150
|
+
*/
|
|
117
151
|
export interface ReasoningPartOptions extends ProviderOptions {
|
|
152
|
+
/**
|
|
153
|
+
* Provider-specific reasoning options for the OpenAI Responses API.
|
|
154
|
+
*/
|
|
118
155
|
readonly openai?: {
|
|
119
156
|
/**
|
|
120
157
|
* The ID of the item to reference.
|
|
@@ -129,7 +166,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
129
166
|
} | null
|
|
130
167
|
}
|
|
131
168
|
|
|
169
|
+
/**
|
|
170
|
+
* OpenAI-specific options for assistant tool-call prompt parts.
|
|
171
|
+
*
|
|
172
|
+
* @category request
|
|
173
|
+
* @since 4.0.0
|
|
174
|
+
*/
|
|
132
175
|
export interface ToolCallPartOptions extends ProviderOptions {
|
|
176
|
+
/**
|
|
177
|
+
* Provider-specific tool-call options for the OpenAI Responses API.
|
|
178
|
+
*/
|
|
133
179
|
readonly openai?: {
|
|
134
180
|
/**
|
|
135
181
|
* The ID of the item to reference.
|
|
@@ -138,7 +184,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
138
184
|
/**
|
|
139
185
|
* The status of item.
|
|
140
186
|
*/
|
|
141
|
-
readonly status?: typeof
|
|
187
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
142
188
|
/**
|
|
143
189
|
* The ID of the approval request.
|
|
144
190
|
*/
|
|
@@ -146,7 +192,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
146
192
|
} | null
|
|
147
193
|
}
|
|
148
194
|
|
|
195
|
+
/**
|
|
196
|
+
* OpenAI-specific options for tool-result prompt parts.
|
|
197
|
+
*
|
|
198
|
+
* @category request
|
|
199
|
+
* @since 4.0.0
|
|
200
|
+
*/
|
|
149
201
|
export interface ToolResultPartOptions extends ProviderOptions {
|
|
202
|
+
/**
|
|
203
|
+
* Provider-specific tool-result options for the OpenAI Responses API.
|
|
204
|
+
*/
|
|
150
205
|
readonly openai?: {
|
|
151
206
|
/**
|
|
152
207
|
* The ID of the item to reference.
|
|
@@ -155,7 +210,7 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
155
210
|
/**
|
|
156
211
|
* The status of item.
|
|
157
212
|
*/
|
|
158
|
-
readonly status?: typeof
|
|
213
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
159
214
|
/**
|
|
160
215
|
* The ID of the approval request.
|
|
161
216
|
*/
|
|
@@ -163,7 +218,16 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
163
218
|
} | null
|
|
164
219
|
}
|
|
165
220
|
|
|
221
|
+
/**
|
|
222
|
+
* OpenAI-specific options for text prompt parts.
|
|
223
|
+
*
|
|
224
|
+
* @category request
|
|
225
|
+
* @since 4.0.0
|
|
226
|
+
*/
|
|
166
227
|
export interface TextPartOptions extends ProviderOptions {
|
|
228
|
+
/**
|
|
229
|
+
* Provider-specific text options for the OpenAI Responses API.
|
|
230
|
+
*/
|
|
167
231
|
readonly openai?: {
|
|
168
232
|
/**
|
|
169
233
|
* The ID of the item to reference.
|
|
@@ -172,18 +236,30 @@ declare module "effect/unstable/ai/Prompt" {
|
|
|
172
236
|
/**
|
|
173
237
|
* The status of item.
|
|
174
238
|
*/
|
|
175
|
-
readonly status?: typeof
|
|
239
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
176
240
|
/**
|
|
177
241
|
* A list of annotations that apply to the output text.
|
|
178
242
|
*/
|
|
179
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
243
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
180
244
|
} | null
|
|
181
245
|
}
|
|
182
246
|
}
|
|
183
247
|
|
|
184
248
|
declare module "effect/unstable/ai/Response" {
|
|
249
|
+
/**
|
|
250
|
+
* OpenAI metadata attached to a complete text response part.
|
|
251
|
+
*
|
|
252
|
+
* @category response
|
|
253
|
+
* @since 4.0.0
|
|
254
|
+
*/
|
|
185
255
|
export interface TextPartMetadata extends ProviderMetadata {
|
|
256
|
+
/**
|
|
257
|
+
* Provider-specific metadata returned for the text part.
|
|
258
|
+
*/
|
|
186
259
|
readonly openai?: {
|
|
260
|
+
/**
|
|
261
|
+
* The OpenAI item ID associated with the text part.
|
|
262
|
+
*/
|
|
187
263
|
readonly itemId?: string | null
|
|
188
264
|
/**
|
|
189
265
|
* If the model emits a refusal content part, the refusal explanation
|
|
@@ -194,63 +270,171 @@ declare module "effect/unstable/ai/Response" {
|
|
|
194
270
|
/**
|
|
195
271
|
* The status of item.
|
|
196
272
|
*/
|
|
197
|
-
readonly status?: typeof
|
|
273
|
+
readonly status?: typeof OpenAiSchema.MessageStatus.Encoded | null
|
|
198
274
|
/**
|
|
199
275
|
* The text content part annotations.
|
|
200
276
|
*/
|
|
201
|
-
readonly annotations?: ReadonlyArray<typeof
|
|
277
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
202
278
|
}
|
|
203
279
|
}
|
|
204
280
|
|
|
281
|
+
/**
|
|
282
|
+
* OpenAI metadata emitted when a streamed text part starts.
|
|
283
|
+
*
|
|
284
|
+
* @category response
|
|
285
|
+
* @since 4.0.0
|
|
286
|
+
*/
|
|
205
287
|
export interface TextStartPartMetadata extends ProviderMetadata {
|
|
288
|
+
/**
|
|
289
|
+
* Provider-specific metadata returned for the streamed text start.
|
|
290
|
+
*/
|
|
206
291
|
readonly openai?: {
|
|
292
|
+
/**
|
|
293
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
294
|
+
*/
|
|
207
295
|
readonly itemId?: string | null
|
|
208
296
|
} | null
|
|
209
297
|
}
|
|
210
298
|
|
|
299
|
+
/**
|
|
300
|
+
* OpenAI metadata emitted when a streamed text part ends.
|
|
301
|
+
*
|
|
302
|
+
* @category response
|
|
303
|
+
* @since 4.0.0
|
|
304
|
+
*/
|
|
211
305
|
export interface TextEndPartMetadata extends ProviderMetadata {
|
|
306
|
+
/**
|
|
307
|
+
* Provider-specific metadata returned for the streamed text end.
|
|
308
|
+
*/
|
|
212
309
|
readonly openai?: {
|
|
310
|
+
/**
|
|
311
|
+
* The OpenAI item ID associated with the streamed text part.
|
|
312
|
+
*/
|
|
213
313
|
readonly itemId?: string | null
|
|
214
|
-
|
|
314
|
+
/**
|
|
315
|
+
* The annotations collected for the completed streamed text part.
|
|
316
|
+
*/
|
|
317
|
+
readonly annotations?: ReadonlyArray<typeof OpenAiSchema.Annotation.Encoded> | null
|
|
215
318
|
} | null
|
|
216
319
|
}
|
|
217
320
|
|
|
321
|
+
/**
|
|
322
|
+
* OpenAI metadata attached to a complete reasoning response part.
|
|
323
|
+
*
|
|
324
|
+
* @category response
|
|
325
|
+
* @since 4.0.0
|
|
326
|
+
*/
|
|
218
327
|
export interface ReasoningPartMetadata extends ProviderMetadata {
|
|
328
|
+
/**
|
|
329
|
+
* Provider-specific metadata returned for the reasoning part.
|
|
330
|
+
*/
|
|
219
331
|
readonly openai?: {
|
|
332
|
+
/**
|
|
333
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
334
|
+
*/
|
|
220
335
|
readonly itemId?: string | null
|
|
336
|
+
/**
|
|
337
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
338
|
+
*/
|
|
221
339
|
readonly encryptedContent?: string | null
|
|
222
340
|
} | null
|
|
223
341
|
}
|
|
224
342
|
|
|
343
|
+
/**
|
|
344
|
+
* OpenAI metadata emitted when a streamed reasoning part starts.
|
|
345
|
+
*
|
|
346
|
+
* @category response
|
|
347
|
+
* @since 4.0.0
|
|
348
|
+
*/
|
|
225
349
|
export interface ReasoningStartPartMetadata extends ProviderMetadata {
|
|
350
|
+
/**
|
|
351
|
+
* Provider-specific metadata returned for the streamed reasoning start.
|
|
352
|
+
*/
|
|
226
353
|
readonly openai?: {
|
|
354
|
+
/**
|
|
355
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
356
|
+
*/
|
|
227
357
|
readonly itemId?: string | null
|
|
358
|
+
/**
|
|
359
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
360
|
+
*/
|
|
228
361
|
readonly encryptedContent?: string | null
|
|
229
362
|
} | null
|
|
230
363
|
}
|
|
231
364
|
|
|
365
|
+
/**
|
|
366
|
+
* OpenAI metadata emitted for a streamed reasoning delta.
|
|
367
|
+
*
|
|
368
|
+
* @category response
|
|
369
|
+
* @since 4.0.0
|
|
370
|
+
*/
|
|
232
371
|
export interface ReasoningDeltaPartMetadata extends ProviderMetadata {
|
|
372
|
+
/**
|
|
373
|
+
* Provider-specific metadata returned for the streamed reasoning delta.
|
|
374
|
+
*/
|
|
233
375
|
readonly openai?: {
|
|
376
|
+
/**
|
|
377
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
378
|
+
*/
|
|
234
379
|
readonly itemId?: string | null
|
|
235
380
|
} | null
|
|
236
381
|
}
|
|
237
382
|
|
|
383
|
+
/**
|
|
384
|
+
* OpenAI metadata emitted when a streamed reasoning part ends.
|
|
385
|
+
*
|
|
386
|
+
* @category response
|
|
387
|
+
* @since 4.0.0
|
|
388
|
+
*/
|
|
238
389
|
export interface ReasoningEndPartMetadata extends ProviderMetadata {
|
|
390
|
+
/**
|
|
391
|
+
* Provider-specific metadata returned for the streamed reasoning end.
|
|
392
|
+
*/
|
|
239
393
|
readonly openai?: {
|
|
394
|
+
/**
|
|
395
|
+
* The OpenAI item ID associated with the reasoning part.
|
|
396
|
+
*/
|
|
240
397
|
readonly itemId?: string | null
|
|
398
|
+
/**
|
|
399
|
+
* Encrypted reasoning content that can be sent back in later requests.
|
|
400
|
+
*/
|
|
241
401
|
readonly encryptedContent?: string
|
|
242
402
|
} | null
|
|
243
403
|
}
|
|
244
404
|
|
|
405
|
+
/**
|
|
406
|
+
* OpenAI metadata attached to tool-call response parts.
|
|
407
|
+
*
|
|
408
|
+
* @category response
|
|
409
|
+
* @since 4.0.0
|
|
410
|
+
*/
|
|
245
411
|
export interface ToolCallPartMetadata extends ProviderMetadata {
|
|
412
|
+
/**
|
|
413
|
+
* Provider-specific metadata returned for the tool call.
|
|
414
|
+
*/
|
|
246
415
|
readonly openai?: {
|
|
416
|
+
/**
|
|
417
|
+
* The OpenAI item ID associated with the tool call.
|
|
418
|
+
*/
|
|
247
419
|
readonly itemId?: string | null
|
|
248
420
|
} | null
|
|
249
421
|
}
|
|
250
422
|
|
|
423
|
+
/**
|
|
424
|
+
* OpenAI metadata attached to document source citations.
|
|
425
|
+
*
|
|
426
|
+
* @category response
|
|
427
|
+
* @since 4.0.0
|
|
428
|
+
*/
|
|
251
429
|
export interface DocumentSourcePartMetadata extends ProviderMetadata {
|
|
430
|
+
/**
|
|
431
|
+
* Provider-specific citation metadata for the OpenAI Responses API.
|
|
432
|
+
*/
|
|
252
433
|
readonly openai?:
|
|
253
434
|
| {
|
|
435
|
+
/**
|
|
436
|
+
* Identifies a citation to an uploaded file.
|
|
437
|
+
*/
|
|
254
438
|
readonly type: "file_citation"
|
|
255
439
|
/**
|
|
256
440
|
* The index of the file in the list of files.
|
|
@@ -262,6 +446,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
262
446
|
readonly fileId: string
|
|
263
447
|
}
|
|
264
448
|
| {
|
|
449
|
+
/**
|
|
450
|
+
* Identifies a citation to a generated file path.
|
|
451
|
+
*/
|
|
265
452
|
readonly type: "file_path"
|
|
266
453
|
/**
|
|
267
454
|
* The index of the file in the list of files.
|
|
@@ -273,6 +460,9 @@ declare module "effect/unstable/ai/Response" {
|
|
|
273
460
|
readonly fileId: string
|
|
274
461
|
}
|
|
275
462
|
| {
|
|
463
|
+
/**
|
|
464
|
+
* Identifies a citation to a file inside a container.
|
|
465
|
+
*/
|
|
276
466
|
readonly type: "container_file_citation"
|
|
277
467
|
/**
|
|
278
468
|
* The ID of the file.
|
|
@@ -286,8 +476,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
286
476
|
| null
|
|
287
477
|
}
|
|
288
478
|
|
|
479
|
+
/**
|
|
480
|
+
* OpenAI metadata attached to URL source citations.
|
|
481
|
+
*
|
|
482
|
+
* @category response
|
|
483
|
+
* @since 4.0.0
|
|
484
|
+
*/
|
|
289
485
|
export interface UrlSourcePartMetadata extends ProviderMetadata {
|
|
486
|
+
/**
|
|
487
|
+
* Provider-specific URL citation metadata for the OpenAI Responses API.
|
|
488
|
+
*/
|
|
290
489
|
readonly openai?: {
|
|
490
|
+
/**
|
|
491
|
+
* Identifies a citation to a URL.
|
|
492
|
+
*/
|
|
291
493
|
readonly type: "url_citation"
|
|
292
494
|
/**
|
|
293
495
|
* The index of the first character of the URL citation in the message.
|
|
@@ -300,8 +502,20 @@ declare module "effect/unstable/ai/Response" {
|
|
|
300
502
|
} | null
|
|
301
503
|
}
|
|
302
504
|
|
|
505
|
+
/**
|
|
506
|
+
* OpenAI metadata attached to finish response parts.
|
|
507
|
+
*
|
|
508
|
+
* @category response
|
|
509
|
+
* @since 4.0.0
|
|
510
|
+
*/
|
|
303
511
|
export interface FinishPartMetadata extends ProviderMetadata {
|
|
512
|
+
/**
|
|
513
|
+
* Provider-specific metadata returned when generation finishes.
|
|
514
|
+
*/
|
|
304
515
|
readonly openai?: {
|
|
516
|
+
/**
|
|
517
|
+
* The service tier reported by OpenAI for the response.
|
|
518
|
+
*/
|
|
305
519
|
readonly serviceTier?: "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
306
520
|
} | null
|
|
307
521
|
}
|
|
@@ -312,31 +526,58 @@ declare module "effect/unstable/ai/Response" {
|
|
|
312
526
|
// =============================================================================
|
|
313
527
|
|
|
314
528
|
/**
|
|
315
|
-
*
|
|
529
|
+
* Creates an OpenAI model descriptor that can be provided with
|
|
530
|
+
* `Effect.provide`.
|
|
531
|
+
*
|
|
532
|
+
* **When to use**
|
|
533
|
+
*
|
|
534
|
+
* Use when you want an OpenAI language model value that carries provider and
|
|
535
|
+
* model metadata and can be supplied directly to an Effect program.
|
|
536
|
+
*
|
|
537
|
+
* @see {@link layer} for creating a `LanguageModel.LanguageModel` layer directly
|
|
538
|
+
* @see {@link make} for constructing the language model service effectfully
|
|
539
|
+
*
|
|
316
540
|
* @category constructors
|
|
541
|
+
* @since 4.0.0
|
|
317
542
|
*/
|
|
318
543
|
export const model = (
|
|
319
544
|
model: (string & {}) | Model,
|
|
320
545
|
config?: Omit<typeof Config.Service, "model">
|
|
321
546
|
): AiModel.Model<"openai", LanguageModel.LanguageModel, OpenAiClient> =>
|
|
322
|
-
AiModel.make("openai", layer({ model, config }))
|
|
547
|
+
AiModel.make("openai", model, layer({ model, config }))
|
|
323
548
|
|
|
324
549
|
// TODO
|
|
325
550
|
// /**
|
|
326
|
-
// * @since
|
|
551
|
+
// * @since 4.0.0
|
|
327
552
|
// * @category constructors
|
|
328
553
|
// */
|
|
329
554
|
// export const modelWithTokenizer = (
|
|
330
555
|
// model: (string & {}) | Model,
|
|
331
556
|
// config?: Omit<typeof Config.Service, "model">
|
|
332
557
|
// ): AiModel.Model<"openai", LanguageModel.LanguageModel | Tokenizer.Tokenizer, OpenAiClient> =>
|
|
333
|
-
// AiModel.make("openai", layerWithTokenizer({ model, config }))
|
|
558
|
+
// AiModel.make("openai", model, layerWithTokenizer({ model, config }))
|
|
334
559
|
|
|
335
560
|
/**
|
|
336
|
-
* Creates an OpenAI
|
|
561
|
+
* Creates an OpenAI `LanguageModel` service from a model identifier and
|
|
562
|
+
* optional request defaults.
|
|
563
|
+
*
|
|
564
|
+
* **When to use**
|
|
565
|
+
*
|
|
566
|
+
* Use to construct an OpenAI Responses API language model service backed by
|
|
567
|
+
* `OpenAiClient`.
|
|
568
|
+
*
|
|
569
|
+
* **Details**
|
|
570
|
+
*
|
|
571
|
+
* The returned effect requires `OpenAiClient`. Request defaults from the
|
|
572
|
+
* `config` option are merged with any `Config` service in the context, with
|
|
573
|
+
* context values taking precedence. The service supports both `generateText`
|
|
574
|
+
* and `streamText`.
|
|
575
|
+
*
|
|
576
|
+
* @see {@link layer} for providing the service as a `Layer`
|
|
577
|
+
* @see {@link model} for creating a model descriptor for `Effect.provide`
|
|
337
578
|
*
|
|
338
|
-
* @since 1.0.0
|
|
339
579
|
* @category constructors
|
|
580
|
+
* @since 4.0.0
|
|
340
581
|
*/
|
|
341
582
|
export const make = Effect.fnUntraced(function*({ model, config: providerConfig }: {
|
|
342
583
|
readonly model: (string & {}) | Model
|
|
@@ -345,7 +586,7 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
345
586
|
const client = yield* OpenAiClient
|
|
346
587
|
|
|
347
588
|
const makeConfig = Effect.gen(function*() {
|
|
348
|
-
const services = yield* Effect.
|
|
589
|
+
const services = yield* Effect.context<never>()
|
|
349
590
|
return { model, ...providerConfig, ...services.mapUnsafe.get(Config.key) }
|
|
350
591
|
})
|
|
351
592
|
|
|
@@ -354,9 +595,9 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
354
595
|
readonly config: typeof Config.Service
|
|
355
596
|
readonly options: LanguageModel.ProviderOptions
|
|
356
597
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
357
|
-
}): Effect.fn.Return<typeof
|
|
358
|
-
const include = new Set<typeof
|
|
359
|
-
const capabilities = getModelCapabilities(config.model
|
|
598
|
+
}): Effect.fn.Return<typeof OpenAiSchema.CreateResponse.Encoded, AiError.AiError> {
|
|
599
|
+
const include = new Set<typeof OpenAiSchema.IncludeEnum.Encoded>()
|
|
600
|
+
const capabilities = getModelCapabilities(config.model as string)
|
|
360
601
|
const messages = yield* prepareMessages({
|
|
361
602
|
config,
|
|
362
603
|
options,
|
|
@@ -369,26 +610,29 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
369
610
|
options,
|
|
370
611
|
toolNameMapper
|
|
371
612
|
})
|
|
372
|
-
const responseFormat = prepareResponseFormat({
|
|
613
|
+
const responseFormat = yield* prepareResponseFormat({
|
|
373
614
|
config,
|
|
374
615
|
options
|
|
375
616
|
})
|
|
376
|
-
const
|
|
377
|
-
|
|
617
|
+
const { fileIdPrefixes: _fip, strictJsonSchema: _sjs, ...apiConfig } = config
|
|
618
|
+
const request: Mutable<typeof OpenAiSchema.CreateResponse.Encoded> = {
|
|
619
|
+
...apiConfig,
|
|
378
620
|
input: messages,
|
|
379
|
-
include: include.size > 0 ? Array.from(include) :
|
|
621
|
+
include: include.size > 0 ? Array.from(include) : undefined,
|
|
380
622
|
text: {
|
|
381
|
-
verbosity: config.text?.verbosity ??
|
|
623
|
+
verbosity: config.text?.verbosity ?? undefined,
|
|
382
624
|
format: responseFormat
|
|
383
|
-
}
|
|
384
|
-
...(Predicate.isNotUndefined(tools) ? { tools } : undefined),
|
|
385
|
-
...(Predicate.isNotUndefined(toolChoice) ? { tool_choice: toolChoice } : undefined)
|
|
625
|
+
}
|
|
386
626
|
}
|
|
627
|
+
if (tools) request.tools = tools
|
|
628
|
+
if (toolChoice) request.tool_choice = toolChoice
|
|
629
|
+
if (options.previousResponseId) request.previous_response_id = options.previousResponseId
|
|
387
630
|
return request
|
|
388
631
|
}
|
|
389
632
|
)
|
|
390
633
|
|
|
391
634
|
return yield* LanguageModel.make({
|
|
635
|
+
codecTransformer: toCodecOpenAI,
|
|
392
636
|
generateText: Effect.fnUntraced(
|
|
393
637
|
function*(options) {
|
|
394
638
|
const config = yield* makeConfig
|
|
@@ -433,10 +677,27 @@ export const make = Effect.fnUntraced(function*({ model, config: providerConfig
|
|
|
433
677
|
})
|
|
434
678
|
|
|
435
679
|
/**
|
|
436
|
-
* Creates a layer
|
|
680
|
+
* Creates a layer that provides the OpenAI `LanguageModel.LanguageModel`
|
|
681
|
+
* service.
|
|
682
|
+
*
|
|
683
|
+
* **When to use**
|
|
684
|
+
*
|
|
685
|
+
* Use when composing application layers and you want OpenAI to satisfy
|
|
686
|
+
* `LanguageModel.LanguageModel` while supplying `OpenAiClient` from another
|
|
687
|
+
* layer.
|
|
688
|
+
*
|
|
689
|
+
* **Details**
|
|
690
|
+
*
|
|
691
|
+
* The `config` option supplies request defaults for the selected model. Scoped
|
|
692
|
+
* values from `withConfigOverride` are merged when each request is built and
|
|
693
|
+
* take precedence over these defaults.
|
|
694
|
+
*
|
|
695
|
+
* @see {@link make} for constructing the language model service effectfully
|
|
696
|
+
* @see {@link model} for creating a model descriptor for `Effect.provide`
|
|
697
|
+
* @see {@link withConfigOverride} for scoped request configuration overrides
|
|
437
698
|
*
|
|
438
|
-
* @since 1.0.0
|
|
439
699
|
* @category layers
|
|
700
|
+
* @since 4.0.0
|
|
440
701
|
*/
|
|
441
702
|
export const layer = (options: {
|
|
442
703
|
readonly model: (string & {}) | Model
|
|
@@ -445,39 +706,114 @@ export const layer = (options: {
|
|
|
445
706
|
Layer.effect(LanguageModel.LanguageModel, make(options))
|
|
446
707
|
|
|
447
708
|
/**
|
|
448
|
-
* Provides config overrides for OpenAI language model operations.
|
|
709
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
710
|
+
*
|
|
711
|
+
* **When to use**
|
|
712
|
+
*
|
|
713
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
714
|
+
* language model operations without changing the defaults passed to `model`,
|
|
715
|
+
* `make`, or `layer`.
|
|
716
|
+
*
|
|
717
|
+
* **Details**
|
|
718
|
+
*
|
|
719
|
+
* The override is dual, so it can be used in pipe form or as
|
|
720
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
721
|
+
* existing `Config` service in the current context, and the override values take
|
|
722
|
+
* precedence.
|
|
723
|
+
*
|
|
724
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
449
725
|
*
|
|
450
|
-
* @since 1.0.0
|
|
451
726
|
* @category configuration
|
|
727
|
+
* @since 4.0.0
|
|
452
728
|
*/
|
|
453
729
|
export const withConfigOverride: {
|
|
454
730
|
/**
|
|
455
|
-
* Provides config overrides for OpenAI language model operations.
|
|
731
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
732
|
+
*
|
|
733
|
+
* **When to use**
|
|
734
|
+
*
|
|
735
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
736
|
+
* language model operations without changing the defaults passed to `model`,
|
|
737
|
+
* `make`, or `layer`.
|
|
738
|
+
*
|
|
739
|
+
* **Details**
|
|
740
|
+
*
|
|
741
|
+
* The override is dual, so it can be used in pipe form or as
|
|
742
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
743
|
+
* existing `Config` service in the current context, and the override values take
|
|
744
|
+
* precedence.
|
|
745
|
+
*
|
|
746
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
456
747
|
*
|
|
457
|
-
* @since 1.0.0
|
|
458
748
|
* @category configuration
|
|
749
|
+
* @since 4.0.0
|
|
459
750
|
*/
|
|
460
751
|
(overrides: typeof Config.Service): <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
461
752
|
/**
|
|
462
|
-
* Provides config overrides for OpenAI language model operations.
|
|
753
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
754
|
+
*
|
|
755
|
+
* **When to use**
|
|
756
|
+
*
|
|
757
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
758
|
+
* language model operations without changing the defaults passed to `model`,
|
|
759
|
+
* `make`, or `layer`.
|
|
760
|
+
*
|
|
761
|
+
* **Details**
|
|
762
|
+
*
|
|
763
|
+
* The override is dual, so it can be used in pipe form or as
|
|
764
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
765
|
+
* existing `Config` service in the current context, and the override values take
|
|
766
|
+
* precedence.
|
|
767
|
+
*
|
|
768
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
463
769
|
*
|
|
464
|
-
* @since 1.0.0
|
|
465
770
|
* @category configuration
|
|
771
|
+
* @since 4.0.0
|
|
466
772
|
*/
|
|
467
773
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service): Effect.Effect<A, E, Exclude<R, Config>>
|
|
468
774
|
} = dual<
|
|
469
775
|
/**
|
|
470
|
-
* Provides config overrides for OpenAI language model operations.
|
|
776
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
777
|
+
*
|
|
778
|
+
* **When to use**
|
|
779
|
+
*
|
|
780
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
781
|
+
* language model operations without changing the defaults passed to `model`,
|
|
782
|
+
* `make`, or `layer`.
|
|
783
|
+
*
|
|
784
|
+
* **Details**
|
|
785
|
+
*
|
|
786
|
+
* The override is dual, so it can be used in pipe form or as
|
|
787
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
788
|
+
* existing `Config` service in the current context, and the override values take
|
|
789
|
+
* precedence.
|
|
790
|
+
*
|
|
791
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
471
792
|
*
|
|
472
|
-
* @since 1.0.0
|
|
473
793
|
* @category configuration
|
|
794
|
+
* @since 4.0.0
|
|
474
795
|
*/
|
|
475
796
|
(overrides: typeof Config.Service) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, Config>>,
|
|
476
797
|
/**
|
|
477
|
-
* Provides config overrides for OpenAI language model operations.
|
|
798
|
+
* Provides scoped config overrides for OpenAI language model operations.
|
|
799
|
+
*
|
|
800
|
+
* **When to use**
|
|
801
|
+
*
|
|
802
|
+
* Use to apply OpenAI Responses API config overrides around one or more
|
|
803
|
+
* language model operations without changing the defaults passed to `model`,
|
|
804
|
+
* `make`, or `layer`.
|
|
805
|
+
*
|
|
806
|
+
* **Details**
|
|
807
|
+
*
|
|
808
|
+
* The override is dual, so it can be used in pipe form or as
|
|
809
|
+
* `withConfigOverride(effect, overrides)`. Overrides are merged with any
|
|
810
|
+
* existing `Config` service in the current context, and the override values take
|
|
811
|
+
* precedence.
|
|
812
|
+
*
|
|
813
|
+
* @see {@link Config} for the scoped configuration service consumed by this function
|
|
478
814
|
*
|
|
479
|
-
* @since 1.0.0
|
|
480
815
|
* @category configuration
|
|
816
|
+
* @since 4.0.0
|
|
481
817
|
*/
|
|
482
818
|
<A, E, R>(self: Effect.Effect<A, E, R>, overrides: typeof Config.Service) => Effect.Effect<A, E, Exclude<R, Config>>
|
|
483
819
|
>(2, (self, overrides) =>
|
|
@@ -512,10 +848,10 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
512
848
|
}: {
|
|
513
849
|
readonly config: typeof Config.Service
|
|
514
850
|
readonly options: LanguageModel.ProviderOptions
|
|
515
|
-
readonly include: Set<typeof
|
|
851
|
+
readonly include: Set<typeof OpenAiSchema.IncludeEnum.Encoded>
|
|
516
852
|
readonly capabilities: ModelCapabilities
|
|
517
853
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
518
|
-
}): Effect.fn.Return<ReadonlyArray<typeof
|
|
854
|
+
}): Effect.fn.Return<ReadonlyArray<typeof OpenAiSchema.InputItem.Encoded>, AiError.AiError> {
|
|
519
855
|
const processedApprovalIds = new Set<string>()
|
|
520
856
|
|
|
521
857
|
const hasConversation = Predicate.isNotNullish(config.conversation)
|
|
@@ -547,27 +883,28 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
547
883
|
if (config.store === false && capabilities.isReasoningModel) {
|
|
548
884
|
include.add("reasoning.encrypted_content")
|
|
549
885
|
}
|
|
550
|
-
if (
|
|
886
|
+
if (codeInterpreterTool) {
|
|
551
887
|
include.add("code_interpreter_call.outputs")
|
|
552
888
|
}
|
|
553
|
-
if (
|
|
889
|
+
if (webSearchTool || webSearchPreviewTool) {
|
|
554
890
|
include.add("web_search_call.action.sources")
|
|
555
891
|
}
|
|
556
892
|
|
|
557
|
-
const messages: Array<typeof
|
|
893
|
+
const messages: Array<typeof OpenAiSchema.InputItem.Encoded> = []
|
|
894
|
+
const prompt = options.incrementalPrompt ?? options.prompt
|
|
558
895
|
|
|
559
|
-
for (const message of
|
|
896
|
+
for (const message of prompt.content) {
|
|
560
897
|
switch (message.role) {
|
|
561
898
|
case "system": {
|
|
562
899
|
messages.push({
|
|
563
|
-
role: getSystemMessageMode(config.model
|
|
900
|
+
role: getSystemMessageMode(config.model as string),
|
|
564
901
|
content: message.content
|
|
565
902
|
})
|
|
566
903
|
break
|
|
567
904
|
}
|
|
568
905
|
|
|
569
906
|
case "user": {
|
|
570
|
-
const content: Array<typeof
|
|
907
|
+
const content: Array<typeof OpenAiSchema.InputContent.Encoded> = []
|
|
571
908
|
|
|
572
909
|
for (let index = 0; index < message.content.length; index++) {
|
|
573
910
|
const part = message.content[index]
|
|
@@ -630,7 +967,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
630
967
|
}
|
|
631
968
|
|
|
632
969
|
case "assistant": {
|
|
633
|
-
const reasoningMessages: Record<string, DeepMutable<typeof
|
|
970
|
+
const reasoningMessages: Record<string, DeepMutable<typeof OpenAiSchema.ReasoningItem.Encoded>> = {}
|
|
634
971
|
|
|
635
972
|
for (const part of message.content) {
|
|
636
973
|
switch (part.type) {
|
|
@@ -689,7 +1026,7 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
689
1026
|
}
|
|
690
1027
|
}
|
|
691
1028
|
} else {
|
|
692
|
-
const summaryParts: Array<typeof
|
|
1029
|
+
const summaryParts: Array<typeof OpenAiSchema.SummaryTextContent.Encoded> = []
|
|
693
1030
|
|
|
694
1031
|
if (part.text.length > 0) {
|
|
695
1032
|
summaryParts.push({ type: "summary_text", text: part.text })
|
|
@@ -700,7 +1037,9 @@ const prepareMessages = Effect.fnUntraced(
|
|
|
700
1037
|
type: "reasoning",
|
|
701
1038
|
id,
|
|
702
1039
|
summary: summaryParts,
|
|
703
|
-
|
|
1040
|
+
...(Predicate.isNotNull(encryptedContent)
|
|
1041
|
+
? { encrypted_content: encryptedContent }
|
|
1042
|
+
: undefined)
|
|
704
1043
|
}
|
|
705
1044
|
|
|
706
1045
|
messages.push(reasoningMessages[id])
|
|
@@ -921,7 +1260,7 @@ const buildHttpRequestDetails = (
|
|
|
921
1260
|
method: request.method,
|
|
922
1261
|
url: request.url,
|
|
923
1262
|
urlParams: Array.from(request.urlParams),
|
|
924
|
-
hash: request.hash,
|
|
1263
|
+
hash: Option.getOrUndefined(request.hash),
|
|
925
1264
|
headers: Redactable.redact(request.headers) as Record<string, string>
|
|
926
1265
|
})
|
|
927
1266
|
|
|
@@ -936,7 +1275,56 @@ const buildHttpResponseDetails = (
|
|
|
936
1275
|
// Response Conversion
|
|
937
1276
|
// =============================================================================
|
|
938
1277
|
|
|
939
|
-
type ResponseStreamEvent = typeof
|
|
1278
|
+
type ResponseStreamEvent = typeof OpenAiSchema.ResponseStreamEvent.Type
|
|
1279
|
+
|
|
1280
|
+
type KnownResponseStreamEventType =
|
|
1281
|
+
| "response.created"
|
|
1282
|
+
| "response.completed"
|
|
1283
|
+
| "response.incomplete"
|
|
1284
|
+
| "response.failed"
|
|
1285
|
+
| "response.output_item.added"
|
|
1286
|
+
| "response.output_item.done"
|
|
1287
|
+
| "response.output_text.delta"
|
|
1288
|
+
| "response.output_text.annotation.added"
|
|
1289
|
+
| "response.reasoning_summary_part.added"
|
|
1290
|
+
| "response.reasoning_summary_part.done"
|
|
1291
|
+
| "response.reasoning_summary_text.delta"
|
|
1292
|
+
| "response.function_call_arguments.delta"
|
|
1293
|
+
| "response.function_call_arguments.done"
|
|
1294
|
+
| "response.code_interpreter_call_code.delta"
|
|
1295
|
+
| "response.code_interpreter_call_code.done"
|
|
1296
|
+
| "response.apply_patch_call_operation_diff.delta"
|
|
1297
|
+
| "response.apply_patch_call_operation_diff.done"
|
|
1298
|
+
| "response.image_generation_call.partial_image"
|
|
1299
|
+
| "error"
|
|
1300
|
+
|
|
1301
|
+
type KnownResponseStreamEvent = Extract<ResponseStreamEvent, { readonly type: KnownResponseStreamEventType }>
|
|
1302
|
+
|
|
1303
|
+
const knownResponseStreamEventTypes = new Set<KnownResponseStreamEventType>([
|
|
1304
|
+
"response.created",
|
|
1305
|
+
"response.completed",
|
|
1306
|
+
"response.incomplete",
|
|
1307
|
+
"response.failed",
|
|
1308
|
+
"response.output_item.added",
|
|
1309
|
+
"response.output_item.done",
|
|
1310
|
+
"response.output_text.delta",
|
|
1311
|
+
"response.output_text.annotation.added",
|
|
1312
|
+
"response.reasoning_summary_part.added",
|
|
1313
|
+
"response.reasoning_summary_part.done",
|
|
1314
|
+
"response.reasoning_summary_text.delta",
|
|
1315
|
+
"response.function_call_arguments.delta",
|
|
1316
|
+
"response.function_call_arguments.done",
|
|
1317
|
+
"response.code_interpreter_call_code.delta",
|
|
1318
|
+
"response.code_interpreter_call_code.done",
|
|
1319
|
+
"response.apply_patch_call_operation_diff.delta",
|
|
1320
|
+
"response.apply_patch_call_operation_diff.done",
|
|
1321
|
+
"response.image_generation_call.partial_image",
|
|
1322
|
+
"error"
|
|
1323
|
+
])
|
|
1324
|
+
|
|
1325
|
+
const isKnownResponseStreamEvent = (
|
|
1326
|
+
event: ResponseStreamEvent
|
|
1327
|
+
): event is KnownResponseStreamEvent => knownResponseStreamEventTypes.has(event.type as KnownResponseStreamEventType)
|
|
940
1328
|
|
|
941
1329
|
const makeResponse = Effect.fnUntraced(
|
|
942
1330
|
function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
@@ -946,7 +1334,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
946
1334
|
toolNameMapper
|
|
947
1335
|
}: {
|
|
948
1336
|
readonly options: LanguageModel.ProviderOptions
|
|
949
|
-
readonly rawResponse:
|
|
1337
|
+
readonly rawResponse: OpenAiSchema.Response
|
|
950
1338
|
readonly response: HttpClientResponse.HttpClientResponse
|
|
951
1339
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
952
1340
|
}): Effect.fn.Return<
|
|
@@ -985,7 +1373,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
985
1373
|
id: part.call_id,
|
|
986
1374
|
name: toolName,
|
|
987
1375
|
params: { call_id: part.call_id, operation: part.operation },
|
|
988
|
-
metadata: { openai:
|
|
1376
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
989
1377
|
})
|
|
990
1378
|
break
|
|
991
1379
|
}
|
|
@@ -1036,10 +1424,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1036
1424
|
|
|
1037
1425
|
case "function_call": {
|
|
1038
1426
|
hasToolCalls = true
|
|
1427
|
+
|
|
1039
1428
|
const toolName = part.name
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
try: () => Tool.unsafeSecureJsonParse(
|
|
1429
|
+
|
|
1430
|
+
const toolParams = yield* Effect.try({
|
|
1431
|
+
try: () => Tool.unsafeSecureJsonParse(part.arguments),
|
|
1043
1432
|
catch: (cause) =>
|
|
1044
1433
|
AiError.make({
|
|
1045
1434
|
module: "OpenAiLanguageModel",
|
|
@@ -1051,12 +1440,15 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1051
1440
|
})
|
|
1052
1441
|
})
|
|
1053
1442
|
})
|
|
1443
|
+
|
|
1444
|
+
const params = yield* transformToolCallParams(options.tools, part.name, toolParams)
|
|
1445
|
+
|
|
1054
1446
|
parts.push({
|
|
1055
1447
|
type: "tool-call",
|
|
1056
1448
|
id: part.call_id,
|
|
1057
1449
|
name: toolName,
|
|
1058
1450
|
params,
|
|
1059
|
-
metadata: { openai:
|
|
1451
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1060
1452
|
})
|
|
1061
1453
|
break
|
|
1062
1454
|
}
|
|
@@ -1087,7 +1479,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1087
1479
|
id: part.call_id,
|
|
1088
1480
|
name: toolName,
|
|
1089
1481
|
params: { action: part.action },
|
|
1090
|
-
metadata: { openai:
|
|
1482
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1091
1483
|
})
|
|
1092
1484
|
break
|
|
1093
1485
|
}
|
|
@@ -1097,13 +1489,17 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1097
1489
|
? (approvalRequests.get(part.approval_request_id) ?? part.id)
|
|
1098
1490
|
: part.id
|
|
1099
1491
|
|
|
1100
|
-
const toolName =
|
|
1492
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1493
|
+
toolNameMapper,
|
|
1494
|
+
toolParams: part.arguments,
|
|
1495
|
+
method: "makeResponse"
|
|
1496
|
+
})
|
|
1101
1497
|
|
|
1102
1498
|
parts.push({
|
|
1103
1499
|
type: "tool-call",
|
|
1104
1500
|
id: toolId,
|
|
1105
1501
|
name: toolName,
|
|
1106
|
-
params
|
|
1502
|
+
params,
|
|
1107
1503
|
providerExecuted: true
|
|
1108
1504
|
})
|
|
1109
1505
|
|
|
@@ -1114,14 +1510,14 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1114
1510
|
isFailure: false,
|
|
1115
1511
|
providerExecuted: true,
|
|
1116
1512
|
result: {
|
|
1117
|
-
type: "
|
|
1513
|
+
type: "mcp_call",
|
|
1118
1514
|
name: part.name,
|
|
1119
1515
|
arguments: part.arguments,
|
|
1120
1516
|
server_label: part.server_label,
|
|
1121
1517
|
...(Predicate.isNotNullish(part.output) ? { output: part.output } : undefined),
|
|
1122
1518
|
...(Predicate.isNotNullish(part.error) ? { error: part.error } : undefined)
|
|
1123
1519
|
},
|
|
1124
|
-
metadata: { openai:
|
|
1520
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1125
1521
|
})
|
|
1126
1522
|
|
|
1127
1523
|
break
|
|
@@ -1135,20 +1531,11 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1135
1531
|
case "mcp_approval_request": {
|
|
1136
1532
|
const approvalRequestId = (part as any).approval_request_id ?? part.id
|
|
1137
1533
|
const toolId = yield* idGenerator.generateId()
|
|
1138
|
-
const toolName = `mcp.${part.name}`
|
|
1139
1534
|
|
|
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
|
-
})
|
|
1535
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
1536
|
+
toolNameMapper,
|
|
1537
|
+
toolParams: part.arguments,
|
|
1538
|
+
method: "makeResponse"
|
|
1152
1539
|
})
|
|
1153
1540
|
|
|
1154
1541
|
parts.push({
|
|
@@ -1296,7 +1683,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1296
1683
|
id: part.call_id,
|
|
1297
1684
|
name: toolName,
|
|
1298
1685
|
params: { action: part.action },
|
|
1299
|
-
metadata: { openai:
|
|
1686
|
+
metadata: { openai: makeItemIdMetadata(part.id) }
|
|
1300
1687
|
})
|
|
1301
1688
|
break
|
|
1302
1689
|
}
|
|
@@ -1335,7 +1722,7 @@ const makeResponse = Effect.fnUntraced(
|
|
|
1335
1722
|
reason: finishReason,
|
|
1336
1723
|
usage: getUsage(rawResponse.usage),
|
|
1337
1724
|
response: buildHttpResponseDetails(response),
|
|
1338
|
-
...(rawResponse.service_tier
|
|
1725
|
+
...toServiceTier(rawResponse.service_tier)
|
|
1339
1726
|
})
|
|
1340
1727
|
|
|
1341
1728
|
return parts
|
|
@@ -1368,18 +1755,44 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1368
1755
|
let hasToolCalls = false
|
|
1369
1756
|
|
|
1370
1757
|
// Track annotations for current message to include in text-end metadata
|
|
1371
|
-
const activeAnnotations: Array<typeof
|
|
1758
|
+
const activeAnnotations: Array<typeof OpenAiSchema.Annotation.Encoded> = []
|
|
1759
|
+
|
|
1760
|
+
type ReasoningSummaryPartStatus = "active" | "can-conclude" | "concluded"
|
|
1761
|
+
type ReasoningPart = {
|
|
1762
|
+
encryptedContent: string | undefined
|
|
1763
|
+
summaryParts: Record<number, ReasoningSummaryPartStatus>
|
|
1764
|
+
}
|
|
1372
1765
|
|
|
1373
1766
|
// Track active reasoning items with state machine for proper concluding logic
|
|
1374
|
-
const activeReasoning: Record<string, {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1767
|
+
const activeReasoning: Record<string, ReasoningPart> = {}
|
|
1768
|
+
|
|
1769
|
+
const getOrCreateReasoningPart = (
|
|
1770
|
+
itemId: string,
|
|
1771
|
+
encryptedContent?: string | null
|
|
1772
|
+
): ReasoningPart => {
|
|
1773
|
+
const activePart = activeReasoning[itemId]
|
|
1774
|
+
if (Predicate.isNotUndefined(activePart)) {
|
|
1775
|
+
if (Predicate.isNotNullish(encryptedContent)) {
|
|
1776
|
+
activePart.encryptedContent = encryptedContent
|
|
1777
|
+
}
|
|
1778
|
+
return activePart
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
const reasoningPart: ReasoningPart = {
|
|
1782
|
+
encryptedContent: Predicate.isNotNullish(encryptedContent) ? encryptedContent : undefined,
|
|
1783
|
+
summaryParts: {}
|
|
1784
|
+
}
|
|
1785
|
+
activeReasoning[itemId] = reasoningPart
|
|
1786
|
+
return reasoningPart
|
|
1787
|
+
}
|
|
1378
1788
|
|
|
1379
1789
|
// Track active tool calls with optional provider-specific state
|
|
1380
1790
|
const activeToolCalls: Record<number, {
|
|
1381
1791
|
readonly id: string
|
|
1382
1792
|
readonly name: string
|
|
1793
|
+
readonly functionCall?: {
|
|
1794
|
+
emitted: boolean
|
|
1795
|
+
}
|
|
1383
1796
|
readonly applyPatch?: {
|
|
1384
1797
|
hasDiff: boolean
|
|
1385
1798
|
endEmitted: boolean
|
|
@@ -1399,6 +1812,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1399
1812
|
Stream.mapEffect(Effect.fnUntraced(function*(event) {
|
|
1400
1813
|
const parts: Array<Response.StreamPartEncoded> = []
|
|
1401
1814
|
|
|
1815
|
+
if (!isKnownResponseStreamEvent(event)) {
|
|
1816
|
+
return parts
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1402
1819
|
switch (event.type) {
|
|
1403
1820
|
case "response.created": {
|
|
1404
1821
|
const createdAt = new Date(event.response.created_at * 1000)
|
|
@@ -1428,7 +1845,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1428
1845
|
),
|
|
1429
1846
|
usage: getUsage(event.response.usage),
|
|
1430
1847
|
response: buildHttpResponseDetails(response),
|
|
1431
|
-
...(event.response.service_tier
|
|
1848
|
+
...toServiceTier(event.response.service_tier)
|
|
1432
1849
|
})
|
|
1433
1850
|
break
|
|
1434
1851
|
}
|
|
@@ -1529,7 +1946,8 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1529
1946
|
case "function_call": {
|
|
1530
1947
|
activeToolCalls[event.output_index] = {
|
|
1531
1948
|
id: event.item.call_id,
|
|
1532
|
-
name: event.item.name
|
|
1949
|
+
name: event.item.name,
|
|
1950
|
+
functionCall: { emitted: false }
|
|
1533
1951
|
}
|
|
1534
1952
|
parts.push({
|
|
1535
1953
|
type: "tool-params-start",
|
|
@@ -1566,34 +1984,33 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1566
1984
|
parts.push({
|
|
1567
1985
|
type: "text-start",
|
|
1568
1986
|
id: event.item.id,
|
|
1569
|
-
metadata: { openai:
|
|
1987
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1570
1988
|
})
|
|
1571
1989
|
break
|
|
1572
1990
|
}
|
|
1573
1991
|
|
|
1574
1992
|
case "reasoning": {
|
|
1575
|
-
const
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
...makeEncryptedContentMetadata(event.item.encrypted_content)
|
|
1993
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
1994
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[0])) {
|
|
1995
|
+
reasoningPart.summaryParts[0] = "active"
|
|
1996
|
+
parts.push({
|
|
1997
|
+
type: "reasoning-start",
|
|
1998
|
+
id: `${event.item.id}:0`,
|
|
1999
|
+
metadata: {
|
|
2000
|
+
openai: {
|
|
2001
|
+
...makeItemIdMetadata(event.item.id),
|
|
2002
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2003
|
+
}
|
|
1587
2004
|
}
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
2005
|
+
})
|
|
2006
|
+
}
|
|
1590
2007
|
break
|
|
1591
2008
|
}
|
|
1592
2009
|
|
|
1593
2010
|
case "shell_call": {
|
|
1594
2011
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1595
2012
|
activeToolCalls[event.output_index] = {
|
|
1596
|
-
id: event.item.id,
|
|
2013
|
+
id: event.item.id ?? event.item.call_id,
|
|
1597
2014
|
name: toolName
|
|
1598
2015
|
}
|
|
1599
2016
|
break
|
|
@@ -1644,7 +2061,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1644
2061
|
parts.push({
|
|
1645
2062
|
type: "tool-params-delta",
|
|
1646
2063
|
id: toolCall.id,
|
|
1647
|
-
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff)
|
|
2064
|
+
delta: InternalUtilities.escapeJSONDelta(event.item.operation.diff ?? "")
|
|
1648
2065
|
})
|
|
1649
2066
|
}
|
|
1650
2067
|
parts.push({
|
|
@@ -1666,7 +2083,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1666
2083
|
id: toolCall.id,
|
|
1667
2084
|
name: toolName,
|
|
1668
2085
|
params: { call_id: event.item.call_id, operation: event.item.operation },
|
|
1669
|
-
metadata: { openai:
|
|
2086
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1670
2087
|
})
|
|
1671
2088
|
}
|
|
1672
2089
|
delete activeToolCalls[event.output_index]
|
|
@@ -1729,12 +2146,20 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1729
2146
|
}
|
|
1730
2147
|
|
|
1731
2148
|
case "function_call": {
|
|
2149
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2150
|
+
if (Predicate.isNotUndefined(toolCall?.functionCall?.emitted) && toolCall.functionCall.emitted) {
|
|
2151
|
+
delete activeToolCalls[event.output_index]
|
|
2152
|
+
break
|
|
2153
|
+
}
|
|
1732
2154
|
delete activeToolCalls[event.output_index]
|
|
2155
|
+
|
|
1733
2156
|
hasToolCalls = true
|
|
2157
|
+
|
|
1734
2158
|
const toolName = event.item.name
|
|
1735
|
-
const
|
|
1736
|
-
|
|
1737
|
-
|
|
2159
|
+
const toolArgs = event.item.arguments
|
|
2160
|
+
|
|
2161
|
+
const toolParams = yield* Effect.try({
|
|
2162
|
+
try: () => Tool.unsafeSecureJsonParse(toolArgs),
|
|
1738
2163
|
catch: (cause) =>
|
|
1739
2164
|
AiError.make({
|
|
1740
2165
|
module: "OpenAiLanguageModel",
|
|
@@ -1746,17 +2171,22 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1746
2171
|
})
|
|
1747
2172
|
})
|
|
1748
2173
|
})
|
|
2174
|
+
|
|
2175
|
+
const params = yield* transformToolCallParams(options.tools, toolName, toolParams)
|
|
2176
|
+
|
|
1749
2177
|
parts.push({
|
|
1750
2178
|
type: "tool-params-end",
|
|
1751
2179
|
id: event.item.call_id
|
|
1752
2180
|
})
|
|
2181
|
+
|
|
1753
2182
|
parts.push({
|
|
1754
2183
|
type: "tool-call",
|
|
1755
2184
|
id: event.item.call_id,
|
|
1756
2185
|
name: toolName,
|
|
1757
2186
|
params,
|
|
1758
|
-
metadata: { openai:
|
|
2187
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1759
2188
|
})
|
|
2189
|
+
|
|
1760
2190
|
break
|
|
1761
2191
|
}
|
|
1762
2192
|
|
|
@@ -1780,7 +2210,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1780
2210
|
id: event.item.call_id,
|
|
1781
2211
|
name: toolName,
|
|
1782
2212
|
params: { action: event.item.action },
|
|
1783
|
-
metadata: { openai:
|
|
2213
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1784
2214
|
})
|
|
1785
2215
|
break
|
|
1786
2216
|
}
|
|
@@ -1794,13 +2224,17 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1794
2224
|
event.item.id)
|
|
1795
2225
|
: event.item.id
|
|
1796
2226
|
|
|
1797
|
-
const toolName =
|
|
2227
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2228
|
+
toolNameMapper,
|
|
2229
|
+
toolParams: event.item.arguments,
|
|
2230
|
+
method: "makeStreamResponse"
|
|
2231
|
+
})
|
|
1798
2232
|
|
|
1799
2233
|
parts.push({
|
|
1800
2234
|
type: "tool-call",
|
|
1801
2235
|
id: toolId,
|
|
1802
2236
|
name: toolName,
|
|
1803
|
-
params
|
|
2237
|
+
params,
|
|
1804
2238
|
providerExecuted: true
|
|
1805
2239
|
})
|
|
1806
2240
|
|
|
@@ -1811,14 +2245,14 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1811
2245
|
isFailure: false,
|
|
1812
2246
|
providerExecuted: true,
|
|
1813
2247
|
result: {
|
|
1814
|
-
type: "
|
|
2248
|
+
type: "mcp_call",
|
|
1815
2249
|
name: event.item.name,
|
|
1816
2250
|
arguments: event.item.arguments,
|
|
1817
2251
|
server_label: event.item.server_label,
|
|
1818
2252
|
...(Predicate.isNotNullish(event.item.output) ? { output: event.item.output } : undefined),
|
|
1819
2253
|
...(Predicate.isNotNullish(event.item.error) ? { error: event.item.error } : undefined)
|
|
1820
2254
|
},
|
|
1821
|
-
metadata: { openai:
|
|
2255
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1822
2256
|
})
|
|
1823
2257
|
|
|
1824
2258
|
break
|
|
@@ -1833,12 +2267,16 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1833
2267
|
const toolId = yield* idGenerator.generateId()
|
|
1834
2268
|
const approvalRequestId = (event.item as any).approval_request_id ?? event.item.id
|
|
1835
2269
|
streamApprovalRequests.set(approvalRequestId, toolId)
|
|
1836
|
-
const toolName =
|
|
2270
|
+
const { toolName, params } = yield* normalizeMcpToolCall({
|
|
2271
|
+
toolNameMapper,
|
|
2272
|
+
toolParams: event.item.arguments,
|
|
2273
|
+
method: "makeStreamResponse"
|
|
2274
|
+
})
|
|
1837
2275
|
parts.push({
|
|
1838
2276
|
type: "tool-call",
|
|
1839
2277
|
id: toolId,
|
|
1840
2278
|
name: toolName,
|
|
1841
|
-
params
|
|
2279
|
+
params,
|
|
1842
2280
|
providerExecuted: true
|
|
1843
2281
|
})
|
|
1844
2282
|
parts.push({
|
|
@@ -1862,7 +2300,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1862
2300
|
}
|
|
1863
2301
|
|
|
1864
2302
|
case "reasoning": {
|
|
1865
|
-
const reasoningPart =
|
|
2303
|
+
const reasoningPart = getOrCreateReasoningPart(event.item.id, event.item.encrypted_content)
|
|
1866
2304
|
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
1867
2305
|
if (status === "active" || status === "can-conclude") {
|
|
1868
2306
|
parts.push({
|
|
@@ -1871,7 +2309,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1871
2309
|
metadata: {
|
|
1872
2310
|
openai: {
|
|
1873
2311
|
...makeItemIdMetadata(event.item.id),
|
|
1874
|
-
...makeEncryptedContentMetadata(
|
|
2312
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
1875
2313
|
}
|
|
1876
2314
|
}
|
|
1877
2315
|
})
|
|
@@ -1886,10 +2324,10 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1886
2324
|
const toolName = toolNameMapper.getCustomName("shell")
|
|
1887
2325
|
parts.push({
|
|
1888
2326
|
type: "tool-call",
|
|
1889
|
-
id: event.item.id,
|
|
2327
|
+
id: event.item.id ?? event.item.call_id,
|
|
1890
2328
|
name: toolName,
|
|
1891
2329
|
params: { action: event.item.action },
|
|
1892
|
-
metadata: { openai:
|
|
2330
|
+
metadata: { openai: makeItemIdMetadata(event.item.id) }
|
|
1893
2331
|
})
|
|
1894
2332
|
break
|
|
1895
2333
|
}
|
|
@@ -1924,7 +2362,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
1924
2362
|
}
|
|
1925
2363
|
|
|
1926
2364
|
case "response.output_text.annotation.added": {
|
|
1927
|
-
const annotation = event.annotation as typeof
|
|
2365
|
+
const annotation = event.annotation as typeof OpenAiSchema.Annotation.Encoded
|
|
1928
2366
|
// Track annotation for text-end metadata
|
|
1929
2367
|
activeAnnotations.push(annotation)
|
|
1930
2368
|
if (annotation.type === "container_file_citation") {
|
|
@@ -2006,6 +2444,48 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2006
2444
|
break
|
|
2007
2445
|
}
|
|
2008
2446
|
|
|
2447
|
+
case "response.function_call_arguments.done": {
|
|
2448
|
+
const toolCall = activeToolCalls[event.output_index]
|
|
2449
|
+
if (
|
|
2450
|
+
Predicate.isNotUndefined(toolCall?.functionCall) &&
|
|
2451
|
+
!toolCall.functionCall.emitted
|
|
2452
|
+
) {
|
|
2453
|
+
hasToolCalls = true
|
|
2454
|
+
|
|
2455
|
+
const toolParams = yield* Effect.try({
|
|
2456
|
+
try: () => Tool.unsafeSecureJsonParse(event.arguments),
|
|
2457
|
+
catch: (cause) =>
|
|
2458
|
+
AiError.make({
|
|
2459
|
+
module: "OpenAiLanguageModel",
|
|
2460
|
+
method: "makeStreamResponse",
|
|
2461
|
+
reason: new AiError.ToolParameterValidationError({
|
|
2462
|
+
toolName: toolCall.name,
|
|
2463
|
+
toolParams: {},
|
|
2464
|
+
description: `Failed securely JSON parse tool parameters: ${cause}`
|
|
2465
|
+
})
|
|
2466
|
+
})
|
|
2467
|
+
})
|
|
2468
|
+
|
|
2469
|
+
const params = yield* transformToolCallParams(options.tools, toolCall.name, toolParams)
|
|
2470
|
+
|
|
2471
|
+
parts.push({
|
|
2472
|
+
type: "tool-params-end",
|
|
2473
|
+
id: toolCall.id
|
|
2474
|
+
})
|
|
2475
|
+
|
|
2476
|
+
parts.push({
|
|
2477
|
+
type: "tool-call",
|
|
2478
|
+
id: toolCall.id,
|
|
2479
|
+
name: toolCall.name,
|
|
2480
|
+
params,
|
|
2481
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2482
|
+
})
|
|
2483
|
+
|
|
2484
|
+
toolCall.functionCall.emitted = true
|
|
2485
|
+
}
|
|
2486
|
+
break
|
|
2487
|
+
}
|
|
2488
|
+
|
|
2009
2489
|
case "response.apply_patch_call_operation_diff.delta": {
|
|
2010
2490
|
const toolCall = activeToolCalls[event.output_index]
|
|
2011
2491
|
if (Predicate.isNotUndefined(toolCall?.applyPatch)) {
|
|
@@ -2095,28 +2575,28 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2095
2575
|
}
|
|
2096
2576
|
|
|
2097
2577
|
case "response.reasoning_summary_part.added": {
|
|
2098
|
-
|
|
2578
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2099
2579
|
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
|
-
}
|
|
2580
|
+
// Conclude all can-conclude parts before starting new one
|
|
2581
|
+
for (const [summaryIndex, status] of Object.entries(reasoningPart.summaryParts)) {
|
|
2582
|
+
if (status === "can-conclude") {
|
|
2583
|
+
parts.push({
|
|
2584
|
+
type: "reasoning-end",
|
|
2585
|
+
id: `${event.item_id}:${summaryIndex}`,
|
|
2586
|
+
metadata: {
|
|
2587
|
+
openai: {
|
|
2588
|
+
...makeItemIdMetadata(event.item_id),
|
|
2589
|
+
...makeEncryptedContentMetadata(reasoningPart.encryptedContent)
|
|
2113
2590
|
}
|
|
2114
|
-
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2591
|
+
}
|
|
2592
|
+
})
|
|
2593
|
+
reasoningPart.summaryParts[Number(summaryIndex)] = "concluded"
|
|
2117
2594
|
}
|
|
2118
|
-
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2119
2595
|
}
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
if (Predicate.isUndefined(reasoningPart.summaryParts[event.summary_index])) {
|
|
2599
|
+
reasoningPart.summaryParts[event.summary_index] = "active"
|
|
2120
2600
|
parts.push({
|
|
2121
2601
|
type: "reasoning-start",
|
|
2122
2602
|
id: `${event.item_id}:${event.summary_index}`,
|
|
@@ -2136,26 +2616,27 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2136
2616
|
type: "reasoning-delta",
|
|
2137
2617
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2138
2618
|
delta: event.delta,
|
|
2139
|
-
metadata: { openai:
|
|
2619
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2140
2620
|
})
|
|
2141
2621
|
break
|
|
2142
2622
|
}
|
|
2143
2623
|
|
|
2144
2624
|
case "response.reasoning_summary_part.done": {
|
|
2625
|
+
const reasoningPart = getOrCreateReasoningPart(event.item_id)
|
|
2145
2626
|
// When OpenAI stores message data, we can immediately conclude the
|
|
2146
2627
|
// reasoning part given that we do not need the encrypted content
|
|
2147
2628
|
if (config.store === true) {
|
|
2148
2629
|
parts.push({
|
|
2149
2630
|
type: "reasoning-end",
|
|
2150
2631
|
id: `${event.item_id}:${event.summary_index}`,
|
|
2151
|
-
metadata: { openai:
|
|
2632
|
+
metadata: { openai: makeItemIdMetadata(event.item_id) }
|
|
2152
2633
|
})
|
|
2153
2634
|
// Mark the summary part concluded
|
|
2154
|
-
|
|
2635
|
+
reasoningPart.summaryParts[event.summary_index] = "concluded"
|
|
2155
2636
|
} else {
|
|
2156
2637
|
// Mark the summary part as can-conclude given we still need a
|
|
2157
2638
|
// final summary part with the encrypted content
|
|
2158
|
-
|
|
2639
|
+
reasoningPart.summaryParts[event.summary_index] = "can-conclude"
|
|
2159
2640
|
}
|
|
2160
2641
|
break
|
|
2161
2642
|
}
|
|
@@ -2174,7 +2655,7 @@ const makeStreamResponse = Effect.fnUntraced(
|
|
|
2174
2655
|
|
|
2175
2656
|
const annotateRequest = (
|
|
2176
2657
|
span: Span,
|
|
2177
|
-
request: typeof
|
|
2658
|
+
request: typeof OpenAiSchema.CreateResponse.Encoded
|
|
2178
2659
|
): void => {
|
|
2179
2660
|
addGenAIAnnotations(span, {
|
|
2180
2661
|
system: "openai",
|
|
@@ -2194,7 +2675,7 @@ const annotateRequest = (
|
|
|
2194
2675
|
})
|
|
2195
2676
|
}
|
|
2196
2677
|
|
|
2197
|
-
const annotateResponse = (span: Span, response:
|
|
2678
|
+
const annotateResponse = (span: Span, response: OpenAiSchema.Response): void => {
|
|
2198
2679
|
const finishReason = response.incomplete_details?.reason as string | undefined
|
|
2199
2680
|
addGenAIAnnotations(span, {
|
|
2200
2681
|
response: {
|
|
@@ -2244,7 +2725,7 @@ const annotateStreamResponse = (span: Span, part: Response.StreamPartEncoded) =>
|
|
|
2244
2725
|
// Tool Conversion
|
|
2245
2726
|
// =============================================================================
|
|
2246
2727
|
|
|
2247
|
-
type OpenAiToolChoice = typeof
|
|
2728
|
+
type OpenAiToolChoice = typeof OpenAiSchema.CreateResponse.Encoded["tool_choice"]
|
|
2248
2729
|
|
|
2249
2730
|
const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
2250
2731
|
config,
|
|
@@ -2255,7 +2736,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2255
2736
|
readonly options: LanguageModel.ProviderOptions
|
|
2256
2737
|
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
2257
2738
|
}): Effect.fn.Return<{
|
|
2258
|
-
readonly tools: ReadonlyArray<typeof
|
|
2739
|
+
readonly tools: ReadonlyArray<typeof OpenAiSchema.Tool.Encoded> | undefined
|
|
2259
2740
|
readonly toolChoice: OpenAiToolChoice | undefined
|
|
2260
2741
|
}, AiError.AiError> {
|
|
2261
2742
|
// Return immediately if no tools are in the toolkit
|
|
@@ -2263,7 +2744,7 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2263
2744
|
return { tools: undefined, toolChoice: undefined }
|
|
2264
2745
|
}
|
|
2265
2746
|
|
|
2266
|
-
const tools: Array<typeof
|
|
2747
|
+
const tools: Array<typeof OpenAiSchema.Tool.Encoded> = []
|
|
2267
2748
|
let toolChoice: OpenAiToolChoice | undefined = undefined
|
|
2268
2749
|
|
|
2269
2750
|
// Filter the incoming tools down to the set of allowed tools as indicated by
|
|
@@ -2279,14 +2760,16 @@ const prepareTools = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Too
|
|
|
2279
2760
|
|
|
2280
2761
|
// Convert the tools in the toolkit to the provider-defined format
|
|
2281
2762
|
for (const tool of allowedTools) {
|
|
2282
|
-
if (Tool.isUserDefined(tool)) {
|
|
2763
|
+
if (Tool.isUserDefined(tool) || Tool.isDynamic(tool)) {
|
|
2283
2764
|
const strict = Tool.getStrictMode(tool) ?? config.strictJsonSchema ?? true
|
|
2765
|
+
const description = Tool.getDescription(tool)
|
|
2766
|
+
const parameters = yield* tryToolJsonSchema(tool, "prepareTools")
|
|
2284
2767
|
tools.push({
|
|
2285
2768
|
type: "function",
|
|
2286
2769
|
name: tool.name,
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2770
|
+
parameters,
|
|
2771
|
+
strict,
|
|
2772
|
+
...(Predicate.isNotUndefined(description) ? { description } : undefined)
|
|
2290
2773
|
})
|
|
2291
2774
|
}
|
|
2292
2775
|
|
|
@@ -2468,35 +2951,63 @@ const getStatus = (
|
|
|
2468
2951
|
| Prompt.TextPart
|
|
2469
2952
|
| Prompt.ToolCallPart
|
|
2470
2953
|
| Prompt.ToolResultPart
|
|
2471
|
-
): typeof
|
|
2954
|
+
): typeof OpenAiSchema.MessageStatus.Encoded | null => part.options.openai?.status ?? null
|
|
2472
2955
|
const getEncryptedContent = (
|
|
2473
2956
|
part: Prompt.ReasoningPart
|
|
2474
2957
|
): string | null => part.options.openai?.encryptedContent ?? null
|
|
2475
2958
|
|
|
2476
2959
|
const getImageDetail = (part: Prompt.FilePart): ImageDetail => part.options.openai?.imageDetail ?? "auto"
|
|
2477
2960
|
|
|
2478
|
-
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } :
|
|
2961
|
+
const makeItemIdMetadata = (itemId: string | undefined) => Predicate.isNotUndefined(itemId) ? { itemId } : {}
|
|
2479
2962
|
|
|
2480
2963
|
const makeEncryptedContentMetadata = (encryptedContent: string | null | undefined) =>
|
|
2481
2964
|
Predicate.isNotNullish(encryptedContent) ? { encryptedContent } : undefined
|
|
2482
2965
|
|
|
2483
|
-
const
|
|
2966
|
+
const unsupportedSchemaError = (error: unknown, method: string): AiError.AiError =>
|
|
2967
|
+
AiError.make({
|
|
2968
|
+
module: "OpenAiLanguageModel",
|
|
2969
|
+
method,
|
|
2970
|
+
reason: new AiError.UnsupportedSchemaError({
|
|
2971
|
+
description: error instanceof Error ? error.message : String(error)
|
|
2972
|
+
})
|
|
2973
|
+
})
|
|
2974
|
+
|
|
2975
|
+
const tryCodecTransform = <S extends Schema.Constraint>(schema: S, method: string) =>
|
|
2976
|
+
Effect.try({
|
|
2977
|
+
try: () => toCodecOpenAI(schema),
|
|
2978
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2979
|
+
})
|
|
2980
|
+
|
|
2981
|
+
const tryJsonSchema = <S extends Schema.Constraint>(schema: S, method: string) =>
|
|
2982
|
+
Effect.try({
|
|
2983
|
+
try: () => Tool.getJsonSchemaFromSchema(schema, { transformer: toCodecOpenAI }),
|
|
2984
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2985
|
+
})
|
|
2986
|
+
|
|
2987
|
+
const tryToolJsonSchema = <T extends Tool.Any>(tool: T, method: string) =>
|
|
2988
|
+
Effect.try({
|
|
2989
|
+
try: () => Tool.getJsonSchema(tool, { transformer: toCodecOpenAI }),
|
|
2990
|
+
catch: (error) => unsupportedSchemaError(error, method)
|
|
2991
|
+
})
|
|
2992
|
+
|
|
2993
|
+
const prepareResponseFormat = Effect.fnUntraced(function*({ config, options }: {
|
|
2484
2994
|
readonly config: typeof Config.Service
|
|
2485
2995
|
readonly options: LanguageModel.ProviderOptions
|
|
2486
|
-
}): typeof
|
|
2996
|
+
}): Effect.fn.Return<typeof OpenAiSchema.TextResponseFormatConfiguration.Encoded, AiError.AiError> {
|
|
2487
2997
|
if (options.responseFormat.type === "json") {
|
|
2488
2998
|
const name = options.responseFormat.objectName
|
|
2489
2999
|
const schema = options.responseFormat.schema
|
|
3000
|
+
const jsonSchema = yield* tryJsonSchema(schema, "prepareResponseFormat")
|
|
2490
3001
|
return {
|
|
2491
3002
|
type: "json_schema",
|
|
2492
3003
|
name,
|
|
2493
3004
|
description: AST.resolveDescription(schema.ast) ?? "Response with a JSON object",
|
|
2494
|
-
schema:
|
|
3005
|
+
schema: jsonSchema,
|
|
2495
3006
|
strict: config.strictJsonSchema ?? true
|
|
2496
3007
|
}
|
|
2497
3008
|
}
|
|
2498
3009
|
return { type: "text" }
|
|
2499
|
-
}
|
|
3010
|
+
})
|
|
2500
3011
|
|
|
2501
3012
|
interface ModelCapabilities {
|
|
2502
3013
|
readonly isReasoningModel: boolean
|
|
@@ -2570,7 +3081,42 @@ const getApprovalRequestIdMapping = (prompt: Prompt.Prompt): ReadonlyMap<string,
|
|
|
2570
3081
|
return mapping
|
|
2571
3082
|
}
|
|
2572
3083
|
|
|
2573
|
-
const
|
|
3084
|
+
const normalizeMcpToolCall = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>({
|
|
3085
|
+
toolNameMapper,
|
|
3086
|
+
toolParams,
|
|
3087
|
+
method
|
|
3088
|
+
}: {
|
|
3089
|
+
readonly toolNameMapper: Tool.NameMapper<Tools>
|
|
3090
|
+
readonly toolParams: unknown
|
|
3091
|
+
readonly method: string
|
|
3092
|
+
}): Effect.fn.Return<{
|
|
3093
|
+
readonly toolName: string
|
|
3094
|
+
readonly params: unknown
|
|
3095
|
+
}, AiError.AiError> {
|
|
3096
|
+
const toolName = toolNameMapper.getCustomName("mcp")
|
|
3097
|
+
|
|
3098
|
+
if (typeof toolParams !== "string") {
|
|
3099
|
+
return { toolName, params: toolParams }
|
|
3100
|
+
}
|
|
3101
|
+
|
|
3102
|
+
const params = yield* Effect.try({
|
|
3103
|
+
try: () => Tool.unsafeSecureJsonParse(toolParams),
|
|
3104
|
+
catch: (cause) =>
|
|
3105
|
+
AiError.make({
|
|
3106
|
+
module: "OpenAiLanguageModel",
|
|
3107
|
+
method,
|
|
3108
|
+
reason: new AiError.ToolParameterValidationError({
|
|
3109
|
+
toolName,
|
|
3110
|
+
toolParams,
|
|
3111
|
+
description: `Failed to securely JSON parse tool parameters: ${cause}`
|
|
3112
|
+
})
|
|
3113
|
+
})
|
|
3114
|
+
})
|
|
3115
|
+
|
|
3116
|
+
return { toolName, params }
|
|
3117
|
+
})
|
|
3118
|
+
|
|
3119
|
+
const getUsage = (usage: OpenAiSchema.ResponseUsage | null | undefined): Response.Usage => {
|
|
2574
3120
|
if (Predicate.isNullish(usage)) {
|
|
2575
3121
|
return {
|
|
2576
3122
|
inputTokens: {
|
|
@@ -2589,8 +3135,8 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2589
3135
|
|
|
2590
3136
|
const inputTokens = usage.input_tokens
|
|
2591
3137
|
const outputTokens = usage.output_tokens
|
|
2592
|
-
const cachedTokens = usage.input_tokens_details
|
|
2593
|
-
const reasoningTokens = usage.output_tokens_details
|
|
3138
|
+
const cachedTokens = getUsageTokenDetail(usage.input_tokens_details, "cached_tokens")
|
|
3139
|
+
const reasoningTokens = getUsageTokenDetail(usage.output_tokens_details, "reasoning_tokens")
|
|
2594
3140
|
|
|
2595
3141
|
return {
|
|
2596
3142
|
inputTokens: {
|
|
@@ -2606,3 +3152,64 @@ const getUsage = (usage: Generated.ResponseUsage | null | undefined): Response.U
|
|
|
2606
3152
|
}
|
|
2607
3153
|
}
|
|
2608
3154
|
}
|
|
3155
|
+
|
|
3156
|
+
type ServiceTier = "default" | "auto" | "flex" | "scale" | "priority" | null
|
|
3157
|
+
|
|
3158
|
+
const toServiceTier = (value: string | undefined): {
|
|
3159
|
+
readonly metadata: {
|
|
3160
|
+
readonly openai: {
|
|
3161
|
+
readonly serviceTier: ServiceTier
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
} | undefined => {
|
|
3165
|
+
switch (value) {
|
|
3166
|
+
case "default":
|
|
3167
|
+
case "auto":
|
|
3168
|
+
case "flex":
|
|
3169
|
+
case "scale":
|
|
3170
|
+
case "priority":
|
|
3171
|
+
return { metadata: { openai: { serviceTier: value } } }
|
|
3172
|
+
default:
|
|
3173
|
+
return undefined
|
|
3174
|
+
}
|
|
3175
|
+
}
|
|
3176
|
+
|
|
3177
|
+
const getUsageTokenDetail = (details: unknown, key: string): number =>
|
|
3178
|
+
Predicate.hasProperty(details, key) && typeof details[key] === "number" ? details[key] : 0
|
|
3179
|
+
|
|
3180
|
+
const transformToolCallParams = Effect.fnUntraced(function*<Tools extends ReadonlyArray<Tool.Any>>(
|
|
3181
|
+
tools: Tools,
|
|
3182
|
+
toolName: string,
|
|
3183
|
+
toolParams: unknown
|
|
3184
|
+
): Effect.fn.Return<unknown, AiError.AiError> {
|
|
3185
|
+
const tool = tools.find((tool) => tool.name === toolName)
|
|
3186
|
+
|
|
3187
|
+
if (Predicate.isUndefined(tool)) {
|
|
3188
|
+
return yield* AiError.make({
|
|
3189
|
+
module: "OpenAiLanguageModel",
|
|
3190
|
+
method: "makeResponse",
|
|
3191
|
+
reason: new AiError.ToolNotFoundError({
|
|
3192
|
+
toolName,
|
|
3193
|
+
availableTools: tools.map((tool) => tool.name)
|
|
3194
|
+
})
|
|
3195
|
+
})
|
|
3196
|
+
}
|
|
3197
|
+
|
|
3198
|
+
const { codec } = yield* tryCodecTransform(tool.parametersSchema, "makeResponse")
|
|
3199
|
+
|
|
3200
|
+
const transform = Schema.decodeEffect(codec)
|
|
3201
|
+
|
|
3202
|
+
return yield* (
|
|
3203
|
+
transform(toolParams) as Effect.Effect<unknown, Schema.SchemaError>
|
|
3204
|
+
).pipe(Effect.mapError((error) =>
|
|
3205
|
+
AiError.make({
|
|
3206
|
+
module: "OpenAiLanguageModel",
|
|
3207
|
+
method: "makeResponse",
|
|
3208
|
+
reason: new AiError.ToolParameterValidationError({
|
|
3209
|
+
toolName,
|
|
3210
|
+
toolParams,
|
|
3211
|
+
description: error.issue.toString()
|
|
3212
|
+
})
|
|
3213
|
+
})
|
|
3214
|
+
))
|
|
3215
|
+
})
|