@ai-sdk/amazon-bedrock 4.0.134 → 4.0.135

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.134" : "0.0.0-test";
38
+ var VERSION = true ? "4.0.135" : "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.134" : "0.0.0-test";
26
+ var VERSION = true ? "4.0.135" : "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.134",
3
+ "version": "4.0.135",
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.96",
48
- "@ai-sdk/openai": "3.0.84",
47
+ "@ai-sdk/anthropic": "3.0.97",
48
+ "@ai-sdk/openai": "3.0.85",
49
49
  "@ai-sdk/provider": "3.0.14",
50
- "@ai-sdk/provider-utils": "4.0.38"
50
+ "@ai-sdk/provider-utils": "4.0.39"
51
51
  },
52
52
  "devDependencies": {
53
53
  "@types/node": "20.17.24",
@@ -16,6 +16,7 @@ import {
16
16
  combineHeaders,
17
17
  createJsonErrorResponseHandler,
18
18
  createJsonResponseHandler,
19
+ injectJsonInstructionIntoMessages,
19
20
  parseProviderOptions,
20
21
  postJsonToApi,
21
22
  resolve,
@@ -78,6 +79,7 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
78
79
  }: LanguageModelV3CallOptions): Promise<{
79
80
  command: BedrockConverseInput;
80
81
  warnings: SharedV3Warning[];
82
+ usesJsonInstruction: boolean;
81
83
  usesJsonResponseTool: boolean;
82
84
  betas: Set<string>;
83
85
  }> {
@@ -145,16 +147,36 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
145
147
  bedrockOptions.reasoningConfig?.type === 'enabled' ||
146
148
  bedrockOptions.reasoningConfig?.type === 'adaptive';
147
149
 
150
+ const modelRejectsNativeStructuredOutput =
151
+ this.modelId.includes('claude-opus-4-7') ||
152
+ this.modelId.includes('claude-opus-4-8') ||
153
+ this.modelId.includes('claude-fable-5') ||
154
+ this.modelId.includes('claude-sonnet-5');
155
+
156
+ const modelSupportsStructuredOutput =
157
+ bedrockChatModelSupportsStructuredOutput(this.modelId);
158
+
148
159
  const useNativeStructuredOutput =
149
160
  isAnthropicModel &&
150
- isThinkingEnabled &&
161
+ !modelRejectsNativeStructuredOutput &&
162
+ (modelSupportsStructuredOutput || isThinkingEnabled) &&
151
163
  responseFormat?.type === 'json' &&
152
164
  responseFormat.schema != null;
153
165
 
166
+ const useJsonInstructionForStructuredOutput =
167
+ isAnthropicModel &&
168
+ (this.modelId.includes('claude-opus-4-7') ||
169
+ this.modelId.includes('claude-opus-4-8')) &&
170
+ responseFormat?.type === 'json' &&
171
+ responseFormat.schema != null &&
172
+ tools != null &&
173
+ tools.length > 0;
174
+
154
175
  const jsonResponseTool: LanguageModelV3FunctionTool | undefined =
155
176
  responseFormat?.type === 'json' &&
156
177
  responseFormat.schema != null &&
157
- !useNativeStructuredOutput
178
+ !useNativeStructuredOutput &&
179
+ !useJsonInstructionForStructuredOutput
158
180
  ? {
159
181
  type: 'function',
160
182
  name: 'json',
@@ -367,6 +389,15 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
367
389
  }
368
390
  }
369
391
 
392
+ if (useJsonInstructionForStructuredOutput) {
393
+ filteredPrompt = injectJsonInstructionIntoMessages({
394
+ messages: filteredPrompt,
395
+ schema: responseFormat!.schema,
396
+ schemaSuffix:
397
+ 'You MUST answer with only a JSON object that matches the JSON schema above. Do not wrap it in markdown fences or include any other text.',
398
+ });
399
+ }
400
+
370
401
  const isMistral = isMistralModel(this.modelId);
371
402
  const { system, messages } = await convertToBedrockChatMessages(
372
403
  filteredPrompt,
@@ -408,6 +439,7 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
408
439
  : {}),
409
440
  },
410
441
  warnings,
442
+ usesJsonInstruction: useJsonInstructionForStructuredOutput,
411
443
  usesJsonResponseTool: jsonResponseTool != null,
412
444
  betas,
413
445
  };
@@ -431,6 +463,7 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
431
463
  const {
432
464
  command: args,
433
465
  warnings,
466
+ usesJsonInstruction,
434
467
  usesJsonResponseTool,
435
468
  } = await this.getArgs(options);
436
469
 
@@ -452,12 +485,18 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
452
485
 
453
486
  const content: Array<LanguageModelV3Content> = [];
454
487
  let isJsonResponseFromTool = false;
