@ai-sdk/amazon-bedrock 4.0.0-beta.60 → 4.0.0-beta.62

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,36 @@
1
1
  # @ai-sdk/amazon-bedrock
2
2
 
3
+ ## 4.0.0-beta.62
4
+
5
+ ### Patch Changes
6
+
7
+ - 88b2c7e: feat(provider/amazon-bedrock,provider/google-vertex-anthropic): add support for tool calling with structured output
8
+
9
+ Added support for combining tool calling with structured outputs in both Amazon Bedrock and Google Vertex Anthropic providers. This allows developers to use tools (like weather lookups, web search, etc.) alongside structured JSON output schemas, enabling multi-step agentic workflows with structured final outputs.
10
+
11
+ **Amazon Bedrock Changes:**
12
+
13
+ - Removed incorrect warning that prevented using tools with JSON response format
14
+ - Updated tool choice to use `{ type: 'required' }` instead of specific tool selection when using structured outputs
15
+ - Added `isJsonResponseFromTool` parameter to finish reason mapping
16
+ - JSON tool responses are correctly converted to text content and finish reason is mapped from `tool_use` to `stop`
17
+ - Added comprehensive test coverage for combining tools with structured outputs
18
+ - Added example files demonstrating the feature
19
+
20
+ **Google Vertex Anthropic Changes:**
21
+
22
+ - Inherits support from underlying Anthropic provider implementation
23
+ - Added test coverage to verify the feature works correctly
24
+ - Added example files demonstrating the feature
25
+
26
+ This brings Anthropic provider's structured output capabilities to the Amazon Bedrock and Google Vertex Anthropic providers.
27
+
28
+ ## 4.0.0-beta.61
29
+
30
+ ### Patch Changes
31
+
32
+ - 0a6fd91: fix(amazon-bedrock): move anthropic_beta to request body
33
+
3
34
  ## 4.0.0-beta.60
4
35
 
5
36
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -11,6 +11,7 @@ declare const bedrockProviderOptions: z.ZodObject<{
11
11
  type: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"enabled">, z.ZodLiteral<"disabled">]>>;
12
12
  budgetTokens: z.ZodOptional<z.ZodNumber>;
13
13
  }, z.core.$strip>>;
14
+ anthropicBeta: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
15
  }, z.core.$strip>;
15
16
  type BedrockProviderOptions = z.infer<typeof bedrockProviderOptions>;
16
17
 
package/dist/index.d.ts CHANGED
@@ -11,6 +11,7 @@ declare const bedrockProviderOptions: z.ZodObject<{
11
11
  type: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"enabled">, z.ZodLiteral<"disabled">]>>;
12
12
  budgetTokens: z.ZodOptional<z.ZodNumber>;
13
13
  }, z.core.$strip>>;
14
+ anthropicBeta: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
15
  }, z.core.$strip>;
15
16
  type BedrockProviderOptions = z.infer<typeof bedrockProviderOptions>;
16
17
 
package/dist/index.js CHANGED
@@ -92,7 +92,11 @@ var bedrockProviderOptions = import_v4.z.object({
92
92
  reasoningConfig: import_v4.z.object({
93
93
  type: import_v4.z.union([import_v4.z.literal("enabled"), import_v4.z.literal("disabled")]).optional(),
94
94
  budgetTokens: import_v4.z.number().optional()
95
- }).optional()
95
+ }).optional(),
96
+ /**
97
+ * Anthropic beta features to enable
98
+ */
99
+ anthropicBeta: import_v4.z.array(import_v4.z.string()).optional()
96
100
  });
97
101
 
98
102
  // src/bedrock-error.ts
@@ -650,7 +654,7 @@ function groupIntoBlocks(prompt) {
650
654
  }
651
655
 
652
656
  // src/map-bedrock-finish-reason.ts
