@tanstack/ai-anthropic 0.18.13 → 0.19.3

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.
@@ -1,4 +1,9 @@
1
- import { EventType, normalizeSystemPrompts } from '@tanstack/ai'
1
+ import {
2
+ EventType,
3
+ fileReferenceFor,
4
+ isFileSource,
5
+ normalizeSystemPrompts,
6
+ } from '@tanstack/ai'
2
7
  import { toRunErrorRawEvent } from '@tanstack/ai/adapter-internals'
3
8
  import { BaseTextAdapter } from '@tanstack/ai/adapters'
4
9
  import { convertToolsToProviderFormat } from '../tools/tool-converter'
@@ -30,20 +35,25 @@ import type {
30
35
  } from '@tanstack/ai/adapters'
31
36
  import type { InternalLogger } from '@tanstack/ai/adapter-internals'
32
37
  import type {
33
- Base64ImageSource,
34
- Base64PDFSource,
35
- ContentBlockParam,
36
- DocumentBlockParam,
37
- ImageBlockParam,
38
38
  ServerToolUseBlockParam,
39
39
  TextBlockParam,
40
40
  ThinkingBlockParam,
41
41
  ToolUseBlockParam,
42
- URLImageSource,
43
- URLPDFSource,
44
42
  WebFetchToolResultBlockParam,
45
43
  WebSearchToolResultBlockParam,
46
44
  } from '@anthropic-ai/sdk/resources/messages'
45
+ import type {
46
+ BetaBase64ImageSource,
47
+ BetaBase64PDFSource,
48
+ BetaContentBlockParam,
49
+ BetaFileDocumentSource,
50
+ BetaFileImageSource,
51
+ BetaImageBlockParam,
52
+ BetaRequestDocumentBlock,
53
+ BetaTextBlockParam,
54
+ BetaURLImageSource,
55
+ BetaURLPDFSource,
56
+ } from '@anthropic-ai/sdk/resources/beta/messages'
47
57
  import type Anthropic_SDK from '@anthropic-ai/sdk'
48
58
  import type { AnthropicBeta } from '@anthropic-ai/sdk/resources/beta/beta'
49
59
  import type {
@@ -147,12 +157,27 @@ function buildServerToolResultBlock(
147
157
  }
148
158
  }
149
159
 
160
+ /**
161
+ * True when any message carries a provider file-handle source, so the request
162
+ * must send the Files API beta header.
163
+ */
164
+ export function messagesHaveFileSource(messages: Array<ModelMessage>): boolean {
165
+ return messages.some(
166
+ (message) =>
167
+ Array.isArray(message.content) &&
168
+ message.content.some(
169
+ (part) => 'source' in part && isFileSource(part.source),
170
+ ),
171
+ )
172
+ }
173
+
150
174
  /**
151
175
  * Computes the `betas` array for a Messages request. Unions:
152
176
  * - `interleaved-thinking-2025-05-14` when interleaved thinking is enabled,
153
177
  * - `code-execution-2025-08-25` when a `code_execution` tool is present,
154
178
  * - `skills-2025-10-02` when that tool carries skills,
155
- * - `context-management-2025-06-27` when `context_management` is set.
179
+ * - `context-management-2025-06-27` when `context_management` is set,
180
+ * - `files-api-2025-04-14` when a message references an uploaded file handle.
156
181
  * Returns `undefined` when none apply (so the call site omits `betas`).
157
182
  */
158
183
  export function computeAnthropicBetas(
@@ -164,11 +189,15 @@ export function computeAnthropicBetas(
164
189
  budget_tokens?: number
165
190
  }
166
191
  context_management?: unknown | null
192
+ mcp_servers?: ReadonlyArray<unknown>
167
193
  }
168
194
  | undefined,
