@ai-sdk/anthropic 3.0.135 → 3.0.137

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,20 @@
1
1
  # @ai-sdk/anthropic
2
2
 
3
+ ## 3.0.137
4
+
5
+ ### Patch Changes
6
+
7
+ - 7f91bae: fix(anthropic): use native structured outputs for Claude models that reject forced tool use (Sonnet 5.5, Opus 5.5, Fable 5.1) even when the provider disables them by default, as Amazon Bedrock and Google Vertex do. Self-referencing schemas keep the JSON response tool. Also keep `definitions` and `$defs` next to a top-level `$ref` when sanitizing structured output schemas, so the reference still resolves.
8
+ - 210ca0a: fix(anthropic): when the JSON response tool is in use but the model answers in text without calling it (for example with manual thinking, where the forced tool choice is downgraded to `auto`), return the final answer text instead of dropping it. Text before a tool call, result, or message continuation is still dropped.
9
+ - 7f91bae: fix(anthropic): keep the JSON response tool for schemas that native structured outputs cannot represent when upgrading models that reject forced tool use. Schemas with open objects (maps, free-form objects) would otherwise come back as `{}`, and schemas with more than 10 optional properties can be rejected as too complex.
10
+
11
+ ## 3.0.136
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [28252da]
16
+ - @ai-sdk/provider-utils@4.0.63
17
+
3
18
  ## 3.0.135
4
19
 
5
20
  ### Patch Changes
package/dist/index.js CHANGED
@@ -32,7 +32,7 @@ var import_provider4 = require("@ai-sdk/provider");
32
32
  var import_provider_utils28 = require("@ai-sdk/provider-utils");
33
33
 
34
34
  // src/version.ts
35
- var VERSION = true ? "3.0.135" : "0.0.0-test";
35
+ var VERSION = true ? "3.0.137" : "0.0.0-test";
36
36
 
37
37
  // src/anthropic-messages-language-model.ts
38
38
  var import_provider3 = require("@ai-sdk/provider");
@@ -3585,9 +3585,8 @@ function sanitizeDefinition(definition) {
3585
3585
  }
