@ai-sdk/openai 4.0.42 → 4.0.44

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 CHANGED
@@ -1,5 +1,33 @@
1
1
  # @ai-sdk/openai
2
2
 
3
+ ## 4.0.44
4
+
5
+ ### Patch Changes
6
+
7
+ - e6087c9: fix: handle empty string tool call IDs
8
+ - Updated dependencies [e6087c9]
9
+ - @ai-sdk/provider-utils@5.0.28
10
+
11
+ ## 4.0.43
12
+
13
+ ### Patch Changes
14
+
15
+ - a062795: fix(openai): support built-in and provider-defined tools in the Responses `allowedTools` option
16
+
17
+ `allowedTools` emitted every allow-list entry as `{ type: 'function', name }`, but OpenAI identifies
18
+ built-in tools by type. Allow-listing a declared provider-defined tool (web search, image generation,
19
+ MCP, custom, ...) therefore failed with `Tool choice '<name>' not found in 'tools' parameter`. Entries
20
+ are now derived from the declared tool, including the MCP server label and custom tool name.
21
+
22
+ Tools that OpenAI cannot allow-list (the tool search tool, deferred tools, and namespaced tools) are
23
+ dropped from the allow-list with a warning, and an error is thrown if that would leave the allow-list
24
+ empty rather than silently sending an unrestricted request.
25
+
26
+ Ambiguous names are now reported instead of resolved silently. A name that matches both a declared tool
27
+ and another tool's provider tool name resolves to the declared tool and warns; a provider tool name
28
+ shared by several tools in the same request (two MCP servers, for example) is dropped with a warning.
29
+ A name that matches no declared tool keeps its existing behavior and is now warned about.
30
+
3
31
  ## 4.0.42
4
32
 
5
33
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1152,7 +1152,7 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
1152
1152
  };
1153
1153
  }
