@ai-sdk/openai 4.0.91 → 4.0.93
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 +21 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +183 -98
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +10 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +236 -151
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +19 -0
- package/package.json +3 -3
- package/src/responses/convert-to-openai-responses-input.ts +105 -1
- package/src/responses/map-openai-responses-annotation.ts +75 -0
- package/src/responses/openai-responses-api.ts +8 -0
- package/src/responses/openai-responses-language-model.ts +168 -134
- package/src/responses/openai-responses-provider-metadata.ts +4 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -757,6 +757,13 @@ for (const toolResult of result.toolResults) {
|
|
|
757
757
|
}
|
|
758
758
|
```
|
|
759
759
|
|
|
760
|
+
`result.sources` and source stream events expose the URLs returned in
|
|
761
|
+
`web_search_call.action.sources`. These may include uncited URLs. Inline
|
|
762
|
+
references are available separately on `text.citations` and `text-end.citations`,
|
|
763
|
+
including titles and provider-supplied text ranges. Raw annotations remain in
|
|
764
|
+
text provider metadata. Citation-only responses and `web_search_preview`
|
|
765
|
+
responses without retrieval data retain citation-derived sources.
|
|
766
|
+
|
|
760
767
|
The web search tool supports the following configuration options:
|
|
761
768
|
|
|
762
769
|
- **externalWebAccess** _boolean_ - Whether to use external web access for fetching live content. Defaults to `true`.
|
|
@@ -1853,6 +1860,10 @@ for (const part of result.content) {
|
|
|
1853
1860
|
// url_citation is returned from web_search and provides:
|
|
1854
1861
|
// properties: type, url, title, start_index and end_index
|
|
1855
1862
|
break;
|
|
1863
|
+
case 'file_search':
|
|
1864
|
+
// Retrieved file-search result; provides fileId.
|
|
1865
|
+
// The filename is available as part.filename.
|
|
1866
|
+
break;
|
|
1856
1867
|
case 'file_citation':
|
|
1857
1868
|
// file_citation is returned from file_search and provides:
|
|
1858
1869
|
// properties: type, file_id, filename and index
|
|
@@ -1931,12 +1942,20 @@ for (const part of result.content) {
|
|
|
1931
1942
|
}
|
|
1932
1943
|
```
|
|
1933
1944
|
|
|
1945
|
+
When file-search results are included in the API response, each retrieved file
|
|
1946
|
+
is exposed once as a document source, even if it has multiple matching passages
|
|
1947
|
+
or is not cited. Inline file citations are available on `text.citations` and
|
|
1948
|
+
`text-end.citations`. If the response omits file-search results, file citations
|
|
1949
|
+
also remain document sources for compatibility. Generated file paths remain
|
|
1950
|
+
source parts and are not treated as citations.
|
|
1951
|
+
|
|
1934
1952
|
#### Typed providerMetadata in Source Document Parts
|
|
1935
1953
|
|
|
1936
1954
|
For source document parts, when `part.type === 'source'` and `sourceType === 'document'`, the `providerMetadata` is provided as `OpenaiResponsesSourceDocumentProviderMetadata`.
|
|
1937
1955
|
|
|
1938
1956
|
This metadata is also a discriminated union with a required `type` field. Supported types include:
|
|
1939
1957
|
|
|
1958
|
+
- `file_search` (retrieved file; includes `fileId`)
|
|
1940
1959
|
- `file_citation`
|
|
1941
1960
|
- `container_file_citation`
|
|
1942
1961
|
- `file_path`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.93",
|
|
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.27",
|
|
39
|
+
"@ai-sdk/provider-utils": "5.0.59"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@ai-sdk/test-server": "2.0.4",
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
parseProviderOptions,
|
|
16
16
|
resolveFullMediaType,
|
|
17
17
|
resolveProviderReference,
|
|
18
|
+
safeValidateTypes,
|
|
18
19
|
validateTypes,
|
|
19
20
|
type ToolNameMapping,
|
|
20
21
|
} from '@ai-sdk/provider-utils';
|
|
@@ -37,6 +38,7 @@ import type {
|
|
|
37
38
|
OpenAIResponsesInput,
|
|
38
39
|
OpenAIResponsesReasoning,
|
|
39
40
|
OpenAIResponsesToolCaller,
|
|
41
|
+
OpenAIResponsesWebSearchCall,
|
|
40
42
|
} from './openai-responses-api';
|
|
41
43
|
import {
|
|
42
44
|
toolSearchInputSchema,
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
programmaticToolCallingInputSchema,
|
|
47
49
|
programmaticToolCallingOutputSchema,
|
|
48
50
|
} from '../tool/programmatic-tool-calling';
|
|
51
|
+
import { webSearchOutputSchema } from '../tool/web-search';
|
|
49
52
|
import {
|
|
50
53
|
getParallelToolCallMetadata,
|
|
51
54
|
type ParallelToolCallMetadata,
|
|
@@ -68,6 +71,65 @@ function mapToolCaller(
|
|
|
68
71
|
: caller;
|
|
69
72
|
}
|
|
70
73
|
|
|
74
|
+
async function convertWebSearchToolResultOutput({
|
|
75
|
+
output,
|
|
76
|
+
id,
|
|
77
|
+
}: {
|
|
78
|
+
output: LanguageModelV4ToolResultOutput;
|
|
79
|
+
id: string;
|
|
80
|
+
}): Promise<OpenAIResponsesWebSearchCall | undefined> {
|
|
81
|
+
if (output.type !== 'json') {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const validation = await safeValidateTypes({
|
|
86
|
+
value: output.value,
|
|
87
|
+
schema: webSearchOutputSchema,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!validation.success || validation.value.action == null) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { action, sources } = validation.value;
|
|
95
|
+
|
|
96
|
+
switch (action.type) {
|
|
97
|
+
case 'search':
|
|
98
|
+
return {
|
|
99
|
+
type: 'web_search_call',
|
|
100
|
+
id,
|
|
101
|
+
status: 'completed',
|
|
102
|
+
action: {
|
|
103
|
+
type: 'search',
|
|
104
|
+
...(action.query != null && { query: action.query }),
|
|
105
|
+
...(action.queries != null && { queries: action.queries }),
|
|
106
|
+
...(sources != null && { sources }),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
case 'openPage':
|
|
110
|
+
return {
|
|
111
|
+
type: 'web_search_call',
|
|
112
|
+
id,
|
|
113
|
+
status: 'completed',
|
|
114
|
+
action: {
|
|
115
|
+
type: 'open_page',
|
|
116
|
+
url: action.url,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
case 'findInPage':
|
|
120
|
+
return {
|
|
121
|
+
type: 'web_search_call',
|
|
122
|
+
id,
|
|
123
|
+
status: 'completed',
|
|
124
|
+
action: {
|
|
125
|
+
type: 'find_in_page',
|
|
126
|
+
url: action.url,
|
|
127
|
+
pattern: action.pattern,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
71
133
|
async function convertFunctionToolResultOutput({
|
|
72
134
|
output,
|
|
73
135
|
toolName,
|
|
@@ -373,6 +435,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
373
435
|
fileIdPrefixes,
|
|
374
436
|
passThroughUnsupportedFiles = false,
|
|
375
437
|
store,
|
|
438
|
+
isReasoningModel = false,
|
|
376
439
|
hasConversation = false,
|
|
377
440
|
hasPreviousResponseId = false,
|
|
378
441
|
hasLocalShellTool = false,
|
|
@@ -393,6 +456,7 @@ export async function convertToOpenAIResponsesInput({
|
|
|
393
456
|
fileIdPrefixes?: readonly string[];
|
|
394
457
|
passThroughUnsupportedFiles?: boolean;
|
|
395
458
|
store: boolean;
|
|
459
|
+
isReasoningModel?: boolean; // when true, stored assistant messages are only referenced together with their reasoning
|
|
396
460
|
hasConversation?: boolean; // when true, skip assistant messages that already have item IDs
|
|
397
461
|
hasPreviousResponseId?: boolean; // when true, skip reasoning and function-call items that already exist in the previous response chain
|
|
398
462
|
hasLocalShellTool?: boolean;
|
|
@@ -652,6 +716,13 @@ export async function convertToOpenAIResponsesInput({
|
|
|
652
716
|
const reasoningMessages: Record<string, OpenAIResponsesReasoning> = {};
|
|
653
717
|
const emittedTextItemIds = new Set<string>();
|
|
654
718
|
|
|
719
|
+
// Stored messages of reasoning models are bound to the reasoning item
|
|
720
|
+
// that produced them. When the reasoning has been removed (e.g. with
|
|
721
|
+
// `pruneMessages`), referencing the message fails with "Item 'msg_...'
|
|
722
|
+
// of type 'message' was provided without its required 'reasoning'
|
|
723
|
+
// item", so it is sent with its content instead.
|
|
724
|
+
let hasPrecedingReasoning = false;
|
|
725
|
+
|
|
655
726
|
for (const part of content) {
|
|
656
727
|
switch (part.type) {
|
|
657
728
|
case 'text': {
|
|
@@ -670,7 +741,11 @@ export async function convertToOpenAIResponsesInput({
|
|
|
670
741
|
}
|
|
671
742
|
|
|
672
743
|
// item references reduce the payload size
|
|
673
|
-
if (
|
|
744
|
+
if (
|
|
745
|
+
store &&
|
|
746
|
+
id != null &&
|
|
747
|
+
(!isReasoningModel || hasPrecedingReasoning)
|
|
748
|
+
) {
|
|
674
749
|
if (!emittedTextItemIds.has(id)) {
|
|
675
750
|
emittedTextItemIds.add(id);
|
|
676
751
|
input.push({ type: 'item_reference', id });
|
|
@@ -1039,6 +1114,33 @@ export async function convertToOpenAIResponsesInput({
|
|
|
1039
1114
|
part.toolName,
|
|
1040
1115
|
);
|
|
1041
1116
|
|
|
1117
|
+
if (
|
|
1118
|
+
resolvedResultToolName === 'web_search' ||
|
|
1119
|
+
resolvedResultToolName === 'web_search_preview'
|
|
1120
|
+
) {
|
|
1121
|
+
const itemId =
|
|
1122
|
+
(
|
|
1123
|
+
part.providerOptions?.[providerOptionsName] as
|
|
1124
|
+
| { itemId?: string }
|
|
1125
|
+
| undefined
|
|
1126
|
+
)?.itemId ?? part.toolCallId;
|
|
1127
|
+
|
|
1128
|
+
if (store) {
|
|
1129
|
+
input.push({ type: 'item_reference', id: itemId });
|
|
1130
|
+
break;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
const webSearchCall = await convertWebSearchToolResultOutput({
|
|
1134
|
+
output: part.output,
|
|
1135
|
+
id: itemId,
|
|
1136
|
+
});
|
|
1137
|
+
|
|
1138
|
+
if (webSearchCall != null) {
|
|
1139
|
+
input.push(webSearchCall);
|
|
1140
|
+
break;
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1042
1144
|
if (part.toolName === toolSearchToolName) {
|
|
1043
1145
|
const itemId = (part.providerOptions?.[providerOptionsName]
|
|
1044
1146
|
?.itemId ??
|
|
@@ -1154,6 +1256,8 @@ export async function convertToOpenAIResponsesInput({
|
|
|
1154
1256
|
}
|
|
1155
1257
|
|
|
1156
1258
|
case 'reasoning': {
|
|
1259
|
+
hasPrecedingReasoning = true;
|
|
1260
|
+
|
|
1157
1261
|
const providerOptions = await parseProviderOptions({
|
|
1158
1262
|
provider: providerOptionsName,
|
|
1159
1263
|
providerOptions: part.providerOptions,
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
LanguageModelV4Citation,
|
|
3
|
+
LanguageModelV4Source,
|
|
4
|
+
} from '@ai-sdk/provider';
|
|
5
|
+
import type { ResponsesTextProviderMetadata } from './openai-responses-provider-metadata';
|
|
6
|
+
|
|
7
|
+
type Annotation = NonNullable<
|
|
8
|
+
ResponsesTextProviderMetadata['annotations']
|
|
9
|
+
>[number];
|
|
10
|
+
|
|
11
|
+
export function mapOpenAIResponsesAnnotationSource(
|
|
12
|
+
annotation: Annotation,
|
|
13
|
+
providerOptionsName: string,
|
|
14
|
+
id: string,
|
|
15
|
+
): LanguageModelV4Source {
|
|
16
|
+
if (annotation.type === 'url_citation') {
|
|
17
|
+
return {
|
|
18
|
+
type: 'source',
|
|
19
|
+
sourceType: 'url',
|
|
20
|
+
id,
|
|
21
|
+
url: annotation.url,
|
|
22
|
+
title: annotation.title,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const filename =
|
|
27
|
+
annotation.type === 'file_path' ? annotation.file_id : annotation.filename;
|
|
28
|
+
return {
|
|
29
|
+
type: 'source',
|
|
30
|
+
sourceType: 'document',
|
|
31
|
+
id,
|
|
32
|
+
mediaType:
|
|
33
|
+
annotation.type === 'file_path'
|
|
34
|
+
? 'application/octet-stream'
|
|
35
|
+
: 'text/plain',
|
|
36
|
+
title: filename,
|
|
37
|
+
filename,
|
|
38
|
+
providerMetadata: {
|
|
39
|
+
[providerOptionsName]: {
|
|
40
|
+
type: annotation.type,
|
|
41
|
+
fileId: annotation.file_id,
|
|
42
|
+
...(annotation.type === 'container_file_citation'
|
|
43
|
+
? { containerId: annotation.container_id }
|
|
44
|
+
: { index: annotation.index }),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function mapOpenAIResponsesCitations(
|
|
51
|
+
annotations: Array<Annotation>,
|
|
52
|
+
providerOptionsName: string,
|
|
53
|
+
): Array<LanguageModelV4Citation> {
|
|
54
|
+
return annotations.flatMap(annotation =>
|
|
55
|
+
annotation.type === 'file_path'
|
|
56
|
+
? []
|
|
57
|
+
: [
|
|
58
|
+
{
|
|
59
|
+
source: mapOpenAIResponsesAnnotationSource(
|
|
60
|
+
annotation,
|
|
61
|
+
providerOptionsName,
|
|
62
|
+
annotation.type === 'url_citation'
|
|
63
|
+
? annotation.url
|
|
64
|
+
: annotation.file_id,
|
|
65
|
+
),
|
|
66
|
+
...('start_index' in annotation
|
|
67
|
+
? {
|
|
68
|
+
startIndex: annotation.start_index,
|
|
69
|
+
endIndex: annotation.end_index,
|
|
70
|
+
}
|
|
71
|
+
: {}),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
);
|
|
75
|
+
}
|
|
@@ -177,6 +177,7 @@ export type OpenAIResponsesInputItem =
|
|
|
177
177
|
| OpenAIResponsesApplyPatchCallOutput
|
|
178
178
|
| OpenAIResponsesToolSearchCall
|
|
179
179
|
| OpenAIResponsesToolSearchOutput
|
|
180
|
+
| OpenAIResponsesWebSearchCall
|
|
180
181
|
| OpenAIResponsesReasoning
|
|
181
182
|
| OpenAIResponsesItemReference
|
|
182
183
|
| OpenAIResponsesCompactionItem
|
|
@@ -342,6 +343,13 @@ export type OpenAIResponsesProgramOutput = {
|
|
|
342
343
|
status: 'completed' | 'incomplete';
|
|
343
344
|
};
|
|
344
345
|
|
|
346
|
+
export type OpenAIResponsesWebSearchCall = {
|
|
347
|
+
type: 'web_search_call';
|
|
348
|
+
id: string;
|
|
349
|
+
status: 'completed';
|
|
350
|
+
action: OpenAIResponsesWebSearchAction;
|
|
351
|
+
};
|
|
352
|
+
|
|
345
353
|
export type OpenAIResponsesCustomToolCall = {
|
|
346
354
|
type: 'custom_tool_call';
|
|
347
355
|
id?: string;
|