488
+ const jsonObjectTextExtractor = usesJsonInstruction
489
+ ? new JsonObjectTextExtractor()
490
+ : undefined;
455
491
 
456
492
  // map response content to content array
457
493
  for (const part of response.output.message.content) {
458
494
  // text
459
495
  if (part.text != null) {
460
- content.push({ type: 'text', text: part.text });
496
+ content.push({
497
+ type: 'text',
498
+ text: jsonObjectTextExtractor?.process(part.text) ?? part.text,
499
+ });
461
500
  }
462
501
 
463
502
  // reasoning
@@ -586,6 +625,7 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
586
625
  const {
587
626
  command: args,
588
627
  warnings,
628
+ usesJsonInstruction,
589
629
  usesJsonResponseTool,
590
630
  } = await this.getArgs(options);
591
631
  const modelId = this.modelId;
@@ -614,6 +654,9 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
614
654
  let providerMetadata: SharedV3ProviderMetadata | undefined = undefined;
615
655
  let isJsonResponseFromTool = false;
616
656
  let stopSequence: string | null = null;
657
+ const jsonObjectTextExtractor = usesJsonInstruction
658
+ ? new JsonObjectTextExtractor()
659
+ : undefined;
617
660
 
618
661
  const contentBlocks: Record<
619
662
  number,
@@ -773,11 +816,18 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
773
816
  });
774
817
  }
775
818
 
776
- controller.enqueue({
777
- type: 'text-delta',
778
- id: String(blockIndex),
779
- delta: value.contentBlockDelta.delta.text,
780
- });
819
+ const textDelta =
820
+ jsonObjectTextExtractor?.process(
821
+ value.contentBlockDelta.delta.text,
822
+ ) ?? value.contentBlockDelta.delta.text;
823
+
824
+ if (textDelta.length > 0) {
825
+ controller.enqueue({
826
+ type: 'text-delta',
827
+ id: String(blockIndex),
828
+ delta: textDelta,
829
+ });
830
+ }
781
831
  }
782
832
 
783
833
  if (value.contentBlockStop?.contentBlockIndex != null) {
@@ -995,6 +1045,83 @@ export class BedrockChatLanguageModel implements LanguageModelV3 {
995
1045
  }
996
1046
  }
997
1047
 
1048
+ function bedrockChatModelSupportsStructuredOutput(modelId: string): boolean {
1049
+ return (
1050
+ modelId.includes('claude-opus-4-8') ||
1051
+ modelId.includes('claude-opus-4-7') ||
1052
+ modelId.includes('claude-fable-5') ||
1053
+ modelId.includes('claude-sonnet-5') ||
1054
+ modelId.includes('claude-sonnet-4-6') ||
1055
+ modelId.includes('claude-opus-4-6') ||
1056
+ modelId.includes('claude-sonnet-4-5') ||
1057
+ modelId.includes('claude-opus-4-5') ||
1058
+ modelId.includes('claude-haiku-4-5') ||
1059
+ modelId.includes('claude-opus-4-1')
1060
+ );
1061
+ }
1062
+
1063
+ class JsonObjectTextExtractor {
1064
+ private started = false;
1065
+ private completed = false;
1066
+ private depth = 0;
1067
+ private inString = false;
1068
+ private escaped = false;
1069
+
1070
+ process(text: string): string {
1071
+ let result = '';
1072
+
1073
+ for (const character of text) {
1074
+ if (this.completed) {
1075
+ break;
1076
+ }
1077
+
1078
+ if (!this.started) {
1079
+ if (character !== '{') {
1080
+ continue;
1081
+ }
1082
+
1083
+ this.started = true;
1084
+ this.depth = 1;
1085
+ result += character;
1086
+ continue;
1087
+ }
1088
+
1089
+ result += character;
1090
+
1091
+ if (this.escaped) {
1092
+ this.escaped = false;
1093
+ continue;
1094
+ }
1095
+
1096
+ if (character === '\\' && this.inString) {
1097
+ this.escaped = true;
1098
+ continue;
1099
+ }
1100
+
1101
+ if (character === '"') {
1102
+ this.inString = !this.inString;
1103
+ continue;
1104
+ }
1105
+
1106
+ if (this.inString) {
1107
+ continue;
1108
+ }
1109
+
1110
+ if (character === '{') {
1111
+ this.depth++;
1112
+ } else if (character === '}') {
1113
+ this.depth--;
1114
+
1115
+ if (this.depth === 0) {
1116
+ this.completed = true;
1117
+ }
1118
+ }
1119
+ }
1120
+
1121
+ return result;
1122
+ }
1123
+ }
1124
+
998
1125
  const BedrockStopReasonSchema = z.union([
999
1126
  z.enum(BEDROCK_STOP_REASONS),
1000
1127
  z.string(),