@ai-sdk/amazon-bedrock 5.0.93 → 5.0.96

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.
@@ -27,6 +27,7 @@ export async function prepareTools({
27
27
  modelFamily,
28
28
  reasoningBudgetTokens,
29
29
  disableParallelToolUse,
30
+ rejectsForcedToolUse = false,
30
31
  }: {
31
32
  tools: LanguageModelV4CallOptions['tools'];
32
33
  toolChoice?: LanguageModelV4CallOptions['toolChoice'];
@@ -34,6 +35,7 @@ export async function prepareTools({
34
35
  modelFamily?: AmazonBedrockChatModelSettings['modelFamily'];
35
36
  reasoningBudgetTokens?: number;
36
37
  disableParallelToolUse?: boolean;
38
+ rejectsForcedToolUse?: boolean;
37
39
  }): Promise<{
38
40
  toolConfig: AmazonBedrockToolConfiguration;
39
41
  additionalTools: Record<string, unknown> | undefined;
@@ -84,13 +86,22 @@ export async function prepareTools({
84
86
  modelFamily,
85
87
  reasoningBudgetTokens,
86
88
  });
87
- const ProviderTools = supportedTools.filter(t => t.type === 'provider');
89
+ const providerTools = supportedTools.filter(t => t.type === 'provider');
88
90
  const functionTools = supportedTools.filter(t => t.type === 'function');
91
+ const rejectsForcedToolChoice =
92
+ isAnthropicModel &&
93
+ rejectsForcedToolUse &&
94
+ (toolChoice?.type === 'required' || toolChoice?.type === 'tool');
95
+ const providerToolsForRequest =
96
+ rejectsForcedToolChoice && toolChoice.type === 'tool'
97
+ ? providerTools.filter(tool => tool.name === toolChoice.toolName)
98
+ : providerTools;
89
99
 
90
100
  let additionalTools: Record<string, unknown> | undefined = undefined;
91
101
  const amazonBedrockTools: AmazonBedrockTool[] = [];
92
102
 
93
- const usingAnthropicTools = isAnthropicModel && ProviderTools.length > 0;
103
+ const usingAnthropicTools =
104
+ isAnthropicModel && providerToolsForRequest.length > 0;
94
105
 
95
106
  // Handle Anthropic provider-defined tools for Anthropic models on Bedrock
96
107
  if (usingAnthropicTools) {
@@ -99,11 +110,12 @@ export async function prepareTools({
99
110
  toolWarnings: anthropicToolWarnings,
100
111
  betas: anthropicBetas,
101
112
  } = await prepareAnthropicTools({
102
- tools: ProviderTools,
113
+ tools: providerTools,
103
114
  toolChoice,
104
115
  disableParallelToolUse,
105
116
  supportsStructuredOutput: false,
106
117
  supportsStrictTools: false,
118
+ rejectsForcedToolUse,
107
119
  });
108
120
 
109
121
  toolWarnings.push(...anthropicToolWarnings);
@@ -118,7 +130,7 @@ export async function prepareTools({
118
130
  }
119
131
 
120
132
  // Create a standard Bedrock tool representation for validation purposes
121
- for (const tool of ProviderTools) {
133
+ for (const tool of providerToolsForRequest) {
122
134
  const toolFactory = Object.values(anthropicTools).find(factory => {
123
135
  const instance = (factory as (args: any) => any)({});
124
136
  return instance.id === tool.id;
@@ -141,11 +153,34 @@ export async function prepareTools({
141
153
  }
142
154
  } else {
143
155
  // Report unsupported provider-defined tools for non-anthropic models
144
- for (const tool of ProviderTools) {
156
+ for (const tool of providerToolsForRequest) {
145
157
  toolWarnings.push({ type: 'unsupported', feature: `tool ${tool.id}` });
146
158
  }
147
159
  }
148
160
 
161
+ let preparedToolChoice = toolChoice;
162
+ if (!usingAnthropicTools && rejectsForcedToolChoice) {
163
+ if (toolChoice.type === 'tool') {
164
+ toolWarnings.push({
165
+ type: 'unsupported',
166
+ feature: 'toolChoice',
167
+ details:
168
+ `toolChoice 'tool' is not supported by this model because it rejects forced tool use. ` +
169
+ `Only the '${toolChoice.toolName}' tool is sent with 'auto' tool choice. ` +
170
+ `Instruct the model to use the tool in the prompt and verify that a tool call was made.`,
171
+ });
172
+ } else {
173
+ toolWarnings.push({
174
+ type: 'unsupported',
175
+ feature: 'toolChoice',
176
+ details:
177
+ `toolChoice 'required' is not supported by this model because it rejects forced tool use. ` +
178
+ `Using 'auto' instead. Instruct the model to use a tool in the prompt and verify that a tool call was made.`,
179
+ });
180
+ }
181
+ preparedToolChoice = { type: 'auto' };
182
+ }
183
+
149
184
  const filteredFunctionTools =
150
185
  toolChoice?.type === 'tool'
151
186
  ? functionTools.filter(t => t.name === toolChoice.toolName)
@@ -193,16 +228,16 @@ export async function prepareTools({
193
228
  !usingAnthropicTools &&
194
229
  disableParallelToolUse &&
195
230
  amazonBedrockTools.length > 0 &&
196
- toolChoice?.type !== 'none'
231
+ preparedToolChoice?.type !== 'none'
197
232
  ) {
198
233
  additionalTools = {
199
234
  tool_choice:
200
- toolChoice?.type === 'required'
235
+ preparedToolChoice?.type === 'required'
201
236
  ? { type: 'any', disable_parallel_tool_use: true }
202
- : toolChoice?.type === 'tool'
237
+ : preparedToolChoice?.type === 'tool'
203
238
  ? {
204
239
  type: 'tool',
205
- name: toolChoice.toolName,
240
+ name: preparedToolChoice.toolName,
206
241
  disable_parallel_tool_use: true,
207
242
  }
208
243
  : { type: 'auto', disable_parallel_tool_use: true },
@@ -216,9 +251,9 @@ export async function prepareTools({
216
251
  !usingAnthropicTools &&
217
252
  additionalTools?.tool_choice == null &&
218
253
  amazonBedrockTools.length > 0 &&
219
- toolChoice
254
+ preparedToolChoice
220
255
  ) {
221
- const type = toolChoice.type;
256
+ const type = preparedToolChoice.type;
222
257
  switch (type) {
223
258
  case 'auto':
224
259
  amazonBedrockToolChoice = { auto: {} };
@@ -231,7 +266,9 @@ export async function prepareTools({
231
266
  amazonBedrockToolChoice = undefined;
232
267
  break;
233
268
  case 'tool':
234
- amazonBedrockToolChoice = { tool: { name: toolChoice.toolName } };
269
+ amazonBedrockToolChoice = {
270
+ tool: { name: preparedToolChoice.toolName },
271
+ };
235
272
  break;
236
273
  default: {
237
274
  const _exhaustiveCheck: never = type;
@@ -16,7 +16,7 @@ export function isMistralModel(modelId: string): boolean {
16
16
  * Bedrock generates tool call IDs in formats like `tooluse_bpe71yCfRu2b5i-nKGDr5g`,
17
17
  * which are incompatible with Mistral's requirements.
18
18
  *
19
- * This function extracts the first 9 alphanumeric characters from the ID.
19
+ * This function hashes incompatible IDs into 9 alphanumeric characters.
20
20
  *
21
21
  * @param toolCallId - The original tool call ID from Bedrock
22
22
  * @param isMistral - Whether the model is a Mistral model
@@ -30,7 +30,40 @@ export function normalizeToolCallId(
30
30
  return toolCallId;
31
31
  }
32
32
 
33
- // Extract only alphanumeric characters and take first 9
34
- const alphanumericChars = toolCallId.replace(/[^a-zA-Z0-9]/g, '');
35
- return alphanumericChars.slice(0, 9);
33
+ if (/^[a-zA-Z0-9]{9}$/.test(toolCallId)) {
34
+ return toolCallId;
35
+ }
36
+
37
+ return convertToBase62Hash(toolCallId);
38
+ }
39
+
40
+ const base62Characters =
41
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
42
+ const base62Length = BigInt(base62Characters.length);
43
+ const normalizedToolCallIdLength = 9;
44
+ const normalizedToolCallIdSpace =
45
+ base62Length ** BigInt(normalizedToolCallIdLength);
46
+ const fnvOffsetBasis64 = BigInt('14695981039346656037');
47
+ const fnvPrime64 = BigInt('1099511628211');
48
+ const fnv64BitMask = BigInt('18446744073709551615');
49
+
50
+ function convertToBase62Hash(value: string): string {
51
+ // FNV-1a 64-bit is deterministic across runtimes and gives the normalized ID
52
+ // access to the full 9-character base62 space.
53
+ let hash = fnvOffsetBasis64;
54
+
55
+ for (let i = 0; i < value.length; i++) {
56
+ hash ^= BigInt(value.charCodeAt(i));
57
+ hash = (hash * fnvPrime64) & fnv64BitMask;
58
+ }
59
+
60
+ let base62Value = hash % normalizedToolCallIdSpace;
61
+ let result = '';
62
+
63
+ for (let i = 0; i < normalizedToolCallIdLength; i++) {
64
+ result = base62Characters[Number(base62Value % base62Length)] + result;
65
+ base62Value /= base62Length;
66
+ }
67
+
68
+ return result;
36
69
  }