653
- function mapBedrockFinishReason(finishReason) {
657
+ function mapBedrockFinishReason(finishReason, isJsonResponseFromTool) {
654
658
  switch (finishReason) {
655
659
  case "stop_sequence":
656
660
  case "end_turn":
@@ -661,7 +665,7 @@ function mapBedrockFinishReason(finishReason) {
661
665
  case "guardrail_intervened":
662
666
  return "content-filter";
663
667
  case "tool_use":
664
- return "tool-calls";
668
+ return isJsonResponseFromTool ? "stop" : "tool-calls";
665
669
  default:
666
670
  return "unknown";
667
671
  }
@@ -693,7 +697,7 @@ var BedrockChatLanguageModel = class {
693
697
  toolChoice,
694
698
  providerOptions
695
699
  }) {
696
- var _a, _b, _c, _d, _e, _f;
700
+ var _a, _b, _c, _d, _e, _f, _g;
697
701
  const bedrockOptions = (_a = await (0, import_provider_utils4.parseProviderOptions)({
698
702
  provider: "bedrock",
699
703
  providerOptions,
@@ -740,14 +744,6 @@ var BedrockChatLanguageModel = class {
740
744
  details: "Only text and json response formats are supported."
741
745
  });
742
746
  }
743
- if (tools != null && (responseFormat == null ? void 0 : responseFormat.type) === "json") {
744
- if (tools.length > 0) {
745
- warnings.push({
746
- type: "other",
747
- message: "JSON response format does not support tools. The provided tools are ignored."
748
- });
749
- }
750
- }
751
747
  const jsonResponseTool = (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null ? {
752
748
  type: "function",
753
749
  name: "json",
@@ -755,8 +751,8 @@ var BedrockChatLanguageModel = class {
755
751
  inputSchema: responseFormat.schema
756
752
  } : void 0;
757
753
  const { toolConfig, additionalTools, toolWarnings, betas } = await prepareTools({
758
- tools: jsonResponseTool ? [jsonResponseTool, ...tools != null ? tools : []] : tools,
759
- toolChoice: jsonResponseTool != null ? { type: "tool", toolName: jsonResponseTool.name } : toolChoice,
754
+ tools: jsonResponseTool ? [...tools != null ? tools : [], jsonResponseTool] : tools,
755
+ toolChoice: jsonResponseTool != null ? { type: "required" } : toolChoice,
760
756
  modelId: this.modelId
761
757
  });
762
758
  warnings.push(...toolWarnings);
@@ -766,8 +762,16 @@ var BedrockChatLanguageModel = class {
766
762
  ...additionalTools
767
763
  };
768
764
  }
769
- const isThinking = ((_b = bedrockOptions.reasoningConfig) == null ? void 0 : _b.type) === "enabled";
770
- const thinkingBudget = (_c = bedrockOptions.reasoningConfig) == null ? void 0 : _c.budgetTokens;
765
+ if (betas.size > 0 || bedrockOptions.anthropicBeta) {
766
+ const existingBetas = (_b = bedrockOptions.anthropicBeta) != null ? _b : [];
767
+ const mergedBetas = betas.size > 0 ? [...existingBetas, ...Array.from(betas)] : existingBetas;
768
+ bedrockOptions.additionalModelRequestFields = {
769
+ ...bedrockOptions.additionalModelRequestFields,
770
+ anthropic_beta: mergedBetas
771
+ };
772
+ }
773
+ const isThinking = ((_c = bedrockOptions.reasoningConfig) == null ? void 0 : _c.type) === "enabled";
774
+ const thinkingBudget = (_d = bedrockOptions.reasoningConfig) == null ? void 0 : _d.budgetTokens;
771
775
  const inferenceConfig = {
772
776
  ...maxOutputTokens != null && { maxTokens: maxOutputTokens },
773
777
  ...temperature != null && { temperature },
@@ -784,7 +788,7 @@ var BedrockChatLanguageModel = class {
784
788
  bedrockOptions.additionalModelRequestFields = {
785
789
  ...bedrockOptions.additionalModelRequestFields,
786
790
  thinking: {
787
- type: (_d = bedrockOptions.reasoningConfig) == null ? void 0 : _d.type,
791
+ type: (_e = bedrockOptions.reasoningConfig) == null ? void 0 : _e.type,
788
792
  budget_tokens: thinkingBudget
789
793
  }
790
794
  };
@@ -813,7 +817,7 @@ var BedrockChatLanguageModel = class {
813
817
  details: "topK is not supported when thinking is enabled"
814
818
  });
815
819
  }
816
- const hasAnyTools = ((_f = (_e = toolConfig.tools) == null ? void 0 : _e.length) != null ? _f : 0) > 0 || additionalTools;
820
+ const hasAnyTools = ((_g = (_f = toolConfig.tools) == null ? void 0 : _f.length) != null ? _g : 0) > 0 || additionalTools;
817
821
  let filteredPrompt = prompt;
818
822
  if (!hasAnyTools) {
819
823
  const hasToolContent = prompt.some(
@@ -862,27 +866,21 @@ var BedrockChatLanguageModel = class {
862
866
  };
863
867
  }
864
868
  async getHeaders({
865
- betas,
866
869
  headers
867
870
  }) {
868
- return (0, import_provider_utils4.combineHeaders)(
869
- await (0, import_provider_utils4.resolve)(this.config.headers),
870
- betas.size > 0 ? { "anthropic-beta": Array.from(betas).join(",") } : {},
871
- headers
872
- );
871
+ return (0, import_provider_utils4.combineHeaders)(await (0, import_provider_utils4.resolve)(this.config.headers), headers);
873
872
  }
874
873
  async doGenerate(options) {
875
874
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
876
875
  const {
877
876
  command: args,
878
877
  warnings,
879
- usesJsonResponseTool,
880
- betas
878
+ usesJsonResponseTool
881
879
  } = await this.getArgs(options);
882
880
  const url = `${this.getUrl(this.modelId)}/converse`;
883
881
  const { value: response, responseHeaders } = await (0, import_provider_utils4.postJsonToApi)({
884
882
  url,
885
- headers: await this.getHeaders({ betas, headers: options.headers }),
883
+ headers: await this.getHeaders({ headers: options.headers }),
886
884
  body: args,
887
885
  failedResponseHandler: (0, import_provider_utils4.createJsonErrorResponseHandler)({
888
886
  errorSchema: BedrockErrorSchema,
@@ -898,11 +896,10 @@ var BedrockChatLanguageModel = class {
898
896
  fetch: this.config.fetch
899
897
  });
900
898
  const content = [];
899
+ let isJsonResponseFromTool = false;
901
900
  for (const part of response.output.message.content) {
902
901
  if (part.text) {
903
- if (!usesJsonResponseTool) {
904
- content.push({ type: "text", text: part.text });
905
- }
902
+ content.push({ type: "text", text: part.text });
906
903
  }
907
904
  if (part.reasoningContent) {
908
905
  if ("reasoningText" in part.reasoningContent) {
@@ -931,21 +928,24 @@ var BedrockChatLanguageModel = class {
931
928
  }
932
929
  }
933
930
  if (part.toolUse) {
934
- content.push(
935
- // when a json response tool is used, the tool call becomes the text:
936
- usesJsonResponseTool ? {
931
+ const isJsonResponseTool = usesJsonResponseTool && part.toolUse.name === "json";
932
+ if (isJsonResponseTool) {
933
+ isJsonResponseFromTool = true;
934
+ content.push({
937
935
  type: "text",
938
936
  text: JSON.stringify(part.toolUse.input)
939
- } : {
937
+ });
938
+ } else {
939
+ content.push({
940
940
  type: "tool-call",
941
941
  toolCallId: (_c = (_b = part.toolUse) == null ? void 0 : _b.toolUseId) != null ? _c : this.config.generateId(),
942
942
  toolName: (_e = (_d = part.toolUse) == null ? void 0 : _d.name) != null ? _e : `tool-${this.config.generateId()}`,
943
943
  input: JSON.stringify((_g = (_f = part.toolUse) == null ? void 0 : _f.input) != null ? _g : "")
944
- }
945
- );
944
+ });
945
+ }
946
946
  }
947
947
  }
948
- const providerMetadata = response.trace || response.usage || usesJsonResponseTool ? {
948
+ const providerMetadata = response.trace || response.usage || isJsonResponseFromTool ? {
949
949
  bedrock: {
950
950
  ...response.trace && typeof response.trace === "object" ? { trace: response.trace } : {},
951
951
  ...((_h = response.usage) == null ? void 0 : _h.cacheWriteInputTokens) != null && {
@@ -953,13 +953,14 @@ var BedrockChatLanguageModel = class {
953
953
  cacheWriteInputTokens: response.usage.cacheWriteInputTokens
954
954
  }
955
955
  },
956
- ...usesJsonResponseTool && { isJsonResponseFromTool: true }
956
+ ...isJsonResponseFromTool && { isJsonResponseFromTool: true }
957
957
  }
958
958
  } : void 0;
959
959
  return {
960
960
  content,
961
961
  finishReason: mapBedrockFinishReason(
962
- response.stopReason
962
+ response.stopReason,
963
+ isJsonResponseFromTool
963
964
  ),
964
965
  usage: {
965
966
  inputTokens: (_i = response.usage) == null ? void 0 : _i.inputTokens,
@@ -979,13 +980,12 @@ var BedrockChatLanguageModel = class {
979
980
  const {
980
981
  command: args,
981
982
  warnings,
982
- usesJsonResponseTool,
983
- betas
983
+ usesJsonResponseTool
984
984
  } = await this.getArgs(options);
985
985
  const url = `${this.getUrl(this.modelId)}/converse-stream`;
986
986
  const { value: response, responseHeaders } = await (0, import_provider_utils4.postJsonToApi)({
987
987
  url,
988
- headers: await this.getHeaders({ betas, headers: options.headers }),
988
+ headers: await this.getHeaders({ headers: options.headers }),
989
989
  body: args,
990
990
  failedResponseHandler: (0, import_provider_utils4.createJsonErrorResponseHandler)({
991
991
  errorSchema: BedrockErrorSchema,
@@ -1002,6 +1002,7 @@ var BedrockChatLanguageModel = class {
1002
1002
  totalTokens: void 0
1003
1003
  };
1004
1004
  let providerMetadata = void 0;
1005
+ let isJsonResponseFromTool = false;
1005
1006
  const contentBlocks = {};
1006
1007
  return {
1007
1008
  stream: response.pipeThrough(
@@ -1041,7 +1042,8 @@ var BedrockChatLanguageModel = class {
1041
1042
  }
1042
1043
  if (value.messageStop) {
1043
1044
  finishReason = mapBedrockFinishReason(
1044
- value.messageStop.stopReason
1045
+ value.messageStop.stopReason,
1046
+ isJsonResponseFromTool
1045
1047
  );
1046
1048
  }
1047
1049
  if (value.metadata) {
@@ -1057,14 +1059,11 @@ var BedrockChatLanguageModel = class {
1057
1059
  const trace = value.metadata.trace ? {
1058
1060
  trace: value.metadata.trace
1059
1061
  } : void 0;
1060
- if (cacheUsage || trace || usesJsonResponseTool) {
1062
+ if (cacheUsage || trace) {
1061
1063
  providerMetadata = {
1062
1064
  bedrock: {
1063
1065
  ...cacheUsage,
1064
- ...trace,
1065
- ...usesJsonResponseTool && {
1066
- isJsonResponseFromTool: true
1067
- }
1066
+ ...trace
1068
1067
  }
1069
1068
  };
1070
1069
  }
@@ -1081,20 +1080,16 @@ var BedrockChatLanguageModel = class {
1081
1080
  const blockIndex = value.contentBlockDelta.contentBlockIndex || 0;
1082
1081
  if (contentBlocks[blockIndex] == null) {
1083
1082
  contentBlocks[blockIndex] = { type: "text" };
1084
- if (!usesJsonResponseTool) {
1085
- controller.enqueue({
1086
- type: "text-start",
1087
- id: String(blockIndex)
1088
- });
1089
- }
1090
- }
1091
- if (!usesJsonResponseTool) {
1092
1083
  controller.enqueue({
1093
- type: "text-delta",
1094
- id: String(blockIndex),
1095
- delta: value.contentBlockDelta.delta.text
1084
+ type: "text-start",
1085
+ id: String(blockIndex)
1096
1086
  });
1097
1087
  }
1088
+ controller.enqueue({
1089
+ type: "text-delta",
1090
+ id: String(blockIndex),
1091
+ delta: value.contentBlockDelta.delta.text
1092
+ });
1098
1093
  }
1099
1094
  if (((_n = value.contentBlockStop) == null ? void 0 : _n.contentBlockIndex) != null) {
1100
1095
  const blockIndex = value.contentBlockStop.contentBlockIndex;
@@ -1106,14 +1101,13 @@ var BedrockChatLanguageModel = class {
1106
1101
  id: String(blockIndex)
1107
1102
  });
1108
1103
  } else if (contentBlock.type === "text") {
1109
- if (!usesJsonResponseTool) {
1110
- controller.enqueue({
1111
- type: "text-end",
1112
- id: String(blockIndex)
1113
- });
1114
- }
1104
+ controller.enqueue({
1105
+ type: "text-end",
1106
+ id: String(blockIndex)
1107
+ });
1115
1108
  } else if (contentBlock.type === "tool-call") {
1116
- if (usesJsonResponseTool) {
1109
+ if (contentBlock.isJsonResponseTool) {
1110
+ isJsonResponseFromTool = true;
1117
1111
  controller.enqueue({
1118
1112
  type: "text-start",
1119
1113
  id: String(blockIndex)
@@ -1187,13 +1181,15 @@ var BedrockChatLanguageModel = class {
1187
1181
  if (((_p = contentBlockStart == null ? void 0 : contentBlockStart.start) == null ? void 0 : _p.toolUse) != null) {
1188
1182
  const toolUse = contentBlockStart.start.toolUse;
1189
1183
  const blockIndex = contentBlockStart.contentBlockIndex;
1184
+ const isJsonResponseTool = usesJsonResponseTool && toolUse.name === "json";
1190
1185
  contentBlocks[blockIndex] = {
1191
1186
  type: "tool-call",
1192
1187
  toolCallId: toolUse.toolUseId,
1193
1188
  toolName: toolUse.name,
1194
- jsonText: ""
1189
+ jsonText: "",
1190
+ isJsonResponseTool
1195
1191
  };
1196
- if (!usesJsonResponseTool) {
1192
+ if (!isJsonResponseTool) {
1197
1193
  controller.enqueue({
1198
1194
  type: "tool-input-start",
1199
1195
  id: toolUse.toolUseId,
@@ -1207,7 +1203,7 @@ var BedrockChatLanguageModel = class {
1207
1203
  const contentBlock = contentBlocks[blockIndex];
1208
1204
  if ((contentBlock == null ? void 0 : contentBlock.type) === "tool-call") {
1209
1205
  const delta = (_q = contentBlockDelta.delta.toolUse.input) != null ? _q : "";
1210
- if (!usesJsonResponseTool) {
1206
+ if (!contentBlock.isJsonResponseTool) {
1211
1207
  controller.enqueue({
1212
1208
  type: "tool-input-delta",
1213
1209
  id: contentBlock.toolCallId,
@@ -1219,6 +1215,20 @@ var BedrockChatLanguageModel = class {
1219
1215
  }
1220
1216
  },
1221
1217
  flush(controller) {
1218
+ if (isJsonResponseFromTool) {
1219
+ if (providerMetadata) {
1220
+ providerMetadata.bedrock = {
1221
+ ...providerMetadata.bedrock,
1222
+ isJsonResponseFromTool: true
1223
+ };
1224
+ } else {
1225
+ providerMetadata = {
1226
+ bedrock: {
1227
+ isJsonResponseFromTool: true
1228
+ }
1229
+ };
1230
+ }
1231
+ }
1222
1232
  controller.enqueue({
1223
1233
  type: "finish",
1224
1234
  finishReason,
@@ -1526,7 +1536,7 @@ var import_provider_utils7 = require("@ai-sdk/provider-utils");
1526
1536
  var import_aws4fetch = require("aws4fetch");
1527
1537
 
1528
1538
  // src/version.ts
1529
- var VERSION = true ? "4.0.0-beta.60" : "0.0.0-test";
1539
+ var VERSION = true ? "4.0.0-beta.62" : "0.0.0-test";
1530
1540
 
1531
1541
  // src/bedrock-sigv4-fetch.ts
1532
1542
  function createSigV4FetchFunction(getCredentials, fetch = globalThis.fetch) {