@ai-sdk/anthropic 2.0.110 → 2.0.112

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,27 @@
1
1
  # @ai-sdk/anthropic
2
2
 
3
+ ## 2.0.112
4
+
5
+ ### Patch Changes
6
+
7
+ - 0a069bd: Enable native structured output for supported Claude 5 models on Bedrock. In automatic mode, Bedrock and Anthropic now fall back for schemas that native structured output cannot represent: recursive references, open objects, or more than ten optional properties. Anthropic uses the JSON response tool; Bedrock Converse uses the JSON tool or JSON instructions for models that reject forced tool use. The JSON-tool fallback excludes disabled and unselected application tools. Explicit `outputFormat` selection remains unchanged.
8
+ - 0a069bd: fix(anthropic): parse valid JSON string tool input and safely serialize BigInt and cycles in tool results
9
+ - 0a069bd: Preserve final JSON text when a model answers without calling the JSON response tool, including open-schema fallbacks on Claude models that reject forced tools. Keep tool preambles out of structured output in generation and streaming.
10
+
11
+ When the model answers in text, streaming buffers that text until the message finishes to avoid exposing tool preambles as partial structured output.
12
+
13
+ - Updated dependencies [0a069bd]
14
+ - Updated dependencies [0a069bd]
15
+ - Updated dependencies [0a069bd]
16
+ - @ai-sdk/provider-utils@3.0.44
17
+
18
+ ## 2.0.111
19
+
20
+ ### Patch Changes
21
+
22
+ - Updated dependencies [a1a772f]
23
+ - @ai-sdk/provider-utils@3.0.43
24
+
3
25
  ## 2.0.110
4
26
 
5
27
  ### Patch Changes
package/dist/index.js CHANGED
@@ -31,7 +31,7 @@ var import_provider4 = require("@ai-sdk/provider");
31
31
  var import_provider_utils22 = require("@ai-sdk/provider-utils");
32
32
 
33
33
  // src/version.ts
34
- var VERSION = true ? "2.0.110" : "0.0.0-test";
34
+ var VERSION = true ? "2.0.112" : "0.0.0-test";
35
35
 
36
36
  // src/anthropic-messages-language-model.ts
37
37
  var import_provider3 = require("@ai-sdk/provider");
