@ai-sdk/openai 4.0.14 → 4.0.16

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.
@@ -300,7 +300,7 @@ The following OpenAI-specific metadata may be returned:
300
300
 
301
301
  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.
302
302
 
303
- ```ts highlight="8-9,16"
303
+ ```ts highlight="10-14,17-23"
304
304
  import {
305
305
  openai,
306
306
  type OpenAILanguageModelResponsesOptions,
@@ -769,6 +769,95 @@ The MCP tool can be configured with:
769
769
  to AI SDK tools first.
770
770
  </Note>
771
771
 
772
+ #### Computer Tool
773
+
774
+ The OpenAI Responses API supports the GA computer tool through
775
+ `openai.tools.computer`. The model returns an ordered batch of browser or
776
+ desktop actions for your application to execute, and your application returns
777
+ an updated screenshot.
778
+
779
+ ```ts
780
+ import { openai } from '@ai-sdk/openai';
781
+ import { generateText, isStepCount } from 'ai';
782
+ import { captureScreenshot, executeComputerAction } from '@/utils/computer-use';
783
+
784
+ const result = await generateText({
785
+ model: openai.responses('gpt-5.4'),
786
+ tools: {
787
+ computer: openai.tools.computer({
788
+ needsApproval: ({ pendingSafetyChecks }) =>
789
+ pendingSafetyChecks.length > 0,
790
+
791
+ execute: async ({ actions, pendingSafetyChecks }) => {
792
+ for (const action of actions) {
793
+ await executeComputerAction(action);
794
+ }
795
+
796
+ return {
797
+ output: {
798
+ type: 'computer_screenshot',
799
+ imageUrl: `data:image/png;base64,${await captureScreenshot()}`,
800
+ detail: 'original',
801
+ },
802
+ acknowledgedSafetyChecks: pendingSafetyChecks,
803
+ };
804
+ },
805
+ }),
806
+ },
807
+ prompt: 'Open the settings page and enable dark mode.',
808
+ stopWhen: isStepCount(10),
809
+ });
810
+ ```
811
+
812
+ The execute callback receives:
813
+
814
+ - **actions** - An ordered array of `click`, `double_click`, `drag`,
815
+ `keypress`, `move`, `screenshot`, `scroll`, `type`, and `wait` actions.
816
+ Execute every action in order before capturing the next screenshot.
817
+ - **pendingSafetyChecks** - Safety checks returned by OpenAI. Review these
818
+ before continuing and include accepted checks in
819
+ `acknowledgedSafetyChecks`.
820
+ - **status** - The computer call status.
821
+
822
+ The action union is also exported as `OpenAIComputerAction`. Action-specific
823
+ fields are:
824
+
825
+ | Action | Fields |
826
+ | -------------- | ---------------------------------------------------- |
827
+ | `click` | `button`, `x`, `y`, optional `keys` |
828
+ | `double_click` | `x`, `y`, optional `keys` |
829
+ | `drag` | `path` containing `{ x, y }` points, optional `keys` |
830
+ | `keypress` | `keys` |
831
+ | `move` | `x`, `y`, optional `keys` |
832
+ | `screenshot` | No additional fields |
833
+ | `scroll` | `x`, `y`, `scrollX`, `scrollY`, optional `keys` |
834
+ | `type` | `text` |
835
+ | `wait` | No additional fields |
836
+
837
+ Safety checks use the exported `OpenAIComputerSafetyCheck` type.
838
+
839
+ Return a `computer_screenshot` with either:
840
+
841
+ - **imageUrl** - A fully qualified URL or image data URL.
842
+ - **fileId** - The ID of an uploaded screenshot file.
843
+
844
+ `detail` can be `auto`, `low`, `high`, or `original`. `original` preserves the
845
+ captured resolution and is recommended when accurate coordinates are
846
+ important.
847
+
848
+ Computer calls and `computer_call_output` screenshot results are carried
849
+ through multi-step generations when using `store`, `previousResponseId`, or
850
+ OpenAI conversations. You can also set `store: false`; the provider will
851
+ serialize the complete call and screenshot result into each follow-up request.
852
+
853
+ <Note type="warning">
854
+ Run computer use in an isolated browser or VM. Restrict reachable domains,
855
+ accounts, credentials, environment variables, and file-system access. Treat
856
+ on-screen content as untrusted prompt-injection input, require confirmation
857
+ immediately before consequential actions, and avoid exposing sensitive
858
+ information in screenshots.
859
+ </Note>
860
+
772
861
  #### Local Shell Tool
773
862
 
774
863
  The OpenAI responses API support the local shell tool for Codex models through the `openai.tools.localShell` tool.
@@ -1854,7 +1943,7 @@ They support additional settings and response metadata:
1854
1943
 
1855
1944
  - You can use the response `usage` to access the number of reasoning tokens that the model generated via `usage.outputTokenDetails.reasoningTokens`.
1856
1945
 
1857
- ```ts highlight="4,7-11,17"
1946
+ ```ts highlight="4,7-11,15"
1858
1947
  import { openai, type OpenAILanguageModelChatOptions } from '@ai-sdk/openai';
1859
1948
  import { generateText } from 'ai';
1860
1949
 
@@ -2112,7 +2201,7 @@ const result = streamText({
2112
2201
  OpenAI provides usage information for predicted outputs (`acceptedPredictionTokens` and `rejectedPredictionTokens`).
2113
2202
  You can access it in the `providerMetadata` object.
2114
2203
 
2115
- ```ts highlight="11"
2204
+ ```ts highlight="3-4"
2116
2205
  const openaiMetadata = (await result.providerMetadata)?.openai;
2117
2206
 
2118
2207
  const acceptedPredictionTokens = openaiMetadata?.acceptedPredictionTokens;
@@ -2207,7 +2296,7 @@ including `gpt-4o` and `gpt-4o-mini`.
2207
2296
  - For GPT-5.6 and later models, `usage.inputTokenDetails.cacheWriteTokens` reports tokens written to the cache.
2208
2297
  - For GPT-5.6 and later models, `promptCacheOptions.ttl` sets a minimum cache lifetime of 30 minutes. Earlier models generally retain in-memory prefixes for 5-10 minutes of inactivity, up to one hour.
2209
2298
 
2210
- ```ts highlight="11"
2299
+ ```ts highlight="9"
2211
2300
  import { openai } from '@ai-sdk/openai';
2212
2301
  import { generateText } from 'ai';
2213
2302
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "4.0.14",
3
+ "version": "4.0.16",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "sideEffects": false,
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@ai-sdk/provider": "4.0.3",
39
- "@ai-sdk/provider-utils": "5.0.10"
39
+ "@ai-sdk/provider-utils": "5.0.11"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/node": "22.19.19",
package/src/index.ts CHANGED
@@ -22,6 +22,10 @@ export type { OpenAIEmbeddingModelOptions } from './embedding/openai-embedding-m
22
22
  export type { OpenAISpeechModelOptions } from './speech/openai-speech-model-options';
23
23
  export type { OpenAITranscriptionModelOptions } from './transcription/openai-transcription-model-options';
24
24
  export type { OpenAIFilesOptions } from './files/openai-files-options';
25
+ export type {
26
+ OpenAIComputerAction,
27
+ OpenAIComputerSafetyCheck,
28
+ } from './tool/computer';
25
29
  export type {
26
30
  OpenaiResponsesCompactionProviderMetadata,
27
31
  OpenaiResponsesProviderMetadata,
@@ -1,5 +1,6 @@
1
1
  import { applyPatch } from './tool/apply-patch';
2
2
  import { codeInterpreter } from './tool/code-interpreter';
3
+ import { computer } from './tool/computer';
3
4
  import { customTool } from './tool/custom';
4
5
  import { fileSearch } from './tool/file-search';
5
6
  import { imageGeneration } from './tool/image-generation';
@@ -39,6 +40,16 @@ export const openaiTools = {
39
40
  */
40
41
  codeInterpreter,
41
42
 
43
+ /**
44
+ * The computer tool allows models to operate a browser or desktop through
45
+ * batched UI actions. Your application executes the actions and returns an
46
+ * updated screenshot.
47
+ *
48
+ * WARNING: Run computer use in an isolated environment, treat on-screen
49
+ * content as untrusted, and require confirmation for consequential actions.
50
+ */
51
+ computer,
52
+
42
53
  /**
43
54
  * File search is a tool available in the Responses API. It enables models to
44
55
  * retrieve information in a knowledge base of previously uploaded files through
@@ -21,6 +21,7 @@ import {
21
21
  applyPatchInputSchema,
22
22
  applyPatchOutputSchema,
23
23
  } from '../tool/apply-patch';
24
+ import { computerInputSchema, computerOutputSchema } from '../tool/computer';
24
25
  import {
25
26
  localShellInputSchema,
26
27
  localShellOutputSchema,
@@ -77,6 +78,7 @@ export async function convertToOpenAIResponsesInput({
77
78
  hasLocalShellTool = false,
78
79
  hasShellTool = false,
79
80
  hasApplyPatchTool = false,
81
+ hasComputerTool = false,
80
82
  customProviderToolNames,
81
83
  }: {
82
84
  prompt: LanguageModelV4Prompt;
@@ -92,6 +94,7 @@ export async function convertToOpenAIResponsesInput({
92
94
  hasLocalShellTool?: boolean;
93
95
  hasShellTool?: boolean;
94
96
  hasApplyPatchTool?: boolean;
97
+ hasComputerTool?: boolean;
95
98
  customProviderToolNames?: Set<string>;
96
99
  }): Promise<{
97
100
  input: OpenAIResponsesInput;
@@ -401,7 +404,7 @@ export async function convertToOpenAIResponsesInput({
401
404
  }
402
405
 
403
406
  // Provider-defined tool calls (local_shell, shell, apply_patch,
404
- // and custom tools) are stored by the API and can be sent as an
407
+ // computer, and custom tools) are stored by the API and can be sent as an
405
408
  // `item_reference` to reduce payload size. Plain client-executed
406
409
  // function calls must NOT be: the matching `function_call_output`
407
410
  // can only reference the call by `call_id` (`call_...`), which
@@ -414,6 +417,7 @@ export async function convertToOpenAIResponsesInput({
414
417
  (hasLocalShellTool && resolvedToolName === 'local_shell') ||
415
418
  (hasShellTool && resolvedToolName === 'shell') ||
416
419
  (hasApplyPatchTool && resolvedToolName === 'apply_patch') ||
420
+ (hasComputerTool && resolvedToolName === 'computer') ||
417
421
  (customProviderToolNames?.has(resolvedToolName) ?? false);
418
422
 
419
423
  if (store && id != null && isProviderDefinedToolCall) {
@@ -479,6 +483,55 @@ export async function convertToOpenAIResponsesInput({
479
483
  break;
480
484
  }
481
485
 
486
+ if (hasComputerTool && resolvedToolName === 'computer') {
487
+ const parsedInput = await validateTypes({
488
+ value: part.input,
489
+ schema: computerInputSchema,
490
+ });
491
+ input.push({
492
+ type: 'computer_call',
493
+ call_id: part.toolCallId,
494
+ id: id!,
495
+ status: parsedInput.status,
496
+ actions: parsedInput.actions.map(action => {
497
+ switch (action.type) {
498
+ case 'click':
499
+ case 'double_click':
500
+ case 'move':
501
+ return {
502
+ ...action,
503
+ keys: action.keys,
504
+ };
505
+ case 'drag':
506
+ return {
507
+ ...action,
508
+ keys: action.keys,
509
+ };
510
+ case 'scroll':
511
+ return {
512
+ type: 'scroll' as const,
513
+ x: action.x,
514
+ y: action.y,
515
+ scroll_x: action.scrollX,
516
+ scroll_y: action.scrollY,
517
+ keys: action.keys,
518
+ };
519
+ default:
520
+ return action;
521
+ }
522
+ }),
523
+ pending_safety_checks: parsedInput.pendingSafetyChecks.map(
524
+ safetyCheck => ({
525
+ id: safetyCheck.id,
526
+ code: safetyCheck.code,
527
+ message: safetyCheck.message,
528
+ }),
529
+ ),
530
+ });
531
+
532
+ break;
533
+ }
534
+
482
535
  if (customProviderToolNames?.has(resolvedToolName)) {
483
536
  input.push({
484
537
  type: 'custom_tool_call',
@@ -869,6 +922,35 @@ export async function convertToOpenAIResponsesInput({
869
922
  continue;
870
923
  }
871
924
 
925
+ if (
926
+ hasComputerTool &&
927
+ resolvedToolName === 'computer' &&
928
+ output.type === 'json'
929
+ ) {
930
+ const parsedOutput = await validateTypes({
931
+ value: output.value,
932
+ schema: computerOutputSchema,
933
+ });
934
+
935
+ input.push({
936
+ type: 'computer_call_output',
937
+ call_id: part.toolCallId,
938
+ output: {
939
+ type: 'computer_screenshot',
940
+ image_url: parsedOutput.output.imageUrl,
941
+ file_id: parsedOutput.output.fileId,
942
+ detail: parsedOutput.output.detail,
943
+ },
944
+ acknowledged_safety_checks:
945
+ parsedOutput.acknowledgedSafetyChecks?.map(safetyCheck => ({
946
+ id: safetyCheck.id,
947
+ code: safetyCheck.code,
948
+ message: safetyCheck.message,
949
+ })),
950
+ });
951
+ continue;
952
+ }
953
+
872
954
  if (customProviderToolNames?.has(resolvedToolName)) {
873
955
  let outputValue: OpenAIResponsesCustomToolCallOutput['output'];
874
956
  switch (output.type) {
@@ -17,6 +17,73 @@ const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
17
17
  ]),
18
18
  );
19
19
 
20
+ const openaiResponsesComputerSafetyCheckSchema = z.object({
21
+ id: z.string(),
22
+ code: z.string().nullish(),
23
+ message: z.string().nullish(),
24
+ });
25
+
26
+ const openaiResponsesComputerActionSchema = z.discriminatedUnion('type', [
27
+ z.object({
28
+ type: z.literal('click'),
29
+ button: z.enum(['left', 'right', 'wheel', 'back', 'forward']),
30
+ x: z.number(),
31
+ y: z.number(),
32
+ keys: z.array(z.string()).nullish(),
33
+ }),
34
+ z.object({
35
+ type: z.literal('double_click'),
36
+ x: z.number(),
37
+ y: z.number(),
38
+ keys: z.array(z.string()).nullish(),
39
+ }),
40
+ z.object({
41
+ type: z.literal('drag'),
42
+ path: z.array(z.object({ x: z.number(), y: z.number() })),
43
+ keys: z.array(z.string()).nullish(),
44
+ }),
45
+ z.object({
46
+ type: z.literal('keypress'),
47
+ keys: z.array(z.string()),
48
+ }),
49
+ z.object({
50
+ type: z.literal('move'),
51
+ x: z.number(),
52
+ y: z.number(),
53
+ keys: z.array(z.string()).nullish(),
54
+ }),
55
+ z.object({
56
+ type: z.literal('screenshot'),
57
+ }),
58
+ z.object({
59
+ type: z.literal('scroll'),
60
+ x: z.number(),
61
+ y: z.number(),
62
+ scroll_x: z.number(),
63
+ scroll_y: z.number(),
64
+ keys: z.array(z.string()).nullish(),
65
+ }),
66
+ z.object({
67
+ type: z.literal('type'),
68
+ text: z.string(),
69
+ }),
70
+ z.object({
71
+ type: z.literal('wait'),
72
+ }),
73
+ ]);
74
+
75
+ const openaiResponsesComputerCallSchema = z.object({
76
+ type: z.literal('computer_call'),
77
+ id: z.string(),
78
+ call_id: z.string().nullish(),
79
+ status: z.enum(['in_progress', 'completed', 'incomplete']),
80
+ action: openaiResponsesComputerActionSchema.nullish(),
81
+ actions: z.array(openaiResponsesComputerActionSchema).nullish(),
82
+ pending_safety_checks: z
83
+ .array(openaiResponsesComputerSafetyCheckSchema)
84
+ .nullish(),
85
+ });
86
+
20
87
  export type OpenAIResponsesInput = Array<OpenAIResponsesInputItem>;
21
88
 
22
89
  export type OpenAIResponsesInputItem =
@@ -29,6 +96,7 @@ export type OpenAIResponsesInputItem =
29
96
  | OpenAIResponsesCustomToolCallOutput
30
97
  | OpenAIResponsesMcpApprovalResponse
31
98
  | OpenAIResponsesComputerCall
99
+ | OpenAIResponsesComputerCallOutput
32
100
  | OpenAIResponsesLocalShellCall
33
101
  | OpenAIResponsesLocalShellCallOutput
34
102
  | OpenAIResponsesShellCall
@@ -185,10 +253,28 @@ export type OpenAIResponsesMcpApprovalResponse = {
185
253
  approve: boolean;
186
254
  };
187
255
 
188
- export type OpenAIResponsesComputerCall = {
189
- type: 'computer_call';
190
- id: string;
191
- status?: string;
256
+ export type OpenAIResponsesComputerAction = InferSchema<
257
+ typeof openaiResponsesComputerActionSchema
258
+ >;
259
+
260
+ export type OpenAIResponsesComputerCall = InferSchema<
261
+ typeof openaiResponsesComputerCallSchema
262
+ >;
263
+
264
+ export type OpenAIResponsesComputerCallOutput = {
265
+ type: 'computer_call_output';
266
+ call_id: string;
267
+ output: {
268
+ type: 'computer_screenshot';
269
+ image_url?: string;
270
+ file_id?: string;
271
+ detail?: 'auto' | 'low' | 'high' | 'original';
272
+ };
273
+ acknowledged_safety_checks?: Array<{
274
+ id: string;
275
+ code?: string;
276
+ message?: string;
277
+ }>;
192
278
  };
193
279
 
194
280
  export type OpenAIResponsesLocalShellCall = {
@@ -355,6 +441,9 @@ export type OpenAIResponsesTool =
355
441
  | {
356
442
  type: 'apply_patch';
357
443
  }
444
+ | {
445
+ type: 'computer';
446
+ }
358
447
  | {
359
448
  type: 'web_search';
360
449
  external_web_access: boolean | undefined;
@@ -678,11 +767,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
678
767
  id: z.string(),
679
768
  status: z.string(),
680
769
  }),
681
- z.object({
682
- type: z.literal('computer_call'),
683
- id: z.string(),
684
- status: z.string(),
685
- }),
770
+ openaiResponsesComputerCallSchema,
686
771
  z.object({
687
772
  type: z.literal('file_search_call'),
688
773
  id: z.string(),
@@ -913,11 +998,7 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
913
998
  env: z.record(z.string(), z.string()).optional(),
914
999
  }),
915
1000
  }),
916
- z.object({
917
- type: z.literal('computer_call'),
918
- id: z.string(),
919
- status: z.literal('completed'),
920
- }),
1001
+ openaiResponsesComputerCallSchema,
921
1002
  z.object({
922
1003
  type: z.literal('mcp_call'),
923
1004
  id: z.string(),
@@ -1340,11 +1421,7 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
1340
1421
  input: z.string(),
1341
1422
  id: z.string(),
1342
1423
  }),
1343
- z.object({
1344
- type: z.literal('computer_call'),
1345
- id: z.string(),
1346
- status: z.string().optional(),
1347
- }),
1424
+ openaiResponsesComputerCallSchema,
1348
1425
  z.object({
1349
1426
  type: z.literal('reasoning'),
1350
1427
  id: z.string(),