@ai-sdk/openai 4.0.52 → 4.0.54
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 +13 -0
- package/dist/index.d.ts +7 -5
- package/dist/index.js +160 -32
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +222 -210
- package/dist/internal/index.js +41 -15
- package/dist/internal/index.js.map +1 -1
- package/package.json +1 -1
- package/src/openai-config.ts +41 -1
- package/src/openai-provider.ts +1 -0
- package/src/openai-responses-batch.ts +135 -14
- package/src/responses/openai-responses-api.ts +21 -19
- package/src/responses/openai-responses-language-model.ts +12 -6
package/package.json
CHANGED
package/src/openai-config.ts
CHANGED
|
@@ -3,10 +3,17 @@ import type {
|
|
|
3
3
|
WebSocketConstructor,
|
|
4
4
|
} from '@ai-sdk/provider-utils';
|
|
5
5
|
|
|
6
|
+
type OpenAIHeaders = Record<string, string | undefined>;
|
|
7
|
+
|
|
8
|
+
type SerializedOpenAIConfig = Omit<Partial<OpenAIConfig>, 'headers'> & {
|
|
9
|
+
headers?: (() => OpenAIHeaders) | OpenAIHeaders;
|
|
10
|
+
};
|
|
11
|
+
|
|
6
12
|
export type OpenAIConfig = {
|
|
7
13
|
provider: string;
|
|
14
|
+
baseURL?: string;
|
|
8
15
|
url: (options: { modelId: string; path: string }) => string;
|
|
9
|
-
headers?: () =>
|
|
16
|
+
headers?: () => OpenAIHeaders;
|
|
10
17
|
fetch?: FetchFunction;
|
|
11
18
|
webSocket?: WebSocketConstructor;
|
|
12
19
|
generateId?: () => string;
|
|
@@ -20,3 +27,36 @@ export type OpenAIConfig = {
|
|
|
20
27
|
*/
|
|
21
28
|
fileIdPrefixes?: readonly string[];
|
|
22
29
|
};
|
|
30
|
+
|
|
31
|
+
export function prepareOpenAIConfigForWorkflowDeserialize(
|
|
32
|
+
config: SerializedOpenAIConfig,
|
|
33
|
+
): OpenAIConfig {
|
|
34
|
+
if (config.provider == null) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
'OpenAI model is missing provider after workflow deserialization.',
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
...config,
|
|
42
|
+
provider: config.provider,
|
|
43
|
+
url:
|
|
44
|
+
typeof config.url === 'function'
|
|
45
|
+
? config.url
|
|
46
|
+
: ({ path }) => {
|
|
47
|
+
if (config.baseURL == null) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'OpenAI model is missing baseURL after workflow deserialization.',
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return `${config.baseURL}${path}`;
|
|
54
|
+
},
|
|
55
|
+
headers:
|
|
56
|
+
typeof config.headers === 'function'
|
|
57
|
+
? config.headers
|
|
58
|
+
: config.headers == null
|
|
59
|
+
? undefined
|
|
60
|
+
: () => config.headers as OpenAIHeaders,
|
|
61
|
+
};
|
|
62
|
+
}
|
package/src/openai-provider.ts
CHANGED
|
@@ -308,6 +308,7 @@ export function createOpenAI(
|
|
|
308
308
|
const createResponsesModel = (modelId: OpenAIResponsesModelId) => {
|
|
309
309
|
return new OpenAIResponsesBatchLanguageModel(modelId, {
|
|
310
310
|
provider: `${providerName}.responses`,
|
|
311
|
+
baseURL,
|
|
311
312
|
url: ({ path }) => `${baseURL}${path}`,
|
|
312
313
|
headers: getHeaders,
|
|
313
314
|
fetch: options.fetch,
|
|
@@ -33,7 +33,10 @@ import {
|
|
|
33
33
|
openaiErrorDataSchema,
|
|
34
34
|
openaiFailedResponseHandler,
|
|
35
35
|
} from './openai-error';
|
|
36
|
-
import
|
|
36
|
+
import {
|
|
37
|
+
prepareOpenAIConfigForWorkflowDeserialize,
|
|
38
|
+
type OpenAIConfig,
|
|
39
|
+
} from './openai-config';
|
|
37
40
|
import { openaiFilesResponseSchema } from './files/openai-files-api';
|
|
38
41
|
import { convertOpenAIResponsesUsage } from './responses/convert-openai-responses-usage';
|
|
39
42
|
import { mapOpenAIResponseFinishReason } from './responses/map-openai-responses-finish-reason';
|
|
@@ -41,7 +44,10 @@ import {
|
|
|
41
44
|
openaiResponsesResponseSchema,
|
|
42
45
|
type OpenAIResponsesLogprobs,
|
|
43
46
|
} from './responses/openai-responses-api';
|
|
44
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
mapWebSearchOutput,
|
|
49
|
+
OpenAIResponsesLanguageModel,
|
|
50
|
+
} from './responses/openai-responses-language-model';
|
|
45
51
|
import type { OpenAIResponsesModelId } from './responses/openai-responses-language-model-options';
|
|
46
52
|
import type { ResponsesReasoningProviderMetadata } from './responses/openai-responses-provider-metadata';
|
|
47
53
|
|
|
@@ -163,6 +169,23 @@ class OpenAIResponsesBatch {
|
|
|
163
169
|
for (const warning of preparedRequest.warnings) {
|
|
164
170
|
warnings.push({ requestId: request.id, warning });
|
|
165
171
|
}
|
|
172
|
+
|
|
173
|
+
for (const tool of request.options.tools ?? []) {
|
|
174
|
+
if (
|
|
175
|
+
tool.type === 'provider' &&
|
|
176
|
+
!openAIBatchConvertibleProviderToolIds.has(tool.id)
|
|
177
|
+
) {
|
|
178
|
+
warnings.push({
|
|
179
|
+
requestId: request.id,
|
|
180
|
+
warning: {
|
|
181
|
+
type: 'unsupported',
|
|
182
|
+
feature: `batch result conversion for tool "${tool.name}"`,
|
|
183
|
+
details:
|
|
184
|
+
'OpenAI may return output for this tool that AI SDK text batches cannot currently convert.',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
166
189
|
}
|
|
167
190
|
|
|
168
191
|
const filename = 'batch.jsonl';
|
|
@@ -382,6 +405,14 @@ class OpenAIResponsesBatch {
|
|
|
382
405
|
}
|
|
383
406
|
}
|
|
384
407
|
|
|
408
|
+
const openAIBatchConvertibleProviderToolIds = new Set([
|
|
409
|
+
'openai.code_interpreter',
|
|
410
|
+
'openai.custom',
|
|
411
|
+
'openai.file_search',
|
|
412
|
+
'openai.web_search',
|
|
413
|
+
'openai.web_search_preview',
|
|
414
|
+
]);
|
|
415
|
+
|
|
385
416
|
export class OpenAIResponsesBatchLanguageModel
|
|
386
417
|
extends OpenAIResponsesLanguageModel
|
|
387
418
|
implements BatchLanguageModelV4
|
|
@@ -393,12 +424,12 @@ export class OpenAIResponsesBatchLanguageModel
|
|
|
393
424
|
}
|
|
394
425
|
|
|
395
426
|
static [WORKFLOW_DESERIALIZE](options: {
|
|
396
|
-
modelId:
|
|
397
|
-
config:
|
|
427
|
+
modelId: string;
|
|
428
|
+
config: Parameters<typeof prepareOpenAIConfigForWorkflowDeserialize>[0];
|
|
398
429
|
}) {
|
|
399
430
|
return new OpenAIResponsesBatchLanguageModel(
|
|
400
|
-
options.modelId,
|
|
401
|
-
options.config,
|
|
431
|
+
options.modelId as OpenAIResponsesModelId,
|
|
432
|
+
prepareOpenAIConfigForWorkflowDeserialize(options.config),
|
|
402
433
|
);
|
|
403
434
|
}
|
|
404
435
|
|
|
@@ -577,6 +608,7 @@ async function convertOpenAIResponsesBatchResponse(
|
|
|
577
608
|
|
|
578
609
|
const content: LanguageModelV4GenerateResult['content'] = [];
|
|
579
610
|
const logprobs: Array<NonNullable<OpenAIResponsesLogprobs>> = [];
|
|
611
|
+
let hasFunctionCall = false;
|
|
580
612
|
|
|
581
613
|
for (const part of response.output) {
|
|
582
614
|
switch (part.type) {
|
|
@@ -612,15 +644,104 @@ async function convertOpenAIResponsesBatchResponse(
|
|
|
612
644
|
}
|
|
613
645
|
|
|
614
646
|
case 'function_call':
|
|
647
|
+
hasFunctionCall = true;
|
|
648
|
+
content.push({
|
|
649
|
+
type: 'tool-call',
|
|
650
|
+
toolCallId: part.call_id,
|
|
651
|
+
toolName: part.name,
|
|
652
|
+
input: part.arguments,
|
|
653
|
+
providerMetadata: {
|
|
654
|
+
openai: {
|
|
655
|
+
itemId: part.id,
|
|
656
|
+
...(part.namespace != null && { namespace: part.namespace }),
|
|
657
|
+
...(part.caller != null && {
|
|
658
|
+
caller:
|
|
659
|
+
part.caller.type === 'program'
|
|
660
|
+
? { type: 'program', callerId: part.caller.caller_id }
|
|
661
|
+
: part.caller,
|
|
662
|
+
}),
|
|
663
|
+
},
|
|
664
|
+
},
|
|
665
|
+
});
|
|
666
|
+
break;
|
|
667
|
+
|
|
615
668
|
case 'custom_tool_call':
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
669
|
+
hasFunctionCall = true;
|
|
670
|
+
content.push({
|
|
671
|
+
type: 'tool-call',
|
|
672
|
+
toolCallId: part.call_id,
|
|
673
|
+
toolName: part.name,
|
|
674
|
+
input: JSON.stringify(part.input),
|
|
675
|
+
providerMetadata: { openai: { itemId: part.id } },
|
|
676
|
+
});
|
|
677
|
+
break;
|
|
678
|
+
|
|
679
|
+
case 'web_search_call':
|
|
680
|
+
content.push({
|
|
681
|
+
type: 'tool-call',
|
|
682
|
+
toolCallId: part.id,
|
|
683
|
+
toolName: 'web_search',
|
|
684
|
+
input: '{}',
|
|
685
|
+
providerExecuted: true,
|
|
686
|
+
dynamic: true,
|
|
687
|
+
});
|
|
688
|
+
content.push({
|
|
689
|
+
type: 'tool-result',
|
|
690
|
+
toolCallId: part.id,
|
|
691
|
+
toolName: 'web_search',
|
|
692
|
+
result: mapWebSearchOutput(part.action),
|
|
693
|
+
dynamic: true,
|
|
694
|
+
});
|
|
695
|
+
break;
|
|
696
|
+
|
|
697
|
+
case 'file_search_call':
|
|
698
|
+
content.push({
|
|
699
|
+
type: 'tool-call',
|
|
700
|
+
toolCallId: part.id,
|
|
701
|
+
toolName: 'file_search',
|
|
702
|
+
input: '{}',
|
|
703
|
+
providerExecuted: true,
|
|
704
|
+
dynamic: true,
|
|
705
|
+
});
|
|
706
|
+
content.push({
|
|
707
|
+
type: 'tool-result',
|
|
708
|
+
toolCallId: part.id,
|
|
709
|
+
toolName: 'file_search',
|
|
710
|
+
result: {
|
|
711
|
+
queries: part.queries,
|
|
712
|
+
results:
|
|
713
|
+
part.results?.map(result => ({
|
|
714
|
+
attributes: result.attributes,
|
|
715
|
+
fileId: result.file_id,
|
|
716
|
+
filename: result.filename,
|
|
717
|
+
score: result.score,
|
|
718
|
+
text: result.text,
|
|
719
|
+
})) ?? null,
|
|
622
720
|
},
|
|
623
|
-
|
|
721
|
+
dynamic: true,
|
|
722
|
+
});
|
|
723
|
+
break;
|
|
724
|
+
|
|
725
|
+
case 'code_interpreter_call':
|
|
726
|
+
content.push({
|
|
727
|
+
type: 'tool-call',
|
|
728
|
+
toolCallId: part.id,
|
|
729
|
+
toolName: 'code_interpreter',
|
|
730
|
+
input: JSON.stringify({
|
|
731
|
+
code: part.code,
|
|
732
|
+
containerId: part.container_id,
|
|
733
|
+
}),
|
|
734
|
+
providerExecuted: true,
|
|
735
|
+
dynamic: true,
|
|
736
|
+
});
|
|
737
|
+
content.push({
|
|
738
|
+
type: 'tool-result',
|
|
739
|
+
toolCallId: part.id,
|
|
740
|
+
toolName: 'code_interpreter',
|
|
741
|
+
result: { outputs: part.outputs },
|
|
742
|
+
dynamic: true,
|
|
743
|
+
});
|
|
744
|
+
break;
|
|
624
745
|
|
|
625
746
|
default:
|
|
626
747
|
return {
|
|
@@ -655,7 +776,7 @@ async function convertOpenAIResponsesBatchResponse(
|
|
|
655
776
|
finishReason: {
|
|
656
777
|
unified: mapOpenAIResponseFinishReason({
|
|
657
778
|
finishReason: response.incomplete_details?.reason,
|
|
658
|
-
hasFunctionCall
|
|
779
|
+
hasFunctionCall,
|
|
659
780
|
}),
|
|
660
781
|
raw: response.incomplete_details?.reason ?? undefined,
|
|
661
782
|
},
|
|
@@ -831,24 +831,26 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
831
831
|
type: z.enum(['response.completed', 'response.incomplete']),
|
|
832
832
|
response: z.object({
|
|
833
833
|
incomplete_details: z.object({ reason: z.string() }).nullish(),
|
|
834
|
-
usage: z
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
834
|
+
usage: z
|
|
835
|
+
.object({
|
|
836
|
+
input_tokens: z.number(),
|
|
837
|
+
input_tokens_details: z
|
|
838
|
+
.object({
|
|
839
|
+
cached_tokens: z.number().nullish(),
|
|
840
|
+
cache_write_tokens: z.number().nullish(),
|
|
841
|
+
orchestration_input_tokens: z.number().nullish(),
|
|
842
|
+
orchestration_input_cached_tokens: z.number().nullish(),
|
|
843
|
+
})
|
|
844
|
+
.nullish(),
|
|
845
|
+
output_tokens: z.number(),
|
|
846
|
+
output_tokens_details: z
|
|
847
|
+
.object({
|
|
848
|
+
reasoning_tokens: z.number().nullish(),
|
|
849
|
+
orchestration_output_tokens: z.number().nullish(),
|
|
850
|
+
})
|
|
851
|
+
.nullish(),
|
|
852
|
+
})
|
|
853
|
+
.nullish(),
|
|
852
854
|
reasoning: z
|
|
853
855
|
.object({
|
|
854
856
|
context: z.string().nullish(),
|
|
@@ -1762,7 +1764,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
|
|
|
1762
1764
|
})
|
|
1763
1765
|
.nullish(),
|
|
1764
1766
|
})
|
|
1765
|
-
.
|
|
1767
|
+
.nullish(),
|
|
1766
1768
|
}),
|
|
1767
1769
|
),
|
|
1768
1770
|
);
|
|
@@ -30,7 +30,10 @@ import {
|
|
|
30
30
|
type InferSchema,
|
|
31
31
|
type ParseResult,
|
|
32
32
|
} from '@ai-sdk/provider-utils';
|
|
33
|
-
import
|
|
33
|
+
import {
|
|
34
|
+
prepareOpenAIConfigForWorkflowDeserialize,
|
|
35
|
+
type OpenAIConfig,
|
|
36
|
+
} from '../openai-config';
|
|
34
37
|
import { openaiFailedResponseHandler } from '../openai-error';
|
|
35
38
|
import { getOpenAILanguageModelCapabilities } from '../openai-language-model-capabilities';
|
|
36
39
|
import {
|
|
@@ -214,10 +217,13 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
214
217
|
}
|
|
215
218
|
|
|
216
219
|
static [WORKFLOW_DESERIALIZE](options: {
|
|
217
|
-
modelId:
|
|
218
|
-
config:
|
|
220
|
+
modelId: string;
|
|
221
|
+
config: Parameters<typeof prepareOpenAIConfigForWorkflowDeserialize>[0];
|
|
219
222
|
}) {
|
|
220
|
-
return new OpenAIResponsesLanguageModel(
|
|
223
|
+
return new OpenAIResponsesLanguageModel(
|
|
224
|
+
options.modelId as OpenAIResponsesModelId,
|
|
225
|
+
prepareOpenAIConfigForWorkflowDeserialize(options.config),
|
|
226
|
+
);
|
|
221
227
|
}
|
|
222
228
|
|
|
223
229
|
constructor(modelId: OpenAIResponsesModelId, config: OpenAIConfig) {
|
|
@@ -2578,7 +2584,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
2578
2584
|
raw: value.response.incomplete_details?.reason ?? undefined,
|
|
2579
2585
|
};
|
|
2580
2586
|
}
|
|
2581
|
-
usage = value.response.usage;
|
|
2587
|
+
usage = value.response.usage ?? undefined;
|
|
2582
2588
|
if (typeof value.response.service_tier === 'string') {
|
|
2583
2589
|
serviceTier = value.response.service_tier;
|
|
2584
2590
|
}
|
|
@@ -2909,7 +2915,7 @@ function isResponseOutputChunk(chunk: OpenAIResponsesChunk): boolean {
|
|
|
2909
2915
|
);
|
|
2910
2916
|
}
|
|
2911
2917
|
|
|
2912
|
-
function mapWebSearchOutput(
|
|
2918
|
+
export function mapWebSearchOutput(
|
|
2913
2919
|
action: OpenAIResponsesWebSearchAction | null | undefined,
|
|
2914
2920
|
): InferSchema<typeof webSearchOutputSchema> {
|
|
2915
2921
|
if (action == null) {
|