@ai-sdk/amazon-bedrock 5.0.0-beta.3 → 5.0.0-beta.30

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.
@@ -514,6 +514,37 @@ console.log(amazonResult.text); // text response
514
514
  See [AI SDK UI: Chatbot](/docs/ai-sdk-ui/chatbot#reasoning) for more details
515
515
  on how to integrate reasoning into your chatbot.
516
516
 
517
+ ## Service Tiers
518
+
519
+ Amazon Bedrock supports selecting an inference service tier per request via the `serviceTier` provider option.
520
+
521
+ ```ts
522
+ import {
523
+ bedrock,
524
+ type AmazonBedrockLanguageModelOptions,
525
+ } from '@ai-sdk/amazon-bedrock';
526
+ import { generateText } from 'ai';
527
+
528
+ const result = await generateText({
529
+ model: bedrock('us.anthropic.claude-sonnet-4-20250514-v1:0'),
530
+ prompt: 'Summarize this support ticket backlog.',
531
+ providerOptions: {
532
+ bedrock: {
533
+ serviceTier: 'priority',
534
+ } satisfies AmazonBedrockLanguageModelOptions,
535
+ },
536
+ });
537
+ ```
538
+
539
+ Supported values are:
540
+
541
+ - `reserved`
542
+ - `priority`
543
+ - `default`
544
+ - `flex`
545
+
546
+ See the [Amazon Bedrock service tiers documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html) for model availability and behavior.
547
+
517
548
  ## Extended Context Window
518
549
 
519
550
  Claude Sonnet 4 models on Amazon Bedrock support an extended context window of up to 1 million tokens when using the `context-1m-2025-08-07` beta feature.
@@ -546,6 +577,11 @@ Via Anthropic, Amazon Bedrock provides three provider-defined tools that can be
546
577
 
547
578
  They are available via the `tools` property of the provider instance.
548
579
 
580
+ <Note>
581
+ Amazon Bedrock does not support strict mode on tool definitions. Setting
582
+ `strict: true` on a tool will be ignored and a warning will be emitted.
583
+ </Note>
584
+
549
585
  ### Bash Tool
550
586
 
551
587
  The Bash Tool allows running bash commands. Here's how to create and use it:
@@ -1375,6 +1411,16 @@ const { text } = await generateText({
1375
1411
  });
1376
1412
  ```
1377
1413
 
1414
+ ### Provider Options
1415
+
1416
+ The following optional provider options are available for Bedrock Anthropic models:
1417
+
1418
+ - `metadata` _object_
1419
+
1420
+ Optional. Metadata to include with the request. See the [Anthropic API documentation](https://platform.claude.com/docs/en/api/messages/create) for details.
1421
+
1422
+ - `userId` _string_ - An external identifier for the end-user.
1423
+
1378
1424
  ### Cache Control
1379
1425
 
1380
1426
  In the messages and message parts, you can use the `providerOptions` property to set cache control breakpoints.
@@ -1428,7 +1474,7 @@ They are available via the `tools` property of the provider instance.
1428
1474
 
1429
1475
  ```ts
1430
1476
  import { bedrockAnthropic } from '@ai-sdk/amazon-bedrock/anthropic';
1431
- import { generateText, stepCountIs } from 'ai';
1477
+ import { generateText, isStepCount } from 'ai';
1432
1478
 
1433
1479
  const result = await generateText({
1434
1480
  model: bedrockAnthropic('us.anthropic.claude-sonnet-4-5-20250929-v1:0'),
@@ -1441,7 +1487,7 @@ const result = await generateText({
1441
1487
  }),
1442
1488
  },
1443
1489
  prompt: 'List the files in my directory.',
1444
- stopWhen: stepCountIs(2),
1490
+ stopWhen: isStepCount(2),
1445
1491
  });
1446
1492
  ```
1447
1493
 
@@ -1449,7 +1495,7 @@ const result = await generateText({
1449
1495
 
1450
1496
  ```ts
