@ai-sdk/openai 4.0.91 → 4.0.92

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.
@@ -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.91",
3
+ "version": "4.0.92",
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.26",
39
- "@ai-sdk/provider-utils": "5.0.58"
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,
@@ -1039,6 +1101,33 @@ export async function convertToOpenAIResponsesInput({
1039
1101
  part.toolName,
1040
1102
  );
1041
1103
 
1104
+ if (
1105
+ resolvedResultToolName === 'web_search' ||
1106
+ resolvedResultToolName === 'web_search_preview'
1107
+ ) {
1108
+ const itemId =
1109
+ (
1110
+ part.providerOptions?.[providerOptionsName] as
1111
+ | { itemId?: string }
1112
+ | undefined
1113
+ )?.itemId ?? part.toolCallId;
1114
+
1115
+ if (store) {
1116
+ input.push({ type: 'item_reference', id: itemId });
1117
+ break;
1118
+ }
1119
+
1120
+ const webSearchCall = await convertWebSearchToolResultOutput({
1121
+ output: part.output,
1122
+ id: itemId,
1123
+ });
1124
+
1125
+ if (webSearchCall != null) {
1126
+ input.push(webSearchCall);
1127
+ break;
1128
+ }
1129
+ }
1130
+
1042
1131
  if (part.toolName === toolSearchToolName) {
1043
1132
  const itemId = (part.providerOptions?.[providerOptionsName]
1044
1133
  ?.itemId ??
@@ -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;