@@ -1937,7 +1937,7 @@ async function convertToAnthropicMessagesPrompt({
1937
1937
  case "json":
1938
1938
  case "error-json":
1939
1939
  default:
1940
- contentValue = JSON.stringify(output.value);
1940
+ contentValue = (0, import_provider_utils11.stringifyWithBigIntAndCycles)(output.value);
1941
1941
  break;
1942
1942
  }
1943
1943
  const toolsetName = getAnthropicToolsetName({
@@ -2111,7 +2111,7 @@ async function convertToAnthropicMessagesPrompt({
2111
2111
  type: "tool_use",
2112
2112
  id: part.toolCallId,
2113
2113
  name: part.toolName,
2114
- input: part.input,
2114
+ input: parseToolCallInput(part.input),
2115
2115
  cache_control: cacheControl
2116
2116
  });
2117
2117
  break;
@@ -2346,6 +2346,18 @@ function getAnthropicToolsetName({
2346
2346
  const fromMetadata = (_a = providerOptions == null ? void 0 : providerOptions.anthropic) == null ? void 0 : _a.toolsetName;
2347
2347
  return typeof fromMetadata === "string" ? fromMetadata : void 0;
2348
2348
  }
2349
+ function parseToolCallInput(input) {
2350
+ if (typeof input === "string") {
2351
+ try {
2352
+ const parsed = (0, import_provider_utils11.secureJsonParse)(input);
2353
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
2354
+ return parsed;
2355
+ }
2356
+ } catch (e) {
2357
+ }
2358
+ }
2359
+ return input;
2360
+ }
2349
2361
 
2350
2362
  // src/map-anthropic-stop-reason.ts
2351
2363
  function mapAnthropicStopReason({
@@ -2516,6 +2528,187 @@ function isPlainObject(value) {
2516
2528
  return typeof value === "object" && value !== null && !Array.isArray(value);
2517
2529
  }
2518
2530
 
2531
+ // src/has-circular-schema-reference.ts
2532
+ var SCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
2533
+ "dependentSchemas",
2534
+ "patternProperties",
2535
+ "properties"
2536
+ ]);
2537
+ var DATA_KEYWORDS = /* @__PURE__ */ new Set(["const", "default", "enum", "examples"]);
2538
+ function hasCircularSchemaReference(schema) {
2539
+ const visiting = /* @__PURE__ */ new Set();
2540
+ const visited = /* @__PURE__ */ new Set();
2541
+ const visit = (pointer) => {
2542
+ if (visiting.has(pointer)) {
2543
+ return true;
2544
+ }
2545
+ if (visited.has(pointer)) {
2546
+ return false;
2547
+ }
2548
+ const target = resolveSchemaPointer(schema, pointer);
2549
+ if (target === void 0) {
2550
+ return false;
2551
+ }
2552
+ visiting.add(pointer);
2553
+ const isCircular = collectLocalRefs(target).some(visit);
2554
+ visiting.delete(pointer);
2555
+ visited.add(pointer);
2556
+ return isCircular;
2557
+ };
2558
+ return visit("#");
2559
+ }
2560
+ function collectLocalRefs(node, refs = []) {
2561
+ if (Array.isArray(node)) {
2562
+ for (const item of node) {
2563
+ collectLocalRefs(item, refs);
2564
+ }
2565
+ return refs;
2566
+ }
2567
+ if (!isRecord(node)) {
2568
+ return refs;
2569
+ }
2570
+ for (const [key, value] of Object.entries(node)) {
2571
+ if (key === "$ref") {
2572
+ if (typeof value === "string" && value.startsWith("#")) {
2573
+ refs.push(value);
2574
+ }
2575
+ } else if (SCHEMA_MAP_KEYWORDS.has(key) && isRecord(value)) {
2576
+ for (const subschema of Object.values(value)) {
2577
+ collectLocalRefs(subschema, refs);
2578
+ }
2579
+ } else if (
2580
+ // definitions are only reached through a `$ref`:
2581
+ key !== "$defs" && key !== "definitions" && !DATA_KEYWORDS.has(key)
2582
+ ) {
2583
+ collectLocalRefs(value, refs);
2584
+ }
2585
+ }
2586
+ return refs;
2587
+ }
2588
+ function resolveSchemaPointer(schema, pointer) {
2589
+ if (pointer === "#") {
2590
+ return schema;
2591
+ }
2592
+ if (!pointer.startsWith("#/")) {
2593
+ return void 0;
2594
+ }
2595
+ let decodedPointer;
2596
+ try {
2597
+ decodedPointer = decodeURIComponent(pointer.slice(2));
2598
+ } catch (e) {
2599
+ return void 0;
2600
+ }
2601
+ let current = schema;
2602
+ for (const rawSegment of decodedPointer.split("/")) {
2603
+ const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
2604
+ if (Array.isArray(current)) {
2605
+ current = current[Number(segment)];
2606
+ } else if (isRecord(current)) {
2607
+ current = current[segment];
2608
+ } else {
2609
+ return void 0;
2610
+ }
2611
+ }
2612
+ return current;
2613
+ }
2614
+ function isRecord(value) {
2615
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2616
+ }
2617
+
2618
+ // src/supports-native-structured-output-schema.ts
2619
+ var MAX_OPTIONAL_PROPERTIES = 10;
2620
+ var SUBSCHEMA_KEYWORDS = [
2621
+ "additionalProperties",
2622
+ "contains",
2623
+ "else",
2624
+ "if",
2625
+ "items",
2626
+ "not",
2627
+ "then"
2628
+ ];
2629
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "oneOf", "prefixItems"];
2630
+ var SUBSCHEMA_MAP_KEYWORDS = [
2631
+ "dependentSchemas",
2632
+ "patternProperties",
2633
+ "properties"
2634
+ ];
2635
+ function supportsNativeStructuredOutputSchema(schema) {
2636
+ if (schema.$ref != null || hasCircularSchemaReference(schema)) {
2637
+ return false;
2638
+ }
2639
+ const { optionalProperties, hasOpenObject } = getSchemaStats(
2640
+ schema,
2641
+ schema,
2642
+ /* @__PURE__ */ new Map()
2643
+ );
2644
+ return !hasOpenObject && optionalProperties <= MAX_OPTIONAL_PROPERTIES;
2645
+ }
2646
+ function getSchemaStats(node, root, refStats) {
2647
+ const stats = { optionalProperties: 0, hasOpenObject: false };
2648
+ if (!isRecord(node)) {
2649
+ return stats;
2650
+ }
2651
+ const add = (child) => {
2652
+ const childStats = getSchemaStats(child, root, refStats);
2653
+ stats.optionalProperties += childStats.optionalProperties;
2654
+ stats.hasOpenObject || (stats.hasOpenObject = childStats.hasOpenObject);
2655
+ };
2656
+ if (typeof node.$ref === "string" && node.$ref.startsWith("#")) {
2657
+ let resolved = refStats.get(node.$ref);
2658
+ if (resolved == null) {
2659
+ resolved = getSchemaStats(
2660
+ resolveSchemaPointer(root, node.$ref),
2661
+ root,
2662
+ refStats
2663
+ );
2664
+ refStats.set(node.$ref, resolved);
2665
+ }
2666
+ stats.optionalProperties += resolved.optionalProperties;
2667
+ stats.hasOpenObject || (stats.hasOpenObject = resolved.hasOpenObject);
2668
+ }
2669
+ if (isOpenObject(node)) {
2670
+ stats.hasOpenObject = true;
2671
+ }
2672
+ if (isRecord(node.properties)) {
2673
+ const required = new Set(Array.isArray(node.required) ? node.required : []);
2674
+ stats.optionalProperties += Object.keys(node.properties).filter(
2675
+ (name) => !required.has(name)
2676
+ ).length;
2677
+ }
2678
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
2679
+ const value = node[keyword];
2680
+ if (Array.isArray(value)) {
2681
+ value.forEach(add);
2682
+ } else {
2683
+ add(value);
2684
+ }
2685
+ }
2686
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
2687
+ const value = node[keyword];
2688
+ if (Array.isArray(value)) {
2689
+ value.forEach(add);
2690
+ }
2691
+ }
2692
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
2693
+ const value = node[keyword];
2694
+ if (isRecord(value)) {
2695
+ Object.values(value).forEach(add);
2696
+ }
2697
+ }
2698
+ return stats;
2699
+ }
2700
+ function isOpenObject(node) {
2701
+ const { additionalProperties, patternProperties, properties, type } = node;
2702
+ if (additionalProperties === true || isRecord(additionalProperties)) {
2703
+ return true;
2704
+ }
2705
+ if (isRecord(patternProperties) && Object.keys(patternProperties).length > 0) {
2706
+ return true;
2707
+ }
2708
+ const isObjectType = type === "object" || Array.isArray(type) && type.includes("object");
2709
+ return isObjectType && properties == null && additionalProperties !== false;
2710
+ }
2711
+
2519
2712
  // src/anthropic-messages-language-model.ts