1154
1154
  async doGenerate(options) {
1155
- var _a, _b, _c, _d, _e, _f, _g, _h;
1155
+ var _a, _b, _c, _d, _e, _f, _g;
1156
1156
  const { args: body, warnings } = await this.getArgs(options);
1157
1157
  const {
1158
1158
  responseHeaders,
@@ -1181,12 +1181,12 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
1181
1181
  for (const toolCall of (_c = choice.message.tool_calls) != null ? _c : []) {
1182
1182
  content.push({
1183
1183
  type: "tool-call",
1184
- toolCallId: (_d = toolCall.id) != null ? _d : generateId(),
1184
+ toolCallId: toolCall.id || generateId(),
1185
1185
  toolName: toolCall.function.name,
1186
1186
  input: toolCall.function.arguments
1187
1187
  });
1188
1188
  }
1189
- for (const annotation of (_e = choice.message.annotations) != null ? _e : []) {
1189
+ for (const annotation of (_d = choice.message.annotations) != null ? _d : []) {
1190
1190
  content.push({
1191
1191
  type: "source",
1192
1192
  sourceType: "url",
@@ -1195,7 +1195,7 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
1195
1195
  title: annotation.url_citation.title
1196
1196
  });
1197
1197
  }
1198
- const completionTokenDetails = (_f = response.usage) == null ? void 0 : _f.completion_tokens_details;
1198
+ const completionTokenDetails = (_e = response.usage) == null ? void 0 : _e.completion_tokens_details;
1199
1199
  const providerMetadata = { openai: {} };
1200
1200
  if ((completionTokenDetails == null ? void 0 : completionTokenDetails.accepted_prediction_tokens) != null) {
1201
1201
  providerMetadata.openai.acceptedPredictionTokens = completionTokenDetails == null ? void 0 : completionTokenDetails.accepted_prediction_tokens;
@@ -1203,14 +1203,14 @@ var OpenAIChatLanguageModel = class _OpenAIChatLanguageModel {
1203
1203
  if ((completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens) != null) {
1204
1204
  providerMetadata.openai.rejectedPredictionTokens = completionTokenDetails == null ? void 0 : completionTokenDetails.rejected_prediction_tokens;
1205
1205
  }
1206
- if (((_g = choice.logprobs) == null ? void 0 : _g.content) != null) {
1206
+ if (((_f = choice.logprobs) == null ? void 0 : _f.content) != null) {
1207
1207
  providerMetadata.openai.logprobs = choice.logprobs.content;
1208
1208
  }
1209
1209
  return {
1210
1210
  content,
1211
1211
  finishReason: {
1212
1212
  unified: mapOpenAIFinishReason(choice.finish_reason),
1213
- raw: (_h = choice.finish_reason) != null ? _h : void 0
1213
+ raw: (_g = choice.finish_reason) != null ? _g : void 0
1214
1214
  },
1215
1215
  usage: convertOpenAIChatUsage(response.usage),
1216
1216
  request: { body },
@@ -5648,7 +5648,7 @@ async function prepareResponsesTools({
5648
5648
  customProviderToolNames,
5649
5649
  outputSchemaToolNames
5650
5650
  }) {
5651
- var _a, _b, _c;
5651
+ var _a, _b, _c, _d;
5652
5652
  tools = (tools == null ? void 0 : tools.length) ? tools : void 0;
5653
5653
  const toolWarnings = [];
5654
5654
  if (tools == null) {
@@ -5657,6 +5657,20 @@ async function prepareResponsesTools({
5657
5657
  const openaiTools2 = [];
5658
5658
  const namespaceTools = /* @__PURE__ */ new Map();
5659
5659
  const resolvedCustomProviderToolNames = customProviderToolNames != null ? customProviderToolNames : /* @__PURE__ */ new Set();
5660
+ const allowedToolResolutions = /* @__PURE__ */ new Map();
5661
+ const allowedToolAliases = /* @__PURE__ */ new Map();
5662
+ const recordAllowedTool = (toolName, resolution, canonicalName) => {
5663
+ allowedToolResolutions.set(toolName, resolution);
5664
+ if (canonicalName == null || canonicalName === toolName) {
5665
+ return;
5666
+ }
5667
+ const existingAlias = allowedToolAliases.get(canonicalName);
5668
+ if (existingAlias == null) {
5669
+ allowedToolAliases.set(canonicalName, resolution);
5670
+ } else if (existingAlias !== "ambiguous" && !isSameAllowedTool(existingAlias, resolution)) {
5671
+ allowedToolAliases.set(canonicalName, "ambiguous");
5672
+ }
5673
+ };
5660
5674
  for (const tool of tools) {
5661
5675
  switch (tool.type) {
5662
5676
  case "function": {
@@ -5689,9 +5703,24 @@ async function prepareResponsesTools({
5689
5703
  }
5690
5704
  namespaceTool.tools.push(openaiFunctionTool);
5691
5705
  }
5706
+ recordAllowedTool(
5707
+ tool.name,
5708
+ namespace != null ? {
5709
+ supported: false,
5710
+ reason: "tools inside an OpenAI tool namespace are not visible to tool_choice.allowed_tools"
5711
+ } : (openaiOptions == null ? void 0 : openaiOptions.deferLoading) === true ? {
5712
+ supported: false,
5713
+ reason: "deferred tools are not visible to tool_choice.allowed_tools"
5714
+ } : {
5715
+ supported: true,
5716
+ entry: { type: "function", name: tool.name }
5717
+ },
5718
+ void 0
5719
+ );
5692
5720
  break;
5693
5721
  }
5694
5722
  case "provider": {
5723
+ const openaiToolCountBefore = openaiTools2.length;
5695
5724
  switch (tool.id) {
5696
5725
  case "openai.file_search": {
5697
5726
  const args = await validateTypes2({
@@ -5864,6 +5893,14 @@ async function prepareResponsesTools({
5864
5893
  break;
5865
5894
  }
5866
5895
  }
5896
+ if (openaiTools2.length > openaiToolCountBefore) {
5897
+ const openaiTool = openaiTools2[openaiToolCountBefore];
5898
+ recordAllowedTool(
5899
+ tool.name,
5900
+ toAllowedToolResolution(openaiTool),
5901
+ toolNameMapping == null ? void 0 : toolNameMapping.toProviderToolName(tool.name)
5902
+ );
5903
+ }
5867
5904
  break;
5868
5905
  }
5869
5906
  default:
@@ -5875,18 +5912,63 @@ async function prepareResponsesTools({
5875
5912
  }
5876
5913
  }
5877
5914
  if (allowedTools != null) {
5915
+ const allowedToolEntries = [];
5916
+ const droppedToolNames = [];
5917
+ for (const name of allowedTools.toolNames) {
5918
+ const directResolution = allowedToolResolutions.get(name);
5919
+ const resolution = directResolution != null ? directResolution : allowedToolAliases.get(name);
5920
+ if (directResolution != null && allowedToolAliases.has(name)) {
5921
+ toolWarnings.push({
5922
+ type: "unsupported",
5923
+ feature: `allowedTools entry "${name}"`,
5924
+ details: "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"
5925
+ });
5926
+ }
5927
+ if (resolution === "ambiguous") {
5928
+ toolWarnings.push({
5929
+ type: "unsupported",
5930
+ feature: `allowedTools entry "${name}"`,
5931
+ details: "several tools in this request share this provider tool name; use the tool name from the tools for this request instead"
5932
+ });
5933
+ droppedToolNames.push(name);
5934
+ continue;
5935
+ }
5936
+ if (resolution == null) {
5937
+ toolWarnings.push({
5938
+ type: "unsupported",
5939
+ feature: `allowedTools entry "${name}"`,
5940
+ details: "the tool is not part of the tools for this request and is sent as a function tool"
5941
+ });
5942
+ allowedToolEntries.push({
5943
+ type: "function",
5944
+ name: (_b = toolNameMapping == null ? void 0 : toolNameMapping.toProviderToolName(name)) != null ? _b : name
5945
+ });
5946
+ continue;
5947
+ }
5948
+ if (!resolution.supported) {
5949
+ toolWarnings.push({
5950
+ type: "unsupported",
5951
+ feature: `allowedTools entry "${name}"`,
5952
+ details: `${resolution.reason}; the tool is removed from the allowed tools`
5953
+ });
5954
+ droppedToolNames.push(name);
5955
+ continue;
5956
+ }
5957
+ allowedToolEntries.push(resolution.entry);
5958
+ }
5959
+ if (allowedToolEntries.length === 0) {
5960
+ throw new UnsupportedFunctionalityError5({
5961
+ functionality: `allowedTools with only tools that cannot be allow-listed (${droppedToolNames.join(
5962
+ ", "
5963
+ )})`
5964
+ });
5965
+ }
5878
5966
  return {
5879
5967
  tools: openaiTools2,
5880
5968
  toolChoice: {
5881
5969
  type: "allowed_tools",
5882
- mode: (_b = allowedTools.mode) != null ? _b : "auto",
5883
- tools: allowedTools.toolNames.map((name) => {
5884
- var _a2;
5885
- return {
5886
- type: "function",
5887
- name: (_a2 = toolNameMapping == null ? void 0 : toolNameMapping.toProviderToolName(name)) != null ? _a2 : name
5888
- };
5889
- })
5970
+ mode: (_c = allowedTools.mode) != null ? _c : "auto",
5971
+ tools: allowedToolEntries
5890
5972
  },
5891
5973
  toolWarnings
5892
5974
  };
@@ -5901,7 +5983,7 @@ async function prepareResponsesTools({
5901
5983
  case "required":
5902
5984
  return { tools: openaiTools2, toolChoice: type, toolWarnings };
5903
5985
  case "tool": {
5904
- const resolvedToolName = (_c = toolNameMapping == null ? void 0 : toolNameMapping.toProviderToolName(toolChoice.toolName)) != null ? _c : toolChoice.toolName;
5986
+ const resolvedToolName = (_d = toolNameMapping == null ? void 0 : toolNameMapping.toProviderToolName(toolChoice.toolName)) != null ? _d : toolChoice.toolName;
5905
5987
  return {
5906
5988
  tools: openaiTools2,
5907
5989
  toolChoice: resolvedToolName === "code_interpreter" || resolvedToolName === "file_search" || resolvedToolName === "image_generation" || resolvedToolName === "web_search_preview" || resolvedToolName === "web_search" || resolvedToolName === "mcp" || resolvedToolName === "apply_patch" || resolvedToolName === "computer" || resolvedToolName === "programmatic_tool_calling" ? { type: resolvedToolName } : resolvedCustomProviderToolNames.has(resolvedToolName) ? { type: "custom", name: resolvedToolName } : { type: "function", name: resolvedToolName },
@@ -5916,6 +5998,53 @@ async function prepareResponsesTools({
5916
5998
  }
5917
5999
  }
5918
6000
  }
6001
+ function allowedToolKey(entry) {
6002
+ switch (entry.type) {
6003
+ case "mcp":
6004
+ return `mcp:${entry.server_label}`;
6005
+ case "function":
6006
+ case "custom":
6007
+ return `${entry.type}:${entry.name}`;
6008
+ default:
6009
+ return entry.type;
6010
+ }
6011
+ }
6012
+ function isSameAllowedTool(a, b) {
6013
+ if (a.supported && b.supported) {
6014
+ return allowedToolKey(a.entry) === allowedToolKey(b.entry);
6015
+ }
6016
+ if (!a.supported && !b.supported) {
6017
+ return a.reason === b.reason;
6018
+ }
6019
+ return false;
6020
+ }
6021
+ function toAllowedToolResolution(tool) {
6022
+ switch (tool.type) {
6023
+ case "custom":
6024
+ return { supported: true, entry: { type: "custom", name: tool.name } };
6025
+ case "mcp":
6026
+ return {
6027
+ supported: true,
6028
+ entry: { type: "mcp", server_label: tool.server_label }
6029
+ };
6030
+ case "file_search":
6031
+ case "web_search":
6032
+ case "web_search_preview":
6033
+ case "image_generation":
6034
+ case "code_interpreter":
6035
+ case "computer":
6036
+ case "apply_patch":
6037
+ case "shell":
6038
+ case "local_shell":
6039
+ case "programmatic_tool_calling":
6040
+ return { supported: true, entry: { type: tool.type } };
6041
+ default:
6042
+ return {
6043
+ supported: false,
6044
+ reason: `OpenAI does not support ${tool.type} tools in tool_choice.allowed_tools`
6045
+ };
6046
+ }
6047
+ }
5919
6048
  function prepareFunctionTool({
5920
6049
  tool,
5921
6050
  options
@@ -10031,7 +10160,7 @@ var OpenAISkills = class {
10031
10160
  };
10032
10161
 
10033
10162
  // src/version.ts
10034
- var VERSION = true ? "4.0.42" : "0.0.0-test";
10163
+ var VERSION = true ? "4.0.44" : "0.0.0-test";
10035
10164
 
10036
10165
  // src/openai-provider.ts
10037
10166
  function createOpenAI(options = {}) {