@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.
@@ -0,0 +1,25 @@
1
+ import { BaseFilesAdapter, FileHandle, FileUploadInput } from '@tanstack/ai/adapters';
2
+ import { AnthropicClientConfig } from '../utils/client.js';
3
+ export interface AnthropicFilesConfig extends AnthropicClientConfig {
4
+ }
5
+ /**
6
+ * Anthropic Files adapter — uploads media to the Anthropic Files API (beta) and
7
+ * references it by `file_id`. Pair with `anthropicText()`: reference the
8
+ * returned handle in an image/document message via `fileSourceFromHandle`.
9
+ */
10
+ export declare class AnthropicFilesAdapter extends BaseFilesAdapter<'anthropic'> {
11
+ readonly name: "anthropic";
12
+ private readonly client;
13
+ constructor(config: AnthropicFilesConfig);
14
+ upload(input: FileUploadInput): Promise<FileHandle<'anthropic'>>;
15
+ get(id: string): Promise<FileHandle<'anthropic'>>;
16
+ delete(id: string): Promise<void>;
17
+ }
18
+ /**
19
+ * Create an Anthropic Files adapter with an explicit API key.
20
+ */
21
+ export declare function createAnthropicFiles(apiKey: string, config?: Omit<AnthropicFilesConfig, 'apiKey'>): AnthropicFilesAdapter;
22
+ /**
23
+ * Create an Anthropic Files adapter, reading the API key from `ANTHROPIC_API_KEY`.
24
+ */
25
+ export declare function anthropicFiles(config?: Omit<AnthropicFilesConfig, 'apiKey'>): AnthropicFilesAdapter;
@@ -0,0 +1,61 @@
1
+ import { createAnthropicClient, getAnthropicApiKeyFromEnv } from "../utils/client.js";
2
+ import { BaseFilesAdapter, normalizeFileUploadInput } from "@tanstack/ai/adapters";
3
+ import { toFile } from "@anthropic-ai/sdk";
4
+ //#region src/adapters/files.ts
5
+ /** Beta header required for the Anthropic Files API. */
6
+ var FILES_API_BETA = "files-api-2025-04-14";
7
+ /**
8
+ * Anthropic Files adapter — uploads media to the Anthropic Files API (beta) and
9
+ * references it by `file_id`. Pair with `anthropicText()`: reference the
10
+ * returned handle in an image/document message via `fileSourceFromHandle`.
11
+ */
12
+ var AnthropicFilesAdapter = class extends BaseFilesAdapter {
13
+ name = "anthropic";
14
+ client;
15
+ constructor(config) {
16
+ super();
17
+ this.client = createAnthropicClient(config);
18
+ }
19
+ async upload(input) {
20
+ const { blob, mimeType, filename } = normalizeFileUploadInput(input);
21
+ const file = await toFile(blob, filename, { ...mimeType ? { type: mimeType } : {} });
22
+ return toFileHandle(await this.client.beta.files.upload({
23
+ file,
24
+ betas: [FILES_API_BETA]
25
+ }));
26
+ }
27
+ async get(id) {
28
+ return toFileHandle(await this.client.beta.files.retrieveMetadata(id, { betas: [FILES_API_BETA] }));
29
+ }
30
+ async delete(id) {
31
+ await this.client.beta.files.delete(id, { betas: [FILES_API_BETA] });
32
+ }
33
+ };
34
+ function toFileHandle(file) {
35
+ return {
36
+ id: file.id,
37
+ provider: "anthropic",
38
+ mimeType: file.mime_type,
39
+ sizeBytes: file.size_bytes,
40
+ filename: file.filename
41
+ };
42
+ }
43
+ /**
44
+ * Create an Anthropic Files adapter with an explicit API key.
45
+ */
46
+ function createAnthropicFiles(apiKey, config) {
47
+ return new AnthropicFilesAdapter({
48
+ ...config,
49
+ apiKey
50
+ });
51
+ }
52
+ /**
53
+ * Create an Anthropic Files adapter, reading the API key from `ANTHROPIC_API_KEY`.
54
+ */
55
+ function anthropicFiles(config) {
56
+ return createAnthropicFiles(getAnthropicApiKeyFromEnv(), config);
57
+ }
58
+ //#endregion
59
+ export { AnthropicFilesAdapter, anthropicFiles, createAnthropicFiles };
60
+
61
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","names":[],"sources":["../../../src/adapters/files.ts"],"sourcesContent":["import { toFile } from '@anthropic-ai/sdk'\nimport {\n BaseFilesAdapter,\n normalizeFileUploadInput,\n} from '@tanstack/ai/adapters'\nimport {\n createAnthropicClient,\n getAnthropicApiKeyFromEnv,\n} from '../utils/client'\nimport type Anthropic_SDK from '@anthropic-ai/sdk'\nimport type { FileMetadata } from '@anthropic-ai/sdk/resources/beta/files'\nimport type { FileHandle, FileUploadInput } from '@tanstack/ai/adapters'\nimport type { AnthropicClientConfig } from '../utils/client'\n\n/** Beta header required for the Anthropic Files API. */\nconst FILES_API_BETA = 'files-api-2025-04-14'\n\nexport interface AnthropicFilesConfig extends AnthropicClientConfig {}\n\n/**\n * Anthropic Files adapter — uploads media to the Anthropic Files API (beta) and\n * references it by `file_id`. Pair with `anthropicText()`: reference the\n * returned handle in an image/document message via `fileSourceFromHandle`.\n */\nexport class AnthropicFilesAdapter extends BaseFilesAdapter<'anthropic'> {\n readonly name = 'anthropic' as const\n private readonly client: Anthropic_SDK\n\n constructor(config: AnthropicFilesConfig) {\n super()\n this.client = createAnthropicClient(config)\n }\n\n async upload(input: FileUploadInput): Promise<FileHandle<'anthropic'>> {\n const { blob, mimeType, filename } = normalizeFileUploadInput(input)\n const file = await toFile(blob, filename, {\n ...(mimeType ? { type: mimeType } : {}),\n })\n const result = await this.client.beta.files.upload({\n file,\n betas: [FILES_API_BETA],\n })\n return toFileHandle(result)\n }\n\n async get(id: string): Promise<FileHandle<'anthropic'>> {\n const result = await this.client.beta.files.retrieveMetadata(id, {\n betas: [FILES_API_BETA],\n })\n return toFileHandle(result)\n }\n\n async delete(id: string): Promise<void> {\n await this.client.beta.files.delete(id, { betas: [FILES_API_BETA] })\n }\n}\n\nfunction toFileHandle(file: FileMetadata): FileHandle<'anthropic'> {\n return {\n id: file.id,\n provider: 'anthropic',\n mimeType: file.mime_type,\n sizeBytes: file.size_bytes,\n filename: file.filename,\n }\n}\n\n/**\n * Create an Anthropic Files adapter with an explicit API key.\n */\nexport function createAnthropicFiles(\n apiKey: string,\n config?: Omit<AnthropicFilesConfig, 'apiKey'>,\n): AnthropicFilesAdapter {\n return new AnthropicFilesAdapter({ ...config, apiKey })\n}\n\n/**\n * Create an Anthropic Files adapter, reading the API key from `ANTHROPIC_API_KEY`.\n */\nexport function anthropicFiles(\n config?: Omit<AnthropicFilesConfig, 'apiKey'>,\n): AnthropicFilesAdapter {\n return createAnthropicFiles(getAnthropicApiKeyFromEnv(), config)\n}\n"],"mappings":";;;;;AAeA,IAAM,iBAAiB;;;;;;AASvB,IAAa,wBAAb,cAA2C,iBAA8B;CACvE,OAAgB;CAChB;CAEA,YAAY,QAA8B;EACxC,MAAM;EACN,KAAK,SAAS,sBAAsB,MAAM;CAC5C;CAEA,MAAM,OAAO,OAA0D;EACrE,MAAM,EAAE,MAAM,UAAU,aAAa,yBAAyB,KAAK;EACnE,MAAM,OAAO,MAAM,OAAO,MAAM,UAAU,EACxC,GAAI,WAAW,EAAE,MAAM,SAAS,IAAI,CAAC,EACvC,CAAC;EAKD,OAAO,aAAa,MAJC,KAAK,OAAO,KAAK,MAAM,OAAO;GACjD;GACA,OAAO,CAAC,cAAc;EACxB,CAAC,CACyB;CAC5B;CAEA,MAAM,IAAI,IAA8C;EAItD,OAAO,aAAa,MAHC,KAAK,OAAO,KAAK,MAAM,iBAAiB,IAAI,EAC/D,OAAO,CAAC,cAAc,EACxB,CAAC,CACyB;CAC5B;CAEA,MAAM,OAAO,IAA2B;EACtC,MAAM,KAAK,OAAO,KAAK,MAAM,OAAO,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE,CAAC;CACrE;AACF;AAEA,SAAS,aAAa,MAA6C;CACjE,OAAO;EACL,IAAI,KAAK;EACT,UAAU;EACV,UAAU,KAAK;EACf,WAAW,KAAK;EAChB,UAAU,KAAK;CACjB;AACF;;;;AAKA,SAAgB,qBACd,QACA,QACuB;CACvB,OAAO,IAAI,sBAAsB;EAAE,GAAG;EAAQ;CAAO,CAAC;AACxD;;;;AAKA,SAAgB,eACd,QACuB;CACvB,OAAO,qBAAqB,0BAA0B,GAAG,MAAM;AACjE"}
@@ -1,16 +1,22 @@
1
1
  import { BaseTextAdapter, StructuredOutputOptions, StructuredOutputResult } from '@tanstack/ai/adapters';
