@ai-sdk/openai 4.0.14 → 4.0.15
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 +6 -0
- package/dist/index.d.ts +285 -24
- package/dist/index.js +1496 -1152
- package/dist/index.js.map +1 -1
- package/dist/internal/index.d.ts +184 -8
- package/dist/internal/index.js +1438 -1104
- package/dist/internal/index.js.map +1 -1
- package/docs/03-openai.mdx +89 -0
- package/package.json +1 -1
- package/src/index.ts +4 -0
- package/src/openai-tools.ts +11 -0
- package/src/responses/convert-to-openai-responses-input.ts +83 -1
- package/src/responses/openai-responses-api.ts +96 -19
- package/src/responses/openai-responses-language-model.ts +162 -31
- package/src/responses/openai-responses-prepare-tools.ts +9 -1
- package/src/tool/computer.ts +147 -0
package/docs/03-openai.mdx
CHANGED
|
@@ -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.
|
package/package.json
CHANGED
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,
|
package/src/openai-tools.ts
CHANGED
|
@@ -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
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(),
|
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
codeInterpreterInputSchema,
|
|
39
39
|
codeInterpreterOutputSchema,
|
|
40
40
|
} from '../tool/code-interpreter';
|
|
41
|
+
import type { computerInputSchema } from '../tool/computer';
|
|
41
42
|
import type { fileSearchOutputSchema } from '../tool/file-search';
|
|
42
43
|
import type { imageGenerationOutputSchema } from '../tool/image-generation';
|
|
43
44
|
import type { localShellInputSchema } from '../tool/local-shell';
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
type OpenAIResponsesWebSearchAction,
|
|
65
66
|
type OpenAIResponsesApplyPatchOperationDiffDeltaChunk,
|
|
66
67
|
type OpenAIResponsesApplyPatchOperationDiffDoneChunk,
|
|
68
|
+
type OpenAIResponsesComputerAction,
|
|
67
69
|
} from './openai-responses-api';
|
|
68
70
|
import {
|
|
69
71
|
openaiLanguageModelResponsesOptionsSchema,
|
|
@@ -104,6 +106,87 @@ function extractApprovalRequestIdToToolCallIdMapping(
|
|
|
104
106
|
return mapping;
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
function mapComputerAction(
|
|
110
|
+
action: OpenAIResponsesComputerAction,
|
|
111
|
+
): InferSchema<typeof computerInputSchema>['actions'][number] {
|
|
112
|
+
switch (action.type) {
|
|
113
|
+
case 'click':
|
|
114
|
+
return {
|
|
115
|
+
type: 'click',
|
|
116
|
+
button: action.button,
|
|
117
|
+
x: action.x,
|
|
118
|
+
y: action.y,
|
|
119
|
+
...(action.keys != null && { keys: action.keys }),
|
|
120
|
+
};
|
|
121
|
+
case 'double_click':
|
|
122
|
+
return {
|
|
123
|
+
type: 'double_click',
|
|
124
|
+
x: action.x,
|
|
125
|
+
y: action.y,
|
|
126
|
+
...(action.keys != null && { keys: action.keys }),
|
|
127
|
+
};
|
|
128
|
+
case 'drag':
|
|
129
|
+
return {
|
|
130
|
+
type: 'drag',
|
|
131
|
+
path: action.path,
|
|
132
|
+
...(action.keys != null && { keys: action.keys }),
|
|
133
|
+
};
|
|
134
|
+
case 'keypress':
|
|
135
|
+
return action;
|
|
136
|
+
case 'move':
|
|
137
|
+
return {
|
|
138
|
+
type: 'move',
|
|
139
|
+
x: action.x,
|
|
140
|
+
y: action.y,
|
|
141
|
+
...(action.keys != null && { keys: action.keys }),
|
|
142
|
+
};
|
|
143
|
+
case 'screenshot':
|
|
144
|
+
return action;
|
|
145
|
+
case 'scroll':
|
|
146
|
+
return {
|
|
147
|
+
type: 'scroll',
|
|
148
|
+
x: action.x,
|
|
149
|
+
y: action.y,
|
|
150
|
+
scrollX: action.scroll_x,
|
|
151
|
+
scrollY: action.scroll_y,
|
|
152
|
+
...(action.keys != null && { keys: action.keys }),
|
|
153
|
+
};
|
|
154
|
+
case 'type':
|
|
155
|
+
return action;
|
|
156
|
+
case 'wait':
|
|
157
|
+
return action;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function mapComputerCallInput({
|
|
162
|
+
action,
|
|
163
|
+
actions,
|
|
164
|
+
pending_safety_checks,
|
|
165
|
+
status,
|
|
166
|
+
}: {
|
|
167
|
+
action?: OpenAIResponsesComputerAction | null;
|
|
168
|
+
actions?: OpenAIResponsesComputerAction[] | null;
|
|
169
|
+
pending_safety_checks?: Array<{
|
|
170
|
+
id: string;
|
|
171
|
+
code?: string | null;
|
|
172
|
+
message?: string | null;
|
|
173
|
+
}> | null;
|
|
174
|
+
status: 'in_progress' | 'completed' | 'incomplete';
|
|
175
|
+
}): InferSchema<typeof computerInputSchema> {
|
|
176
|
+
return {
|
|
177
|
+
actions: (actions ?? (action != null ? [action] : [])).map(
|
|
178
|
+
mapComputerAction,
|
|
179
|
+
),
|
|
180
|
+
pendingSafetyChecks:
|
|
181
|
+
pending_safety_checks?.map(safetyCheck => ({
|
|
182
|
+
id: safetyCheck.id,
|
|
183
|
+
...(safetyCheck.code != null && { code: safetyCheck.code }),
|
|
184
|
+
...(safetyCheck.message != null && { message: safetyCheck.message }),
|
|
185
|
+
})) ?? [],
|
|
186
|
+
status,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
107
190
|
export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
108
191
|
readonly specificationVersion = 'v4';
|
|
109
192
|
|
|
@@ -220,6 +303,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
220
303
|
tools,
|
|
221
304
|
providerToolNames: {
|
|
222
305
|
'openai.code_interpreter': 'code_interpreter',
|
|
306
|
+
'openai.computer': 'computer',
|
|
223
307
|
'openai.file_search': 'file_search',
|
|
224
308
|
'openai.image_generation': 'image_generation',
|
|
225
309
|
'openai.local_shell': 'local_shell',
|
|
@@ -264,6 +348,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
264
348
|
hasLocalShellTool: hasOpenAITool('openai.local_shell'),
|
|
265
349
|
hasShellTool: hasOpenAITool('openai.shell'),
|
|
266
350
|
hasApplyPatchTool: hasOpenAITool('openai.apply_patch'),
|
|
351
|
+
hasComputerTool: hasOpenAITool('openai.computer'),
|
|
267
352
|
customProviderToolNames:
|
|
268
353
|
customProviderToolNames.size > 0
|
|
269
354
|
? customProviderToolNames
|
|
@@ -957,21 +1042,39 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
957
1042
|
}
|
|
958
1043
|
|
|
959
1044
|
case 'computer_call': {
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1045
|
+
if (part.call_id == null) {
|
|
1046
|
+
content.push({
|
|
1047
|
+
type: 'tool-call',
|
|
1048
|
+
toolCallId: part.id,
|
|
1049
|
+
toolName: toolNameMapping.toCustomToolName('computer_use'),
|
|
1050
|
+
input: '',
|
|
1051
|
+
providerExecuted: true,
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
content.push({
|
|
1055
|
+
type: 'tool-result',
|
|
1056
|
+
toolCallId: part.id,
|
|
1057
|
+
toolName: toolNameMapping.toCustomToolName('computer_use'),
|
|
1058
|
+
result: {
|
|
1059
|
+
type: 'computer_use_tool_result',
|
|
1060
|
+
status: part.status,
|
|
1061
|
+
},
|
|
1062
|
+
});
|
|
1063
|
+
break;
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
hasFunctionCall = true;
|
|
1067
|
+
const toolName = toolNameMapping.toCustomToolName('computer');
|
|
967
1068
|
|
|
968
1069
|
content.push({
|
|
969
|
-
type: 'tool-
|
|
970
|
-
toolCallId: part.
|
|
971
|
-
toolName
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1070
|
+
type: 'tool-call',
|
|
1071
|
+
toolCallId: part.call_id,
|
|
1072
|
+
toolName,
|
|
1073
|
+
input: JSON.stringify(mapComputerCallInput(part)),
|
|
1074
|
+
providerMetadata: {
|
|
1075
|
+
[providerOptionsName]: {
|
|
1076
|
+
itemId: part.id,
|
|
1077
|
+
},
|
|
975
1078
|
},
|
|
976
1079
|
});
|
|
977
1080
|
break;
|
|
@@ -1303,16 +1406,16 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1303
1406
|
providerExecuted: true,
|
|
1304
1407
|
});
|
|
1305
1408
|
} else if (value.item.type === 'computer_call') {
|
|
1409
|
+
const toolCallId = value.item.call_id ?? value.item.id;
|
|
1306
1410
|
ongoingToolCalls[value.output_index] = {
|
|
1307
|
-
toolName: toolNameMapping.toCustomToolName('
|
|
1308
|
-
toolCallId
|
|
1411
|
+
toolName: toolNameMapping.toCustomToolName('computer'),
|
|
1412
|
+
toolCallId,
|
|
1309
1413
|
};
|
|
1310
1414
|
|
|
1311
1415
|
controller.enqueue({
|
|
1312
1416
|
type: 'tool-input-start',
|
|
1313
|
-
id:
|
|
1314
|
-
toolName: toolNameMapping.toCustomToolName('
|
|
1315
|
-
providerExecuted: true,
|
|
1417
|
+
id: toolCallId,
|
|
1418
|
+
toolName: toolNameMapping.toCustomToolName('computer'),
|
|
1316
1419
|
});
|
|
1317
1420
|
} else if (value.item.type === 'code_interpreter_call') {
|
|
1318
1421
|
ongoingToolCalls[value.output_index] = {
|
|
@@ -1553,26 +1656,54 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV4 {
|
|
|
1553
1656
|
} else if (value.item.type === 'computer_call') {
|
|
1554
1657
|
ongoingToolCalls[value.output_index] = undefined;
|
|
1555
1658
|
|
|
1659
|
+
if (value.item.call_id == null) {
|
|
1660
|
+
controller.enqueue({
|
|
1661
|
+
type: 'tool-input-end',
|
|
1662
|
+
id: value.item.id,
|
|
1663
|
+
});
|
|
1664
|
+
controller.enqueue({
|
|
1665
|
+
type: 'tool-call',
|
|
1666
|
+
toolCallId: value.item.id,
|
|
1667
|
+
toolName: toolNameMapping.toCustomToolName('computer_use'),
|
|
1668
|
+
input: '',
|
|
1669
|
+
providerExecuted: true,
|
|
1670
|
+
});
|
|
1671
|
+
controller.enqueue({
|
|
1672
|
+
type: 'tool-result',
|
|
1673
|
+
toolCallId: value.item.id,
|
|
1674
|
+
toolName: toolNameMapping.toCustomToolName('computer_use'),
|
|
1675
|
+
result: {
|
|
1676
|
+
type: 'computer_use_tool_result',
|
|
1677
|
+
status: value.item.status,
|
|
1678
|
+
},
|
|
1679
|
+
});
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
hasFunctionCall = true;
|
|
1684
|
+
const toolName = toolNameMapping.toCustomToolName('computer');
|
|
1685
|
+
const input = JSON.stringify(mapComputerCallInput(value.item));
|
|
1686
|
+
|
|
1556
1687
|
controller.enqueue({
|
|
1557
|
-
type: 'tool-input-
|
|
1558
|
-
id: value.item.
|
|
1688
|
+
type: 'tool-input-delta',
|
|
1689
|
+
id: value.item.call_id,
|
|
1690
|
+
delta: input,
|
|
1559
1691
|
});
|
|
1560
1692
|
|
|
1561
1693
|
controller.enqueue({
|
|
1562
|
-
type: 'tool-
|
|
1563
|
-
|
|
1564
|
-
toolName: toolNameMapping.toCustomToolName('computer_use'),
|
|
1565
|
-
input: '',
|
|
1566
|
-
providerExecuted: true,
|
|
1694
|
+
type: 'tool-input-end',
|
|
1695
|
+
id: value.item.call_id,
|
|
1567
1696
|
});
|
|
1568
1697
|
|
|
1569
1698
|
controller.enqueue({
|
|
1570
|
-
type: 'tool-
|
|
1571
|
-
toolCallId: value.item.
|
|
1572
|
-
toolName
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1699
|
+
type: 'tool-call',
|
|
1700
|
+
toolCallId: value.item.call_id,
|
|
1701
|
+
toolName,
|
|
1702
|
+
input,
|
|
1703
|
+
providerMetadata: {
|
|
1704
|
+
[providerOptionsName]: {
|
|
1705
|
+
itemId: value.item.id,
|
|
1706
|
+
},
|
|
1576
1707
|
},
|
|
1577
1708
|
});
|
|
1578
1709
|
} else if (value.item.type === 'file_search_call') {
|