1451
1497
  import { bedrockAnthropic } from '@ai-sdk/amazon-bedrock/anthropic';
1452
- import { generateText, stepCountIs } from 'ai';
1498
+ import { generateText, isStepCount } from 'ai';
1453
1499
 
1454
1500
  const result = await generateText({
1455
1501
  model: bedrockAnthropic('us.anthropic.claude-sonnet-4-5-20250929-v1:0'),
@@ -1462,7 +1508,7 @@ const result = await generateText({
1462
1508
  }),
1463
1509
  },
1464
1510
  prompt: 'Update my README file.',
1465
- stopWhen: stepCountIs(5),
1511
+ stopWhen: isStepCount(5),
1466
1512
  });
1467
1513
  ```
1468
1514
 
@@ -1470,7 +1516,7 @@ const result = await generateText({
1470
1516
 
1471
1517
  ```ts
1472
1518
  import { bedrockAnthropic } from '@ai-sdk/amazon-bedrock/anthropic';
1473
- import { generateText, stepCountIs } from 'ai';
1519
+ import { generateText, isStepCount } from 'ai';
1474
1520
  import fs from 'fs';
1475
1521
 
1476
1522
  const result = await generateText({
@@ -1505,7 +1551,7 @@ const result = await generateText({
1505
1551
  }),
1506
1552
  },
1507
1553
  prompt: 'Take a screenshot.',
1508
- stopWhen: stepCountIs(3),
1554
+ stopWhen: isStepCount(3),
1509
1555
  });
1510
1556
  ```
1511
1557
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-sdk/amazon-bedrock",
3
- "version": "5.0.0-beta.3",
3
+ "version": "5.0.0-beta.30",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -38,9 +38,9 @@
38
38
  "@smithy/eventstream-codec": "^4.0.1",
39
39
  "@smithy/util-utf8": "^4.0.0",
40
40
  "aws4fetch": "^1.0.20",
41
- "@ai-sdk/anthropic": "4.0.0-beta.3",
42
- "@ai-sdk/provider": "4.0.0-beta.0",
43
- "@ai-sdk/provider-utils": "5.0.0-beta.1"
41
+ "@ai-sdk/anthropic": "4.0.0-beta.26",
42
+ "@ai-sdk/provider": "4.0.0-beta.10",
43
+ "@ai-sdk/provider-utils": "5.0.0-beta.18"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "20.17.24",
@@ -74,9 +74,7 @@
74
74
  "build": "pnpm clean && tsup --tsconfig tsconfig.build.json",
75
75
  "build:watch": "pnpm clean && tsup --watch",
76
76
  "clean": "del-cli dist docs *.tsbuildinfo",
77
- "lint": "eslint \"./**/*.ts*\"",
78
77
  "type-check": "tsc --build",
79
- "prettier-check": "prettier --check \"./**/*.ts*\"",
80
78
  "test": "pnpm test:node && pnpm test:edge",
81
79
  "test:update": "pnpm test:node -u",
82
80
  "test:watch": "vitest --config vitest.node.config.js",
@@ -3,14 +3,40 @@ import {
3
3
  FetchFunction,
4
4
  safeParseJSON,
5
5
  } from '@ai-sdk/provider-utils';
6
+ import { z } from 'zod/v4';
6
7
  import { createBedrockEventStreamDecoder } from '../bedrock-event-stream-decoder';
7
8
 
9
+ const bedrockErrorSchema = z.looseObject({
10
+ message: z.string().optional(),
11
+ });
12
+
8
13
  export function createBedrockAnthropicFetch(
9
14
  baseFetch: FetchFunction,
10
15
  ): FetchFunction {
11
16
  return async (url, options) => {
12
17
  const response = await baseFetch(url, options);
13
18
 
19
+ // Transform Bedrock error responses into Anthropic error format
20
+ // so that anthropicFailedResponseHandler can extract the message.
21
+ if (!response.ok) {
22
+ const text = await response.text();
23
+ const parsed = await safeParseJSON({ text, schema: bedrockErrorSchema });
24
+
25
+ const message =
26
+ parsed.success && parsed.value.message ? parsed.value.message : text;
27
+
28
+ const anthropicError = JSON.stringify({
29
+ type: 'error',
30
+ error: { type: 'error', message },
31
+ });
32
+
33
+ return new Response(anthropicError, {
34
+ status: response.status,
35
+ statusText: response.statusText,
36
+ headers: response.headers,
37
+ });
38
+ }
39
+
14
40
  const contentType = response.headers.get('content-type');
15
41
  if (
16
42
  contentType?.includes('application/vnd.amazon.eventstream') &&
@@ -47,6 +47,10 @@ const BEDROCK_TOOL_BETA_MAP: Record<string, string> = {
47
47
  text_editor_20250728: 'computer-use-2025-01-24',
48
48
  computer_20250124: 'computer-use-2025-01-24',
49
49
  computer_20241022: 'computer-use-2024-10-22',
50
+ tool_search_tool_regex_20251119: 'tool-search-tool-2025-10-19',
51
+ // BM25 is not currently supported on Bedrock, but including the beta flag
52
+ // so that Bedrock returns a more useful error message if it's used.
53
+ tool_search_tool_bm25_20251119: 'tool-search-tool-2025-10-19',
50
54
  };
51
55
 
52
56
  export interface BedrockAnthropicProvider extends ProviderV4 {
@@ -257,7 +261,13 @@ export function createBedrockAnthropic(
257
261
  }`,
