@ai-sdk/anthropic 2.0.99 → 2.0.100

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,13 @@
1
1
  # @ai-sdk/anthropic
2
2
 
3
+ ## 2.0.100
4
+
5
+ ### Patch Changes
6
+
7
+ - cd8f35c: Preserve tool calls when using JSON structured output.
8
+ - Updated dependencies [26165ee]
9
+ - @ai-sdk/provider-utils@3.0.36
10
+
3
11
  ## 2.0.99
4
12
 
5
13
  ### Patch Changes
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var import_provider4 = require("@ai-sdk/provider");
31
31
  var import_provider_utils20 = require("@ai-sdk/provider-utils");
32
32
 
33
33
  // src/version.ts
34
- var VERSION = true ? "2.0.99" : "0.0.0-test";
34
+ var VERSION = true ? "2.0.100" : "0.0.0-test";
35
35
 
36
36
  // src/anthropic-messages-language-model.ts
37
37
  var import_provider3 = require("@ai-sdk/provider");
@@ -704,7 +704,7 @@ var anthropicProviderOptions = import_v43.z.object({
704
704
  * Determines how structured outputs are generated.
705
705
  *
706
706
  * - `outputFormat`: Use the `output_format` parameter to specify the structured output format.
707
- * - `jsonTool`: Use a special 'json' tool to specify the structured output format (default).
707
+ * - `jsonTool`: Use a special JSON response tool to specify the structured output format (default).
708
708
  * - `auto`: Use 'outputFormat' when supported, otherwise use 'jsonTool'.
709
709
  */
710
710
  structuredOutputMode: import_v43.z.enum(["outputFormat", "jsonTool", "auto"]).optional(),
@@ -2249,6 +2249,15 @@ function isPlainObject(value) {
2249
2249
  }
2250
2250
 
2251
2251
  // src/anthropic-messages-language-model.ts
2252
+ function getJsonResponseToolName(tools) {
2253
+ const toolNames = new Set(tools == null ? void 0 : tools.map((tool) => tool.name));
2254
+ let name = "json";
2255
+ let suffix = 1;
2256
+ while (toolNames.has(name)) {
2257
+ name = `json_${suffix++}`;
2258
+ }
2259
+ return name;
2260
+ }
2252
2261
  function createCitationSource(citation, citationDocuments, generateId3) {
2253
2262
  var _a;
2254
2263
  if (citation.type !== "page_location" && citation.type !== "char_location") {
@@ -2354,12 +2363,6 @@ var AnthropicMessagesLanguageModel = class {
2354
2363
  setting: "responseFormat",
2355
2364
  details: "JSON response format requires a schema. The response format is ignored."
2356
2365
  });
2357
- } else if (tools != null) {
2358
- warnings.push({
2359
- type: "unsupported-setting",
2360
- setting: "tools",
2361
- details: "JSON response format does not support tools. The provided tools are ignored."
2362
- });
2363
2366
  }
2364
2367
  }
2365
2368
  const anthropicOptions = await (0, import_provider_utils10.parseProviderOptions)({
@@ -2409,9 +2412,10 @@ var AnthropicMessagesLanguageModel = class {
2409
2412
  const isAnthropicModel = isKnownModel || this.modelId.includes("claude-");
2410
2413
  const structureOutputMode = (_a = anthropicOptions == null ? void 0 : anthropicOptions.structuredOutputMode) != null ? _a : "jsonTool";
2411
2414
  const useStructuredOutput = structureOutputMode === "outputFormat" || structureOutputMode === "auto" && supportsStructuredOutput;
2415
+ const jsonResponseToolName = getJsonResponseToolName(tools);
2412
2416
  const jsonResponseTool = (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && !useStructuredOutput ? {
2413
2417
  type: "function",
2414
- name: "json",
2418
+ name: jsonResponseToolName,
2415
2419
  description: "Respond with a JSON object.",
2416
2420
  inputSchema: responseFormat.schema
2417
2421
  } : void 0;
@@ -2654,8 +2658,8 @@ var AnthropicMessagesLanguageModel = class {
2654
2658
  betas: toolsBetas
2655
2659
  } = await prepareTools(
2656
2660
  jsonResponseTool != null ? {
2657
- tools: [jsonResponseTool],
2658
- toolChoice: { type: "tool", toolName: jsonResponseTool.name },
2661
+ tools: [...tools != null ? tools : [], jsonResponseTool],
2662
+ toolChoice: tools != null && tools.length > 0 ? { type: "required" } : { type: "tool", toolName: jsonResponseTool.name },
2659
2663
  disableParallelToolUse: true,
2660
2664
  cacheControlValidator
2661
2665
  } : {
@@ -2679,7 +2683,7 @@ var AnthropicMessagesLanguageModel = class {
2679
2683
  ...userSuppliedBetas,
2680
2684
  ...(_i = anthropicOptions == null ? void 0 : anthropicOptions.anthropicBeta) != null ? _i : []
2681
2685
  ]),
2682
- usesJsonResponseTool: jsonResponseTool != null
2686
+ jsonResponseToolName: jsonResponseTool != null ? jsonResponseToolName : void 0
2683
2687
  };
2684
2688
  }
2685
2689
  async getHeaders({
@@ -2737,7 +2741,7 @@ var AnthropicMessagesLanguageModel = class {
2737
2741
  }
2738
2742
  async doGenerate(options) {
2739
2743
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2740
- const { args, warnings, betas, usesJsonResponseTool } = await this.getArgs({
2744
+ const { args, warnings, betas, jsonResponseToolName } = await this.getArgs({
2741
2745
  ...options,
2742
2746
  userSuppliedBetas: await this.getBetasFromHeaders(options.headers)
2743
2747
  });
@@ -2758,10 +2762,11 @@ var AnthropicMessagesLanguageModel = class {
2758
2762
  fetch: this.config.fetch
2759
2763
  });
2760
2764
  const content = [];
2765
+ let isJsonResponseFromTool = false;
2761
2766
  for (const part of response.content) {
2762
2767
  switch (part.type) {
2763
2768
  case "text": {
2764
- if (!usesJsonResponseTool) {
2769
+ if (jsonResponseToolName == null) {
2765
2770
  const webSearchCitations = (_a = part.citations) == null ? void 0 : _a.filter(
2766
2771
  (citation) => citation.type === "web_search_result_location"
2767
2772
  );
@@ -2828,18 +2833,21 @@ var AnthropicMessagesLanguageModel = class {
2828
2833
  break;
2829
2834
  }
2830
2835
  case "tool_use": {
2831
- content.push(
2832
- // when a json response tool is used, the tool call becomes the text:
2833
- usesJsonResponseTool ? {
2836
+ const isJsonResponseTool = part.name === jsonResponseToolName;
2837
+ if (isJsonResponseTool) {
2838
+ isJsonResponseFromTool = true;
2839
+ content.push({
2834
2840
  type: "text",
2835
2841
  text: JSON.stringify(part.input)
2836
- } : {
2842
+ });
2843
+ } else {
2844
+ content.push({
2837
2845
  type: "tool-call",
2838
2846
  toolCallId: part.id,
2839
2847
  toolName: part.name,
2840
2848
  input: JSON.stringify(part.input)
2841
- }
2842
- );
2849
+ });
2850
+ }
2843
2851
  break;
2844
2852
  }
2845
2853
  case "server_tool_use": {
@@ -3021,7 +3029,7 @@ var AnthropicMessagesLanguageModel = class {
3021
3029
  content,
3022
3030
  finishReason: mapAnthropicStopReason({
3023
3031
  finishReason: response.stop_reason,
3024
- isJsonResponseFromTool: usesJsonResponseTool
3032
+ isJsonResponseFromTool
3025
3033
  }),
3026
3034
  usage: {
3027
3035
  inputTokens,
@@ -3089,7 +3097,7 @@ var AnthropicMessagesLanguageModel = class {
3089
3097
  }
3090
3098
  async doStream(options) {
3091
3099
  var _a, _b;
3092
- const { args, warnings, betas, usesJsonResponseTool } = await this.getArgs({
3100
+ const { args, warnings, betas, jsonResponseToolName } = await this.getArgs({
3093
3101
  ...options,
3094
3102
  userSuppliedBetas: await this.getBetasFromHeaders(options.headers)
3095
3103
  });
@@ -3121,6 +3129,7 @@ var AnthropicMessagesLanguageModel = class {
3121
3129
  let container = null;
3122
3130
  let iterations = null;
3123
3131
  let contextManagement = null;
3132
+ let isJsonResponseFromTool = false;
3124
3133
  let isMessageOpen = false;
3125
3134
  let activeMessageId;
3126
3135
  let hasInvalidMessageSequence = false;
@@ -3204,20 +3213,31 @@ var AnthropicMessagesLanguageModel = class {
3204
3213
  return;
3205
3214
  }
3206
3215
  case "tool_use": {
3207
- contentBlocks[value.index] = usesJsonResponseTool ? { type: "text", citations: [] } : {
3208
- type: "tool-call",
3209
- toolCallId: value.content_block.id,
3210
- toolName: value.content_block.name,
3211
- input: "",
3212
- firstDelta: true
3213
- };
3214
- controller.enqueue(
3215
- usesJsonResponseTool ? { type: "text-start", id: String(value.index) } : {
3216
+ const isJsonResponseTool = value.content_block.name === jsonResponseToolName;
3217
+ if (isJsonResponseTool) {
3218
+ isJsonResponseFromTool = true;
3219
+ contentBlocks[value.index] = {
3220
+ type: "text",
3221
+ citations: []
3222
+ };
3223
+ controller.enqueue({
3224
+ type: "text-start",
3225
+ id: String(value.index)
3226
+ });
3227
+ } else {
3228
+ contentBlocks[value.index] = {
3229
+ type: "tool-call",
3230
+ toolCallId: value.content_block.id,
3231
+ toolName: value.content_block.name,
3232
+ input: "",
3233
+ firstDelta: true
3234
+ };
3235
+ controller.enqueue({
3216
3236
  type: "tool-input-start",
3217
3237
  id: value.content_block.id,
3218
3238
  toolName: value.content_block.name
3219
- }
3220
- );
3239
+ });
3240
+ }
3221
3241
  return;
3222
3242
  }
3223
3243
  case "server_tool_use": {
@@ -3413,22 +3433,21 @@ var AnthropicMessagesLanguageModel = class {
3413
3433
  });
3414
3434
  break;
3415
3435
  }
3416
- case "tool-call":
3417
- if (!usesJsonResponseTool) {
3418
- controller.enqueue({
3419
- type: "tool-input-end",
3420
- id: contentBlock.toolCallId
3421
- });
3422
- const toolName = contentBlock.toolName === "text_editor_code_execution" || contentBlock.toolName === "bash_code_execution" ? "code_execution" : contentBlock.toolName;
3423
- controller.enqueue({
3424
- type: "tool-call",
3425
- toolCallId: contentBlock.toolCallId,
3426
- toolName,
3427
- input: contentBlock.input === "" ? "{}" : contentBlock.input,
3428
- providerExecuted: contentBlock.providerExecuted
3429
- });
3430
- }
3436
+ case "tool-call": {
3437
+ controller.enqueue({
3438
+ type: "tool-input-end",
3439
+ id: contentBlock.toolCallId
3440
+ });
3441
+ const toolName = contentBlock.toolName === "text_editor_code_execution" || contentBlock.toolName === "bash_code_execution" ? "code_execution" : contentBlock.toolName;
3442
+ controller.enqueue({
3443
+ type: "tool-call",
3444
+ toolCallId: contentBlock.toolCallId,
3445
+ toolName,
3446
+ input: contentBlock.input === "" ? "{}" : contentBlock.input,
3447
+ providerExecuted: contentBlock.providerExecuted
3448
+ });
3431
3449
  break;
3450
+ }
3432
3451
  }
3433
3452
  delete contentBlocks[value.index];
3434
3453
  }
@@ -3439,7 +3458,7 @@ var AnthropicMessagesLanguageModel = class {
3439
3458
  const deltaType = value.delta.type;
3440
3459
  switch (deltaType) {
3441
3460
  case "text_delta": {
3442
- if (usesJsonResponseTool) {
3461
+ if (jsonResponseToolName != null) {
3443
3462
  return;
3444
3463
  }
3445
3464
  controller.enqueue({
@@ -3488,10 +3507,7 @@ var AnthropicMessagesLanguageModel = class {
3488
3507
  if (delta.length === 0) {
3489
3508
  return;
3490
3509
  }
3491
- if (usesJsonResponseTool) {
3492
- if ((contentBlock == null ? void 0 : contentBlock.type) !== "text") {
3493
- return;
3494
- }
3510
+ if ((contentBlock == null ? void 0 : contentBlock.type) === "text") {
3495
3511
  controller.enqueue({
3496
3512
  type: "text-delta",
3497
3513
  id: String(value.index),
@@ -3600,7 +3616,7 @@ var AnthropicMessagesLanguageModel = class {
3600
3616
  usage.totalTokens = ((_g = usage.inputTokens) != null ? _g : 0) + ((_h = usage.outputTokens) != null ? _h : 0);
3601
3617
  finishReason = mapAnthropicStopReason({
3602
3618
  finishReason: value.delta.stop_reason,
3603
- isJsonResponseFromTool: usesJsonResponseTool
3619
+ isJsonResponseFromTool
3604
3620
  });
3605
3621
  stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
3606
3622
  stopSequence = (_i = value.delta.stop_sequence) != null ? _i : null;