@ai-sdk/openai 3.0.28 → 3.0.30
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 +12 -0
- package/dist/index.d.mts +72 -1
- package/dist/index.d.ts +72 -1
- package/dist/index.js +283 -13
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +283 -13
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.d.mts +30 -0
- package/dist/internal/index.d.ts +30 -0
- package/dist/internal/index.js +282 -12
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +282 -12
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-openai.mdx +129 -8
- package/package.json +3 -3
- package/src/image/openai-image-model.ts +46 -2
- package/src/responses/convert-to-openai-responses-input.ts +40 -7
- package/src/responses/openai-responses-api.ts +106 -1
- package/src/responses/openai-responses-language-model.ts +69 -1
- package/src/responses/openai-responses-prepare-tools.ts +114 -0
- package/src/tool/shell.ts +119 -1
package/docs/03-openai.mdx
CHANGED
|
@@ -708,8 +708,8 @@ const result = await generateText({
|
|
|
708
708
|
|
|
709
709
|
#### Shell Tool
|
|
710
710
|
|
|
711
|
-
The OpenAI Responses API supports the shell tool
|
|
712
|
-
The shell tool allows
|
|
711
|
+
The OpenAI Responses API supports the shell tool through the `openai.tools.shell` tool.
|
|
712
|
+
The shell tool allows running bash commands and interacting with a command line.
|
|
713
713
|
The model proposes shell commands; your integration executes them and returns the outputs.
|
|
714
714
|
|
|
715
715
|
<Note type="warning">
|
|
@@ -717,16 +717,18 @@ The model proposes shell commands; your integration executes them and returns th
|
|
|
717
717
|
add strict allow-/deny-lists before forwarding a command to the system shell.
|
|
718
718
|
</Note>
|
|
719
719
|
|
|
720
|
+
The shell tool supports three environment modes that control where commands are executed:
|
|
721
|
+
|
|
722
|
+
##### Local Execution (default)
|
|
723
|
+
|
|
724
|
+
When no `environment` is specified (or `type: 'local'` is used), commands are executed locally via your `execute` callback:
|
|
725
|
+
|
|
720
726
|
```ts
|
|
721
727
|
import { openai } from '@ai-sdk/openai';
|
|
722
728
|
import { generateText } from 'ai';
|
|
723
|
-
import { exec } from 'child_process';
|
|
724
|
-
import { promisify } from 'util';
|
|
725
|
-
|
|
726
|
-
const execAsync = promisify(exec);
|
|
727
729
|
|
|
728
730
|
const result = await generateText({
|
|
729
|
-
model: openai('gpt-5.
|
|
731
|
+
model: openai('gpt-5.2'),
|
|
730
732
|
tools: {
|
|
731
733
|
shell: openai.tools.shell({
|
|
732
734
|
execute: async ({ action }) => {
|
|
@@ -739,12 +741,131 @@ const result = await generateText({
|
|
|
739
741
|
});
|
|
740
742
|
```
|
|
741
743
|
|
|
742
|
-
|
|
744
|
+
##### Hosted Container (auto)
|
|
745
|
+
|
|
746
|
+
Set `environment.type` to `'containerAuto'` to run commands in an OpenAI-hosted container. No `execute` callback is needed — OpenAI handles execution server-side:
|
|
747
|
+
|
|
748
|
+
```ts
|
|
749
|
+
const result = await generateText({
|
|
750
|
+
model: openai('gpt-5.2'),
|
|
751
|
+
tools: {
|
|
752
|
+
shell: openai.tools.shell({
|
|
753
|
+
environment: {
|
|
754
|
+
type: 'containerAuto',
|
|
755
|
+
// optional configuration:
|
|
756
|
+
memoryLimit: '4g',
|
|
757
|
+
fileIds: ['file-abc123'],
|
|
758
|
+
networkPolicy: {
|
|
759
|
+
type: 'allowlist',
|
|
760
|
+
allowedDomains: ['example.com'],
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
}),
|
|
764
|
+
},
|
|
765
|
+
prompt: 'Install numpy and compute the eigenvalues of a 3x3 matrix.',
|
|
766
|
+
});
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
The `containerAuto` environment supports:
|
|
770
|
+
|
|
771
|
+
- **fileIds** _string[]_ - File IDs to make available in the container
|
|
772
|
+
- **memoryLimit** _'1g' | '4g' | '16g' | '64g'_ - Memory limit for the container
|
|
773
|
+
- **networkPolicy** - Network access policy:
|
|
774
|
+
- `{ type: 'disabled' }` — no network access
|
|
775
|
+
- `{ type: 'allowlist', allowedDomains: string[], domainSecrets?: Array<{ domain, name, value }> }` — allow specific domains with optional secrets
|
|
776
|
+
|
|
777
|
+
##### Existing Container Reference
|
|
778
|
+
|
|
779
|
+
Set `environment.type` to `'containerReference'` to use an existing container by ID:
|
|
780
|
+
|
|
781
|
+
```ts
|
|
782
|
+
const result = await generateText({
|
|
783
|
+
model: openai('gpt-5.2'),
|
|
784
|
+
tools: {
|
|
785
|
+
shell: openai.tools.shell({
|
|
786
|
+
environment: {
|
|
787
|
+
type: 'containerReference',
|
|
788
|
+
containerId: 'cntr_abc123',
|
|
789
|
+
},
|
|
790
|
+
}),
|
|
791
|
+
},
|
|
792
|
+
prompt: 'Check the status of running processes.',
|
|
793
|
+
});
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
##### Execute Callback
|
|
797
|
+
|
|
798
|
+
For local execution (default or `type: 'local'`), your execute function must return an output array with results for each command:
|
|
743
799
|
|
|
744
800
|
- **stdout** _string_ - Standard output from the command
|
|
745
801
|
- **stderr** _string_ - Standard error from the command
|
|
746
802
|
- **outcome** - Either `{ type: 'timeout' }` or `{ type: 'exit', exitCode: number }`
|
|
747
803
|
|
|
804
|
+
##### Skills
|
|
805
|
+
|
|
806
|
+
[Skills](https://platform.openai.com/docs/guides/tools-skills) are versioned bundles of files with a `SKILL.md` manifest that extend the shell tool's capabilities. They can be attached to both `containerAuto` and `local` environments.
|
|
807
|
+
|
|
808
|
+
**Container skills** support two formats — by reference (for skills uploaded to OpenAI) or inline (as a base64-encoded zip):
|
|
809
|
+
|
|
810
|
+
```ts
|
|
811
|
+
const result = await generateText({
|
|
812
|
+
model: openai('gpt-5.2'),
|
|
813
|
+
tools: {
|
|
814
|
+
shell: openai.tools.shell({
|
|
815
|
+
environment: {
|
|
816
|
+
type: 'containerAuto',
|
|
817
|
+
skills: [
|
|
818
|
+
// By reference:
|
|
819
|
+
{ type: 'skillReference', skillId: 'skill_abc123' },
|
|
820
|
+
// Or inline:
|
|
821
|
+
{
|
|
822
|
+
type: 'inline',
|
|
823
|
+
name: 'my-skill',
|
|
824
|
+
description: 'What this skill does',
|
|
825
|
+
source: {
|
|
826
|
+
type: 'base64',
|
|
827
|
+
mediaType: 'application/zip',
|
|
828
|
+
data: readFileSync('./my-skill.zip').toString('base64'),
|
|
829
|
+
},
|
|
830
|
+
},
|
|
831
|
+
],
|
|
832
|
+
},
|
|
833
|
+
}),
|
|
834
|
+
},
|
|
835
|
+
prompt: 'Use the skill to solve this problem.',
|
|
836
|
+
});
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
**Local skills** point to a directory on disk containing a `SKILL.md` file:
|
|
840
|
+
|
|
841
|
+
```ts
|
|
842
|
+
const result = await generateText({
|
|
843
|
+
model: openai('gpt-5.2'),
|
|
844
|
+
tools: {
|
|
845
|
+
shell: openai.tools.shell({
|
|
846
|
+
execute: async ({ action }) => {
|
|
847
|
+
// ... your local execution implementation ...
|
|
848
|
+
return { output: results };
|
|
849
|
+
},
|
|
850
|
+
environment: {
|
|
851
|
+
type: 'local',
|
|
852
|
+
skills: [
|
|
853
|
+
{
|
|
854
|
+
name: 'my-skill',
|
|
855
|
+
description: 'What this skill does',
|
|
856
|
+
path: resolve('path/to/skill-directory'),
|
|
857
|
+
},
|
|
858
|
+
],
|
|
859
|
+
},
|
|
860
|
+
}),
|
|
861
|
+
},
|
|
862
|
+
prompt: 'Use the skill to solve this problem.',
|
|
863
|
+
stopWhen: stepCountIs(5),
|
|
864
|
+
});
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
For more details on creating skills, see the [OpenAI Skills documentation](https://platform.openai.com/docs/guides/tools-skills).
|
|
868
|
+
|
|
748
869
|
#### Apply Patch Tool
|
|
749
870
|
|
|
750
871
|
The OpenAI Responses API supports the apply patch tool for GPT-5.1 models through the `openai.tools.applyPatch` tool.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.30",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"tsup": "^8",
|
|
45
45
|
"typescript": "5.8.3",
|
|
46
46
|
"zod": "3.25.76",
|
|
47
|
-
"@ai-
|
|
48
|
-
"@
|
|
47
|
+
"@vercel/ai-tsconfig": "0.0.0",
|
|
48
|
+
"@ai-sdk/test-server": "1.0.3"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
51
|
"zod": "^3.25.76 || ^4.1.8"
|
|
@@ -133,7 +133,7 @@ export class OpenAIImageModel implements ImageModelV3 {
|
|
|
133
133
|
},
|
|
134
134
|
providerMetadata: {
|
|
135
135
|
openai: {
|
|
136
|
-
images: response.data.map(item => ({
|
|
136
|
+
images: response.data.map((item, index) => ({
|
|
137
137
|
...(item.revised_prompt
|
|
138
138
|
? { revisedPrompt: item.revised_prompt }
|
|
139
139
|
: {}),
|
|
@@ -142,6 +142,11 @@ export class OpenAIImageModel implements ImageModelV3 {
|
|
|
142
142
|
quality: response.quality ?? undefined,
|
|
143
143
|
background: response.background ?? undefined,
|
|
144
144
|
outputFormat: response.output_format ?? undefined,
|
|
145
|
+
...distributeTokenDetails(
|
|
146
|
+
response.usage?.input_tokens_details,
|
|
147
|
+
index,
|
|
148
|
+
response.data.length,
|
|
149
|
+
),
|
|
145
150
|
})),
|
|
146
151
|
},
|
|
147
152
|
},
|
|
@@ -190,7 +195,7 @@ export class OpenAIImageModel implements ImageModelV3 {
|
|
|
190
195
|
},
|
|
191
196
|
providerMetadata: {
|
|
192
197
|
openai: {
|
|
193
|
-
images: response.data.map(item => ({
|
|
198
|
+
images: response.data.map((item, index) => ({
|
|
194
199
|
...(item.revised_prompt
|
|
195
200
|
? { revisedPrompt: item.revised_prompt }
|
|
196
201
|
: {}),
|
|
@@ -199,6 +204,11 @@ export class OpenAIImageModel implements ImageModelV3 {
|
|
|
199
204
|
quality: response.quality ?? undefined,
|
|
200
205
|
background: response.background ?? undefined,
|
|
201
206
|
outputFormat: response.output_format ?? undefined,
|
|
207
|
+
...distributeTokenDetails(
|
|
208
|
+
response.usage?.input_tokens_details,
|
|
209
|
+
index,
|
|
210
|
+
response.data.length,
|
|
211
|
+
),
|
|
202
212
|
})),
|
|
203
213
|
},
|
|
204
214
|
},
|
|
@@ -206,6 +216,40 @@ export class OpenAIImageModel implements ImageModelV3 {
|
|
|
206
216
|
}
|
|
207
217
|
}
|
|
208
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Distributes input token details evenly across images, with the remainder
|
|
221
|
+
* assigned to the last image so that summing across all entries gives the
|
|
222
|
+
* exact total.
|
|
223
|
+
*/
|
|
224
|
+
function distributeTokenDetails(
|
|
225
|
+
details:
|
|
226
|
+
| { image_tokens?: number | null; text_tokens?: number | null }
|
|
227
|
+
| null
|
|
228
|
+
| undefined,
|
|
229
|
+
index: number,
|
|
230
|
+
total: number,
|
|
231
|
+
): { imageTokens?: number; textTokens?: number } {
|
|
232
|
+
if (details == null) {
|
|
233
|
+
return {};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const result: { imageTokens?: number; textTokens?: number } = {};
|
|
237
|
+
|
|
238
|
+
if (details.image_tokens != null) {
|
|
239
|
+
const base = Math.floor(details.image_tokens / total);
|
|
240
|
+
const remainder = details.image_tokens - base * (total - 1);
|
|
241
|
+
result.imageTokens = index === total - 1 ? remainder : base;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (details.text_tokens != null) {
|
|
245
|
+
const base = Math.floor(details.text_tokens / total);
|
|
246
|
+
const remainder = details.text_tokens - base * (total - 1);
|
|
247
|
+
result.textTokens = index === total - 1 ? remainder : base;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return result;
|
|
251
|
+
}
|
|
252
|
+
|
|
209
253
|
type OpenAIImageEditInput = {
|
|
210
254
|
/**
|
|
211
255
|
* Allows to set transparency for the background of the generated image(s).
|
|
@@ -302,16 +302,49 @@ export async function convertToOpenAIResponsesInput({
|
|
|
302
302
|
break;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
const resolvedResultToolName = toolNameMapping.toProviderToolName(
|
|
306
|
+
part.toolName,
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
/*
|
|
310
|
+
* Shell tool results are separate output items (shell_call_output)
|
|
311
|
+
* with their own item IDs distinct from the shell_call's item ID.
|
|
312
|
+
* Since the pipeline only preserves the shell_call's item ID in
|
|
313
|
+
* callProviderMetadata, we reconstruct the full shell_call_output
|
|
314
|
+
* instead of using an item_reference with the wrong ID.
|
|
315
|
+
*/
|
|
316
|
+
if (hasShellTool && resolvedResultToolName === 'shell') {
|
|
317
|
+
if (part.output.type === 'json') {
|
|
318
|
+
const parsedOutput = await validateTypes({
|
|
319
|
+
value: part.output.value,
|
|
320
|
+
schema: shellOutputSchema,
|
|
321
|
+
});
|
|
322
|
+
input.push({
|
|
323
|
+
type: 'shell_call_output',
|
|
324
|
+
call_id: part.toolCallId,
|
|
325
|
+
output: parsedOutput.output.map(item => ({
|
|
326
|
+
stdout: item.stdout,
|
|
327
|
+
stderr: item.stderr,
|
|
328
|
+
outcome:
|
|
329
|
+
item.outcome.type === 'timeout'
|
|
330
|
+
? { type: 'timeout' as const }
|
|
331
|
+
: {
|
|
332
|
+
type: 'exit' as const,
|
|
333
|
+
exit_code: item.outcome.exitCode,
|
|
334
|
+
},
|
|
335
|
+
})),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
break;
|
|
339
|
+
}
|
|
340
|
+
|
|
305
341
|
if (store) {
|
|
306
342
|
const itemId =
|
|
307
343
|
(
|
|
308
|
-
part as
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
).providerMetadata?.[providerOptionsName]?.itemId ??
|
|
314
|
-
part.toolCallId;
|
|
344
|
+
part.providerOptions?.[providerOptionsName] as
|
|
345
|
+
| { itemId?: string }
|
|
346
|
+
| undefined
|
|
347
|
+
)?.itemId ?? part.toolCallId;
|
|
315
348
|
input.push({ type: 'item_reference', id: itemId });
|
|
316
349
|
} else {
|
|
317
350
|
warnings.push({
|
|
@@ -142,8 +142,10 @@ export type OpenAIResponsesShellCall = {
|
|
|
142
142
|
|
|
143
143
|
export type OpenAIResponsesShellCallOutput = {
|
|
144
144
|
type: 'shell_call_output';
|
|
145
|
+
id?: string;
|
|
145
146
|
call_id: string;
|
|
146
|
-
|
|
147
|
+
status?: 'in_progress' | 'completed' | 'incomplete';
|
|
148
|
+
max_output_length?: number | null;
|
|
147
149
|
output: Array<{
|
|
148
150
|
stdout: string;
|
|
149
151
|
stderr: string;
|
|
@@ -328,6 +330,52 @@ export type OpenAIResponsesTool =
|
|
|
328
330
|
}
|
|
329
331
|
| {
|
|
330
332
|
type: 'shell';
|
|
333
|
+
environment?:
|
|
334
|
+
| {
|
|
335
|
+
type: 'container_auto';
|
|
336
|
+
file_ids?: string[];
|
|
337
|
+
memory_limit?: '1g' | '4g' | '16g' | '64g';
|
|
338
|
+
network_policy?:
|
|
339
|
+
| { type: 'disabled' }
|
|
340
|
+
| {
|
|
341
|
+
type: 'allowlist';
|
|
342
|
+
allowed_domains: string[];
|
|
343
|
+
domain_secrets?: Array<{
|
|
344
|
+
domain: string;
|
|
345
|
+
name: string;
|
|
346
|
+
value: string;
|
|
347
|
+
}>;
|
|
348
|
+
};
|
|
349
|
+
skills?: Array<
|
|
350
|
+
| {
|
|
351
|
+
type: 'skill_reference';
|
|
352
|
+
skill_id: string;
|
|
353
|
+
version?: string;
|
|
354
|
+
}
|
|
355
|
+
| {
|
|
356
|
+
type: 'inline';
|
|
357
|
+
name: string;
|
|
358
|
+
description: string;
|
|
359
|
+
source: {
|
|
360
|
+
type: 'base64';
|
|
361
|
+
media_type: 'application/zip';
|
|
362
|
+
data: string;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
>;
|
|
366
|
+
}
|
|
367
|
+
| {
|
|
368
|
+
type: 'container_reference';
|
|
369
|
+
container_id: string;
|
|
370
|
+
}
|
|
371
|
+
| {
|
|
372
|
+
type: 'local';
|
|
373
|
+
skills?: Array<{
|
|
374
|
+
name: string;
|
|
375
|
+
description: string;
|
|
376
|
+
path: string;
|
|
377
|
+
}>;
|
|
378
|
+
};
|
|
331
379
|
};
|
|
332
380
|
|
|
333
381
|
export type OpenAIResponsesReasoning = {
|
|
@@ -486,6 +534,25 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
486
534
|
commands: z.array(z.string()),
|
|
487
535
|
}),
|
|
488
536
|
}),
|
|
537
|
+
z.object({
|
|
538
|
+
type: z.literal('shell_call_output'),
|
|
539
|
+
id: z.string(),
|
|
540
|
+
call_id: z.string(),
|
|
541
|
+
status: z.enum(['in_progress', 'completed', 'incomplete']),
|
|
542
|
+
output: z.array(
|
|
543
|
+
z.object({
|
|
544
|
+
stdout: z.string(),
|
|
545
|
+
stderr: z.string(),
|
|
546
|
+
outcome: z.discriminatedUnion('type', [
|
|
547
|
+
z.object({ type: z.literal('timeout') }),
|
|
548
|
+
z.object({
|
|
549
|
+
type: z.literal('exit'),
|
|
550
|
+
exit_code: z.number(),
|
|
551
|
+
}),
|
|
552
|
+
]),
|
|
553
|
+
}),
|
|
554
|
+
),
|
|
555
|
+
}),
|
|
489
556
|
]),
|
|
490
557
|
}),
|
|
491
558
|
z.object({
|
|
@@ -679,6 +746,25 @@ export const openaiResponsesChunkSchema = lazySchema(() =>
|
|
|
679
746
|
commands: z.array(z.string()),
|
|
680
747
|
}),
|
|
681
748
|
}),
|
|
749
|
+
z.object({
|
|
750
|
+
type: z.literal('shell_call_output'),
|
|
751
|
+
id: z.string(),
|
|
752
|
+
call_id: z.string(),
|
|
753
|
+
status: z.enum(['in_progress', 'completed', 'incomplete']),
|
|
754
|
+
output: z.array(
|
|
755
|
+
z.object({
|
|
756
|
+
stdout: z.string(),
|
|
757
|
+
stderr: z.string(),
|
|
758
|
+
outcome: z.discriminatedUnion('type', [
|
|
759
|
+
z.object({ type: z.literal('timeout') }),
|
|
760
|
+
z.object({
|
|
761
|
+
type: z.literal('exit'),
|
|
762
|
+
exit_code: z.number(),
|
|
763
|
+
}),
|
|
764
|
+
]),
|
|
765
|
+
}),
|
|
766
|
+
),
|
|
767
|
+
}),
|
|
682
768
|
]),
|
|
683
769
|
}),
|
|
684
770
|
z.object({
|
|
@@ -1064,6 +1150,25 @@ export const openaiResponsesResponseSchema = lazySchema(() =>
|
|
|
1064
1150
|
commands: z.array(z.string()),
|
|
1065
1151
|
}),
|
|
1066
1152
|
}),
|
|
1153
|
+
z.object({
|
|
1154
|
+
type: z.literal('shell_call_output'),
|
|
1155
|
+
id: z.string(),
|
|
1156
|
+
call_id: z.string(),
|
|
1157
|
+
status: z.enum(['in_progress', 'completed', 'incomplete']),
|
|
1158
|
+
output: z.array(
|
|
1159
|
+
z.object({
|
|
1160
|
+
stdout: z.string(),
|
|
1161
|
+
stderr: z.string(),
|
|
1162
|
+
outcome: z.discriminatedUnion('type', [
|
|
1163
|
+
z.object({ type: z.literal('timeout') }),
|
|
1164
|
+
z.object({
|
|
1165
|
+
type: z.literal('exit'),
|
|
1166
|
+
exit_code: z.number(),
|
|
1167
|
+
}),
|
|
1168
|
+
]),
|
|
1169
|
+
}),
|
|
1170
|
+
),
|
|
1171
|
+
}),
|
|
1067
1172
|
]),
|
|
1068
1173
|
)
|
|
1069
1174
|
.optional(),
|
|
@@ -37,7 +37,7 @@ import { fileSearchOutputSchema } from '../tool/file-search';
|
|
|
37
37
|
import { imageGenerationOutputSchema } from '../tool/image-generation';
|
|
38
38
|
import { localShellInputSchema } from '../tool/local-shell';
|
|
39
39
|
import { mcpOutputSchema } from '../tool/mcp';
|
|
40
|
-
import { shellInputSchema } from '../tool/shell';
|
|
40
|
+
import { shellInputSchema, shellOutputSchema } from '../tool/shell';
|
|
41
41
|
import { webSearchOutputSchema } from '../tool/web-search';
|
|
42
42
|
import {
|
|
43
43
|
convertOpenAIResponsesUsage,
|
|
@@ -417,6 +417,16 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
417
417
|
toolChoice,
|
|
418
418
|
});
|
|
419
419
|
|
|
420
|
+
const shellToolEnvType = (
|
|
421
|
+
tools?.find(
|
|
422
|
+
tool => tool.type === 'provider' && tool.id === 'openai.shell',
|
|
423
|
+
) as { args?: { environment?: { type?: string } } } | undefined
|
|
424
|
+
)?.args?.environment?.type;
|
|
425
|
+
|
|
426
|
+
const isShellProviderExecuted =
|
|
427
|
+
shellToolEnvType === 'containerAuto' ||
|
|
428
|
+
shellToolEnvType === 'containerReference';
|
|
429
|
+
|
|
420
430
|
return {
|
|
421
431
|
webSearchToolName,
|
|
422
432
|
args: {
|
|
@@ -428,6 +438,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
428
438
|
store,
|
|
429
439
|
toolNameMapping,
|
|
430
440
|
providerOptionsName,
|
|
441
|
+
isShellProviderExecuted,
|
|
431
442
|
};
|
|
432
443
|
}
|
|
433
444
|
|
|
@@ -440,6 +451,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
440
451
|
webSearchToolName,
|
|
441
452
|
toolNameMapping,
|
|
442
453
|
providerOptionsName,
|
|
454
|
+
isShellProviderExecuted,
|
|
443
455
|
} = await this.getArgs(options);
|
|
444
456
|
const url = this.config.url({
|
|
445
457
|
path: '/responses',
|
|
@@ -556,6 +568,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
556
568
|
commands: part.action.commands,
|
|
557
569
|
},
|
|
558
570
|
} satisfies InferSchema<typeof shellInputSchema>),
|
|
571
|
+
...(isShellProviderExecuted && { providerExecuted: true }),
|
|
559
572
|
providerMetadata: {
|
|
560
573
|
[providerOptionsName]: {
|
|
561
574
|
itemId: part.id,
|
|
@@ -566,6 +579,28 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
566
579
|
break;
|
|
567
580
|
}
|
|
568
581
|
|
|
582
|
+
case 'shell_call_output': {
|
|
583
|
+
content.push({
|
|
584
|
+
type: 'tool-result',
|
|
585
|
+
toolCallId: part.call_id,
|
|
586
|
+
toolName: toolNameMapping.toCustomToolName('shell'),
|
|
587
|
+
result: {
|
|
588
|
+
output: part.output.map(item => ({
|
|
589
|
+
stdout: item.stdout,
|
|
590
|
+
stderr: item.stderr,
|
|
591
|
+
outcome:
|
|
592
|
+
item.outcome.type === 'exit'
|
|
593
|
+
? {
|
|
594
|
+
type: 'exit' as const,
|
|
595
|
+
exitCode: item.outcome.exit_code,
|
|
596
|
+
}
|
|
597
|
+
: { type: 'timeout' as const },
|
|
598
|
+
})),
|
|
599
|
+
} satisfies InferSchema<typeof shellOutputSchema>,
|
|
600
|
+
});
|
|
601
|
+
break;
|
|
602
|
+
}
|
|
603
|
+
|
|
569
604
|
case 'message': {
|
|
570
605
|
for (const contentPart of part.content) {
|
|
571
606
|
if (
|
|
@@ -910,6 +945,7 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
910
945
|
toolNameMapping,
|
|
911
946
|
store,
|
|
912
947
|
providerOptionsName,
|
|
948
|
+
isShellProviderExecuted,
|
|
913
949
|
} = await this.getArgs(options);
|
|
914
950
|
|
|
915
951
|
const { responseHeaders, value: response } = await postJsonToApi({
|
|
@@ -1160,6 +1196,8 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
1160
1196
|
toolName: toolNameMapping.toCustomToolName('shell'),
|
|
1161
1197
|
toolCallId: value.item.call_id,
|
|
1162
1198
|
};
|
|
1199
|
+
} else if (value.item.type === 'shell_call_output') {
|
|
1200
|
+
// shell_call_output is handled in output_item.done
|
|
1163
1201
|
} else if (value.item.type === 'message') {
|
|
1164
1202
|
ongoingAnnotations.splice(0, ongoingAnnotations.length);
|
|
1165
1203
|
controller.enqueue({
|
|
@@ -1469,10 +1507,40 @@ export class OpenAIResponsesLanguageModel implements LanguageModelV3 {
|
|
|
1469
1507
|
commands: value.item.action.commands,
|
|
1470
1508
|
},
|
|
1471
1509
|
} satisfies InferSchema<typeof shellInputSchema>),
|
|
1510
|
+
...(isShellProviderExecuted && {
|
|
1511
|
+
providerExecuted: true,
|
|
1512
|
+
}),
|
|
1472
1513
|
providerMetadata: {
|
|
1473
1514
|
[providerOptionsName]: { itemId: value.item.id },
|
|
1474
1515
|
},
|
|
1475
1516
|
});
|
|
1517
|
+
} else if (value.item.type === 'shell_call_output') {
|
|
1518
|
+
controller.enqueue({
|
|
1519
|
+
type: 'tool-result',
|
|
1520
|
+
toolCallId: value.item.call_id,
|
|
1521
|
+
toolName: toolNameMapping.toCustomToolName('shell'),
|
|
1522
|
+
result: {
|
|
1523
|
+
output: value.item.output.map(
|
|
1524
|
+
(item: {
|
|
1525
|
+
stdout: string;
|
|
1526
|
+
stderr: string;
|
|
1527
|
+
outcome:
|
|
1528
|
+
| { type: 'exit'; exit_code: number }
|
|
1529
|
+
| { type: 'timeout' };
|
|
1530
|
+
}) => ({
|
|
1531
|
+
stdout: item.stdout,
|
|
1532
|
+
stderr: item.stderr,
|
|
1533
|
+
outcome:
|
|
1534
|
+
item.outcome.type === 'exit'
|
|
1535
|
+
? {
|
|
1536
|
+
type: 'exit' as const,
|
|
1537
|
+
exitCode: item.outcome.exit_code,
|
|
1538
|
+
}
|
|
1539
|
+
: { type: 'timeout' as const },
|
|
1540
|
+
}),
|
|
1541
|
+
),
|
|
1542
|
+
} satisfies InferSchema<typeof shellOutputSchema>,
|
|
1543
|
+
});
|
|
1476
1544
|
} else if (value.item.type === 'reasoning') {
|
|
1477
1545
|
const activeReasoningPart = activeReasoning[value.item.id];
|
|
1478
1546
|
|