@ai-sdk/openai 3.0.96 → 3.0.98
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 +27 -0
- package/dist/index.js +138 -11
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +138 -11
- package/dist/index.mjs.map +1 -1
- package/dist/internal/index.js +137 -10
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/index.mjs +137 -10
- package/dist/internal/index.mjs.map +1 -1
- package/docs/03-openai.mdx +57 -0
- package/package.json +2 -2
- package/src/responses/openai-responses-api.ts +15 -0
- package/src/responses/openai-responses-prepare-tools.ts +186 -5
package/docs/03-openai.mdx
CHANGED
|
@@ -1277,6 +1277,63 @@ The custom tool can be configured with:
|
|
|
1277
1277
|
- **definition** _string_ - (grammar only) The grammar definition string (a regex pattern or Lark grammar).
|
|
1278
1278
|
- **execute** _function_ (optional) - An async function that receives the raw string input and returns a string result. Enables multi-turn tool calling.
|
|
1279
1279
|
|
|
1280
|
+
#### Restricting Callable Tools
|
|
1281
|
+
|
|
1282
|
+
The `allowedTools` provider option restricts which of the tools you declared the model is allowed to
|
|
1283
|
+
call, while still sending the full `tools` list to OpenAI. Because the tools list stays byte-identical
|
|
1284
|
+
across requests, the prompt cache is preserved — unlike `activeTools`, which removes tools from the
|
|
1285
|
+
request and invalidates the cache whenever the allow-list changes.
|
|
1286
|
+
|
|
1287
|
+
`allowedTools` is only supported by the Responses API, and it overrides the request-level `toolChoice`.
|
|
1288
|
+
|
|
1289
|
+
```ts
|
|
1290
|
+
import { openai } from '@ai-sdk/openai';
|
|
1291
|
+
import { generateText } from 'ai';
|
|
1292
|
+
|
|
1293
|
+
const result = await generateText({
|
|
1294
|
+
model: openai.responses('gpt-5.5'),
|
|
1295
|
+
tools: {
|
|
1296
|
+
weather: weatherTool,
|
|
1297
|
+
cityAttractions: cityAttractionsTool,
|
|
1298
|
+
search: openai.tools.webSearch(),
|
|
1299
|
+
},
|
|
1300
|
+
providerOptions: {
|
|
1301
|
+
openai: {
|
|
1302
|
+
allowedTools: { toolNames: ['weather', 'search'], mode: 'auto' },
|
|
1303
|
+
},
|
|
1304
|
+
},
|
|
1305
|
+
prompt: 'What is the weather in San Francisco?',
|
|
1306
|
+
});
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
- **toolNames** _string[]_ - The tools the model may call, named as you declared them in `tools`. For
|
|
1310
|
+
provider-defined tools you may also use the canonical OpenAI name (for example `web_search`).
|
|
1311
|
+
- **mode** _'auto' | 'required'_ (optional) - `'auto'` (default) lets the model pick one of the allowed
|
|
1312
|
+
tools or answer with a message; `'required'` forces it to call at least one of them.
|
|
1313
|
+
|
|
1314
|
+
Both function tools and provider-defined tools can be allow-listed, including web search, file search,
|
|
1315
|
+
image generation, code interpreter, apply patch, shell, custom tools, and MCP servers.
|
|
1316
|
+
|
|
1317
|
+
If a name matches both a tool you declared and the canonical OpenAI name of a different tool, the tool
|
|
1318
|
+
you declared under that name wins, and a warning reports the ambiguity. If several tools share the same
|
|
1319
|
+
canonical name — two MCP servers, for example — that name is ambiguous, so it is dropped with a warning;
|
|
1320
|
+
name those tools as you declared them instead.
|
|
1321
|
+
|
|
1322
|
+
<Note>
|
|
1323
|
+
Three kinds of tools cannot be allow-listed, because OpenAI's `allowed_tools`
|
|
1324
|
+
only sees the eagerly-loaded tool list: the [tool search tool](#tool-search),
|
|
1325
|
+
tools marked with `deferLoading`, and tools grouped into a namespace. Naming
|
|
1326
|
+
one of these in `allowedTools` removes it from the allow-list and emits a
|
|
1327
|
+
warning; if that leaves no tools at all, the request fails with an error
|
|
1328
|
+
instead of silently sending no restriction.
|
|
1329
|
+
</Note>
|
|
1330
|
+
|
|
1331
|
+
<Note>
|
|
1332
|
+
An MCP server is allow-listed as a whole: the entry carries the server label,
|
|
1333
|
+
so every tool on that server is callable. To restrict which tools on a server
|
|
1334
|
+
the model may call, use the MCP tool's own `allowedTools` argument.
|
|
1335
|
+
</Note>
|
|
1336
|
+
|
|
1280
1337
|
#### Image Inputs
|
|
1281
1338
|
|
|
1282
1339
|
The OpenAI Responses API supports Image inputs for appropriate models.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ai-sdk/openai",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.98",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@ai-sdk/provider": "3.0.15",
|
|
40
|
-
"@ai-sdk/provider-utils": "4.0.
|
|
40
|
+
"@ai-sdk/provider-utils": "4.0.46"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "20.17.24",
|
|
@@ -337,6 +337,21 @@ export type OpenAIResponsesFunctionTool = {
|
|
|
337
337
|
defer_loading?: boolean;
|
|
338
338
|
};
|
|
339
339
|
|
|
340
|
+
/**
|
|
341
|
+
* Entry in `tool_choice.allowed_tools.tools`. OpenAI identifies most built-in
|
|
342
|
+
* tools by type alone; only `function`, `custom` and `mcp` carry an identifier.
|
|
343
|
+
*/
|
|
344
|
+
export type OpenAIResponsesAllowedTool =
|
|
345
|
+
| { type: 'function'; name: string }
|
|
346
|
+
| { type: 'custom'; name: string }
|
|
347
|
+
| { type: 'mcp'; server_label: string }
|
|
348
|
+
| {
|
|
349
|
+
type: Exclude<
|
|
350
|
+
OpenAIResponsesTool['type'],
|
|
351
|
+
'function' | 'custom' | 'mcp' | 'namespace' | 'tool_search'
|
|
352
|
+
>;
|
|
353
|
+
};
|
|
354
|
+
|
|
340
355
|
export type OpenAIResponsesTool =
|
|
341
356
|
| OpenAIResponsesFunctionTool
|
|
342
357
|
| {
|
|
@@ -15,10 +15,15 @@ import { toolSearchArgsSchema } from '../tool/tool-search';
|
|
|
15
15
|
import { webSearchArgsSchema } from '../tool/web-search';
|
|
16
16
|
import { webSearchPreviewArgsSchema } from '../tool/web-search-preview';
|
|
17
17
|
import type {
|
|
18
|
+
OpenAIResponsesAllowedTool,
|
|
18
19
|
OpenAIResponsesFunctionTool,
|
|
19
20
|
OpenAIResponsesTool,
|
|
20
21
|
} from './openai-responses-api';
|
|
21
22
|
|
|
23
|
+
type AllowedToolResolution =
|
|
24
|
+
| { supported: true; entry: OpenAIResponsesAllowedTool }
|
|
25
|
+
| { supported: false; reason: string };
|
|
26
|
+
|
|
22
27
|
type OpenAIToolOptions = {
|
|
23
28
|
deferLoading?: boolean;
|
|
24
29
|
namespace?: {
|
|
@@ -60,7 +65,7 @@ export async function prepareResponsesTools({
|
|
|
60
65
|
| {
|
|
61
66
|
type: 'allowed_tools';
|
|
62
67
|
mode: 'auto' | 'required';
|
|
63
|
-
tools: Array<
|
|
68
|
+
tools: Array<OpenAIResponsesAllowedTool>;
|
|
64
69
|
};
|
|
65
70
|
toolWarnings: SharedV3Warning[];
|
|
66
71
|
}> {
|
|
@@ -81,6 +86,35 @@ export async function prepareResponsesTools({
|
|
|
81
86
|
const resolvedCustomProviderToolNames =
|
|
82
87
|
customProviderToolNames ?? new Set<string>();
|
|
83
88
|
|
|
89
|
+
const allowedToolResolutions = new Map<string, AllowedToolResolution>();
|
|
90
|
+
const allowedToolAliases = new Map<
|
|
91
|
+
string,
|
|
92
|
+
AllowedToolResolution | 'ambiguous'
|
|
93
|
+
>();
|
|
94
|
+
|
|
95
|
+
const recordAllowedTool = (
|
|
96
|
+
toolName: string,
|
|
97
|
+
resolution: AllowedToolResolution,
|
|
98
|
+
canonicalName: string | undefined,
|
|
99
|
+
) => {
|
|
100
|
+
allowedToolResolutions.set(toolName, resolution);
|
|
101
|
+
|
|
102
|
+
if (canonicalName == null || canonicalName === toolName) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const existingAlias = allowedToolAliases.get(canonicalName);
|
|
107
|
+
|
|
108
|
+
if (existingAlias == null) {
|
|
109
|
+
allowedToolAliases.set(canonicalName, resolution);
|
|
110
|
+
} else if (
|
|
111
|
+
existingAlias !== 'ambiguous' &&
|
|
112
|
+
!isSameAllowedTool(existingAlias, resolution)
|
|
113
|
+
) {
|
|
114
|
+
allowedToolAliases.set(canonicalName, 'ambiguous');
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
84
118
|
for (const tool of tools) {
|
|
85
119
|
switch (tool.type) {
|
|
86
120
|
case 'function': {
|
|
@@ -115,9 +149,32 @@ export async function prepareResponsesTools({
|
|
|
115
149
|
|
|
116
150
|
namespaceTool.tools.push(openaiFunctionTool);
|
|
117
151
|
}
|
|
152
|
+
|
|
153
|
+
recordAllowedTool(
|
|
154
|
+
tool.name,
|
|
155
|
+
namespace != null
|
|
156
|
+
? {
|
|
157
|
+
supported: false,
|
|
158
|
+
reason:
|
|
159
|
+
'tools inside an OpenAI tool namespace are not visible to tool_choice.allowed_tools',
|
|
160
|
+
}
|
|
161
|
+
: openaiOptions?.deferLoading === true
|
|
162
|
+
? {
|
|
163
|
+
supported: false,
|
|
164
|
+
reason:
|
|
165
|
+
'deferred tools are not visible to tool_choice.allowed_tools',
|
|
166
|
+
}
|
|
167
|
+
: {
|
|
168
|
+
supported: true,
|
|
169
|
+
entry: { type: 'function', name: tool.name },
|
|
170
|
+
},
|
|
171
|
+
undefined,
|
|
172
|
+
);
|
|
118
173
|
break;
|
|
119
174
|
}
|
|
120
175
|
case 'provider': {
|
|
176
|
+
const openaiToolCountBefore = openaiTools.length;
|
|
177
|
+
|
|
121
178
|
switch (tool.id) {
|
|
122
179
|
case 'openai.file_search': {
|
|
123
180
|
const args = await validateTypes({
|
|
@@ -321,6 +378,16 @@ export async function prepareResponsesTools({
|
|
|
321
378
|
break;
|
|
322
379
|
}
|
|
323
380
|
}
|
|
381
|
+
|
|
382
|
+
if (openaiTools.length > openaiToolCountBefore) {
|
|
383
|
+
const openaiTool = openaiTools[openaiToolCountBefore];
|
|
384
|
+
|
|
385
|
+
recordAllowedTool(
|
|
386
|
+
tool.name,
|
|
387
|
+
toAllowedToolResolution(openaiTool),
|
|
388
|
+
toolNameMapping?.toProviderToolName(tool.name),
|
|
389
|
+
);
|
|
390
|
+
}
|
|
324
391
|
break;
|
|
325
392
|
}
|
|
326
393
|
default:
|
|
@@ -333,15 +400,74 @@ export async function prepareResponsesTools({
|
|
|
333
400
|
}
|
|
334
401
|
|
|
335
402
|
if (allowedTools != null) {
|
|
403
|
+
const allowedToolEntries: Array<OpenAIResponsesAllowedTool> = [];
|
|
404
|
+
const droppedToolNames: string[] = [];
|
|
405
|
+
|
|
406
|
+
for (const name of allowedTools.toolNames) {
|
|
407
|
+
const directResolution = allowedToolResolutions.get(name);
|
|
408
|
+
const resolution = directResolution ?? allowedToolAliases.get(name);
|
|
409
|
+
|
|
410
|
+
if (directResolution != null && allowedToolAliases.has(name)) {
|
|
411
|
+
toolWarnings.push({
|
|
412
|
+
type: 'unsupported',
|
|
413
|
+
feature: `allowedTools entry "${name}"`,
|
|
414
|
+
details:
|
|
415
|
+
'this name is both a tool name and the provider tool name of another tool in this request; the tool with this name is allowed',
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (resolution === 'ambiguous') {
|
|
420
|
+
toolWarnings.push({
|
|
421
|
+
type: 'unsupported',
|
|
422
|
+
feature: `allowedTools entry "${name}"`,
|
|
423
|
+
details:
|
|
424
|
+
'several tools in this request share this provider tool name; use the tool name from the tools for this request instead',
|
|
425
|
+
});
|
|
426
|
+
droppedToolNames.push(name);
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (resolution == null) {
|
|
431
|
+
toolWarnings.push({
|
|
432
|
+
type: 'unsupported',
|
|
433
|
+
feature: `allowedTools entry "${name}"`,
|
|
434
|
+
details:
|
|
435
|
+
'the tool is not part of the tools for this request and is sent as a function tool',
|
|
436
|
+
});
|
|
437
|
+
allowedToolEntries.push({
|
|
438
|
+
type: 'function',
|
|
439
|
+
name: toolNameMapping?.toProviderToolName(name) ?? name,
|
|
440
|
+
});
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (!resolution.supported) {
|
|
445
|
+
toolWarnings.push({
|
|
446
|
+
type: 'unsupported',
|
|
447
|
+
feature: `allowedTools entry "${name}"`,
|
|
448
|
+
details: `${resolution.reason}; the tool is removed from the allowed tools`,
|
|
449
|
+
});
|
|
450
|
+
droppedToolNames.push(name);
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
allowedToolEntries.push(resolution.entry);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (allowedToolEntries.length === 0) {
|
|
458
|
+
throw new UnsupportedFunctionalityError({
|
|
459
|
+
functionality: `allowedTools with only tools that cannot be allow-listed (${droppedToolNames.join(
|
|
460
|
+
', ',
|
|
461
|
+
)})`,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
336
465
|
return {
|
|
337
466
|
tools: openaiTools,
|
|
338
467
|
toolChoice: {
|
|
339
468
|
type: 'allowed_tools',
|
|
340
469
|
mode: allowedTools.mode ?? 'auto',
|
|
341
|
-
tools:
|
|
342
|
-
type: 'function',
|
|
343
|
-
name: toolNameMapping?.toProviderToolName(name) ?? name,
|
|
344
|
-
})),
|
|
470
|
+
tools: allowedToolEntries,
|
|
345
471
|
},
|
|
346
472
|
toolWarnings,
|
|
347
473
|
};
|
|
@@ -389,6 +515,61 @@ export async function prepareResponsesTools({
|
|
|
389
515
|
}
|
|
390
516
|
}
|
|
391
517
|
|
|
518
|
+
function allowedToolKey(entry: OpenAIResponsesAllowedTool): string {
|
|
519
|
+
switch (entry.type) {
|
|
520
|
+
case 'mcp':
|
|
521
|
+
return `mcp:${entry.server_label}`;
|
|
522
|
+
case 'function':
|
|
523
|
+
case 'custom':
|
|
524
|
+
return `${entry.type}:${entry.name}`;
|
|
525
|
+
default:
|
|
526
|
+
return entry.type;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
function isSameAllowedTool(
|
|
531
|
+
a: AllowedToolResolution,
|
|
532
|
+
b: AllowedToolResolution,
|
|
533
|
+
): boolean {
|
|
534
|
+
if (a.supported && b.supported) {
|
|
535
|
+
return allowedToolKey(a.entry) === allowedToolKey(b.entry);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (!a.supported && !b.supported) {
|
|
539
|
+
return a.reason === b.reason;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function toAllowedToolResolution(
|
|
546
|
+
tool: OpenAIResponsesTool,
|
|
547
|
+
): AllowedToolResolution {
|
|
548
|
+
switch (tool.type) {
|
|
549
|
+
case 'custom':
|
|
550
|
+
return { supported: true, entry: { type: 'custom', name: tool.name } };
|
|
551
|
+
case 'mcp':
|
|
552
|
+
return {
|
|
553
|
+
supported: true,
|
|
554
|
+
entry: { type: 'mcp', server_label: tool.server_label },
|
|
555
|
+
};
|
|
556
|
+
case 'file_search':
|
|
557
|
+
case 'web_search':
|
|
558
|
+
case 'web_search_preview':
|
|
559
|
+
case 'image_generation':
|
|
560
|
+
case 'code_interpreter':
|
|
561
|
+
case 'apply_patch':
|
|
562
|
+
case 'shell':
|
|
563
|
+
case 'local_shell':
|
|
564
|
+
return { supported: true, entry: { type: tool.type } };
|
|
565
|
+
default:
|
|
566
|
+
return {
|
|
567
|
+
supported: false,
|
|
568
|
+
reason: `OpenAI does not support ${tool.type} tools in tool_choice.allowed_tools`,
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
392
573
|
function prepareFunctionTool({
|
|
393
574
|
tool,
|
|
394
575
|
options,
|