3586
3586
  function sanitizeSchema(schema) {
3587
3587
  const result = {};
3588
- const schemaWithDefs = schema;
3589
3588
  if (schema.$ref != null) {
3590
- return { $ref: schema.$ref };
3589
+ return { $ref: schema.$ref, ...sanitizeSchemaDefinitions(schema) };
3591
3590
  }
3592
3591
  if (schema.$schema != null) {
3593
3592
  result.$schema = schema.$schema;
@@ -3621,23 +3620,7 @@ function sanitizeSchema(schema) {
3621
3620
  if (schema.allOf != null) {
3622
3621
  result.allOf = schema.allOf.map(sanitizeDefinition);
3623
3622
  }
3624
- if (schema.definitions != null) {
3625
- result.definitions = Object.fromEntries(
3626
- Object.entries(schema.definitions).map(([name, definition]) => [
3627
- name,
3628
- sanitizeDefinition(definition)
3629
- ])
3630
- );
3631
- }
3632
- if (schemaWithDefs.$defs != null) {
3633
- const resultWithDefs = result;
3634
- resultWithDefs.$defs = Object.fromEntries(
3635
- Object.entries(schemaWithDefs.$defs).map(([name, definition]) => [
3636
- name,
3637
- sanitizeDefinition(definition)
3638
- ])
3639
- );
3640
- }
3623
+ Object.assign(result, sanitizeSchemaDefinitions(schema));
3641
3624
  if (schema.type === "object" || schema.properties != null) {
3642
3625
  if (schema.properties != null) {
3643
3626
  result.properties = Object.fromEntries(
@@ -3665,6 +3648,21 @@ ${constraintDescription}`;
3665
3648
  }
3666
3649
  return result;
3667
3650
  }
3651
+ function sanitizeSchemaDefinitions(schema) {
3652
+ const { $defs } = schema;
3653
+ const sanitizeAll = (definitions) => Object.fromEntries(
3654
+ Object.entries(definitions).map(([name, definition]) => [
3655
+ name,
3656
+ sanitizeDefinition(definition)
3657
+ ])
3658
+ );
3659
+ return {
3660
+ ...schema.definitions != null && {
3661
+ definitions: sanitizeAll(schema.definitions)
3662
+ },
3663
+ ...$defs != null && { $defs: sanitizeAll($defs) }
3664
+ };
3665
+ }
3668
3666
  function getConstraintDescription(schema) {
3669
3667
  const descriptions = DESCRIPTION_CONSTRAINT_KEYS.flatMap((key) => {
3670
3668
  const value = schema[key];
@@ -3691,6 +3689,187 @@ function isPlainObject(value) {
3691
3689
  return typeof value === "object" && value !== null && !Array.isArray(value);
3692
3690
  }
3693
3691
 
3692
+ // src/has-circular-schema-reference.ts
3693
+ var SCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
3694
+ "dependentSchemas",
3695
+ "patternProperties",
3696
+ "properties"
3697
+ ]);
3698
+ var DATA_KEYWORDS = /* @__PURE__ */ new Set(["const", "default", "enum", "examples"]);
3699
+ function hasCircularSchemaReference(schema) {
3700
+ const visiting = /* @__PURE__ */ new Set();
3701
+ const visited = /* @__PURE__ */ new Set();
3702
+ const visit = (pointer) => {
3703
+ if (visiting.has(pointer)) {
3704
+ return true;
3705
+ }
3706
+ if (visited.has(pointer)) {
3707
+ return false;
3708
+ }
3709
+ const target = resolveSchemaPointer(schema, pointer);
3710
+ if (target === void 0) {
3711
+ return false;
3712
+ }
3713
+ visiting.add(pointer);
3714
+ const isCircular = collectLocalRefs(target).some(visit);
3715
+ visiting.delete(pointer);
3716
+ visited.add(pointer);
3717
+ return isCircular;
3718
+ };
3719
+ return visit("#");
3720
+ }
3721
+ function collectLocalRefs(node, refs = []) {
3722
+ if (Array.isArray(node)) {
3723
+ for (const item of node) {
3724
+ collectLocalRefs(item, refs);
3725
+ }
3726
+ return refs;
3727
+ }
3728
+ if (!isRecord(node)) {
3729
+ return refs;
3730
+ }
3731
+ for (const [key, value] of Object.entries(node)) {
3732
+ if (key === "$ref") {
3733
+ if (typeof value === "string" && value.startsWith("#")) {
3734
+ refs.push(value);
3735
+ }
3736
+ } else if (SCHEMA_MAP_KEYWORDS.has(key) && isRecord(value)) {
3737
+ for (const subschema of Object.values(value)) {
3738
+ collectLocalRefs(subschema, refs);
3739
+ }
3740
+ } else if (
3741
+ // definitions are only reached through a `$ref`:
3742
+ key !== "$defs" && key !== "definitions" && !DATA_KEYWORDS.has(key)
3743
+ ) {
3744
+ collectLocalRefs(value, refs);
3745
+ }
3746
+ }
3747
+ return refs;
3748
+ }
3749
+ function resolveSchemaPointer(schema, pointer) {
3750
+ if (pointer === "#") {
3751
+ return schema;
3752
+ }
3753
+ if (!pointer.startsWith("#/")) {
3754
+ return void 0;
3755
+ }
3756
+ let decodedPointer;
3757
+ try {
3758
+ decodedPointer = decodeURIComponent(pointer.slice(2));
3759
+ } catch (e) {
3760
+ return void 0;
3761
+ }
3762
+ let current = schema;
3763
+ for (const rawSegment of decodedPointer.split("/")) {
3764
+ const segment = rawSegment.replace(/~1/g, "/").replace(/~0/g, "~");
3765
+ if (Array.isArray(current)) {
3766
+ current = current[Number(segment)];
3767
+ } else if (isRecord(current)) {
3768
+ current = current[segment];
3769
+ } else {
3770
+ return void 0;
3771
+ }
3772
+ }
3773
+ return current;
3774
+ }
3775
+ function isRecord(value) {
3776
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3777
+ }
3778
+
3779
+ // src/supports-native-structured-output-schema.ts
3780
+ var MAX_OPTIONAL_PROPERTIES = 10;
3781
+ var SUBSCHEMA_KEYWORDS = [
3782
+ "additionalProperties",
3783
+ "contains",
3784
+ "else",
3785
+ "if",
3786
+ "items",
3787
+ "not",
3788
+ "then"
3789
+ ];
3790
+ var SUBSCHEMA_LIST_KEYWORDS = ["allOf", "anyOf", "oneOf", "prefixItems"];
3791
+ var SUBSCHEMA_MAP_KEYWORDS = [
3792
+ "dependentSchemas",
3793
+ "patternProperties",
3794
+ "properties"
3795
+ ];
3796
+ function supportsNativeStructuredOutputSchema(schema) {
3797
+ if (hasCircularSchemaReference(schema)) {
3798
+ return false;
3799
+ }
3800
+ const { optionalProperties, hasOpenObject } = getSchemaStats(
3801
+ schema,
3802
+ schema,
3803
+ /* @__PURE__ */ new Map()
3804
+ );
3805
+ return !hasOpenObject && optionalProperties <= MAX_OPTIONAL_PROPERTIES;
3806
+ }
3807
+ function getSchemaStats(node, root, refStats) {
3808
+ const stats = { optionalProperties: 0, hasOpenObject: false };
3809
+ if (!isRecord(node)) {
3810
+ return stats;
3811
+ }
3812
+ const add = (child) => {
3813
+ const childStats = getSchemaStats(child, root, refStats);
3814
+ stats.optionalProperties += childStats.optionalProperties;
3815
+ stats.hasOpenObject || (stats.hasOpenObject = childStats.hasOpenObject);
3816
+ };
3817
+ if (typeof node.$ref === "string" && node.$ref.startsWith("#")) {
3818
+ let resolved = refStats.get(node.$ref);
3819
+ if (resolved == null) {
3820
+ resolved = getSchemaStats(
3821
+ resolveSchemaPointer(root, node.$ref),
3822
+ root,
3823
+ refStats
3824
+ );
3825
+ refStats.set(node.$ref, resolved);
3826
+ }
3827
+ stats.optionalProperties += resolved.optionalProperties;
3828
+ stats.hasOpenObject || (stats.hasOpenObject = resolved.hasOpenObject);
3829
+ }
3830
+ if (isOpenObject(node)) {
3831
+ stats.hasOpenObject = true;
3832
+ }
3833
+ if (isRecord(node.properties)) {
3834
+ const required = new Set(Array.isArray(node.required) ? node.required : []);
3835
+ stats.optionalProperties += Object.keys(node.properties).filter(
3836
+ (name) => !required.has(name)
3837
+ ).length;
3838
+ }
3839
+ for (const keyword of SUBSCHEMA_KEYWORDS) {
3840
+ const value = node[keyword];
3841
+ if (Array.isArray(value)) {
3842
+ value.forEach(add);
3843
+ } else {
3844
+ add(value);
3845
+ }
3846
+ }
3847
+ for (const keyword of SUBSCHEMA_LIST_KEYWORDS) {
3848
+ const value = node[keyword];
3849
+ if (Array.isArray(value)) {
3850
+ value.forEach(add);
3851
+ }
3852
+ }
3853
+ for (const keyword of SUBSCHEMA_MAP_KEYWORDS) {
3854
+ const value = node[keyword];
3855
+ if (isRecord(value)) {
3856
+ Object.values(value).forEach(add);
3857
+ }
3858
+ }
3859
+ return stats;
3860
+ }
3861
+ function isOpenObject(node) {
3862
+ const { additionalProperties, patternProperties, properties, type } = node;
3863
+ if (additionalProperties === true || isRecord(additionalProperties)) {
3864
+ return true;
3865
+ }
3866
+ if (isRecord(patternProperties) && Object.keys(patternProperties).length > 0) {
3867
+ return true;
3868
+ }
3869
+ const isObjectType = type === "object" || Array.isArray(type) && type.includes("object");
3870
+ return isObjectType && properties == null && additionalProperties !== false;
3871
+ }
3872
+
3694
3873
  // src/anthropic-messages-language-model.ts
3695
3874
  function createCitationSource(citation, citationDocuments, generateId3) {
3696
3875
  var _a;
@@ -3764,6 +3943,31 @@ function toToolsetMemberInput({
3764
3943
  ...typeof input === "object" && input !== null && !Array.isArray(input) ? input : {}
3765
3944
  };
3766
3945
  }
3946
+ var TEXT_OR_THINKING_BLOCK_TYPES = /* @__PURE__ */ new Set([
3947
+ "redacted_thinking",
3948
+ "text",
3949
+ "thinking"
3950
+ ]);
3951
+ function isContinuationStopReason(stopReason) {
3952
+ return stopReason === "pause_turn" || stopReason === "tool_use";
3953
+ }
3954
+ function getJsonResponseToolFallbackText(content, stopReason) {
3955
+ const text = /* @__PURE__ */ new Set();
3956
+ if (isContinuationStopReason(stopReason)) {
3957
+ return text;
3958
+ }
3959
+ for (const part of content) {
3960
+ if (part.type === "tool_use" && part.name === "json") {
3961
+ return /* @__PURE__ */ new Set();
3962
+ }
3963
+ if (part.type === "text") {
3964
+ text.add(part);
3965
+ } else if (!TEXT_OR_THINKING_BLOCK_TYPES.has(part.type)) {
3966
+ text.clear();
3967
+ }
3968
+ }
3969
+ return text;
3970
+ }
3767
3971
  var AnthropicMessagesLanguageModel = class {
3768
3972
  constructor(modelId, config) {
3769
3973
  this.specificationVersion = "v3";
@@ -3911,7 +4115,7 @@ var AnthropicMessagesLanguageModel = class {
3911
4115
  const supportsStrictTools = ((_c = this.config.supportsStrictTools) != null ? _c : true) && modelSupportsStructuredOutput;
3912
4116
  const structureOutputMode = (_d = anthropicOptions == null ? void 0 : anthropicOptions.structuredOutputMode) != null ? _d : "auto";
3913
4117
  let useStructuredOutput = structureOutputMode === "outputFormat" || structureOutputMode === "auto" && supportsStructuredOutput;
3914
- if (!useStructuredOutput && rejectsForcedToolUse && supportsStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null) {
4118
+ if (!useStructuredOutput && rejectsForcedToolUse && modelSupportsStructuredOutput && (responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && supportsNativeStructuredOutputSchema(responseFormat.schema)) {
3915
4119
  warnings.push({
3916
4120
  type: "unsupported",
3917
4121
  feature: "providerOptions.anthropic.structuredOutputMode",
@@ -4417,10 +4621,11 @@ var AnthropicMessagesLanguageModel = class {
4417
4621
  const mcpToolCalls = {};
4418
4622
  const serverToolCalls = {};
4419
4623
  let isJsonResponseFromTool = false;
4624
+ const jsonResponseToolFallbackText = usesJsonResponseTool ? getJsonResponseToolFallbackText(response.content, response.stop_reason) : void 0;
4420
4625
  for (const part of response.content) {
4421
4626
  switch (part.type) {
4422
4627
  case "text": {
4423
- if (!usesJsonResponseTool) {
4628
+ if (jsonResponseToolFallbackText == null || jsonResponseToolFallbackText.has(part)) {
4424
4629
  const webSearchCitations = (_a = part.citations) == null ? void 0 : _a.filter(
4425
4630
  (citation) => citation.type === "web_search_result_location"
4426
4631
  );
@@ -4956,6 +5161,15 @@ var AnthropicMessagesLanguageModel = class {
4956
5161
  let safeguardResults;
4957
5162
  let container = null;
4958
5163
  let isJsonResponseFromTool = false;
5164
+ const heldBackText = [];
5165
+ const heldBackTextByIndex = /* @__PURE__ */ new Map();
5166
+ const createTextEnd = (id, citations) => ({
5167
+ type: "text-end",
5168
+ id,
5169
+ ...citations.length > 0 && {
5170
+ providerMetadata: { anthropic: { citations } }
5171
+ }
5172
+ });
4959
5173
  let isMessageOpen = false;
4960
5174
  let activeMessageId;
4961
5175
  let hasInvalidMessageSequence = false;
@@ -4967,7 +5181,7 @@ var AnthropicMessagesLanguageModel = class {
4967
5181
  controller.enqueue({ type: "stream-start", warnings });
4968
5182
  },
4969
5183
  transform(chunk, controller) {
4970
- var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
5184
+ var _a2, _b2, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o;
4971
5185
  if (hasInvalidMessageSequence) {
4972
5186
  return;
4973
5187
  }
@@ -4990,9 +5204,19 @@ var AnthropicMessagesLanguageModel = class {
4990
5204
  return;
4991
5205
  }
4992
5206
  blockType = contentBlockType;
5207
+ if (!TEXT_OR_THINKING_BLOCK_TYPES.has(contentBlockType)) {
5208
+ heldBackText.length = 0;
5209
+ }
4993
5210
  switch (contentBlockType) {
4994
5211
  case "text": {
4995
5212
  if (usesJsonResponseTool) {
5213
+ const entry = {
5214
+ id: String(value.index),
5215
+ text: "",
5216
+ citations: []
5217
+ };
5218
+ heldBackText.push(entry);
5219
+ heldBackTextByIndex.set(value.index, entry);
4996
5220
  return;
4997
5221
  }
4998
5222
  contentBlocks[value.index] = {
@@ -5447,17 +5671,12 @@ var AnthropicMessagesLanguageModel = class {
5447
5671
  const contentBlock = contentBlocks[value.index];
5448
5672
  switch (contentBlock.type) {
5449
5673
  case "text": {
5450
- controller.enqueue({
5451
- type: "text-end",
5452
- id: String(value.index),
5453
- ...contentBlock.citations.length > 0 && {
5454
- providerMetadata: {
5455
- anthropic: {
5456
- citations: contentBlock.citations
5457
- }
5458
- }
5459
- }
5460
- });
5674
+ controller.enqueue(
5675
+ createTextEnd(
5676
+ String(value.index),
5677
+ contentBlock.citations
5678
+ )
5679
+ );
5461
5680
  break;
5462
5681
  }
5463
5682
  case "reasoning": {
@@ -5543,6 +5762,10 @@ var AnthropicMessagesLanguageModel = class {
5543
5762
  switch (deltaType) {
5544
5763
  case "text_delta": {
5545
5764
  if (usesJsonResponseTool) {
5765
+ const entry = heldBackTextByIndex.get(value.index);
5766
+ if (entry != null) {
5767
+ entry.text += value.delta.text;
5768
+ }
5546
5769
  return;
5547
5770
  }
5548
5771
  controller.enqueue({
@@ -5624,8 +5847,11 @@ var AnthropicMessagesLanguageModel = class {
5624
5847
  case "citations_delta": {
5625
5848
  const citation = value.delta.citation;
5626
5849
  const contentBlock = contentBlocks[value.index];
5627
- if ((contentBlock == null ? void 0 : contentBlock.type) === "text" && citation.type === "web_search_result_location") {
5628
- contentBlock.citations.push(citation);
5850
+ if (citation.type === "web_search_result_location") {
5851
+ if ((contentBlock == null ? void 0 : contentBlock.type) === "text") {
5852
+ contentBlock.citations.push(citation);
5853
+ }
5854
+ (_e = heldBackTextByIndex.get(value.index)) == null ? void 0 : _e.citations.push(citation);
5629
5855
  }
5630
5856
  const source = createCitationSource(
5631
5857
  citation,
@@ -5662,13 +5888,14 @@ var AnthropicMessagesLanguageModel = class {
5662
5888
  }
5663
5889
  isMessageOpen = true;
5664
5890
  activeMessageId = value.message.id;
5891
+ heldBackTextByIndex.clear();
5665
5892
  usage.input_tokens = value.message.usage.input_tokens;
5666
- usage.cache_read_input_tokens = (_e = value.message.usage.cache_read_input_tokens) != null ? _e : 0;
5667
- usage.cache_creation_input_tokens = (_f = value.message.usage.cache_creation_input_tokens) != null ? _f : 0;
5893
+ usage.cache_read_input_tokens = (_f = value.message.usage.cache_read_input_tokens) != null ? _f : 0;
5894
+ usage.cache_creation_input_tokens = (_g = value.message.usage.cache_creation_input_tokens) != null ? _g : 0;
5668
5895
  rawUsage = {
5669
5896
  ...value.message.usage
5670
5897
  };
5671
- cacheCreationInputTokens = (_g = value.message.usage.cache_creation_input_tokens) != null ? _g : null;
5898
+ cacheCreationInputTokens = (_h = value.message.usage.cache_creation_input_tokens) != null ? _h : null;
5672
5899
  if (value.message.input_transformations != null) {
5673
5900
  inputTransformations = value.message.input_transformations;
5674
5901
  }
@@ -5690,8 +5917,8 @@ var AnthropicMessagesLanguageModel = class {
5690
5917
  }
5691
5918
  controller.enqueue({
5692
5919
  type: "response-metadata",
5693
- id: (_h = value.message.id) != null ? _h : void 0,
5694
- modelId: (_i = value.message.model) != null ? _i : void 0
5920
+ id: (_i = value.message.id) != null ? _i : void 0,
5921
+ modelId: (_j = value.message.model) != null ? _j : void 0
5695
5922
  });
5696
5923
  if (value.message.content != null) {
5697
5924
  for (let contentIndex = 0; contentIndex < value.message.content.length; contentIndex++) {
@@ -5703,7 +5930,7 @@ var AnthropicMessagesLanguageModel = class {
5703
5930
  id: part.id,
5704
5931
  toolName: part.name
5705
5932
  });
5706
- const inputStr = JSON.stringify((_j = part.input) != null ? _j : {});
5933
+ const inputStr = JSON.stringify((_k = part.input) != null ? _k : {});
5707
5934
  controller.enqueue({
5708
5935
  type: "tool-input-delta",
5709
5936
  id: part.id,
@@ -5754,18 +5981,18 @@ var AnthropicMessagesLanguageModel = class {
5754
5981
  finishReason: value.delta.stop_reason,
5755
5982
  isJsonResponseFromTool
5756
5983
  }),
5757
- raw: (_k = value.delta.stop_reason) != null ? _k : void 0
5984
+ raw: (_l = value.delta.stop_reason) != null ? _l : void 0
5758
5985
  };
5759
- stopSequence = (_l = value.delta.stop_sequence) != null ? _l : null;
5986
+ stopSequence = (_m = value.delta.stop_sequence) != null ? _m : null;
5760
5987
  stopDetails = mapAnthropicStopDetails(value.delta.stop_details);
5761
5988
  container = value.delta.container != null ? {
5762
5989
  expiresAt: value.delta.container.expires_at,
5763
5990
  id: value.delta.container.id,
5764
- skills: (_n = (_m = value.delta.container.skills) == null ? void 0 : _m.map((skill) => ({
5991
+ skills: (_o = (_n = value.delta.container.skills) == null ? void 0 : _n.map((skill) => ({
5765
5992
  type: skill.type,
5766
5993
  skillId: skill.skill_id,
5767
5994
  version: skill.version
5768
- }))) != null ? _n : null
5995
+ }))) != null ? _o : null
5769
5996
  } : null;
5770
5997
  if (value.context_management) {
5771
5998
  contextManagement = mapAnthropicResponseContextManagement(
@@ -5817,6 +6044,15 @@ var AnthropicMessagesLanguageModel = class {
5817
6044
  if (usedCustomProviderKey && providerOptionsName !== "anthropic") {
5818
6045
  providerMetadata[providerOptionsName] = anthropicMetadata;
5819
6046
  }
6047
+ if (!isJsonResponseFromTool && !isContinuationStopReason(finishReason.raw)) {
6048
+ for (const { id, text, citations } of heldBackText) {
6049
+ if (text.length === 0) continue;
6050
+ controller.enqueue({ type: "text-start", id });
6051
+ controller.enqueue({ type: "text-delta", id, delta: text });
6052
+ controller.enqueue(createTextEnd(id, citations));
6053
+ }
6054
+ }
6055
+ heldBackText.length = 0;
5820
6056
  controller.enqueue({
5821
6057
  type: "finish",
5822
6058
  finishReason,