@ai-sdk/anthropic 2.0.98 → 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,19 @@
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
+
11
+ ## 2.0.99
12
+
13
+ ### Patch Changes
14
+
15
+ - 78c7304: fix(anthropic): order client tool calls after provider-executed tool results
16
+
3
17
  ## 2.0.98
4
18
 
5
19
  ### 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.98" : "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(),
@@ -1994,7 +1994,10 @@ async function convertToAnthropicMessagesPrompt({
1994
1994
  }
1995
1995
  }
1996
1996
  }
1997
- messages.push({ role: "assistant", content: anthropicContent });
1997
+ messages.push({
1998
+ role: "assistant",
1999
+ content: moveToolUseBlocksToEnd(anthropicContent)
2000
+ });
1998
2001
  break;
1999
2002
  }
2000
2003
  default: {
@@ -2054,6 +2057,27 @@ function groupIntoBlocks(prompt) {
2054
2057
  }
2055
2058
  return blocks;
2056
2059
  }
2060
+ function moveToolUseBlocksToEnd(content) {
2061
+ const result = [];
2062
+ let segment = [];
2063
+ function flushSegment() {
2064
+ result.push(
2065
+ ...segment.filter((part) => part.type !== "tool_use"),
2066
+ ...segment.filter((part) => part.type === "tool_use")
2067
+ );
2068
+ segment = [];
2069
+ }
2070
+ for (const part of content) {
2071
+ if (part.type === "thinking" || part.type === "redacted_thinking") {
2072
+ flushSegment();
2073
+ result.push(part);
2074
+ } else {
2075
+ segment.push(part);
2076
+ }
2077
+ }
2078
+ flushSegment();
2079
+ return result;
2080
+ }
2057
2081
 
2058
2082
  // src/map-anthropic-stop-reason.ts
2059
2083
  function mapAnthropicStopReason({
@@ -2225,6 +2249,15 @@ function isPlainObject(value) {
2225
2249
  }
2226
2250
 
2227
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
+ }
2228
2261
  function createCitationSource(citation, citationDocuments, generateId3) {
2229
2262
  var _a;
2230
2263
  if (citation.type !== "page_location" && citation.type !== "char_location") {
@@ -2330,12 +2363,6 @@ var AnthropicMessagesLanguageModel = class {
2330
2363
  setting: "responseFormat",
2331
2364
  details: "JSON response format requires a schema. The response format is ignored."
2332
2365
  });
2333
- } else if (tools != null) {
2334
- warnings.push({
2335
- type: "unsupported-setting",
2336
- setting: "tools",
2337
- details: "JSON response format does not support tools. The provided tools are ignored."
2338
- });
2339
2366
  }
2340
2367
  }
