Skip to content
Open
6 changes: 6 additions & 0 deletions .changeset/tiny-horses-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@ai-sdk/openai': patch
'@ai-sdk/azure': patch
---

add shell and local_shell in azure responses built in tools
67 changes: 67 additions & 0 deletions content/providers/01-ai-sdk-providers/04-azure.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,73 @@ The code interpreter tool can be configured with:
Source Document Parts](#typed-providermetadata-in-source-document-parts).
</Note>

#### Local Shell Tool

The Azure OpenAI responses API support the local shell tool for Codex models through the `azure.tools.localShell` tool.
Local shell is a tool that allows agents to run shell commands locally on a machine you or the user provides.

```ts
import { azure } from '@ai-sdk/azure';
import { generateText, stepCountIs } from 'ai';

const result = await generateText({
model: azure.responses('gpt-5-codex'),
tools: {
local_shell: azure.tools.localShell({
execute: async ({ action }) => {
// ... your implementation, e.g. sandbox access ...
return { output: stdout };
},
}),
},
prompt: 'List the files in my home directory.',
stopWhen: stepCountIs(2),
});
```

#### Shell Tool

The Azure OpenAI Responses API supports the shell tool through the `azure.tools.shell` tool.
The shell tool allows running bash commands and interacting with a command line.
The model proposes shell commands; your integration executes them and returns the outputs.

<Note type="warning">
Running arbitrary shell commands can be dangerous. Always sandbox execution or
add strict allow-/deny-lists before forwarding a command to the system shell.
</Note>

<Note type="warning">
The Azure OpenAI Responses API provides the shell tool, but it currently
does not support OpenAI-hosted container environments (`containerAuto`,
`containerReference`) or Skills.
This means:
- Hosted container execution is not available.
- `skills` configuration is not supported.
- The `environment` parameter is currently not supported.
When using `@ai-sdk/azure`, the shell tool must be executed via a local
`execute` callback, and sandboxing must be handled entirely by your
application.

</Note>

```ts
import { azure } from '@ai-sdk/azure';
import { generateText } from 'ai';

const result = await generateText({
model: azure('gpt-5.2'),
tools: {
shell: azure.tools.shell({
execute: async ({ action }) => {
// ... your implementation, e.g. sandbox access ...
return { output: results };
},
}),
},
prompt: 'List the files in the current directory and show disk usage.',
});
```

#### PDF support

The Azure OpenAI provider supports reading PDF files.
Expand Down
31 changes: 31 additions & 0 deletions examples/ai-functions/src/generate-text/azure/local-shell-tool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { azure } from '@ai-sdk/azure';
import { generateText, stepCountIs } from 'ai';
import { run } from '../../lib/run';

run(async () => {
const result = await generateText({
model: azure.responses('gpt-5-codex'),
tools: {
local_shell: azure.tools.localShell({
execute: async ({ action }) => {
console.log('ACTION');
console.dir(action, { depth: Infinity });

const stdout = `
❯ ls
README.md build data node_modules package.json src tsconfig.json
`;

return { output: stdout };
},
}),
},
prompt: 'List the files in my home directory.',
stopWhen: stepCountIs(20),
onStepFinish: step => {
console.dir(step.content, { depth: Infinity });
},
});
console.log('result:');
console.log(result.text);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { azure } from '@ai-sdk/azure';
import { generateText } from 'ai';
import { readFileSync } from 'fs';
import { run } from '../../lib/run';

/**
* *** NOTICE ***
*
* This example is provided for reference only.
* The `skills` and `container` configuration is not currently supported on the Microsoft Azure platform.
*
* Once Azure adds support for this feature, the example will function as expected
* and will be updated accordingly.
*/

const skillZip = readFileSync('data/island-rescue-skill.zip').toString(
'base64',
);

run(async () => {
const result = await generateText({
model: azure.responses('gpt-5.2'),
tools: {
shell: azure.tools.shell({
environment: {
type: 'containerAuto',
skills: [
{
type: 'inline',
name: 'island-rescue',
description: 'How to be rescued from a lonely island',
source: {
type: 'base64',
mediaType: 'application/zip',
data: skillZip,
},
},
],
},
}),
},
prompt:
'You are trapped and lost on a lonely island in 1895. Find a way to get rescued!',
});

console.log('Result:', result.text);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { azure } from '@ai-sdk/azure';
import { generateText } from 'ai';
import { run } from '../../lib/run';

/**
* *** NOTICE ***
*
* This example is provided for reference only.
* The `container` configuration is not currently supported on the Microsoft Azure platform.
*
* Once Azure adds support for this feature, the example will function as expected
* and will be updated accordingly.
*/

run(async () => {
const result = await generateText({
model: azure.responses('gpt-5.2'),
tools: {
shell: azure.tools.shell({
environment: {
type: 'containerAuto',
},
}),
},
prompt:
'Print "Hello from container!" and show the system info using uname -a',
});

console.log('Result:', result.text);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { azure } from '@ai-sdk/azure';
import { generateText, stepCountIs } from 'ai';
import { resolve } from 'path';
import { executeShellCommand } from '../../lib/shell-executor';
import { run } from '../../lib/run';

/**
* *** NOTICE ***
*
* This example is provided for reference only.
* The `skills` configuration is not currently supported on the Microsoft Azure platform.
*
* Once Azure adds support for this feature, the example will function as expected
* and will be updated accordingly.
*/

run(async () => {
const result = await generateText({
model: azure.responses('gpt-5.2'),
tools: {
shell: azure.tools.shell({
execute: async ({ action }) => {
const outputs = await Promise.all(
action.commands.map(command =>
executeShellCommand(command, action.timeoutMs),
),
);

return { output: outputs };
},
environment: {
type: 'local',
skills: [
{
name: 'island-rescue',
description: 'How to be rescued from a lonely island',
path: resolve('data/island-rescue'),
},
],
},
}),
},
prompt:
'You are trapped and lost on a lonely island in 1895. Find a way to get rescued!',
stopWhen: stepCountIs(5),
});

console.log('Result:', result.text);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { azure } from '@ai-sdk/azure';
import { generateText, stepCountIs } from 'ai';
import { executeShellCommand } from '../../lib/shell-executor';
import { run } from '../../lib/run';

run(async () => {
const result = await generateText({
model: azure.responses('gpt-5.1'),
tools: {
shell: azure.tools.shell({
execute: async ({ action }) => {
const outputs = await Promise.all(
action.commands.map(command =>
executeShellCommand(command, action.timeoutMs),
),
);

return { output: outputs };
},
}),
},
prompt:
'Create a file in my current directory called dec1.txt with the text: THIS WORKS!',
stopWhen: stepCountIs(5),
});

console.log('Result:', result.text);
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { run } from '../../lib/run';

/**
* By registering a Skill in advance through the OpenAI Platform Skills management page (Storage > Skills),
* you can obtain a `skill_id` that can be referenced when using it.
* By specifying the registered `skill_id`,
* https://platform.openai.com/storage/skills
* the Skill becomes available within the container execution environment of the Responses API.
*
* In this example, `skills/use-ai-sdk/SKILL.md` from the AI SDK repository is registered as a Skill.
* https://github.com/vercel/ai/blob/main/skills/use-ai-sdk/SKILL.md
*/

const skillId = 'skill_69abcc2c483c8190b28cf1623f8ab0220a0a16fe847a2a76';

run(async () => {
const result = await generateText({
model: openai.responses('gpt-5.2'),
tools: {
shell: openai.tools.shell({
environment: {
type: 'containerAuto',
skills: [
{
type: 'skillReference',
skillId,
},
],
},
}),
},
prompt: 'Summarize the information obtained from the skill `ai-sdk`',
});

console.log('Result:', result.text);
});
54 changes: 54 additions & 0 deletions examples/ai-functions/src/stream-text/azure/local-shell-tool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { azure } from '@ai-sdk/azure';
import { stepCountIs, streamText } from 'ai';
import { run } from '../../lib/run';

run(async () => {
const result = streamText({
model: azure.responses('gpt-5-codex'),

tools: {
local_shell: azure.tools.localShell({
execute: async ({ action }) => {
console.log('ACTION');
console.dir(action, { depth: Infinity });

const stdout = `
❯ ls
README.md build data node_modules package.json src tsconfig.json
`;

return { output: stdout };
},
}),
},
prompt: 'List the files in my home directory.',
stopWhen: stepCountIs(20),
});

for await (const chunk of result.fullStream) {
switch (chunk.type) {
case 'text-delta': {
process.stdout.write(chunk.text);
break;
}

case 'tool-call': {
console.log(
`\x1b[32m\x1b[1mTool call:\x1b[22m ${JSON.stringify(chunk, null, 2)}\x1b[0m`,
);
break;
}

case 'tool-result': {
console.log(
`\x1b[32m\x1b[1mTool result:\x1b[22m ${JSON.stringify(chunk, null, 2)}\x1b[0m`,
);
break;
}

case 'error':
console.error('Error:', chunk.error);
break;
}
}
});
Loading
Loading