@ai-sdk/openai 4.0.59 → 4.0.61
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/CHANGELOG.md +20 -0
- package/dist/index.d.ts +47 -11
- package/dist/index.js +278 -138
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +45 -11
- package/dist/internal/index.js +188 -51
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +138 -10
- package/package.json +3 -3
- package/src/chat/openai-chat-language-model.ts +29 -1
- package/src/index.ts +1 -0
- package/src/{openai-responses-batch.ts → openai-batch.ts} +71 -87
- package/src/openai-language-model-capabilities.ts +10 -0
- package/src/openai-provider.ts +27 -6
- package/src/openai-tools.ts +1 -0
- package/src/responses/convert-to-openai-responses-input.ts +13 -0
- package/src/responses/openai-responses-api.ts +18 -0
- package/src/responses/openai-responses-language-model-options.ts +12 -0
- package/src/responses/openai-responses-language-model.ts +137 -29
- package/src/responses/openai-responses-prepare-tools.ts +47 -0
- package/src/responses/openai-responses-provider-metadata.ts +16 -0
- package/src/tool/custom.ts +7 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -192,9 +192,23 @@ The following provider options are available:
|
|
|
192
192
|
|
|
193
193
|
<Note>
|
|
194
194
|
Supported reasoning efforts vary by model. GPT-5.6 supports `'none'`, `'low'`,
|
|
195
|
-
`'medium'`, `'high'`, `'xhigh'`, and `'max'`.
|
|
195
|
+
`'medium'`, `'high'`, `'xhigh'`, and `'max'`. GPT-6 and later models support
|
|
196
|
+
`'low'`, `'medium'`, `'high'`, `'xhigh'`, and `'max'`.
|
|
196
197
|
</Note>
|
|
197
198
|
|
|
199
|
+
<Note type="warning">
|
|
200
|
+
GPT-6 and later models do not support `temperature`, `topP`, `logprobs`, or
|
|
201
|
+
the legacy `promptCacheRetention` option. The provider removes these settings
|
|
202
|
+
and returns a warning. Use the Responses API for GPT-6 tool calling.
|
|
203
|
+
</Note>
|
|
204
|
+
|
|
205
|
+
- **reasoningEffortUpdate** _'low' | 'medium' | 'high' | 'xhigh' | 'max'_
|
|
206
|
+
Updates the reasoning effort for GPT-6 and later models starting with the
|
|
207
|
+
current response without changing the request-level effort. Use this with
|
|
208
|
+
`previousResponseId` to preserve the original prompt prefix for caching.
|
|
209
|
+
Configuration updates require standard, single-agent mode and cannot be
|
|
210
|
+
combined with automatic compaction or automatic truncation.
|
|
211
|
+
|
|
198
212
|
- **reasoningMode** _'standard' | 'pro'_
|
|
199
213
|
Controls how much model work GPT-5.6 performs before returning a final answer. `'standard'` is the default. Use `'pro'` for difficult tasks where quality matters more than latency and token usage.
|
|
200
214
|
|
|
@@ -302,6 +316,57 @@ The following OpenAI-specific metadata may be returned:
|
|
|
302
316
|
- **reasoningContext** _(optional)_
|
|
303
317
|
Effective persisted-reasoning context returned by GPT-5.6 (`'current_turn'` or `'all_turns'`).
|
|
304
318
|
|
|
319
|
+
#### Changing Reasoning Effort Mid-Conversation
|
|
320
|
+
|
|
321
|
+
GPT-6 and later models can change reasoning effort between responses without
|
|
322
|
+
changing the request-level `reasoning` setting. The provider sends
|
|
323
|
+
`reasoningEffortUpdate` as an OpenAI `configuration_update` input item before
|
|
324
|
+
the next user message. Keeping the request-level effort unchanged preserves the
|
|
325
|
+
original prompt prefix for prompt caching.
|
|
326
|
+
|
|
327
|
+
```ts highlight="8,26,32"
|
|
328
|
+
import {
|
|
329
|
+
openai,
|
|
330
|
+
type OpenAILanguageModelResponsesOptions,
|
|
331
|
+
type OpenaiResponsesProviderMetadata,
|
|
332
|
+
} from '@ai-sdk/openai';
|
|
333
|
+
import { generateText } from 'ai';
|
|
334
|
+
|
|
335
|
+
const first = await generateText({
|
|
336
|
+
model: openai.responses('gpt-6-astra'),
|
|
337
|
+
reasoning: 'low',
|
|
338
|
+
prompt: 'Draft a database migration plan.',
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const metadata = first.finalStep.providerMetadata as
|
|
342
|
+
| OpenaiResponsesProviderMetadata
|
|
343
|
+
| undefined;
|
|
344
|
+
const previousResponseId = metadata?.openai.responseId;
|
|
345
|
+
|
|
346
|
+
if (!previousResponseId) {
|
|
347
|
+
throw new Error('OpenAI did not return a response ID.');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const second = await generateText({
|
|
351
|
+
model: openai.responses('gpt-6-astra'),
|
|
352
|
+
reasoning: 'low',
|
|
353
|
+
prompt: 'Analyze the failure modes and propose rollback steps.',
|
|
354
|
+
providerOptions: {
|
|
355
|
+
openai: {
|
|
356
|
+
previousResponseId,
|
|
357
|
+
reasoningEffortUpdate: 'high',
|
|
358
|
+
} satisfies OpenAILanguageModelResponsesOptions,
|
|
359
|
+
},
|
|
360
|
+
});
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
The response metadata continues to report the request-level reasoning effort,
|
|
364
|
+
not the effective effort selected by `reasoningEffortUpdate`. Configuration
|
|
365
|
+
updates are not supported with `reasoningMode: 'pro'`,
|
|
366
|
+
`contextManagement`, or `truncation: 'auto'`. Explicit compaction with
|
|
367
|
+
`compactionTrigger` remains supported; send a new `reasoningEffortUpdate` after
|
|
368
|
+
compaction when the effort should change again.
|
|
369
|
+
|
|
305
370
|
#### Reasoning Output
|
|
306
371
|
|
|
307
372
|
For reasoning models like `gpt-5`, you can enable reasoning summaries to see the model's thought process. Different models support different summarizers—for example, `o4-mini` supports detailed summaries. Set `reasoningSummary: "auto"` to automatically receive the richest level available. When `reasoningEffort` is set to a value other than `'none'`, the OpenAI Responses provider defaults `reasoningSummary` to `'detailed'`; set `reasoningSummary: null` to omit reasoning summaries.
|
|
@@ -463,6 +528,55 @@ metadata on tool-call parts. The SDK uses `providerMetadata.openai.namespace` or
|
|
|
463
528
|
`providerOptions.openai.namespace` to round-trip the namespace back to OpenAI on
|
|
464
529
|
subsequent requests.
|
|
465
530
|
|
|
531
|
+
#### Async Tool Calling
|
|
532
|
+
|
|
533
|
+
GPT-6 Astra and later Responses models support
|
|
534
|
+
[async tool calling](https://developers.openai.com/api/docs/guides/async-tool-calling).
|
|
535
|
+
An async tool lets the model continue generating independent output after issuing
|
|
536
|
+
the call instead of waiting for its result. Your application still executes the
|
|
537
|
+
tool and sends its result in a later request using the original tool call ID.
|
|
538
|
+
|
|
539
|
+
Enable async calling on a function tool with `providerOptions.openai.async`:
|
|
540
|
+
|
|
541
|
+
```ts
|
|
542
|
+
import { openai, type OpenAIToolOptions } from '@ai-sdk/openai';
|
|
543
|
+
import { generateText, tool } from 'ai';
|
|
544
|
+
import { z } from 'zod';
|
|
545
|
+
|
|
546
|
+
const result = await generateText({
|
|
547
|
+
model: openai.responses('gpt-6-astra'),
|
|
548
|
+
tools: {
|
|
549
|
+
getWeather: tool({
|
|
550
|
+
description: 'Get the weather for a city.',
|
|
551
|
+
inputSchema: z.object({ city: z.string() }),
|
|
552
|
+
outputSchema: z.object({
|
|
553
|
+
city: z.string(),
|
|
554
|
+
temperatureC: z.number(),
|
|
555
|
+
}),
|
|
556
|
+
providerOptions: {
|
|
557
|
+
openai: { async: true } satisfies OpenAIToolOptions,
|
|
558
|
+
},
|
|
559
|
+
}),
|
|
560
|
+
},
|
|
561
|
+
prompt:
|
|
562
|
+
'Start the weather lookup for Paris, then list three general packing essentials without waiting.',
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
The generated tool call exposes the provider marker as
|
|
567
|
+
`providerMetadata.openai.async`. Use `providerMetadata.openai.responseId` as the
|
|
568
|
+
next request's `previousResponseId`, and submit the result in a tool message with
|
|
569
|
+
the original `toolCallId`.
|
|
570
|
+
|
|
571
|
+
With `streamText`, OpenAI can continue streaming text after the completed
|
|
572
|
+
`tool-call` part. Use the tool's `onInputAvailable` callback to start work as soon
|
|
573
|
+
as that part arrives. If `execute` returns the same already-running promise, tool
|
|
574
|
+
execution overlaps the rest of the model stream. Omit `execute` when the job
|
|
575
|
+
should outlive the current generation and submit its result in a later request.
|
|
576
|
+
|
|
577
|
+
Async calling applies to directly called function and custom tools. It does not
|
|
578
|
+
apply to hosted tools and should not be combined with programmatic tool calling.
|
|
579
|
+
|
|
466
580
|
#### Programmatic Tool Calling
|
|
467
581
|
|
|
468
582
|
OpenAI Programmatic Tool Calling lets supported Responses models generate and run
|
|
@@ -1970,27 +2084,37 @@ import { openai } from '@ai-sdk/openai';
|
|
|
1970
2084
|
import {
|
|
1971
2085
|
experimental_getBatchResults as getBatchResults,
|
|
1972
2086
|
experimental_getBatchStatus as getBatchStatus,
|
|
1973
|
-
|
|
2087
|
+
experimental_startBatch as startBatch,
|
|
1974
2088
|
} from 'ai';
|
|
1975
2089
|
import { setTimeout } from 'node:timers/promises';
|
|
1976
2090
|
|
|
1977
|
-
const model =
|
|
2091
|
+
const model = 'gpt-4.1-nano';
|
|
1978
2092
|
|
|
1979
|
-
const batch = await
|
|
1980
|
-
|
|
2093
|
+
const batch = await startBatch({
|
|
2094
|
+
provider: openai,
|
|
1981
2095
|
requests: [
|
|
1982
|
-
{
|
|
1983
|
-
|
|
2096
|
+
{
|
|
2097
|
+
id: 'capital-france',
|
|
2098
|
+
type: 'text',
|
|
2099
|
+
model,
|
|
2100
|
+
prompt: 'What is the capital of France?',
|
|
2101
|
+
},
|
|
2102
|
+
{
|
|
2103
|
+
id: 'capital-germany',
|
|
2104
|
+
type: 'text',
|
|
2105
|
+
model,
|
|
2106
|
+
prompt: 'What is the capital of Germany?',
|
|
2107
|
+
},
|
|
1984
2108
|
],
|
|
1985
2109
|
});
|
|
1986
2110
|
|
|
1987
2111
|
let status = batch.status;
|
|
1988
2112
|
while (status === 'pending') {
|
|
1989
2113
|
await setTimeout(60_000);
|
|
1990
|
-
({ status } = await getBatchStatus({
|
|
2114
|
+
({ status } = await getBatchStatus({ provider: openai, batch }));
|
|
1991
2115
|
}
|
|
1992
2116
|
|
|
1993
|
-
for await (const item of getBatchResults({
|
|
2117
|
+
for await (const item of getBatchResults({ provider: openai, batch })) {
|
|
1994
2118
|
if (item.status === 'succeeded') {
|
|
1995
2119
|
console.log(item.id, item.text);
|
|
1996
2120
|
} else {
|
|
@@ -1999,11 +2123,15 @@ for await (const item of getBatchResults({ model, batch })) {
|
|
|
1999
2123
|
}
|
|
2000
2124
|
```
|
|
2001
2125
|
|
|
2002
|
-
`
|
|
2126
|
+
`startBatch` returns a serializable batch reference. Persist this reference
|
|
2003
2127
|
to check the batch status or retrieve its results from another process. Results
|
|
2004
2128
|
can arrive in a different order from the input requests, so match each result by
|
|
2005
2129
|
its `id`.
|
|
2006
2130
|
|
|
2131
|
+
Each request specifies its `type` and `model`. OpenAI requires every text
|
|
2132
|
+
request in a batch to use the same model and throws before submission when the
|
|
2133
|
+
models differ.
|
|
2134
|
+
|
|
2007
2135
|
#### Webhooks
|
|
2008
2136
|
|
|
2009
2137
|
<Note>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.61",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@ai-sdk/provider": "4.0.
|
|
39
|
-
"@ai-sdk/provider-utils": "5.0.
|
|
38
|
+
"@ai-sdk/provider": "4.0.11",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.37"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.1",
|
|
@@ -118,10 +118,25 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
118
118
|
const modelCapabilities = getOpenAILanguageModelCapabilities(this.modelId);
|
|
119
119
|
|
|
120
120
|
// AI SDK reasoning values map directly to the OpenAI reasoning values.
|
|
121
|
-
|
|
121
|
+
let resolvedReasoningEffort =
|
|
122
122
|
openaiOptions.reasoningEffort ??
|
|
123
123
|
(isCustomReasoning(reasoning) ? reasoning : undefined);
|
|
124
124
|
|
|
125
|
+
if (
|
|
126
|
+
resolvedReasoningEffort != null &&
|
|
127
|
+
modelCapabilities.supportedReasoningEfforts != null &&
|
|
128
|
+
!modelCapabilities.supportedReasoningEfforts.includes(
|
|
129
|
+
resolvedReasoningEffort,
|
|
130
|
+
)
|
|
131
|
+
) {
|
|
132
|
+
warnings.push({
|
|
133
|
+
type: 'unsupported',
|
|
134
|
+
feature: 'reasoningEffort',
|
|
135
|
+
details: `${this.modelId} only supports the following reasoning efforts: ${modelCapabilities.supportedReasoningEfforts.join(', ')}`,
|
|
136
|
+
});
|
|
137
|
+
resolvedReasoningEffort = undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
125
140
|
const isReasoningModel =
|
|
126
141
|
openaiOptions.forceReasoning ?? modelCapabilities.isReasoningModel;
|
|
127
142
|
|
|
@@ -207,6 +222,19 @@ export class OpenAIChatLanguageModel implements LanguageModelV4 {
|
|
|
207
222
|
messages,
|
|
208
223
|
};
|
|
209
224
|
|
|
225
|
+
if (
|
|
226
|
+
modelCapabilities.supportedReasoningEfforts != null &&
|
|
227
|
+
baseArgs.prompt_cache_retention != null
|
|
228
|
+
) {
|
|
229
|
+
baseArgs.prompt_cache_retention = undefined;
|
|
230
|
+
warnings.push({
|
|
231
|
+
type: 'unsupported',
|
|
232
|
+
feature: 'promptCacheRetention',
|
|
233
|
+
details:
|
|
234
|
+
'promptCacheRetention is not supported by GPT-6 and later models; use promptCacheOptions instead',
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
210
238
|
// remove unsupported settings for reasoning models
|
|
211
239
|
// see https://platform.openai.com/docs/guides/reasoning#limitations
|
|
212
240
|
if (isReasoningModel) {
|
package/src/index.ts
CHANGED
|
@@ -44,6 +44,7 @@ export type {
|
|
|
44
44
|
OpenaiResponsesCompactionProviderMetadata,
|
|
45
45
|
OpenaiResponsesProviderMetadata,
|
|
46
46
|
OpenaiResponsesReasoningProviderMetadata,
|
|
47
|
+
OpenaiResponsesToolCallProviderMetadata,
|
|
47
48
|
OpenaiResponsesTextProviderMetadata,
|
|
48
49
|
OpenaiResponsesSourceDocumentProviderMetadata,
|
|
49
50
|
} from './responses/openai-responses-provider-metadata';
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
InvalidArgumentError,
|
|
3
3
|
InvalidResponseDataError,
|
|
4
|
-
type
|
|
5
|
-
type Experimental_BatchV4StartOptions as BatchV4StartOptions,
|
|
4
|
+
type Experimental_BatchV4 as BatchV4,
|
|
6
5
|
type Experimental_BatchV4StartResult as BatchV4StartResult,
|
|
7
6
|
type Experimental_BatchV4Error as BatchV4Error,
|
|
8
7
|
type Experimental_BatchV4ItemResult as BatchV4ItemResult,
|
|
9
8
|
type Experimental_BatchV4OperationOptions as BatchV4OperationOptions,
|
|
10
9
|
type Experimental_BatchV4Status as BatchV4Status,
|
|
10
|
+
type Experimental_TextBatchV4ItemResult as TextBatchV4ItemResult,
|
|
11
|
+
type Experimental_BatchV4StartOptions as BatchV4StartOptions,
|
|
12
|
+
type Experimental_TextBatchV4Request as TextBatchV4Request,
|
|
11
13
|
type LanguageModelV4GenerateResult,
|
|
12
14
|
type SharedV4ProviderMetadata,
|
|
13
15
|
type SharedV4Warning,
|
|
@@ -24,8 +26,6 @@ import {
|
|
|
24
26
|
postJsonToApi,
|
|
25
27
|
postToApi,
|
|
26
28
|
safeValidateTypes,
|
|
27
|
-
WORKFLOW_DESERIALIZE,
|
|
28
|
-
WORKFLOW_SERIALIZE,
|
|
29
29
|
zodSchema,
|
|
30
30
|
type InferSchema,
|
|
31
31
|
} from '@ai-sdk/provider-utils';
|
|
@@ -34,10 +34,7 @@ import {
|
|
|
34
34
|
openaiErrorDataSchema,
|
|
35
35
|
openaiFailedResponseHandler,
|
|
36
36
|
} from './openai-error';
|
|
37
|
-
import {
|
|
38
|
-
prepareOpenAIConfigForWorkflowDeserialize,
|
|
39
|
-
type OpenAIConfig,
|
|
40
|
-
} from './openai-config';
|
|
37
|
+
import type { OpenAIConfig } from './openai-config';
|
|
41
38
|
import { openaiFilesResponseSchema } from './files/openai-files-api';
|
|
42
39
|
import { convertOpenAIResponsesUsage } from './responses/convert-openai-responses-usage';
|
|
43
40
|
import { mapOpenAIResponseFinishReason } from './responses/map-openai-responses-finish-reason';
|
|
@@ -48,6 +45,7 @@ import {
|
|
|
48
45
|
import {
|
|
49
46
|
mapWebSearchOutput,
|
|
50
47
|
OpenAIResponsesLanguageModel,
|
|
48
|
+
openaiResponsesSupportedUrls,
|
|
51
49
|
} from './responses/openai-responses-language-model';
|
|
52
50
|
import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
|
|
53
51
|
import type { ResponsesReasoningProviderMetadata } from './responses/openai-responses-provider-metadata';
|
|
@@ -73,16 +71,18 @@ const openaiBatchProviderOptionsSchema = lazySchema(() =>
|
|
|
73
71
|
),
|
|
74
72
|
);
|
|
75
73
|
|
|
76
|
-
type
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
type OpenAIBatchModelIds = {
|
|
75
|
+
readonly text: OpenAIResponsesModelId;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type OpenAIBatchRequest = TextBatchV4Request<OpenAIResponsesModelId>;
|
|
79
79
|
|
|
80
80
|
type OpenAIBatchPreparedRequest = {
|
|
81
81
|
body: unknown;
|
|
82
82
|
warnings: SharedV4Warning[];
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
-
type
|
|
85
|
+
type OpenAIBatchResultConversion =
|
|
86
86
|
| { success: true; result: LanguageModelV4GenerateResult }
|
|
87
87
|
| { success: false; error: BatchV4Error };
|
|
88
88
|
|
|
@@ -143,20 +143,25 @@ const openaiBatchResultLineSchema = lazySchema(() =>
|
|
|
143
143
|
|
|
144
144
|
type OpenAIBatchResultLine = InferSchema<typeof openaiBatchResultLineSchema>;
|
|
145
145
|
|
|
146
|
-
class
|
|
146
|
+
export class OpenAIBatch implements BatchV4<OpenAIBatchModelIds> {
|
|
147
|
+
readonly specificationVersion = 'v4' as const;
|
|
148
|
+
readonly provider: string;
|
|
149
|
+
readonly supportedUrls = openaiResponsesSupportedUrls;
|
|
150
|
+
|
|
147
151
|
constructor(
|
|
148
152
|
private readonly options: {
|
|
149
|
-
|
|
153
|
+
provider: string;
|
|
150
154
|
config: OpenAIConfig;
|
|
151
|
-
prepareRequest: (
|
|
152
|
-
request: OpenAIBatchRequest,
|
|
153
|
-
) => PromiseLike<OpenAIBatchPreparedRequest>;
|
|
154
155
|
},
|
|
155
|
-
) {
|
|
156
|
+
) {
|
|
157
|
+
this.provider = options.provider;
|
|
158
|
+
}
|
|
156
159
|
|
|
157
|
-
async
|
|
158
|
-
options: BatchV4StartOptions<
|
|
160
|
+
async doStartBatch(
|
|
161
|
+
options: BatchV4StartOptions<OpenAIBatchModelIds>,
|
|
159
162
|
): Promise<BatchV4StartResult> {
|
|
163
|
+
validateSingleModel(options.requests);
|
|
164
|
+
|
|
160
165
|
const fileParts: string[] = [];
|
|
161
166
|
const warnings: BatchV4StartResult['warnings'] =
|
|
162
167
|
options.webhookUrl == null
|
|
@@ -180,7 +185,7 @@ class OpenAIResponsesBatch {
|
|
|
180
185
|
openaiBatchInputFileDefaultExpiresAfterSeconds;
|
|
181
186
|
|
|
182
187
|
for (const request of options.requests) {
|
|
183
|
-
const preparedRequest = await this.
|
|
188
|
+
const preparedRequest = await this.prepareRequest(request);
|
|
184
189
|
|
|
185
190
|
fileParts.push(
|
|
186
191
|
JSON.stringify({
|
|
@@ -285,7 +290,7 @@ class OpenAIResponsesBatch {
|
|
|
285
290
|
}
|
|
286
291
|
|
|
287
292
|
private async parseBatchProviderOptions(
|
|
288
|
-
providerOptions: BatchV4StartOptions<
|
|
293
|
+
providerOptions: BatchV4StartOptions<OpenAIBatchModelIds>['providerOptions'],
|
|
289
294
|
) {
|
|
290
295
|
const providerOptionsName = this.options.config.provider.includes('azure')
|
|
291
296
|
? 'azure'
|
|
@@ -307,16 +312,16 @@ class OpenAIResponsesBatch {
|
|
|
307
312
|
return batchOptions;
|
|
308
313
|
}
|
|
309
314
|
|
|
310
|
-
async
|
|
315
|
+
async doGetBatchStatus(
|
|
311
316
|
options: BatchV4OperationOptions,
|
|
312
317
|
): Promise<BatchV4Status> {
|
|
313
318
|
const batch = await this.retrieveBatch(options);
|
|
314
319
|
return convertOpenAIBatchStatus(batch);
|
|
315
320
|
}
|
|
316
321
|
|
|
317
|
-
async
|
|
322
|
+
async doGetBatchResults(
|
|
318
323
|
options: BatchV4OperationOptions,
|
|
319
|
-
): Promise<ReadableStream<BatchV4ItemResult
|
|
324
|
+
): Promise<ReadableStream<BatchV4ItemResult>> {
|
|
320
325
|
const batch = await this.retrieveBatch(options);
|
|
321
326
|
|
|
322
327
|
const batchStatus = convertOpenAIBatchStatus(batch);
|
|
@@ -368,7 +373,7 @@ class OpenAIResponsesBatch {
|
|
|
368
373
|
}: {
|
|
369
374
|
fileIds: string[];
|
|
370
375
|
options: BatchV4OperationOptions;
|
|
371
|
-
}): AsyncGenerator<BatchV4ItemResult
|
|
376
|
+
}): AsyncGenerator<BatchV4ItemResult> {
|
|
372
377
|
for (const fileId of fileIds) {
|
|
373
378
|
const { value: lines } = await getFromApi({
|
|
374
379
|
url: this.getUrl(`/files/${encodeURIComponent(fileId)}/content`),
|
|
@@ -393,7 +398,7 @@ class OpenAIResponsesBatch {
|
|
|
393
398
|
|
|
394
399
|
private async convertResultLine(
|
|
395
400
|
line: OpenAIBatchResultLine,
|
|
396
|
-
): Promise<
|
|
401
|
+
): Promise<TextBatchV4ItemResult> {
|
|
397
402
|
if (line.error != null) {
|
|
398
403
|
const error = {
|
|
399
404
|
message: line.error.message,
|
|
@@ -401,18 +406,19 @@ class OpenAIResponsesBatch {
|
|
|
401
406
|
};
|
|
402
407
|
|
|
403
408
|
if (line.error.code === 'batch_cancelled') {
|
|
404
|
-
return { id: line.custom_id, status: 'cancelled', error };
|
|
409
|
+
return { type: 'text', id: line.custom_id, status: 'cancelled', error };
|
|
405
410
|
}
|
|
406
411
|
|
|
407
412
|
if (line.error.code === 'batch_expired') {
|
|
408
|
-
return { id: line.custom_id, status: 'expired', error };
|
|
413
|
+
return { type: 'text', id: line.custom_id, status: 'expired', error };
|
|
409
414
|
}
|
|
410
415
|
|
|
411
|
-
return { id: line.custom_id, status: 'failed', error };
|
|
416
|
+
return { type: 'text', id: line.custom_id, status: 'failed', error };
|
|
412
417
|
}
|
|
413
418
|
|
|
414
419
|
if (line.response == null) {
|
|
415
420
|
return {
|
|
421
|
+
type: 'text',
|
|
416
422
|
id: line.custom_id,
|
|
417
423
|
status: 'failed',
|
|
418
424
|
error: {
|
|
@@ -425,6 +431,7 @@ class OpenAIResponsesBatch {
|
|
|
425
431
|
|
|
426
432
|
if (line.response.status_code < 200 || line.response.status_code >= 300) {
|
|
427
433
|
return {
|
|
434
|
+
type: 'text',
|
|
428
435
|
id: line.custom_id,
|
|
429
436
|
status: 'failed',
|
|
430
437
|
error: await convertOpenAIErrorResponse({
|
|
@@ -434,11 +441,10 @@ class OpenAIResponsesBatch {
|
|
|
434
441
|
};
|
|
435
442
|
}
|
|
436
443
|
|
|
437
|
-
const conversion = await
|
|
438
|
-
line.response.body,
|
|
439
|
-
);
|
|
444
|
+
const conversion = await convertOpenAIBatchResult(line.response.body);
|
|
440
445
|
if (!conversion.success) {
|
|
441
446
|
return {
|
|
447
|
+
type: 'text',
|
|
442
448
|
id: line.custom_id,
|
|
443
449
|
status: 'failed',
|
|
444
450
|
error: conversion.error,
|
|
@@ -446,17 +452,43 @@ class OpenAIResponsesBatch {
|
|
|
446
452
|
}
|
|
447
453
|
|
|
448
454
|
return {
|
|
455
|
+
type: 'text',
|
|
449
456
|
id: line.custom_id,
|
|
450
457
|
status: 'succeeded',
|
|
451
458
|
result: conversion.result,
|
|
452
459
|
};
|
|
453
460
|
}
|
|
454
461
|
|
|
462
|
+
private async prepareRequest(
|
|
463
|
+
request: OpenAIBatchRequest,
|
|
464
|
+
): Promise<OpenAIBatchPreparedRequest> {
|
|
465
|
+
const { args: body, warnings } =
|
|
466
|
+
await OpenAIResponsesLanguageModel.prepareRequest({
|
|
467
|
+
modelId: request.modelId,
|
|
468
|
+
config: this.options.config,
|
|
469
|
+
options: request.options,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
return { body, warnings };
|
|
473
|
+
}
|
|
474
|
+
|
|
455
475
|
private getUrl(path: string) {
|
|
456
|
-
return this.options.config.url({
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
476
|
+
return this.options.config.url({ path, modelId: '' });
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function validateSingleModel(requests: ReadonlyArray<OpenAIBatchRequest>) {
|
|
481
|
+
const modelId = requests[0]?.modelId;
|
|
482
|
+
|
|
483
|
+
for (const request of requests) {
|
|
484
|
+
if (request.modelId !== modelId) {
|
|
485
|
+
throw new InvalidArgumentError({
|
|
486
|
+
argument: 'requests',
|
|
487
|
+
message:
|
|
488
|
+
'The OpenAI Batch API requires all requests in a batch to use the ' +
|
|
489
|
+
`same model. Found "${modelId}" and "${request.modelId}".`,
|
|
490
|
+
});
|
|
491
|
+
}
|
|
460
492
|
}
|
|
461
493
|
}
|
|
462
494
|
|
|
@@ -468,54 +500,6 @@ const openAIBatchConvertibleProviderToolIds = new Set([
|
|
|
468
500
|
'openai.web_search_preview',
|
|
469
501
|
]);
|
|
470
502
|
|
|
471
|
-
export class OpenAIResponsesBatchLanguageModel
|
|
472
|
-
extends OpenAIResponsesLanguageModel
|
|
473
|
-
implements BatchLanguageModelV4
|
|
474
|
-
{
|
|
475
|
-
private readonly batch: OpenAIResponsesBatch;
|
|
476
|
-
|
|
477
|
-
static [WORKFLOW_SERIALIZE](model: OpenAIResponsesLanguageModel) {
|
|
478
|
-
return OpenAIResponsesLanguageModel[WORKFLOW_SERIALIZE](model);
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
static [WORKFLOW_DESERIALIZE](options: {
|
|
482
|
-
modelId: string;
|
|
483
|
-
config: Parameters<typeof prepareOpenAIConfigForWorkflowDeserialize>[0];
|
|
484
|
-
}) {
|
|
485
|
-
return new OpenAIResponsesBatchLanguageModel(
|
|
486
|
-
options.modelId as OpenAIResponsesModelId,
|
|
487
|
-
prepareOpenAIConfigForWorkflowDeserialize(options.config),
|
|
488
|
-
);
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
constructor(modelId: OpenAIResponsesModelId, config: OpenAIConfig) {
|
|
492
|
-
super(modelId, config);
|
|
493
|
-
this.batch = new OpenAIResponsesBatch({
|
|
494
|
-
modelId,
|
|
495
|
-
config,
|
|
496
|
-
prepareRequest: async request => {
|
|
497
|
-
const { args: body, warnings } = await this.getArgs(request.options);
|
|
498
|
-
|
|
499
|
-
return { body, warnings };
|
|
500
|
-
},
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
experimental_doStartBatch(
|
|
505
|
-
options: Parameters<BatchLanguageModelV4['experimental_doStartBatch']>[0],
|
|
506
|
-
) {
|
|
507
|
-
return this.batch.startBatch(options);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
experimental_doGetBatchStatus(options: BatchV4OperationOptions) {
|
|
511
|
-
return this.batch.getBatchStatus(options);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
experimental_doGetBatchResults(options: BatchV4OperationOptions) {
|
|
515
|
-
return this.batch.getBatchResults(options);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
503
|
function convertOpenAIBatchStatus(batch: OpenAIBatchResponse): BatchV4Status {
|
|
520
504
|
const status = mapOpenAIBatchStatus(batch.status);
|
|
521
505
|
const firstError = batch.errors?.data?.[0];
|
|
@@ -616,9 +600,9 @@ async function convertOpenAIErrorResponse({
|
|
|
616
600
|
};
|
|
617
601
|
}
|
|
618
602
|
|
|
619
|
-
async function
|
|
603
|
+
async function convertOpenAIBatchResult(
|
|
620
604
|
body: unknown,
|
|
621
|
-
): Promise<
|
|
605
|
+
): Promise<OpenAIBatchResultConversion> {
|
|
622
606
|
const validation = await safeValidateTypes({
|
|
623
607
|
value: body,
|
|
624
608
|
schema: openaiResponsesResponseSchema,
|
|
@@ -3,6 +3,9 @@ export type OpenAILanguageModelCapabilities = {
|
|
|
3
3
|
systemMessageMode: 'remove' | 'system' | 'developer';
|
|
4
4
|
supportsFlexProcessing: boolean;
|
|
5
5
|
supportsPriorityProcessing: boolean;
|
|
6
|
+
supportsConfigurationUpdate: boolean;
|
|
7
|
+
supportsAsyncToolCalling: boolean;
|
|
8
|
+
supportedReasoningEfforts: readonly string[] | undefined;
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
11
|
* Allow temperature, topP, logProbs when reasoningEffort is none.
|
|
@@ -19,6 +22,7 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
19
22
|
gptVersion?.minor == null &&
|
|
20
23
|
(gptVersion?.variant?.startsWith('chat') ?? false);
|
|
21
24
|
const isGptNanoModel = gptVersion?.variant?.startsWith('nano') ?? false;
|
|
25
|
+
const isGpt6OrLaterModel = gptVersion != null && gptVersion.major >= 6;
|
|
22
26
|
|
|
23
27
|
const supportsFlexProcessing =
|
|
24
28
|
(oSeriesVersion != null && oSeriesVersion >= 3) ||
|
|
@@ -41,6 +45,7 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
41
45
|
// https://platform.openai.com/docs/guides/latest-model#gpt-5-1-parameter-compatibility
|
|
42
46
|
// GPT-5.1 and later model families support temperature, topP, logProbs when reasoningEffort is none.
|
|
43
47
|
const supportsNonReasoningParameters =
|
|
48
|
+
!isGpt6OrLaterModel &&
|
|
44
49
|
gptVersion != null &&
|
|
45
50
|
(gptVersion.major > 5 ||
|
|
46
51
|
(gptVersion.major === 5 && (gptVersion.minor ?? 0) >= 1));
|
|
@@ -50,6 +55,11 @@ export function getOpenAILanguageModelCapabilities(
|
|
|
50
55
|
return {
|
|
51
56
|
supportsFlexProcessing,
|
|
52
57
|
supportsPriorityProcessing,
|
|
58
|
+
supportsConfigurationUpdate: isGpt6OrLaterModel,
|
|
59
|
+
supportsAsyncToolCalling: isGpt6OrLaterModel,
|
|
60
|
+
supportedReasoningEfforts: isGpt6OrLaterModel
|
|
61
|
+
? ['low', 'medium', 'high', 'xhigh', 'max']
|
|
62
|
+
: undefined,
|
|
53
63
|
isReasoningModel,
|
|
54
64
|
systemMessageMode,
|
|
55
65
|
supportsNonReasoningParameters,
|