@core-ai/anthropic 0.13.0 → 0.14.0

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.
Files changed (2) hide show
  1. package/dist/index.js +138 -39
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ import { APIError, APIUserAbortError } from "@anthropic-ai/sdk";
15
15
  import {
16
16
  AbortedError,
17
17
  asObject,
18
+ clampReasoningEffort,
18
19
  getProviderMetadata,
19
20
  ProviderError,
20
21
  ValidationError,
@@ -69,6 +70,21 @@ var MANUAL_THINKING_MODELS = /* @__PURE__ */ new Set([
69
70
  "claude-haiku-4-5",
70
71
  "claude-sonnet-3-7"
71
72
  ]);
73
+ var MANUAL_INTERLEAVED_THINKING_MODELS = /* @__PURE__ */ new Set([
74
+ "claude-opus-4-5",
75
+ "claude-sonnet-4-5",
76
+ "claude-opus-4-1",
77
+ "claude-opus-4",
78
+ "claude-sonnet-4"
79
+ ]);
80
+ var ALWAYS_RESTRICTED_SAMPLING_MODELS = /* @__PURE__ */ new Set([
81
+ "claude-fable-5",
82
+ "claude-mythos-5",
83
+ "claude-mythos-preview",
84
+ "claude-opus-4-8",
85
+ "claude-opus-4-7",
86
+ "claude-sonnet-5"
87
+ ]);
72
88
  var ANTHROPIC_ADAPTIVE_EFFORT_MAP = {
73
89
  minimal: "low",
74
90
  low: "low",
@@ -97,14 +113,21 @@ function getAnthropicThinkingMode(modelId) {
97
113
  function supportsAnthropicMaxEffort(modelId) {
98
114
  return ADAPTIVE_MAX_EFFORT_MODELS.has(normalizeModelId(modelId));
99
115
  }
116
+ function requiresAnthropicInterleavedThinkingBeta(modelId) {
117
+ return MANUAL_INTERLEAVED_THINKING_MODELS.has(normalizeModelId(modelId));
118
+ }
119
+ function restrictsAnthropicSamplingParamsAlways(modelId) {
120
+ return ALWAYS_RESTRICTED_SAMPLING_MODELS.has(normalizeModelId(modelId));
121
+ }
100
122
  function toAnthropicAdaptiveEffort(effort, supportsMaxEffort) {
101
123
  if (effort === "max") {
102
124
  return supportsMaxEffort ? "max" : "high";
103
125
  }
104
126
  return ANTHROPIC_ADAPTIVE_EFFORT_MAP[effort];
105
127
  }
106
- function toAnthropicManualBudget(effort) {
107
- return ANTHROPIC_MANUAL_BUDGET_MAP[effort];
128
+ function toAnthropicManualBudget(effort, maxTokens) {
129
+ const targetBudget = ANTHROPIC_MANUAL_BUDGET_MAP[effort];
130
+ return maxTokens === void 0 ? targetBudget : Math.min(targetBudget, maxTokens - 1);
108
131
  }
109
132
 
110
133
  // src/provider-options.ts
@@ -198,18 +221,17 @@ function convertMessages(messages) {
198
221
  });
199
222
  continue;
200
223
  }
201
- if (part.text.length === 0) {
224
+ if (typeof signature === "string") {
225
+ contentBlocks.push({
226
+ type: "thinking",
227
+ thinking: part.text,
228
+ signature
229
+ });
202
230
  continue;
203
231
  }
204
- if (typeof signature !== "string") {
232
+ if (part.text.length > 0) {
205
233
  contentBlocks.push({ type: "text", text: part.text });
206
- continue;
207
234
  }
208
- contentBlocks.push({
209
- type: "thinking",
210
- thinking: part.text,
211
- signature
212
- });
213
235
  }
214
236
  convertedMessages.push({
215
237
  role: "assistant",
@@ -395,13 +417,23 @@ function createStreamRequest(modelId, defaultMaxTokens, options) {
395
417
  return mapAnthropicProviderOptionsToRequest(baseRequest, anthropicOptions);
396
418
  }
397
419
  function createRequestBase(modelId, defaultMaxTokens, options, anthropicOptions) {
398
- validateAnthropicReasoningConfig(modelId, options, anthropicOptions);
420
+ const maxTokens = options.maxTokens ?? defaultMaxTokens;
421
+ validateAnthropicReasoningConfig(
422
+ modelId,
423
+ maxTokens,
424
+ options,
425
+ anthropicOptions
426
+ );
399
427
  const converted = convertMessages(options.messages);
400
- const reasoningFields = mapReasoningToRequestFields(modelId, options);
428
+ const reasoningFields = mapReasoningToRequestFields(
429
+ modelId,
430
+ maxTokens,
431
+ options
432
+ );
401
433
  return {
402
434
  model: modelId,
403
435
  messages: converted.messages,
404
- max_tokens: options.maxTokens ?? defaultMaxTokens,
436
+ max_tokens: maxTokens,
405
437
  ...converted.system ? { system: converted.system } : {},
406
438
  ...options.tools && Object.keys(options.tools).length > 0 ? { tools: convertTools(options.tools) } : {},
407
439
  ...options.toolChoice ? { tool_choice: convertToolChoice(options.toolChoice) } : {},
@@ -415,11 +447,40 @@ function mapSamplingToRequestFields(options) {
415
447
  ...options.topP !== void 0 ? { top_p: options.topP } : {}
416
448
  };
417
449
  }
418
- function validateAnthropicReasoningConfig(modelId, options, anthropicOptions) {
450
+ function validateAnthropicReasoningConfig(modelId, maxTokens, options, anthropicOptions) {
451
+ const alwaysRestrictsSampling = restrictsAnthropicSamplingParamsAlways(modelId);
452
+ if (alwaysRestrictsSampling && options.temperature !== void 0 && options.temperature !== 1) {
453
+ throw new ValidationError(
454
+ `Anthropic model "${modelId}" only supports the default temperature of 1`,
455
+ void 0,
456
+ "anthropic"
457
+ );
458
+ }
459
+ if (alwaysRestrictsSampling && options.topP !== void 0 && options.topP !== 1) {
460
+ throw new ValidationError(
461
+ `Anthropic model "${modelId}" only supports the default topP of 1`,
462
+ void 0,
463
+ "anthropic"
464
+ );
465
+ }
466
+ if (alwaysRestrictsSampling && anthropicOptions?.topK !== void 0) {
467
+ throw new ValidationError(
468
+ `Anthropic model "${modelId}" does not support top_k`,
469
+ void 0,
470
+ "anthropic"
471
+ );
472
+ }
419
473
  if (!options.reasoning) {
420
474
  return;
421
475
  }
422
- if (options.temperature !== void 0) {
476
+ if (getAnthropicThinkingMode(modelId) === "manual" && maxTokens <= 1024) {
477
+ throw new ValidationError(
478
+ `Anthropic model "${modelId}" requires maxTokens greater than 1024 when reasoning is enabled`,
479
+ void 0,
480
+ "anthropic"
481
+ );
482
+ }
483
+ if (options.temperature !== void 0 && (!alwaysRestrictsSampling || options.temperature !== 1)) {
423
484
  throw new ValidationError(
424
485
  `Anthropic model "${modelId}" does not support temperature when reasoning is enabled`,
425
486
  void 0,
@@ -448,20 +509,25 @@ function validateAnthropicReasoningConfig(modelId, options, anthropicOptions) {
448
509
  );
449
510
  }
450
511
  }
451
- function mapReasoningToRequestFields(modelId, options) {
512
+ function mapReasoningToRequestFields(modelId, maxTokens, options) {
452
513
  if (!options.reasoning) {
453
514
  return {};
454
515
  }
455
516
  const thinkingMode = getAnthropicThinkingMode(modelId);
517
+ const capabilities = getAnthropicModelCapabilities(modelId);
518
+ const effort = clampReasoningEffort(
519
+ options.reasoning.effort,
520
+ capabilities.reasoning.supportedEfforts
521
+ );
456
522
  const baseFields = {};
457
- if (options.tools && Object.keys(options.tools).length > 0) {
458
- baseFields["betas"] = ["interleaved-thinking-2025-05-14"];
459
- }
460
523
  if (thinkingMode === "adaptive") {
461
- baseFields["thinking"] = { type: "adaptive" };
524
+ baseFields["thinking"] = {
525
+ type: "adaptive",
526
+ display: "summarized"
527
+ };
462
528
  baseFields["output_config"] = {
463
529
  effort: toAnthropicAdaptiveEffort(
464
- options.reasoning.effort,
530
+ effort,
465
531
  supportsAnthropicMaxEffort(modelId)
466
532
  )
467
533
  };
@@ -469,10 +535,22 @@ function mapReasoningToRequestFields(modelId, options) {
469
535
  }
470
536
  baseFields["thinking"] = {
471
537
  type: "enabled",
472
- budget_tokens: toAnthropicManualBudget(options.reasoning.effort)
538
+ budget_tokens: toAnthropicManualBudget(effort, maxTokens),
539
+ display: "summarized"
473
540
  };
474
541
  return baseFields;
475
542
  }
543
+ function getAnthropicRequestBetas(modelId, options) {
544
+ const providerOptions = parseAnthropicGenerateProviderOptions(
545
+ options.providerOptions
546
+ );
547
+ const configuredBetas = providerOptions?.betas ?? [];
548
+ const shouldEnableInterleavedThinking = options.reasoning !== void 0 && options.tools !== void 0 && Object.keys(options.tools).length > 0 && requiresAnthropicInterleavedThinkingBeta(modelId);
549
+ return uniqueStrings([
550
+ ...configuredBetas,
551
+ ...shouldEnableInterleavedThinking ? ["interleaved-thinking-2025-05-14"] : []
552
+ ]);
553
+ }
476
554
  function mapAnthropicProviderOptionsToRequest(baseRequest, providerOptions) {
477
555
  if (!providerOptions) {
478
556
  return baseRequest;
@@ -484,17 +562,12 @@ function mapAnthropicProviderOptionsToRequest(baseRequest, providerOptions) {
484
562
  ...baseOutputConfig,
485
563
  ...providerOptions.outputConfig ?? {}
486
564
  };
487
- const mergedBetas = [
488
- ...asStringArray(baseRequest.betas),
489
- ...providerOptions.betas ?? []
490
- ];
491
565
  const mergedRequest = {
492
566
  ...baseRequest,
493
567
  ...providerOptions.cacheControl ? { cache_control: providerOptions.cacheControl } : {},
494
568
  ...providerOptions.topK !== void 0 ? { top_k: providerOptions.topK } : {},
495
569
  ...providerOptions.stopSequences ? { stop_sequences: providerOptions.stopSequences } : {},
496
- ...Object.keys(mergedOutputConfig).length > 0 ? { output_config: mergedOutputConfig } : {},
497
- ...mergedBetas.length > 0 ? { betas: uniqueStrings(mergedBetas) } : {}
570
+ ...Object.keys(mergedOutputConfig).length > 0 ? { output_config: mergedOutputConfig } : {}
498
571
  };
499
572
  return mergedRequest;
500
573
  }
@@ -563,7 +636,7 @@ function mapGenerateResponse(response) {
563
636
  cacheReadTokens,
564
637
  cacheWriteTokens
565
638
  },
566
- outputTokenDetails: {}
639
+ outputTokenDetails: mapAnthropicOutputTokenDetails(response.usage)
567
640
  }
568
641
  };
569
642
  }
@@ -594,7 +667,9 @@ async function* transformStream(stream) {
594
667
  cacheReadTokens,
595
668
  cacheWriteTokens
596
669
  },
597
- outputTokenDetails: {}
670
+ outputTokenDetails: mapAnthropicOutputTokenDetails(
671
+ event.message.usage
672
+ )
598
673
  };
599
674
  continue;
600
675
  }
@@ -606,6 +681,12 @@ async function* transformStream(stream) {
606
681
  };
607
682
  continue;
608
683
  }
684
+ if (event.content_block.type === "text") {
685
+ yield {
686
+ type: "text-start"
687
+ };
688
+ continue;
689
+ }
609
690
  if (event.content_block.type === "tool_use") {
610
691
  const block = event.content_block;
611
692
  const initialArguments = block.input && typeof block.input === "object" ? Object.keys(block.input).length > 0 ? JSON.stringify(block.input) : "" : "";
@@ -663,6 +744,13 @@ async function* transformStream(stream) {
663
744
  continue;
664
745
  }
665
746
  if (event.type === "content_block_stop") {
747
+ if (contentBlockTypeByIndex.get(event.index) === "text") {
748
+ contentBlockTypeByIndex.delete(event.index);
749
+ yield {
750
+ type: "text-end"
751
+ };
752
+ continue;
753
+ }
666
754
  if (contentBlockTypeByIndex.get(event.index) === "thinking") {
667
755
  const signature = reasoningSignatureByIndex.get(event.index);
668
756
  reasoningSignatureByIndex.delete(event.index);
@@ -703,7 +791,10 @@ async function* transformStream(stream) {
703
791
  cacheReadTokens,
704
792
  cacheWriteTokens
705
793
  },
706
- outputTokenDetails: {}
794
+ outputTokenDetails: {
795
+ ...usage.outputTokenDetails,
796
+ ...mapAnthropicOutputTokenDetails(event.usage)
797
+ }
707
798
  };
708
799
  continue;
709
800
  }
@@ -729,11 +820,10 @@ function mapStopReason(reason) {
729
820
  }
730
821
  return "unknown";
731
822
  }
732
- function asStringArray(value) {
733
- if (!Array.isArray(value)) {
734
- return [];
735
- }
736
- return value.filter((item) => typeof item === "string");
823
+ function mapAnthropicOutputTokenDetails(usage) {
824
+ const outputTokenDetails = asObject(asObject(usage).output_tokens_details);
825
+ const reasoningTokens = outputTokenDetails.thinking_tokens;
826
+ return typeof reasoningTokens === "number" ? { reasoningTokens } : {};
737
827
  }
738
828
  function uniqueStrings(values) {
739
829
  return [...new Set(values)];
@@ -776,9 +866,14 @@ function wrapError(error) {
776
866
  // src/chat-model.ts
777
867
  function createAnthropicChatModel(client, modelId, defaultMaxTokens) {
778
868
  const provider = "anthropic";
779
- async function callAnthropicMessagesApi(request, signal) {
869
+ async function callAnthropicMessagesApi(request, betas, signal) {
780
870
  try {
781
871
  return await client.messages.create(request, {
872
+ ...betas.length > 0 ? {
873
+ headers: {
874
+ "anthropic-beta": betas.join(",")
875
+ }
876
+ } : {},
782
877
  signal
783
878
  });
784
879
  } catch (error) {
@@ -791,14 +886,18 @@ function createAnthropicChatModel(client, modelId, defaultMaxTokens) {
791
886
  defaultMaxTokens,
792
887
  options
793
888
  );
794
- const response = await callAnthropicMessagesApi(request, options.signal);
889
+ const response = await callAnthropicMessagesApi(request, getAnthropicRequestBetas(modelId, options), options.signal);
795
890
  return mapGenerateResponse(response);
796
891
  }
797
892
  async function streamChat(options) {
798
893
  const request = createStreamRequest(modelId, defaultMaxTokens, options);
799
894
  return createChatStream(
800
895
  async () => transformStream(
801
- await callAnthropicMessagesApi(request, options.signal)
896
+ await callAnthropicMessagesApi(
897
+ request,
898
+ getAnthropicRequestBetas(modelId, options),
899
+ options.signal
900
+ )
802
901
  ),
803
902
  { signal: options.signal }
804
903
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@core-ai/anthropic",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Anthropic provider package for @core-ai/core-ai",
5
5
  "license": "MIT",
6
6
  "author": "Omnifact (https://omnifact.ai)",
@@ -44,8 +44,8 @@
44
44
  "test:watch": "vitest"
45
45
  },
46
46
  "dependencies": {
47
- "@core-ai/core-ai": "^0.13.0",
48
- "@anthropic-ai/sdk": "^0.78.0"
47
+ "@anthropic-ai/sdk": "^0.110.0",
48
+ "@core-ai/core-ai": "^0.14.0"
49
49
  },
50
50
  "peerDependencies": {
51
51
  "zod": "^4.0.0"