2
2
  import { ANTHROPIC_MODELS, AnthropicChatModelProviderOptionsByName, AnthropicChatModelToolCapabilitiesByName, AnthropicModelInputModalitiesByName } from '../model-meta.js';
3
3
  import { AnthropicBeta } from '@anthropic-ai/sdk/resources/beta/beta';
4
- import { AnyTool, Modality, AdapterYieldChunk, TextOptions } from '@tanstack/ai';
4
+ import { AnyTool, Modality, ModelMessage, AdapterYieldChunk, TextOptions } from '@tanstack/ai';
5
5
  import { AnthropicSystemPromptMetadata, ExternalTextProviderOptions } from '../text/text-provider-options.js';
6
6
  import { AnthropicMessageMetadataByModality } from '../message-types.js';
7
7
  import { AnthropicClientConfig, AnthropicMessagesClient } from '../utils/client.js';
8
+ /**
9
+ * True when any message carries a provider file-handle source, so the request
10
+ * must send the Files API beta header.
11
+ */
12
+ export declare function messagesHaveFileSource(messages: Array<ModelMessage>): boolean;
8
13
  /**
9
14
  * Computes the `betas` array for a Messages request. Unions:
10
15
  * - `interleaved-thinking-2025-05-14` when interleaved thinking is enabled,
11
16
  * - `code-execution-2025-08-25` when a `code_execution` tool is present,
12
17
  * - `skills-2025-10-02` when that tool carries skills,
13
- * - `context-management-2025-06-27` when `context_management` is set.
18
+ * - `context-management-2025-06-27` when `context_management` is set,
19
+ * - `files-api-2025-04-14` when a message references an uploaded file handle.
14
20
  * Returns `undefined` when none apply (so the call site omits `betas`).
15
21
  */
