@ai-sdk/amazon-bedrock 4.0.161 → 4.0.165

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.
@@ -35,7 +35,7 @@ var import_provider_utils = require("@ai-sdk/provider-utils");
35
35
  var import_aws4fetch = require("aws4fetch");
36
36
 
37
37
  // src/version.ts
38
- var VERSION = true ? "4.0.161" : "0.0.0-test";
38
+ var VERSION = true ? "4.0.165" : "0.0.0-test";
39
39
 
40
40
  // src/bedrock-sigv4-fetch.ts
41
41
  function createSigV4FetchFunction(getCredentials, fetch, service = "bedrock") {
@@ -23,7 +23,7 @@ import {
23
23
  import { AwsV4Signer } from "aws4fetch";
24
24
 
25
25
  // src/version.ts
26
- var VERSION = true ? "4.0.161" : "0.0.0-test";
26
+ var VERSION = true ? "4.0.165" : "0.0.0-test";
27
27
 
28
28
  // src/bedrock-sigv4-fetch.ts
29
29
  function createSigV4FetchFunction(getCredentials, fetch, service = "bedrock") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/amazon-bedrock",
3
- "version": "4.0.161",
3
+ "version": "4.0.165",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -44,10 +44,10 @@
44
44
  "@smithy/eventstream-codec": "^4.0.1",
45
45
  "@smithy/util-utf8": "^4.0.0",
46
46
  "aws4fetch": "^1.0.20",
47
- "@ai-sdk/anthropic": "3.0.113",
48
- "@ai-sdk/openai": "3.0.101",
47
+ "@ai-sdk/anthropic": "3.0.114",
48
+ "@ai-sdk/openai": "3.0.104",
49
49
  "@ai-sdk/provider": "3.0.15",
50
- "@ai-sdk/provider-utils": "4.0.48"
50
+ "@ai-sdk/provider-utils": "4.0.49"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "20.17.24",
@@ -61,6 +61,10 @@ type BedrockChatConfig = {
61
61
  generateId: () => string;
62
62
  };
63
63
 
64
+ const anthropicProviderOptions = z.object({
65
+ disableParallelToolUse: z.boolean().optional(),
66
+ });
67
+
64
68
  export class BedrockChatLanguageModel implements LanguageModelV3 {
65
69
  readonly specificationVersion = 'v3';
66
70
  readonly provider = 'amazon-bedrock';
@@ -99,6 +103,12 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
99
103
  schema: amazonBedrockLanguageModelOptions,
100
104
  })) ?? {};
101
105
 
106
+ const anthropicOptions = await parseProviderOptions({
107
+ provider: 'anthropic',
108
+ providerOptions,
109
+ schema: anthropicProviderOptions,
110
+ });
111
+
102
112
  const warnings: SharedV3Warning[] = [];
103
113
 
104
114
  if (frequencyPenalty != null) {
@@ -151,6 +161,10 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
151
161
  }
152
162
 
153
163
  const isAnthropicModel = this.modelId.includes('anthropic');
164
+ const openAIModelId = /^(?:[^.]+\.)?(openai\..+)$/.exec(this.modelId)?.[1];
165
+ const isOpenAIModel = openAIModelId != null;
166
+ const isOpenAIGptOssModel =
167
+ openAIModelId?.startsWith('openai.gpt-oss-') ?? false;
154
168
  const isThinkingEnabled =
155
169
  bedrockOptions.reasoningConfig?.type === 'enabled' ||
156
170
  bedrockOptions.reasoningConfig?.type === 'adaptive';
@@ -192,6 +206,7 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
192
206
  toolChoice:
193
207
  jsonResponseTool != null ? { type: 'required' } : toolChoice,
194
208
  modelId: this.modelId,
209
+ disableParallelToolUse: anthropicOptions?.disableParallelToolUse,
195
210
  });
196
211
 
197
212
  warnings.push(...toolWarnings);
@@ -279,7 +294,6 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
279
294
 
280
295
  const maxReasoningEffort =
281
296
  bedrockOptions.reasoningConfig?.maxReasoningEffort;