2520
2713
  function getAnthropicToolsetNames(tools) {
2521
2714
  const toolsetNames = {};
@@ -2573,6 +2766,31 @@ function createCitationSource(citation, citationDocuments, generateId3) {
2573
2766
  }
2574
2767
  };
2575
2768
  }
2769
+ var TEXT_OR_THINKING_BLOCK_TYPES = /* @__PURE__ */ new Set([
2770
+ "redacted_thinking",
2771
+ "text",
2772
+ "thinking"
2773
+ ]);
2774
+ function isContinuationStopReason(stopReason) {
2775
+ return stopReason === "pause_turn" || stopReason === "tool_use";
2776
+ }
2777
+ function getJsonResponseToolFallbackText(content, stopReason, jsonResponseToolName) {
2778
+ const text = /* @__PURE__ */ new Set();
2779
+ if (isContinuationStopReason(stopReason)) {
2780
+ return text;
2781
+ }
2782
+ for (const part of content) {
2783
+ if (part.type === "tool_use" && part.name === jsonResponseToolName) {
2784
+ return /* @__PURE__ */ new Set();
2785
+ }
2786
+ if (part.type === "text") {
2787
+ text.add(part);
2788
+ } else if (!TEXT_OR_THINKING_BLOCK_TYPES.has(part.type)) {
2789
+ text.clear();
2790
+ }
2791
+ }
2792
+ return text;
2793
+ }
2576
2794
  var AnthropicMessagesLanguageModel = class {
2577
2795
  constructor(modelId, config) {
2578
2796
  this.specificationVersion = "v2";
@@ -2700,9 +2918,10 @@ var AnthropicMessagesLanguageModel = class {
2700
2918
  }
2701
2919
  }
2702
2920
  const isAnthropicModel = isKnownModel || this.modelId.includes("claude-");
2921
+ const supportsResponseSchema = (responseFormat == null ? void 0 : responseFormat.type) !== "json" || responseFormat.schema == null || supportsNativeStructuredOutputSchema(responseFormat.schema);
2703
2922
  const structureOutputMode = (_a = anthropicOptions == null ? void 0 : anthropicOptions.structuredOutputMode) != null ? _a : this.modelId.includes("claude-fable-5-1") || this.modelId.includes("claude-opus-5-5") || this.modelId.includes("claude-sonnet-5-5") ? "auto" : "jsonTool";
2704
- let useStructuredOutput = structureOutputMode === "outputFormat" || structureOutputMode === "auto" && supportsStructuredOutput;
2705
- if (!useStructuredOutput && rejectsForcedToolUse && supportsStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) {
2923
+ let useStructuredOutput = structureOutputMode === "outputFormat" || structureOutputMode === "auto" && supportsStructuredOutput && supportsResponseSchema;
2924
+ if (!useStructuredOutput && rejectsForcedToolUse && supportsStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && supportsResponseSchema) {
2706
2925
  warnings.push({
2707
2926
  type: "unsupported-setting",
2708
2927
  setting: "providerOptions.anthropic.structuredOutputMode",
@@ -3011,8 +3230,13 @@ var AnthropicMessagesLanguageModel = class {
3011
3230
  betas: toolsBetas
3012
3231
  } = await prepareTools(
3013
3232
  jsonResponseTool != null ? {
3014
- tools: [...tools != null ? tools : [], jsonResponseTool],
3015
- toolChoice: tools != null && tools.length > 0 ? { type: "required" } : { type: "tool", toolName: jsonResponseTool.name },
3233
+ tools: [
3234
+ ...(tools != null ? tools : []).filter(
3235
+ (tool) => (toolChoice == null ? void 0 : toolChoice.type) !== "none" && ((toolChoice == null ? void 0 : toolChoice.type) !== "tool" || tool.name === toolChoice.toolName)
3236
+ ),
3237
+ jsonResponseTool
3238
+ ],
3239
+ toolChoice: rejectsForcedToolUse ? { type: "auto" } : tools != null && tools.length > 0 ? { type: "required" } : { type: "tool", toolName: jsonResponseTool.name },
3016
3240
  disableParallelToolUse: true,
3017
3241
  cacheControlValidator,
3018
3242
  rejectsForcedToolUse
@@ -3118,10 +3342,15 @@ var AnthropicMessagesLanguageModel = class {
3118
3342
  });
3119
3343
  const content = [];
3120
3344
  let isJsonResponseFromTool = false;
3345
+ const jsonResponseToolFallbackText = jsonResponseToolName != null ? getJsonResponseToolFallbackText(
3346
+ response.content,
3347
+ response.stop_reason,
3348
+ jsonResponseToolName
3349
+ ) : void 0;
3121
3350
  for (const part of response.content) {
3122
3351
  switch (part.type) {
3123
3352
  case "text": {
3124
- if (jsonResponseToolName == null) {
3353
+ if (jsonResponseToolFallbackText == null || jsonResponseToolFallbackText.has(part)) {
3125
3354
  const webSearchCitations = (_a = part.citations) == null ? void 0 : _a.filter(
3126
3355
  (citation) => citation.type === "web_search_result_location"
3127
3356
  );
@@ -3500,6 +3729,16 @@ var AnthropicMessagesLanguageModel = class {
3500
3729
  let iterations = null;
3501
3730
  let contextManagement = null;
3502
3731
  let isJsonResponseFromTool = false;
3732
+ let rawStopReason;
3733
+ const heldBackText = [];
3734
+ const heldBackTextByIndex = /* @__PURE__ */ new Map();
3735
+ const createTextEnd = (id, citations) => ({
3736
+ type: "text-end",
3737
+ id,
3738
+ ...citations.length > 0 && {
3739
+ providerMetadata: { anthropic: { citations } }
3740
+ }
3741
+ });
3503
3742
  let isMessageOpen = false;
3504
3743
  let activeMessageId;
3505
3744
  let hasInvalidMessageSequence = false;
@@ -3511,7 +3750,7 @@ var AnthropicMessagesLanguageModel = class {
3511
3750
  controller.enqueue({ type: "stream-start", warnings });
3512
3751
  },
3513
3752
  transform(chunk, controller) {
3514
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k;
3753
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
3515
3754
  if (hasInvalidMessageSequence) {
3516
3755
  return;
3517
3756
  }
@@ -3533,8 +3772,21 @@ var AnthropicMessagesLanguageModel = class {
3533
3772
  return;
3534
3773
  }
3535
3774
  blockType = contentBlockType;
3775
+ if (!TEXT_OR_THINKING_BLOCK_TYPES.has(contentBlockType)) {
3776
+ heldBackText.length = 0;
3777
+ }
3536
3778
  switch (contentBlockType) {
3537
3779
  case "text": {
3780
+ if (jsonResponseToolName != null) {
3781
+ const entry = {
3782
+ id: String(value.index),
3783
+ text: "",
3784
+ citations: []
3785
+ };
3786
+ heldBackText.push(entry);
3787
+ heldBackTextByIndex.set(value.index, entry);
3788
+ return;
3789
+ }
3538
3790
  contentBlocks[value.index] = {
3539
3791
  type: "text",
3540
3792
  citations: []
@@ -3870,6 +4122,8 @@ var AnthropicMessagesLanguageModel = class {
3870
4122
  switch (deltaType) {
3871
4123
  case "text_delta": {
3872
4124
  if (jsonResponseToolName != null) {
4125
+ const entry = heldBackTextByIndex.get(value.index);
4126
+ if (entry != null) entry.text += value.delta.text;
3873
4127
  return;
3874
4128
  }
3875
4129
  controller.enqueue({
@@ -3948,8 +4202,10 @@ var AnthropicMessagesLanguageModel = class {
3948
4202
  case "citations_delta": {
3949
4203
  const citation = value.delta.citation;
3950
4204
  const contentBlock = contentBlocks[value.index];
3951
- if ((contentBlock == null ? void 0 : contentBlock.type) === "text" && citation.type === "web_search_result_location") {
3952
- contentBlock.citations.push(citation);
4205
+ if (citation.type === "web_search_result_location") {
4206
+ if ((contentBlock == null ? void 0 : contentBlock.type) === "text")
4207
+ contentBlock.citations.push(citation);
4208
+ (_b2 = heldBackTextByIndex.get(value.index)) == null ? void 0 : _b2.citations.push(citation);
3953
4209
  }
3954
4210
  const source = createCitationSource(
3955
4211
  citation,
@@ -3986,16 +4242,17 @@ var AnthropicMessagesLanguageModel = class {
3986
4242
  }
3987
4243
  isMessageOpen = true;
3988
4244
  activeMessageId = value.message.id;
4245
+ heldBackTextByIndex.clear();
3989
4246
  usage.inputTokens = value.message.usage.input_tokens;
3990
- usage.cachedInputTokens = (_b2 = value.message.usage.cache_read_input_tokens) != null ? _b2 : void 0;
4247
+ usage.cachedInputTokens = (_c = value.message.usage.cache_read_input_tokens) != null ? _c : void 0;
3991
4248
  rawUsage = {
3992
4249
  ...value.message.usage
3993
4250
  };
3994
- cacheCreationInputTokens = (_c = value.message.usage.cache_creation_input_tokens) != null ? _c : null;
4251
+ cacheCreationInputTokens = (_d = value.message.usage.cache_creation_input_tokens) != null ? _d : null;
3995
4252
  controller.enqueue({
3996
4253
  type: "response-metadata",
3997
- id: (_d = value.message.id) != null ? _d : void 0,
3998
- modelId: (_e = value.message.model) != null ? _e : void 0
4254
+ id: (_e = value.message.id) != null ? _e : void 0,
4255
+ modelId: (_f = value.message.model) != null ? _f : void 0
3999
4256
  });
4000
4257
  return;
4001
4258
  }
@@ -4009,7 +4266,7 @@ var AnthropicMessagesLanguageModel = class {
4009
4266
  if (value.usage.iterations != null) {
4010
4267
  iterations = value.usage.iterations;
4011
4268
  }
4012
- const servedByFallback = (_f = value.usage.iterations) == null ? void 0 : _f.some(
4269
+ const servedByFallback = (_g = value.usage.iterations) == null ? void 0 : _g.some(
4013
4270
  (iter) => iter.type === "fallback_message"
4014
4271
  );
4015
4272
  if (value.usage.iterations && value.usage.iterations.length > 0 && !servedByFallback) {
@@ -4028,21 +4285,22 @@ var AnthropicMessagesLanguageModel = class {
4028
4285
  }
4029
4286
  usage.outputTokens = value.usage.output_tokens;
4030
4287
  }
4031
- usage.totalTokens = ((_g = usage.inputTokens) != null ? _g : 0) + ((_h = usage.outputTokens) != null ? _h : 0);
4288
+ usage.totalTokens = ((_h = usage.inputTokens) != null ? _h : 0) + ((_i = usage.outputTokens) != null ? _i : 0);
4289
+ rawStopReason = value.delta.stop_reason;
4032
4290
  finishReason = mapAnthropicStopReason({
4033
4291
  finishReason: value.delta.stop_reason,
4034
4292
  isJsonResponseFromTool
4035
4293
  });
4036
4294
  stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
4037
- stopSequence = (_i = value.delta.stop_sequence) != null ? _i : null;
4295
+ stopSequence = (_j = value.delta.stop_sequence) != null ? _j : null;
4038
4296
  container = value.delta.container != null ? {
4039
4297
  expiresAt: value.delta.container.expires_at,
4040
4298
  id: value.delta.container.id,
4041
- skills: (_k = (_j = value.delta.container.skills) == null ? void 0 : _j.map((skill) => ({
4299
+ skills: (_l = (_k = value.delta.container.skills) == null ? void 0 : _k.map((skill) => ({
4042
4300
  type: skill.type,
4043
4301
  skillId: skill.skill_id,
4044
4302
  version: skill.version
4045
- }))) != null ? _k : null
4303
+ }))) != null ? _l : null
4046
4304
  } : null;
4047
4305
  if (value.context_management != null) {
4048
4306
  contextManagement = {
@@ -4080,6 +4338,15 @@ var AnthropicMessagesLanguageModel = class {
4080
4338
  case "message_stop": {
4081
4339
  isMessageOpen = false;
4082
4340
  activeMessageId = void 0;
4341
+ if (!isJsonResponseFromTool && !isContinuationStopReason(rawStopReason)) {
4342
+ for (const { id, text, citations } of heldBackText) {
4343
+ if (text.length === 0) continue;
4344
+ controller.enqueue({ type: "text-start", id });
4345
+ controller.enqueue({ type: "text-delta", id, delta: text });
4346
+ controller.enqueue(createTextEnd(id, citations));
4347
+ }
4348
+ }
4349
+ heldBackText.length = 0;
4083
4350
  controller.enqueue({
4084
4351
  type: "finish",
4085
4352
  finishReason,