16
22
  export declare function computeAnthropicBetas(tools: Array<AnyTool> | undefined, modelOptions: {
@@ -19,7 +25,8 @@ export declare function computeAnthropicBetas(tools: Array<AnyTool> | undefined,
19
25
  budget_tokens?: number;
20
26
  };
21
27
  context_management?: unknown | null;
22
- } | undefined): Array<AnthropicBeta> | undefined;
28
+ mcp_servers?: ReadonlyArray<unknown>;
29
+ } | undefined, hasFileSource?: boolean): Array<AnthropicBeta> | undefined;
23
30
  /**
24
31
  * Configuration for Anthropic text adapter
25
32
  */
@@ -52,6 +59,7 @@ type ResolveToolCapabilities<TModel extends string> = TModel extends keyof Anthr
52
59
  export declare class AnthropicTextAdapter<TModel extends (typeof ANTHROPIC_MODELS)[number], TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>, TInputModalities extends ReadonlyArray<Modality> = ResolveInputModalities<TModel>, TToolCapabilities extends ReadonlyArray<string> = ResolveToolCapabilities<TModel>> extends BaseTextAdapter<TModel, TProviderOptions, TInputModalities, AnthropicMessageMetadataByModality, TToolCapabilities, unknown, AnthropicSystemPromptMetadata> {
53
60
  readonly kind: "text";
54
61
  readonly name: "anthropic";
62
+ readonly supportsFileSources = true;
55
63
  private readonly client;
56
64
  constructor(config: AnthropicTextAdapterConfig, model: TModel);
57
65
  chatStream(options: TextOptions<TProviderOptions>): AsyncIterable<AdapterYieldChunk>;
@@ -5,7 +5,7 @@ import { validateTextProviderOptions } from "../text/text-provider-options.js";
5
5
  import { buildAnthropicUsage } from "../usage.js";
6
6
  import { createAnthropicClient, generateId, getAnthropicApiKeyFromEnv } from "../utils/client.js";
7
7
  import { ANTHROPIC_COMBINED_TOOLS_AND_SCHEMA_MODELS, getAnthropicDefaultMaxTokens } from "../model-meta.js";
8
- import { EventType, normalizeSystemPrompts } from "@tanstack/ai";
8
+ import { EventType, fileReferenceFor, isFileSource, normalizeSystemPrompts } from "@tanstack/ai";
9
9
  import { toRunErrorRawEvent } from "@tanstack/ai/adapter-internals";
10
10
  import { BaseTextAdapter } from "@tanstack/ai/adapters";
11
11
  //#region src/adapters/text.ts
@@ -45,17 +45,27 @@ function buildServerToolResultBlock(toolUseId, meta) {
45
45
  };
46
46
  }