282
- const isOpenAIModel = this.modelId.startsWith('openai.');
283
297
 
284
298
  if (maxReasoningEffort != null) {
285
299
  if (isAnthropicModel) {
@@ -291,11 +305,20 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
291
305
  },
292
306
  };
293
307
  } else if (isOpenAIModel) {
294
- // OpenAI models on Bedrock expect `reasoning_effort` as a flat value
295
- bedrockOptions.additionalModelRequestFields = {
296
- ...bedrockOptions.additionalModelRequestFields,
297
- reasoning_effort: maxReasoningEffort,
298
- };
308
+ // gpt-oss models expect `reasoning_effort` as a flat value, while
309
+ // GPT-5.x models expect a nested `reasoning.effort` object.
310
+ bedrockOptions.additionalModelRequestFields = isOpenAIGptOssModel
311
+ ? {
312
+ ...bedrockOptions.additionalModelRequestFields,
313
+ reasoning_effort: maxReasoningEffort,
314
+ }
315
+ : {
316
+ ...bedrockOptions.additionalModelRequestFields,
317
+ reasoning: {
318
+ ...bedrockOptions.additionalModelRequestFields?.reasoning,
319
+ effort: maxReasoningEffort,
320
+ },
321
+ };
299
322
  } else {
300
323
  // other models (such as Nova 2) use reasoningConfig format
301
324
  bedrockOptions.additionalModelRequestFields = {
@@ -1185,6 +1208,13 @@ const BedrockRedactedReasoningSchema = z.object({
1185
1208
  data: z.string(),
1186
1209
  });
1187
1210
 
1211
+ const AmazonBedrockCacheDetailSchema = z
1212
+ .object({
1213
+ inputTokens: z.number(),
1214
+ ttl: z.string(),
1215
+ })
1216
+ .catchall(z.json());
1217
+
1188
1218
  // limited version of the schema, focused on what is needed for the implementation
1189
1219
  // this approach limits breakages when the API changes and increases efficiency
1190
1220
  const BedrockResponseSchema = z.object({
@@ -1227,16 +1257,16 @@ const BedrockResponseSchema = z.object({
1227
1257
  trace: z.unknown().nullish(),
1228
1258
  performanceConfig: z.object({ latency: z.string() }).nullish(),
1229
1259
  serviceTier: z.object({ type: z.string() }).nullish(),
1230
- usage: z.object({
1231
- inputTokens: z.number(),
1232
- outputTokens: z.number(),
1233
- totalTokens: z.number(),
1234
- cacheReadInputTokens: z.number().nullish(),
1235
- cacheWriteInputTokens: z.number().nullish(),
1236
- cacheDetails: z
1237
- .array(z.object({ inputTokens: z.number(), ttl: z.string() }))
1238
- .nullish(),
1239
- }),
1260
+ usage: z
1261
+ .object({
1262
+ inputTokens: z.number(),
1263
+ outputTokens: z.number(),
1264
+ totalTokens: z.number(),
1265
+ cacheReadInputTokens: z.number().nullish(),
1266
+ cacheWriteInputTokens: z.number().nullish(),
1267
+ cacheDetails: z.array(AmazonBedrockCacheDetailSchema).nullish(),
1268
+ })
1269
+ .catchall(z.json()),
1240
1270
  });
1241
1271
 
1242
1272
  // limited version of the schema, focussed on what is needed for the implementation
@@ -1302,12 +1332,12 @@ const BedrockStreamSchema = z.object({
1302
1332
  .object({
1303
1333
  cacheReadInputTokens: z.number().nullish(),
1304
1334
  cacheWriteInputTokens: z.number().nullish(),
1305
- cacheDetails: z
1306
- .array(z.object({ inputTokens: z.number(), ttl: z.string() }))
1307
- .nullish(),
1335
+ cacheDetails: z.array(AmazonBedrockCacheDetailSchema).nullish(),
1308
1336
  inputTokens: z.number(),
1309
1337
  outputTokens: z.number(),
1338
+ totalTokens: z.number().optional(),
1310
1339
  })
1340
+ .catchall(z.json())
1311
1341
  .nullish(),
1312
1342
  })
1313
1343
  .nullish(),
@@ -19,10 +19,12 @@ export async function prepareTools({
19
19
  tools,
20
20
  toolChoice,
21
21
  modelId,
22
+ disableParallelToolUse,
22
23
  }: {
23
24
  tools: LanguageModelV3CallOptions['tools'];
24
25
  toolChoice?: LanguageModelV3CallOptions['toolChoice'];
25
26
  modelId: string;
27
+ disableParallelToolUse?: boolean;
26
28
  }): Promise<{
27
29
  toolConfig: BedrockToolConfiguration;
28
30
  additionalTools: Record<string, unknown> | undefined;
@@ -85,6 +87,7 @@ export async function prepareTools({
85
87
  } = await prepareAnthropicTools({
86
88
  tools: ProviderTools,
87
89
  toolChoice,
90
+ disableParallelToolUse,
88
91
  supportsStructuredOutput: false,
89
92
  supportsStrictTools: false,
90
93
  });
@@ -161,9 +164,35 @@ export async function prepareTools({
161
164
  });
162
165
  }
163
166
 
167
+ if (
168
+ isAnthropicModel &&
169
+ !usingAnthropicTools &&
170
+ disableParallelToolUse &&
171
+ bedrockTools.length > 0 &&
172
+ toolChoice?.type !== 'none'
173
+ ) {
174
+ additionalTools = {
175
+ tool_choice:
176
+ toolChoice?.type === 'required'
177
+ ? { type: 'any', disable_parallel_tool_use: true }
178
+ : toolChoice?.type === 'tool'
179
+ ? {
180
+ type: 'tool',
181
+ name: toolChoice.toolName,
182
+ disable_parallel_tool_use: true,
183
+ }
184
+ : { type: 'auto', disable_parallel_tool_use: true },
185
+ };
186
+ }
187
+
164
188
  // Handle toolChoice for standard Bedrock tools, but NOT for Anthropic provider-defined tools
165
189
  let bedrockToolChoice: BedrockToolConfiguration['toolChoice'] = undefined;
166
- if (!usingAnthropicTools && bedrockTools.length > 0 && toolChoice) {
190
+ if (
191
+ !usingAnthropicTools &&
192
+ additionalTools?.tool_choice == null &&
193
+ bedrockTools.length > 0 &&
194
+ toolChoice
195
+ ) {
167
196
  const type = toolChoice.type;
168
197
  switch (type) {
169
198
  case 'auto':
@@ -1,11 +1,17 @@
1
- import type { LanguageModelV3Usage } from '@ai-sdk/provider';
1
+ import type { JSONValue, LanguageModelV3Usage } from '@ai-sdk/provider';
2
2
 
3
3
  export type BedrockUsage = {
4
+ [key: string]: JSONValue | undefined;
4
5
  inputTokens: number;
5
6
  outputTokens: number;
6
7
  totalTokens?: number;
7
8
  cacheReadInputTokens?: number | null;
8
9
  cacheWriteInputTokens?: number | null;
10
+ cacheDetails?: Array<{
11
+ [key: string]: JSONValue | undefined;
12
+ inputTokens: number;
13
+ ttl: string;
14
+ }> | null;
9
15
  };
10
16
 
11
17
  export function convertBedrockUsage(
@@ -334,7 +334,12 @@ export async function convertToBedrockChatMessages(
334
334
  pushCachePoint(bedrockContent, providerOptions);
335
335
  }
336
336
 
337
- messages.push({ role: 'user', content: bedrockContent });
337
+ const previousMessage = messages.at(-1);
338
+ if (previousMessage?.role === 'user') {
339
+ previousMessage.content.push(...bedrockContent);
340
+ } else {
341
+ messages.push({ role: 'user', content: bedrockContent });
342
+ }
338
343
 
339
344
  break;
340
345
  }
@@ -347,8 +352,56 @@ export async function convertToBedrockChatMessages(
347
352
  const message = block.messages[j];
348
353
  const isLastMessage = j === block.messages.length - 1;
349
354
  const { content } = message;
350
- const hasReasoningBlocks = content.some(
351
- part => part.type === 'reasoning',
355
+ const convertedReasoningContent: Array<
356
+ BedrockAssistantMessage['content'][number] | undefined
357
+ > = await Promise.all(
358
+ content.map(async part => {
359
+ if (part.type !== 'reasoning') {
360
+ return undefined;
361
+ }
362
+
363
+ const metadata = await parseProviderOptions({
364
+ provider: 'bedrock',
365
+ providerOptions: part.providerOptions,
366
+ schema: bedrockReasoningMetadataSchema,
367
+ });
368
+
369
+ if (metadata?.signature != null) {
370
+ return {
371
+ reasoningContent: {
372
+ reasoningText: {
373
+ // do not trim reasoning text when a signature is present:
374
+ // the signature validates the exact original bytes
375
+ text: part.text,
376
+ signature: metadata.signature,
377
+ },
378
+ },
379
+ };
380
+ }
381
+
382
+ if (metadata?.redactedContent != null) {
383
+ return {
384
+ reasoningContent: {
385
+ redactedContent: metadata.redactedContent,
386
+ },
387
+ };
388
+ }
389
+
390
+ if (metadata?.redactedData != null) {
391
+ return {
392
+ reasoningContent: {
393
+ redactedReasoning: {
394
+ data: metadata.redactedData,
395
+ },
396
+ },
397
+ };
398
+ }
399
+
400
+ return undefined;
401
+ }),
402
+ );
403
+ const hasReplayableReasoningBlocks = convertedReasoningContent.some(
404
+ part => part != null,
352
405
  );
353
406
 
354
407
  for (let k = 0; k < content.length; k++) {
@@ -357,8 +410,9 @@ export async function convertToBedrockChatMessages(
357
410
 
358
411
  switch (part.type) {
359
412
  case 'text': {
360
- // Skip empty text blocks unless reasoning blocks are present
361
- if (!part.text.trim() && !hasReasoningBlocks) {
413
+ // Skip empty text blocks unless replayable reasoning blocks are
414
+ // present and the original block order must be preserved.
415
+ if (!part.text.trim() && !hasReplayableReasoningBlocks) {
362
416
  break;
363
417
  }
364
418
 
@@ -378,37 +432,9 @@ export async function convertToBedrockChatMessages(
378
432
  }
379
433
 
380
434
  case 'reasoning': {
381
- const reasoningMetadata = await parseProviderOptions({
382
- provider: 'bedrock',
383
- providerOptions: part.providerOptions,
384
- schema: bedrockReasoningMetadataSchema,
385
- });
386
-
387
- if (reasoningMetadata?.signature != null) {
388
- // do not trim reasoning text when a signature is present:
389
- // the signature validates the exact original bytes
390
- bedrockContent.push({
391
- reasoningContent: {
392
- reasoningText: {
393
- text: part.text,
394
- signature: reasoningMetadata.signature,
395
- },
396
- },
397
- });
398
- } else if (reasoningMetadata?.redactedContent != null) {
399
- bedrockContent.push({
400
- reasoningContent: {
401
- redactedContent: reasoningMetadata.redactedContent,
402
- },
403
- });
404
- } else if (reasoningMetadata?.redactedData != null) {
405
- bedrockContent.push({
406
- reasoningContent: {
407
- redactedReasoning: {
408
- data: reasoningMetadata.redactedData,
409
- },
410
- },
411
- });
435
+ const convertedPart = convertedReasoningContent[k];
436
+ if (convertedPart != null) {
437
+ bedrockContent.push(convertedPart);
412
438
  }
413
439
  // Unsigned reasoning is intentionally not replayed. Some
414
440
  // Bedrock models (for example OpenAI gpt-oss) return reasoning
@@ -434,7 +460,9 @@ export async function convertToBedrockChatMessages(
434
460
  pushCachePoint(bedrockContent, message.providerOptions);
435
461
  }
436
462
 
437
- messages.push({ role: 'assistant', content: bedrockContent });
463
+ if (bedrockContent.length > 0) {
464
+ messages.push({ role: 'assistant', content: bedrockContent });
465
+ }
438
466
 
439
467
  break;
440
468
  }