258
262
 
259
263
  transformRequestBody: (args, betas) => {
260
- const { model, stream, tool_choice, tools, ...rest } = args;
264
+ const {
265
+ model: _model,
266
+ stream: _stream,
267
+ tool_choice,
268
+ tools,
269
+ ...rest
270
+ } = args;
261
271
 
262
272
  const transformedToolChoice =
263
273
  tool_choice != null
@@ -13,6 +13,9 @@ export interface BedrockConverseInput {
13
13
  };
14
14
  additionalModelRequestFields?: Record<string, unknown>;
15
15
  additionalModelResponseFieldPaths?: string[];
16
+ serviceTier?: {
17
+ type: string;
18
+ };
16
19
  guardrailConfig?:
17
20
  | BedrockGuardrailConfiguration
18
21
  | BedrockGuardrailStreamConfiguration
@@ -19,10 +19,14 @@ import {
19
19
  combineHeaders,
20
20
  createJsonErrorResponseHandler,
21
21
  createJsonResponseHandler,
22
+ isCustomReasoning,
23
+ mapReasoningToProviderBudget,
24
+ mapReasoningToProviderEffort,
22
25
  parseProviderOptions,
23
26
  postJsonToApi,
24
27
  resolve,
25
28
  } from '@ai-sdk/provider-utils';
29
+ import { getModelCapabilities } from '@ai-sdk/anthropic/internal';
26
30
  import { z } from 'zod/v4';
27
31
  import {
28
32
  BEDROCK_STOP_REASONS,
@@ -30,6 +34,7 @@ import {
30
34
  BedrockStopReason,
31
35
  } from './bedrock-api-types';
32
36
  import {
37
+ AmazonBedrockLanguageModelOptions,
33
38
  BedrockChatModelId,
34
39
  amazonBedrockLanguageModelOptions,
35
40
  } from './bedrock-chat-options';
@@ -70,6 +75,7 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
70
75
  seed,
71
76
  tools,
72
77
  toolChoice,
78
+ reasoning,
73
79
  providerOptions,
74
80
  }: LanguageModelV4CallOptions): Promise<{
75
81
  command: BedrockConverseInput;
@@ -78,7 +84,7 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
78
84
  betas: Set<string>;
79
85
  }> {
80
86
  // Parse provider options
81
- const bedrockOptions =
87
+ let bedrockOptions =
82
88
  (await parseProviderOptions({
83
89
  provider: 'bedrock',
84
90
  providerOptions,
@@ -137,13 +143,26 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
137
143
  }
138
144
 
139
145
  const isAnthropicModel = this.modelId.includes('anthropic');
146
+ const isOpenAIModel = this.modelId.startsWith('openai.');
147
+
148
+ bedrockOptions = resolveBedrockReasoningConfig({
149
+ reasoning,
150
+ bedrockOptions,
151
+ warnings,
152
+ isAnthropicModel,
153
+ modelId: this.modelId,
154
+ });
155
+
140
156
  const isThinkingEnabled =
141
157
  bedrockOptions.reasoningConfig?.type === 'enabled' ||
142
158
  bedrockOptions.reasoningConfig?.type === 'adaptive';
143
159
 
160
+ const { supportsStructuredOutput: modelSupportsStructuredOutput } =
161
+ getModelCapabilities(this.modelId);
162
+
144
163
  const useNativeStructuredOutput =
145
164
  isAnthropicModel &&
146
- isThinkingEnabled &&
165
+ (modelSupportsStructuredOutput || isThinkingEnabled) &&
147
166
  responseFormat?.type === 'json' &&
148
167
  responseFormat.schema != null;
149
168
 
@@ -247,7 +266,6 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
247
266
 
248
267
  const maxReasoningEffort =
249
268
  bedrockOptions.reasoningConfig?.maxReasoningEffort;
250
- const isOpenAIModel = this.modelId.startsWith('openai.');
251
269
 
252
270
  if (maxReasoningEffort != null) {
253
271
  if (isAnthropicModel) {
@@ -368,6 +386,7 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
368
386
  const {
369
387
  reasoningConfig: _,
370
388
  additionalModelRequestFields: __,
389
+ serviceTier: ___,
371
390
  ...filteredBedrockOptions
372
391
  } = providerOptions?.bedrock || {};
373
392
 
@@ -387,6 +406,11 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
387
406
  ...(Object.keys(inferenceConfig).length > 0 && {
388
407
  inferenceConfig,
389
408
  }),
409
+ ...(bedrockOptions.serviceTier != null && {
410
+ serviceTier: {
411
+ type: bedrockOptions.serviceTier,
412
+ },
413
+ }),
390
414
  ...filteredBedrockOptions,
391
415
  ...(toolConfig.tools !== undefined && toolConfig.tools.length > 0
392
416
  ? { toolConfig }
@@ -441,7 +465,7 @@ export class BedrockChatLanguageModel implements LanguageModelV4 {
441
465
  // map response content to content array
442
466
  for (const part of response.output.message.content) {
443
467
  // text
444
- if (part.text) {
468
+ if (part.text != null) {
445
469
  content.push({ type: 'text', text: part.text });
446
470
  }
447
471
 
@@ -1122,3 +1146,73 @@ export const bedrockReasoningMetadataSchema = z.object({
1122
1146
  export type BedrockReasoningMetadata = z.infer<
1123
1147
  typeof bedrockReasoningMetadataSchema
1124
1148
  >;
1149
+
1150
+ const bedrockReasoningEffortMap: Partial<
1151
+ Record<string, 'low' | 'medium' | 'high' | 'max'>
1152
+ > = {
1153
+ minimal: 'low',
1154
+ low: 'low',
1155
+ medium: 'medium',
1156
+ high: 'high',
1157
+ xhigh: 'max',
1158
+ };
1159
+
1160
+ function resolveBedrockReasoningConfig({
1161
+ reasoning,
1162
+ bedrockOptions,
1163
+ warnings,
1164
+ isAnthropicModel,
1165
+ modelId,
1166
+ }: {
1167
+ reasoning: LanguageModelV4CallOptions['reasoning'];
1168
+ bedrockOptions: AmazonBedrockLanguageModelOptions;
1169
+ warnings: SharedV4Warning[];
1170
+ isAnthropicModel: boolean;
1171
+ modelId: string;
1172
+ }): AmazonBedrockLanguageModelOptions {
1173
+ if (!isCustomReasoning(reasoning) || bedrockOptions.reasoningConfig != null) {
1174
+ return bedrockOptions;
1175
+ }
1176
+
1177
+ const result = { ...bedrockOptions };
1178
+
1179
+ if (isAnthropicModel) {
1180
+ const capabilities = getModelCapabilities(modelId);
1181
+
1182
+ if (reasoning === 'none') {
1183
+ result.reasoningConfig = { type: 'disabled' };
1184
+ } else if (capabilities.supportsAdaptiveThinking) {
1185
+ const effort = mapReasoningToProviderEffort({
1186
+ reasoning,
1187
+ effortMap: bedrockReasoningEffortMap,
1188
+ warnings,
1189
+ });
1190
+ result.reasoningConfig = {
1191
+ type: 'adaptive',
1192
+ maxReasoningEffort: effort,
1193
+ };
1194
+ } else {
1195
+ const budgetTokens = mapReasoningToProviderBudget({
1196
+ reasoning,
1197
+ maxOutputTokens: capabilities.maxOutputTokens,
1198
+ maxReasoningBudget: capabilities.maxOutputTokens,
1199
+ warnings,
1200
+ });
1201
+ if (budgetTokens != null) {
1202
+ result.reasoningConfig = {
1203
+ type: 'enabled',
1204
+ budgetTokens,
1205
+ };
1206
+ }
1207
+ }
1208
+ } else if (reasoning !== 'none') {
1209
+ const effort = mapReasoningToProviderEffort({
1210
+ reasoning,
1211
+ effortMap: bedrockReasoningEffortMap,
1212
+ warnings,
1213
+ });
1214
+ result.reasoningConfig = { maxReasoningEffort: effort };
1215
+ }
1216
+
1217
+ return result;
1218
+ }
@@ -122,6 +122,16 @@ export const amazonBedrockLanguageModelOptions = z.object({
122
122
  * Anthropic beta features to enable
123
123
  */
124
124
  anthropicBeta: z.array(z.string()).optional(),
125
+ /**
126
+ * Service tier for the request.
127
+ * @see https://docs.aws.amazon.com/bedrock/latest/userguide/service-tiers-inference.html
128
+ *
129
+ * - 'reserved': Uses provisioned throughput capacity
130
+ * - 'priority': Prioritizes low-latency inference when capacity is available
131
+ * - 'default': Standard on-demand tier
132
+ * - 'flex': Lower-cost tier for flexible latency workloads
133
+ */
134
+ serviceTier: z.enum(['reserved', 'priority', 'default', 'flex']).optional(),
125
135
  });
126
136
 
127
137
  export type AmazonBedrockLanguageModelOptions = z.infer<
@@ -82,6 +82,7 @@ export async function prepareTools({
82
82
  tools: ProviderTools,
83
83
  toolChoice,
84
84
  supportsStructuredOutput: false,
85
+ supportsStrictTools: false,
85
86
  });
86
87
 
87
88
  toolWarnings.push(...anthropicToolWarnings);
@@ -7,6 +7,7 @@ import {
7
7
  } from '@ai-sdk/provider';
8
8
  import {
9
9
  convertToBase64,
10
+ isProviderReference,
10
11
  parseProviderOptions,
11
12
  stripFileExtension,
12
13
  } from '@ai-sdk/provider-utils';
@@ -112,6 +113,12 @@ export async function convertToBedrockChatMessages(
112
113
  }
113
114
 
114
115
  case 'file': {
116
+ if (isProviderReference(part.data)) {
117
+ throw new UnsupportedFunctionalityError({
118
+ functionality: 'file parts with provider references',
119
+ });
120
+ }
121
+
115
122
  if (part.data instanceof URL) {
116
123
  // The AI SDK automatically downloads files for user file parts with URLs
117
124
  throw new UnsupportedFunctionalityError({
@@ -253,6 +260,9 @@ export async function convertToBedrockChatMessages(
253
260
  const message = block.messages[j];
254
261
  const isLastMessage = j === block.messages.length - 1;
255
262
  const { content } = message;
263
+ const hasReasoningBlocks = content.some(
264
+ part => part.type === 'reasoning',
265
+ );
256
266
 
257
267
  for (let k = 0; k < content.length; k++) {
258
268
  const part = content[k];
@@ -260,8 +270,8 @@ export async function convertToBedrockChatMessages(
260
270
 
261
271
  switch (part.type) {
262
272
  case 'text': {
263
- // Skip empty text blocks
264
- if (!part.text.trim()) {
273
+ // Skip empty text blocks unless reasoning blocks are present
274
+ if (!part.text.trim() && !hasReasoningBlocks) {
265
275
  break;
266
276
  }
267
277
 
@@ -287,33 +297,41 @@ export async function convertToBedrockChatMessages(
287
297
  schema: bedrockReasoningMetadataSchema,
288
298
  });
289
299
 
290
- if (reasoningMetadata != null) {
291
- if (reasoningMetadata.signature != null) {
292
- bedrockContent.push({
293
- reasoningContent: {
294
- reasoningText: {
295
- // trim the last text part if it's the last message in the block
296
- // because Bedrock does not allow trailing whitespace
297
- // in pre-filled assistant responses
298
- text: trimIfLast(
299
- isLastBlock,
300
- isLastMessage,
301
- isLastContentPart,
302
- part.text,
303
- ),
304
- signature: reasoningMetadata.signature,
305
- },
300
+ if (reasoningMetadata?.signature != null) {
301
+ // do not trim reasoning text when a signature is present:
302
+ // the signature validates the exact original bytes
303
+ bedrockContent.push({
304
+ reasoningContent: {
305
+ reasoningText: {
306
+ text: part.text,
307
+ signature: reasoningMetadata.signature,
306
308
  },
307
- });
308
- } else if (reasoningMetadata.redactedData != null) {
309
- bedrockContent.push({
310
- reasoningContent: {
311
- redactedReasoning: {
312
- data: reasoningMetadata.redactedData,
313
- },
309
+ },
310
+ });
311
+ } else if (reasoningMetadata?.redactedData != null) {
312
+ bedrockContent.push({
313
+ reasoningContent: {
314
+ redactedReasoning: {
315
+ data: reasoningMetadata.redactedData,
314
316
  },
315
- });
316
- }
317
+ },
318
+ });
319
+ } else {
320
+ // trim the last text part if it's the last message in the block
321
+ // because Bedrock does not allow trailing whitespace
322
+ // in pre-filled assistant responses
323
+ bedrockContent.push({
324
+ reasoningContent: {
325
+ reasoningText: {
326
+ text: trimIfLast(
327
+ isLastBlock,
328
+ isLastMessage,
329
+ isLastContentPart,
330
+ part.text,
331
+ ),
332
+ },
333
+ },
334
+ });
317
335
  }
318
336
 
319
337
  break;
@@ -352,12 +370,6 @@ export async function convertToBedrockChatMessages(
352
370
  return { system, messages };
353
371
  }
354
372
 
355
- function isBedrockImageFormat(format: string): format is BedrockImageFormat {
356
- return Object.values(BEDROCK_IMAGE_MIME_TYPES).includes(
357
- format as BedrockImageFormat,
358
- );
359
- }
360
-
361
373
  function getBedrockImageFormat(mimeType?: string): BedrockImageFormat {
362
374
  if (!mimeType) {
363
375
  throw new UnsupportedFunctionalityError({