@ai-sdk/openai 3.0.68 → 3.0.69

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.
@@ -434,6 +434,38 @@ The `textVerbosity` parameter scales output length without changing the underlyi
434
434
  - `'medium'`: Balanced detail (default)
435
435
  - `'high'`: Verbose responses with comprehensive detail
436
436
 
437
+ #### Namespaced Function Calls
438
+
439
+ OpenAI supports grouping related function tools into
440
+ [namespaces](https://developers.openai.com/api/docs/guides/function-calling#defining-namespaces).
441
+ When the Responses API returns a `function_call` with a `namespace`, the OpenAI provider
442
+ exposes this value on the generated `tool-call` part as
443
+ `providerMetadata.openai.namespace`.
444
+
445
+ ```ts
446
+ for (const part of result.content) {
447
+ if (part.type === 'tool-call') {
448
+ console.log(part.providerMetadata?.openai?.namespace);
449
+ }
450
+ }
451
+ ```
452
+
453
+ When using `streamText`, the namespace is available on the `tool-input-end` event and on
454
+ the final `tool-call` event:
455
+
456
+ ```ts
457
+ for await (const part of result.stream) {
458
+ if (part.type === 'tool-input-end' || part.type === 'tool-call') {
459
+ console.log(part.providerMetadata?.openai?.namespace);
460
+ }
461
+ }
462
+ ```
463
+
464
+ If you persist or reconstruct messages for later turns, preserve the OpenAI provider
465
+ metadata on tool-call parts. The SDK uses `providerMetadata.openai.namespace` or
466
+ `providerOptions.openai.namespace` to round-trip the namespace back to OpenAI on
467
+ subsequent requests.
468
+
437
469
  #### Web Search Tool
438
470
 
439
471
  The OpenAI responses API supports web search through the `openai.tools.webSearch` tool.
@@ -1024,6 +1056,52 @@ In hosted mode, the model internally searches the deferred tools, loads the rele
1024
1056
  proceeds to call them — all within a single response. The `tool_search_call` and
1025
1057
  `tool_search_output` items appear in the response with `execution: 'server'` and `call_id: null`.
1026
1058
 
1059
+ ##### Namespaces
1060
+
1061
+ Use `providerOptions.openai.namespace` to group related function tools for OpenAI.
1062
+ The SDK keeps each tool executable as a normal AI SDK tool, but serializes grouped
1063
+ tools as OpenAI `namespace` entries in the request:
1064
+
1065
+ ```ts
1066
+ const crmNamespace = {
1067
+ name: 'crm',
1068
+ description: 'CRM tools for customer lookup and order management.',
1069
+ };
1070
+
1071
+ const result = await generateText({
1072
+ model: openai.responses('gpt-5.4'),
1073
+ prompt: 'List open orders for customer cust_123.',
1074
+ tools: {
1075
+ toolSearch: openai.tools.toolSearch(),
1076
+
1077
+ get_customer_profile: tool({
1078
+ description: 'Fetch a customer profile by customer ID.',
1079
+ inputSchema: z.object({ customer_id: z.string() }),
1080
+ execute: async ({ customer_id }) => ({ customer_id }),
1081
+ providerOptions: {
1082
+ openai: { namespace: crmNamespace },
1083
+ },
1084
+ }),
1085
+
1086
+ list_open_orders: tool({
1087
+ description: 'List open orders for a customer ID.',
1088
+ inputSchema: z.object({ customer_id: z.string() }),
1089
+ execute: async ({ customer_id }) => ({ customer_id, orders: [] }),
1090
+ providerOptions: {
1091
+ openai: {
1092
+ namespace: crmNamespace,
1093
+ deferLoading: true,
1094
+ },
1095
+ },
1096
+ }),
1097
+ },
1098
+ });
1099
+ ```
1100
+
1101
+ Tools in the same namespace must use the same namespace `name` and `description`.
1102
+ For best results with tool search, keep namespace descriptions concise and put
1103
+ detailed usage guidance on the individual function tools.
1104
+
1027
1105
  ##### Client-Executed Tool Search
1028
1106
 
1029
1107
  Use client-executed tool search when tool discovery depends on runtime state — for example,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/openai",
3
- "version": "3.0.68",
3
+ "version": "3.0.69",
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-sdk/test-server": "1.0.5",
48
- "@vercel/ai-tsconfig": "0.0.0"
47
+ "@vercel/ai-tsconfig": "0.0.0",
48
+ "@ai-sdk/test-server": "1.0.5"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "zod": "^3.25.76 || ^4.1.8"
@@ -279,14 +279,22 @@ export type OpenAIResponsesFileSearchToolCompoundFilter = {
279
279
  >;
280
280
  };
281
281
 
282
+ export type OpenAIResponsesFunctionTool = {
283
+ type: 'function';
284
+ name: string;
285
+ description: string | undefined;
286
+ parameters: JSONSchema7;
287
+ strict?: boolean;
288
+ defer_loading?: boolean;
289
+ };
290
+
282
291
  export type OpenAIResponsesTool =
292
+ | OpenAIResponsesFunctionTool
283
293
  | {
284
- type: 'function';
294
+ type: 'namespace';
285
295
  name: string;
286
- description: string | undefined;
287
- parameters: JSONSchema7;
288
- strict?: boolean;
289
- defer_loading?: boolean;
296
+ description: string;
297
+ tools: Array<OpenAIResponsesFunctionTool>;
290
298
  }
291
299
  | {
292
300
  type: 'apply_patch';
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  UnsupportedFunctionalityError,
3
3
  type LanguageModelV3CallOptions,
4
+ type LanguageModelV3FunctionTool,
4
5
  type SharedV3Warning,
5
6
  } from '@ai-sdk/provider';
6
7
  import { validateTypes, type ToolNameMapping } from '@ai-sdk/provider-utils';
@@ -13,7 +14,18 @@ import { shellArgsSchema } from '../tool/shell';
13
14
  import { toolSearchArgsSchema } from '../tool/tool-search';
14
15
  import { webSearchArgsSchema } from '../tool/web-search';
15
16
  import { webSearchPreviewArgsSchema } from '../tool/web-search-preview';
16
- import type { OpenAIResponsesTool } from './openai-responses-api';
17
+ import type {
18
+ OpenAIResponsesFunctionTool,
19
+ OpenAIResponsesTool,
20
+ } from './openai-responses-api';
21
+
22
+ type OpenAIToolOptions = {
23
+ deferLoading?: boolean;
24
+ namespace?: {
25
+ name: string;
26
+ description: string;
27
+ };
28
+ };
17
29
 
18
30
  export async function prepareResponsesTools({
19
31
  tools,
@@ -62,6 +74,10 @@ export async function prepareResponsesTools({
62
74
  }
63
75
 
64
76
  const openaiTools: Array<OpenAIResponsesTool> = [];
77
+ const namespaceTools = new Map<
78
+ string,
79
+ Extract<OpenAIResponsesTool, { type: 'namespace' }>
80
+ >();
65
81
  const resolvedCustomProviderToolNames =
66
82
  customProviderToolNames ?? new Set<string>();
67
83
 
@@ -69,18 +85,36 @@ export async function prepareResponsesTools({
69
85
  switch (tool.type) {
70
86
  case 'function': {
71
87
  const openaiOptions = tool.providerOptions?.openai as
72
- | { deferLoading?: boolean }
88
+ | OpenAIToolOptions
73
89
  | undefined;
74
- const deferLoading = openaiOptions?.deferLoading;
75
-
76
- openaiTools.push({
77
- type: 'function',
78
- name: tool.name,
79
- description: tool.description,
80
- parameters: tool.inputSchema,
81
- ...(tool.strict != null ? { strict: tool.strict } : {}),
82
- ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
90
+ const openaiFunctionTool = prepareFunctionTool({
91
+ tool,
92
+ options: openaiOptions,
83
93
  });
94
+ const namespace = openaiOptions?.namespace;
95
+
96
+ if (namespace == null) {
97
+ openaiTools.push(openaiFunctionTool);
98
+ } else {
99
+ let namespaceTool = namespaceTools.get(namespace.name);
100
+
101
+ if (namespaceTool == null) {
102
+ namespaceTool = {
103
+ type: 'namespace',
104
+ name: namespace.name,
105
+ description: namespace.description,
106
+ tools: [],
107
+ };
108
+ namespaceTools.set(namespace.name, namespaceTool);
109
+ openaiTools.push(namespaceTool);
110
+ } else if (namespaceTool.description !== namespace.description) {
111
+ throw new UnsupportedFunctionalityError({
112
+ functionality: `conflicting descriptions for OpenAI tool namespace "${namespace.name}"`,
113
+ });
114
+ }
115
+
116
+ namespaceTool.tools.push(openaiFunctionTool);
117
+ }
84
118
  break;
85
119
  }
86
120
  case 'provider': {
@@ -352,6 +386,25 @@ export async function prepareResponsesTools({
352
386
  }
353
387
  }
354
388
 
389
+ function prepareFunctionTool({
390
+ tool,
391
+ options,
392
+ }: {
393
+ tool: LanguageModelV3FunctionTool;
394
+ options: OpenAIToolOptions | undefined;
395
+ }): OpenAIResponsesFunctionTool {
396
+ const deferLoading = options?.deferLoading;
397
+
398
+ return {
399
+ type: 'function',
400
+ name: tool.name,
401
+ description: tool.description,
402
+ parameters: tool.inputSchema,
403
+ ...(tool.strict != null ? { strict: tool.strict } : {}),
404
+ ...(deferLoading != null ? { defer_loading: deferLoading } : {}),
405
+ };
406
+ }
407
+
355
408
  function mapShellEnvironment(environment: {
356
409
  type?: string;
357
410
  [key: string]: unknown;