47
47
  /**
48
+ * True when any message carries a provider file-handle source, so the request
49
+ * must send the Files API beta header.
50
+ */
51
+ function messagesHaveFileSource(messages) {
52
+ return messages.some((message) => Array.isArray(message.content) && message.content.some((part) => "source" in part && isFileSource(part.source)));
53
+ }
54
+ /**
48
55
  * Computes the `betas` array for a Messages request. Unions:
49
56
  * - `interleaved-thinking-2025-05-14` when interleaved thinking is enabled,
50
57
  * - `code-execution-2025-08-25` when a `code_execution` tool is present,
51
58
  * - `skills-2025-10-02` when that tool carries skills,
52
- * - `context-management-2025-06-27` when `context_management` is set.
59
+ * - `context-management-2025-06-27` when `context_management` is set,
60
+ * - `files-api-2025-04-14` when a message references an uploaded file handle.
53
61
  * Returns `undefined` when none apply (so the call site omits `betas`).
54
62
  */
55
- function computeAnthropicBetas(tools, modelOptions) {
63
+ function computeAnthropicBetas(tools, modelOptions, hasFileSource = false) {
56
64
  const betas = /* @__PURE__ */ new Set();
65
+ if (hasFileSource) betas.add("files-api-2025-04-14");
57
66
  if (modelOptions?.thinking?.type === "enabled" && typeof modelOptions.thinking.budget_tokens === "number" && modelOptions.thinking.budget_tokens > 0) betas.add("interleaved-thinking-2025-05-14");
58
67
  if (modelOptions?.context_management != null) betas.add("context-management-2025-06-27");
68
+ if (modelOptions?.mcp_servers && modelOptions.mcp_servers.length > 0) betas.add("mcp-client-2025-04-04");
59
69
  const codeExecTool = tools?.find((tool) => getAnthropicProviderToolKind(tool) === "code_execution");
60
70
  if (codeExecTool) {
61
71
  const cfgType = readCodeExecutionConfig(codeExecTool)?.type;
@@ -81,6 +91,7 @@ function asSdkAnthropicMessagesClient(client) {
81
91
  var AnthropicTextAdapter = class extends BaseTextAdapter {
82
92
  kind = "text";
83
93
  name = "anthropic";
94
+ supportsFileSources = true;
84
95
  client;
85
96
  constructor(config, model) {
86
97
  super({}, model);
@@ -94,7 +105,7 @@ var AnthropicTextAdapter = class extends BaseTextAdapter {
94
105
  provider: "anthropic",
95
106
  model: this.model
96
107
  });
97
- const betas = computeAnthropicBetas(options.tools, options.modelOptions);
108
+ const betas = computeAnthropicBetas(options.tools, options.modelOptions, messagesHaveFileSource(options.messages));
98
109
  const stream = await this.client.beta.messages.create({
99
110
  ...requestParams,
100
111
  stream: true,
@@ -149,7 +160,7 @@ var AnthropicTextAdapter = class extends BaseTextAdapter {
149
160
  provider: "anthropic",
150
161
  model: this.model
151
162
  });
152
- const betas = computeAnthropicBetas(chatOptions.tools, chatOptions.modelOptions);
163
+ const betas = computeAnthropicBetas(chatOptions.tools, chatOptions.modelOptions, messagesHaveFileSource(chatOptions.messages));
153
164
  const response = await this.client.beta.messages.create({
154
165
  ...requestParams,
155
166
  stream: false,
@@ -287,32 +298,46 @@ var AnthropicTextAdapter = class extends BaseTextAdapter {
287
298
  }
288
299
  case "image": {
289
300
  const metadata = part.metadata;
301
+ let imageSource;
302
+ if (isFileSource(part.source)) imageSource = {
303
+ type: "file",
304
+ file_id: fileReferenceFor(part.source, this.name)
305
+ };
306
+ else if (part.source.type === "data") imageSource = {
307
+ type: "base64",
308
+ data: part.source.value,
309
+ media_type: part.source.mimeType
310
+ };
311
+ else imageSource = {
312
+ type: "url",
313
+ url: part.source.value
314
+ };
290
315
  return {
291
316
  type: "image",
292
- source: part.source.type === "data" ? {
293
- type: "base64",
294
- data: part.source.value,
295
- media_type: part.source.mimeType
296
- } : {
297
- type: "url",
298
- url: part.source.value
299
- },
317
+ source: imageSource,
300
318
  ...metadata?.cache_control !== void 0 && { cache_control: metadata.cache_control }
301
319
  };
302
320
  }
303
321
  case "document": {
304
322
  const metadata = part.metadata;
305
323
  const title = metadata?.title ?? metadata?.filename;
324
+ let docSource;
325
+ if (isFileSource(part.source)) docSource = {
326
+ type: "file",
327
+ file_id: fileReferenceFor(part.source, this.name)
328
+ };
329
+ else if (part.source.type === "data") docSource = {
330
+ type: "base64",
331
+ data: part.source.value,
332
+ media_type: part.source.mimeType
333
+ };
334
+ else docSource = {
335
+ type: "url",
336
+ url: part.source.value
337
+ };
306
338
  return {
307
339
  type: "document",
308
- source: part.source.type === "data" ? {
309
- type: "base64",
310
- data: part.source.value,
311
- media_type: part.source.mimeType
312
- } : {
313
- type: "url",
314
- url: part.source.value
315
- },
340
+ source: docSource,
316
341
  ...metadata?.cache_control !== void 0 && { cache_control: metadata.cache_control },
317
342
  ...metadata?.citations !== void 0 && { citations: metadata.citations },
318
343
  ...metadata?.context !== void 0 && { context: metadata.context },
@@ -873,6 +898,6 @@ function anthropicText(model, config) {
873
898
  return createAnthropicChat(model, getAnthropicApiKeyFromEnv(), config);
874
899
  }
875
900
  //#endregion
876
- export { AnthropicTextAdapter, anthropicText, computeAnthropicBetas, createAnthropicChat, createAnthropicChatWithClient };
901
+ export { AnthropicTextAdapter, anthropicText, computeAnthropicBetas, createAnthropicChat, createAnthropicChatWithClient, messagesHaveFileSource };
877
902
 
878
903
  //# sourceMappingURL=text.js.map