2341
2368
  const anthropicOptions = await (0, import_provider_utils10.parseProviderOptions)({
@@ -2385,9 +2412,10 @@ var AnthropicMessagesLanguageModel = class {
2385
2412
  const isAnthropicModel = isKnownModel || this.modelId.includes("claude-");
2386
2413
  const structureOutputMode = (_a = anthropicOptions == null ? void 0 : anthropicOptions.structuredOutputMode) != null ? _a : "jsonTool";
2387
2414
  const useStructuredOutput = structureOutputMode === "outputFormat" || structureOutputMode === "auto" && supportsStructuredOutput;
2415
+ const jsonResponseToolName = getJsonResponseToolName(tools);
2388
2416
  const jsonResponseTool = (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && !useStructuredOutput ? {
2389
2417
  type: "function",
2390
- name: "json",
2418
+ name: jsonResponseToolName,
2391
2419
  description: "Respond with a JSON object.",
2392
2420
  inputSchema: responseFormat.schema
2393
2421
  } : void 0;
@@ -2630,8 +2658,8 @@ var AnthropicMessagesLanguageModel = class {
2630
2658
  betas: toolsBetas
2631
2659
  } = await prepareTools(
2632
2660
  jsonResponseTool != null ? {
2633
- tools: [jsonResponseTool],
2634
- 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 },
2635
2663
  disableParallelToolUse: true,
2636
2664
  cacheControlValidator
2637
2665
  } : {
@@ -2655,7 +2683,7 @@ var AnthropicMessagesLanguageModel = class {
2655
2683
  ...userSuppliedBetas,
2656
2684
  ...(_i = anthropicOptions == null ? void 0 : anthropicOptions.anthropicBeta) != null ? _i : []
2657
2685
  ]),
2658
- usesJsonResponseTool: jsonResponseTool != null
2686
+ jsonResponseToolName: jsonResponseTool != null ? jsonResponseToolName : void 0
2659
2687
  };
2660
2688
  }
2661
2689
  async getHeaders({
@@ -2713,7 +2741,7 @@ var AnthropicMessagesLanguageModel = class {
2713
2741
  }
2714
2742
  async doGenerate(options) {
2715
2743
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
2716
- const { args, warnings, betas, usesJsonResponseTool } = await this.getArgs({
2744
+ const { args, warnings, betas, jsonResponseToolName } = await this.getArgs({
2717
2745
  ...options,
2718
2746
  userSuppliedBetas: await this.getBetasFromHeaders(options.headers)
2719
2747
  });
@@ -2734,10 +2762,11 @@ var AnthropicMessagesLanguageModel = class {
2734
2762
  fetch: this.config.fetch
2735
2763
  });
2736
2764
  const content = [];
2765
+ let isJsonResponseFromTool = false;
2737
2766
  for (const part of response.content) {
2738
2767
  switch (part.type) {
2739
2768
  case "text": {
2740
- if (!usesJsonResponseTool) {
2769
+ if (jsonResponseToolName == null) {
2741
2770
  const webSearchCitations = (_a = part.citations) == null ? void 0 : _a.filter(
2742
2771
  (citation) => citation.type === "web_search_result_location"
2743
2772
  );
@@ -2804,18 +2833,21 @@ var AnthropicMessagesLanguageModel = class {
2804
2833
  break;
2805
2834
  }
2806
2835
  case "tool_use": {
2807
- content.push(
2808
- // when a json response tool is used, the tool call becomes the text:
2809
- usesJsonResponseTool ? {
2836
+ const isJsonResponseTool = part.name === jsonResponseToolName;
2837
+ if (isJsonResponseTool) {
2838
+ isJsonResponseFromTool = true;
2839
+ content.push({
2810
2840
  type: "text",
2811
2841
  text: JSON.stringify(part.input)
2812
- } : {
2842
+ });
2843
+ } else {
2844
+ content.push({
2813
2845
  type: "tool-call",
2814
2846
  toolCallId: part.id,
2815
2847
  toolName: part.name,
2816
2848
  input: JSON.stringify(part.input)
2817
- }
2818
- );
2849
+ });
2850
+ }
2819
2851
  break;
2820
2852
  }
2821
2853
  case "server_tool_use": {
@@ -2997,7 +3029,7 @@ var AnthropicMessagesLanguageModel = class {
2997
3029
  content,
2998
3030
  finishReason: mapAnthropicStopReason({
2999
3031
  finishReason: response.stop_reason,
3000
- isJsonResponseFromTool: usesJsonResponseTool
3032
+ isJsonResponseFromTool
3001
3033
  }),
3002
3034
  usage: {
3003
3035
  inputTokens,
@@ -3065,7 +3097,7 @@ var AnthropicMessagesLanguageModel = class {
3065
3097
  }
3066
3098
  async doStream(options) {
3067
3099
  var _a, _b;
3068
- const { args, warnings, betas, usesJsonResponseTool } = await this.getArgs({
3100
+ const { args, warnings, betas, jsonResponseToolName } = await this.getArgs({
3069
3101
  ...options,
3070
3102
  userSuppliedBetas: await this.getBetasFromHeaders(options.headers)
3071
3103
  });
@@ -3097,6 +3129,7 @@ var AnthropicMessagesLanguageModel = class {
3097
3129
  let container = null;
3098
3130
  let iterations = null;
3099
3131
  let contextManagement = null;
3132
+ let isJsonResponseFromTool = false;
3100
3133
  let isMessageOpen = false;
3101
3134
  let activeMessageId;
3102
3135
  let hasInvalidMessageSequence = false;
@@ -3180,20 +3213,31 @@ var AnthropicMessagesLanguageModel = class {
3180
3213
  return;
3181
3214
  }
3182
3215
  case "tool_use": {
3183
- contentBlocks[value.index] = usesJsonResponseTool ? { type: "text", citations: [] } : {
3184
- type: "tool-call",
3185
- toolCallId: value.content_block.id,
3186
- toolName: value.content_block.name,
3187
- input: "",
3188
- firstDelta: true
3189
- };
3190
- controller.enqueue(
3191
- 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({
3192
3236
  type: "tool-input-start",
3193
3237
  id: value.content_block.id,
3194
3238
  toolName: value.content_block.name
3195
- }
3196
- );
3239
+ });
3240
+ }
3197
3241
  return;
3198
3242
  }
3199
3243
  case "server_tool_use": {
@@ -3389,22 +3433,21 @@ var AnthropicMessagesLanguageModel = class {
3389
3433
  });
3390
3434
  break;
3391
3435
  }
3392
- case "tool-call":
3393
- if (!usesJsonResponseTool) {
3394
- controller.enqueue({
3395
- type: "tool-input-end",
3396
- id: contentBlock.toolCallId
3397
- });
3398
- const toolName = contentBlock.toolName === "text_editor_code_execution" || contentBlock.toolName === "bash_code_execution" ? "code_execution" : contentBlock.toolName;
3399
- controller.enqueue({
3400
- type: "tool-call",
3401
- toolCallId: contentBlock.toolCallId,
3402
- toolName,
3403
- input: contentBlock.input === "" ? "{}" : contentBlock.input,
3404
- providerExecuted: contentBlock.providerExecuted
3405
- });
3406
- }
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
+ });
3407
3449
  break;
3450
+ }
3408
3451
  }
3409
3452
  delete contentBlocks[value.index];
3410
3453
  }
@@ -3415,7 +3458,7 @@ var AnthropicMessagesLanguageModel = class {
3415
3458
  const deltaType = value.delta.type;
3416
3459
  switch (deltaType) {
3417
3460
  case "text_delta": {
3418
- if (usesJsonResponseTool) {
3461
+ if (jsonResponseToolName != null) {
3419
3462
  return;
3420
3463
  }
3421
3464
  controller.enqueue({
@@ -3464,10 +3507,7 @@ var AnthropicMessagesLanguageModel = class {
3464
3507
  if (delta.length === 0) {
3465
3508
  return;
3466
3509
  }
3467
- if (usesJsonResponseTool) {
3468
- if ((contentBlock == null ? void 0 : contentBlock.type) !== "text") {
3469
- return;
3470
- }
3510
+ if ((contentBlock == null ? void 0 : contentBlock.type) === "text") {
3471
3511
  controller.enqueue({
3472
3512
  type: "text-delta",
3473
3513
  id: String(value.index),
@@ -3576,7 +3616,7 @@ var AnthropicMessagesLanguageModel = class {
3576
3616
  usage.totalTokens = ((_g = usage.inputTokens) != null ? _g : 0) + ((_h = usage.outputTokens) != null ? _h : 0);
3577
3617
  finishReason = mapAnthropicStopReason({
3578
3618
  finishReason: value.delta.stop_reason,
3579
- isJsonResponseFromTool: usesJsonResponseTool
3619
+ isJsonResponseFromTool
3580
3620
  });
3581
3621
  stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
3582
3622
  stopSequence = (_i = value.delta.stop_sequence) != null ? _i : null;