195
+ hasFileSource = false,
169
196
  ): Array<AnthropicBeta> | undefined {
170
197
  const betas = new Set<AnthropicBeta>()
171
198
 
199
+ if (hasFileSource) betas.add('files-api-2025-04-14')
200
+
172
201
  const useInterleavedThinking =
173
202
  modelOptions?.thinking?.type === 'enabled' &&
174
203
  typeof modelOptions.thinking.budget_tokens === 'number' &&
@@ -181,6 +210,15 @@ export function computeAnthropicBetas(
181
210
  betas.add('context-management-2025-06-27')
182
211
  }
183
212
 
213
+ // The MCP connector needs its beta header as well (same shape as #1074);
214
+ // an empty array is a typed "unset" — do not enable the beta.
215
+ // ponytail: 2025-04-04 matches the `mcp_servers` shape we type
216
+ // (`tool_configuration` on each server). 2025-11-20 needs an `mcp_toolset`
217
+ // in `tools` for every server, which this adapter does not send yet.
218
+ if (modelOptions?.mcp_servers && modelOptions.mcp_servers.length > 0) {
219
+ betas.add('mcp-client-2025-04-04')
220
+ }
221
+
184
222
  // Code-execution beta is version-aware: select from the FIRST code_execution
185
223
  // tool's config type.
186
224
  const codeExecTool = tools?.find(
@@ -301,6 +339,8 @@ export class AnthropicTextAdapter<
301
339
  > {
302
340
  override readonly kind = 'text' as const
303
341
  readonly name = 'anthropic' as const
342
+ // Consumes `file_id` sources issued by anthropicFiles() (Files API beta).
343
+ override readonly supportsFileSources = true
304
344
 
305
345
  private readonly client: SdkAnthropicMessagesClient
306
346
 
@@ -326,7 +366,11 @@ export class AnthropicTextAdapter<
326
366
 
327
367
  // `betas` is attached at the call site rather than in the shared mapper
328
368
  // because the beta set depends on both the tools and the modelOptions.
329
- const betas = computeAnthropicBetas(options.tools, options.modelOptions)
369
+ const betas = computeAnthropicBetas(
370
+ options.tools,
371
+ options.modelOptions,
372
+ messagesHaveFileSource(options.messages),
373
+ )
330
374
 
331
375
  // `client.beta.messages` is Anthropic's permanent staging surface, not a
332
376
  // sunset path: it's a superset of `client.messages` that additionally
@@ -416,6 +460,7 @@ export class AnthropicTextAdapter<
416
460
  const betas = computeAnthropicBetas(
417
461
  chatOptions.tools,
418
462
  chatOptions.modelOptions,
463
+ messagesHaveFileSource(chatOptions.messages),
419
464
  )
420
465
  // Make non-streaming request with tool_choice forced to our structured output tool
421
466
  const response = await this.client.beta.messages.create(
@@ -657,7 +702,7 @@ export class AnthropicTextAdapter<
657
702
 
658
703
  private convertContentPartToAnthropic(
659
704
  part: ContentPart,
660
- ): TextBlockParam | ImageBlockParam | DocumentBlockParam {
705
+ ): BetaTextBlockParam | BetaImageBlockParam | BetaRequestDocumentBlock {
661
706
  switch (part.type) {
662
707
  case 'text': {
663
708
  const metadata = part.metadata as AnthropicTextMetadata | undefined
@@ -670,21 +715,31 @@ export class AnthropicTextAdapter<
670
715
 
671
716
  case 'image': {
672
717
  const metadata = part.metadata as AnthropicImageMetadata | undefined
673
- const imageSource: Base64ImageSource | URLImageSource =
674
- part.source.type === 'data'
675
- ? {
676
- type: 'base64',
677
- data: part.source.value,
678
- media_type: part.source.mimeType as
679
- | 'image/jpeg'
680
- | 'image/png'
681
- | 'image/gif'
682
- | 'image/webp',
683
- }
684
- : {
685
- type: 'url',
686
- url: part.source.value,
687
- }
718
+ let imageSource:
719
+ | BetaBase64ImageSource
720
+ | BetaURLImageSource
721
+ | BetaFileImageSource
722
+ if (isFileSource(part.source)) {
723
+ imageSource = {
724
+ type: 'file',
725
+ file_id: fileReferenceFor(part.source, this.name),
726
+ }
727
+ } else if (part.source.type === 'data') {
728
+ imageSource = {
729
+ type: 'base64',
730
+ data: part.source.value,
731
+ media_type: part.source.mimeType as
732
+ | 'image/jpeg'
733
+ | 'image/png'
734
+ | 'image/gif'
735
+ | 'image/webp',
736
+ }
737
+ } else {
738
+ imageSource = {
739
+ type: 'url',
740
+ url: part.source.value,
741
+ }
742
+ }
688
743
  return {
689
744
  type: 'image',
690
745
  source: imageSource,
@@ -696,17 +751,27 @@ export class AnthropicTextAdapter<
696
751
  case 'document': {
697
752
  const metadata = part.metadata as AnthropicDocumentMetadata | undefined
698
753
  const title = metadata?.title ?? metadata?.filename
699
- const docSource: Base64PDFSource | URLPDFSource =
700
- part.source.type === 'data'
701
- ? {
702
- type: 'base64',
703
- data: part.source.value,
704
- media_type: part.source.mimeType as 'application/pdf',
705
- }
706
- : {
707
- type: 'url',
708
- url: part.source.value,
709
- }
754
+ let docSource:
755
+ | BetaBase64PDFSource
756
+ | BetaURLPDFSource
757
+ | BetaFileDocumentSource
758
+ if (isFileSource(part.source)) {
759
+ docSource = {
760
+ type: 'file',
761
+ file_id: fileReferenceFor(part.source, this.name),
762
+ }
763
+ } else if (part.source.type === 'data') {
764
+ docSource = {
765
+ type: 'base64',
766
+ data: part.source.value,
767
+ media_type: part.source.mimeType as 'application/pdf',
768
+ }
769
+ } else {
770
+ docSource = {
771
+ type: 'url',
772
+ url: part.source.value,
773
+ }
774
+ }
710
775
  return {
711
776
  type: 'document',
712
777
  source: docSource,
@@ -766,7 +831,7 @@ export class AnthropicTextAdapter<
766
831
  }
767
832
 
768
833
  if (role === 'assistant' && message.toolCalls?.length) {
769
- const contentBlocks: Array<ContentBlockParam> = []
834
+ const contentBlocks: Array<BetaContentBlockParam> = []
770
835
 
771
836
  this.appendThinkingBlocks(contentBlocks, message.thinking)
772
837
 
@@ -828,7 +893,7 @@ export class AnthropicTextAdapter<
828
893
  }
829
894
 
830
895
  if (role === 'assistant') {
831
- const contentBlocks: Array<ContentBlockParam> = []
896
+ const contentBlocks: Array<BetaContentBlockParam> = []
832
897
  this.appendThinkingBlocks(contentBlocks, message.thinking)
833
898
 
834
899
  if (Array.isArray(message.content)) {
@@ -880,7 +945,7 @@ export class AnthropicTextAdapter<
880
945
  }
881
946
 
882
947
  private appendThinkingBlocks(
883
- contentBlocks: Array<ContentBlockParam>,
948
+ contentBlocks: Array<BetaContentBlockParam>,
884
949
  thinkingParts: ModelMessage['thinking'],
885
950
  ): void {
886
951
  if (!thinkingParts?.length) return
package/src/index.ts CHANGED
@@ -22,6 +22,14 @@ export {
22
22
  type AnthropicSummarizeConfig,
23
23
  type AnthropicSummarizeModel,
24
24
  } from './adapters/summarize'
25
+
26
+ // Files adapter - upload media to the Anthropic Files API (beta) by file_id
27
+ export {
28
+ AnthropicFilesAdapter,
29
+ createAnthropicFiles,
30
+ anthropicFiles,
31
+ type AnthropicFilesConfig,
32
+ } from './adapters/files'
25
33
  // ============================================================================
26
34
  // Type Exports
27
35
  // ============================================================================
package/src/model-meta.ts CHANGED
@@ -608,7 +608,48 @@ const CLAUDE_FABLE_5_1 = {
608
608
  AnthropicOutputConfigOptions
609
609
  >
610
610
 
611
+ const CLAUDE_OPUS_5_5 = {
612
+ name: 'claude-opus-5-5',
613
+ id: 'claude-opus-5-5',
614
+ context_window: 1_000_000,
615
+ max_output_tokens: 128_000,
616
+ supports: {
617
+ input: ['text', 'image', 'document'],
618
+ // No 'computer_use': this model accepts only `computer_toolset_20260801`,
619
+ // and `computerUseTool()` sends the older versions, which return a 400.
620
+ tools: [
621
+ 'web_search',
622
+ 'web_fetch',
623
+ 'code_execution',
624
+ 'bash',
625
+ 'text_editor',
626
+ 'memory',
627
+ ],
628
+ },
629
+ pricing: {
630
+ input: {
631
+ normal: 4,
632
+ cached: 0.2,
633
+ },
634
+ output: {
635
+ normal: 20,
636
+ },
637
+ },
638
+ } as const satisfies ModelMeta<
639
+ AnthropicCacheControlOptions &
640
+ AnthropicContainerOptions &
641
+ AnthropicContextManagementOptions &
642
+ AnthropicMCPOptions &
643
+ AnthropicServiceTierOptions &
644
+ AnthropicStopSequencesOptions &
645
+ AnthropicAdaptiveOnlyThinkingOptions &
646
+ AnthropicToolChoiceOptions &
647
+ AnthropicSamplingOptions &
648
+ AnthropicOutputConfigOptions
649
+ >
650
+
611
651
  export const ANTHROPIC_MODELS = [
652
+ CLAUDE_OPUS_5_5.id,
612
653
  CLAUDE_FABLE_5_1.id,
613
654
  CLAUDE_OPUS_5.id,
614
655
  CLAUDE_OPUS_5_FAST.id,
@@ -686,6 +727,7 @@ const ANTHROPIC_MODEL_MAX_OUTPUT_TOKENS: Record<string, number> = {
686
727
  [CLAUDE_OPUS_5.id]: CLAUDE_OPUS_5.max_output_tokens,
687
728
  [CLAUDE_OPUS_5_FAST.id]: CLAUDE_OPUS_5_FAST.max_output_tokens,
688
729
  [CLAUDE_FABLE_5_1.id]: CLAUDE_FABLE_5_1.max_output_tokens,
730
+ [CLAUDE_OPUS_5_5.id]: CLAUDE_OPUS_5_5.max_output_tokens,
689
731
  }
690
732
 
691
733
  /**
@@ -741,6 +783,7 @@ export const ANTHROPIC_COMBINED_TOOLS_AND_SCHEMA_MODELS = new Set<string>([
741
783
  CLAUDE_OPUS_5.id,
742
784
  CLAUDE_FABLE_5.id,
743
785
  CLAUDE_FABLE_5_1.id,
786
+ CLAUDE_OPUS_5_5.id,
744
787
  CLAUDE_SONNET_5.id,
745
788
  CLAUDE_SONNET_4_5.id,
746
789
  CLAUDE_SONNET_4_6.id,
@@ -891,6 +934,16 @@ export type AnthropicChatModelProviderOptionsByName = {
891
934
  AnthropicToolChoiceOptions &
892
935
  AnthropicMaxTokensOptions &
893
936
  AnthropicOutputConfigOptions
937
+ [CLAUDE_OPUS_5_5.id]: AnthropicCacheControlOptions &
938
+ AnthropicContainerOptions &
939
+ AnthropicContextManagementOptions &
940
+ AnthropicMCPOptions &
941
+ AnthropicServiceTierOptions &
942
+ AnthropicStopSequencesOptions &
943
+ AnthropicAdaptiveOnlyThinkingOptions &
944
+ AnthropicToolChoiceOptions &
945
+ AnthropicSamplingOptions &
946
+ AnthropicOutputConfigOptions
894
947
  }
895
948
 
896
949
  export type AnthropicChatModelToolCapabilitiesByName = {
@@ -907,6 +960,7 @@ export type AnthropicChatModelToolCapabilitiesByName = {
907
960
  [CLAUDE_OPUS_5.id]: typeof CLAUDE_OPUS_5.supports.tools
908
961
  [CLAUDE_OPUS_5_FAST.id]: typeof CLAUDE_OPUS_5_FAST.supports.tools
909
962
  [CLAUDE_FABLE_5_1.id]: typeof CLAUDE_FABLE_5_1.supports.tools
963
+ [CLAUDE_OPUS_5_5.id]: typeof CLAUDE_OPUS_5_5.supports.tools
910
964
  }
911
965
 
912
966
  /**
@@ -934,4 +988,5 @@ export type AnthropicModelInputModalitiesByName = {
934
988
  [CLAUDE_OPUS_5.id]: typeof CLAUDE_OPUS_5.supports.input
935
989
  [CLAUDE_OPUS_5_FAST.id]: typeof CLAUDE_OPUS_5_FAST.supports.input
936
990
  [CLAUDE_FABLE_5_1.id]: typeof CLAUDE_FABLE_5_1.supports.input
991
+ [CLAUDE_OPUS_5_5.id]: typeof CLAUDE_OPUS_5_5.supports.input
937
992
  }
@@ -1,15 +1,13 @@
1
1
  import type {
2
2
  BetaContextManagementConfig,
3
+ BetaMessageParam,
3
4
  BetaToolChoiceAny,
4
5
  BetaToolChoiceAuto,
5
6
  BetaToolChoiceTool,
6
7
  } from '@anthropic-ai/sdk/resources/beta/messages/messages'
7
8
  import type { CacheControlEphemeral } from '@anthropic-ai/sdk/resources'
8
9
  import type { AnthropicContainerSkill, AnthropicTool } from '../tools/index'
9
- import type {
10
- MessageParam,
11
- TextBlockParam,
12
- } from '@anthropic-ai/sdk/resources/messages'
10
+ import type { TextBlockParam } from '@anthropic-ai/sdk/resources/messages'
13
11
 
14
12
  /**
15
13
  * Per-prompt metadata Anthropic understands on `systemPrompts` entries.
@@ -320,7 +318,7 @@ export type ExternalTextProviderOptions = AnthropicCacheControlOptions &
320
318
  export interface InternalTextProviderOptions extends ExternalTextProviderOptions {
321
319
  model: string
322
320
 
323
- messages: Array<MessageParam>
321
+ messages: Array<BetaMessageParam>
324
322
 
325
323
  /**
326
324
  * The maximum number of tokens to generate before stopping. This parameter only specifies the absolute maximum number of tokens to generate.