@tanstack/ai-anthropic 0.7.5 → 0.8.1

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 (49) hide show
  1. package/dist/esm/adapters/summarize.js +79 -49
  2. package/dist/esm/adapters/summarize.js.map +1 -1
  3. package/dist/esm/adapters/text.d.ts +5 -4
  4. package/dist/esm/adapters/text.js +28 -2
  5. package/dist/esm/adapters/text.js.map +1 -1
  6. package/dist/esm/index.d.ts +1 -1
  7. package/dist/esm/model-meta.d.ts +28 -0
  8. package/dist/esm/model-meta.js.map +1 -1
  9. package/dist/esm/tools/bash-tool.d.ts +7 -4
  10. package/dist/esm/tools/bash-tool.js +8 -0
  11. package/dist/esm/tools/bash-tool.js.map +1 -1
  12. package/dist/esm/tools/code-execution-tool.d.ts +7 -4
  13. package/dist/esm/tools/code-execution-tool.js +8 -0
  14. package/dist/esm/tools/code-execution-tool.js.map +1 -1
  15. package/dist/esm/tools/computer-use-tool.d.ts +7 -4
  16. package/dist/esm/tools/computer-use-tool.js +8 -0
  17. package/dist/esm/tools/computer-use-tool.js.map +1 -1
  18. package/dist/esm/tools/custom-tool.d.ts +4 -2
  19. package/dist/esm/tools/custom-tool.js +12 -1
  20. package/dist/esm/tools/custom-tool.js.map +1 -1
  21. package/dist/esm/tools/index.d.ts +8 -1
  22. package/dist/esm/tools/index.js +19 -0
  23. package/dist/esm/tools/index.js.map +1 -0
  24. package/dist/esm/tools/memory-tool.d.ts +7 -4
  25. package/dist/esm/tools/memory-tool.js +9 -1
  26. package/dist/esm/tools/memory-tool.js.map +1 -1
  27. package/dist/esm/tools/text-editor-tool.d.ts +7 -4
  28. package/dist/esm/tools/text-editor-tool.js +9 -1
  29. package/dist/esm/tools/text-editor-tool.js.map +1 -1
  30. package/dist/esm/tools/web-fetch-tool.d.ts +7 -4
  31. package/dist/esm/tools/web-fetch-tool.js +9 -1
  32. package/dist/esm/tools/web-fetch-tool.js.map +1 -1
  33. package/dist/esm/tools/web-search-tool.d.ts +7 -4
  34. package/dist/esm/tools/web-search-tool.js +41 -1
  35. package/dist/esm/tools/web-search-tool.js.map +1 -1
  36. package/package.json +7 -3
  37. package/src/adapters/summarize.ts +88 -54
  38. package/src/adapters/text.ts +45 -6
  39. package/src/index.ts +2 -1
  40. package/src/model-meta.ts +126 -0
  41. package/src/tools/bash-tool.ts +13 -6
  42. package/src/tools/code-execution-tool.ts +17 -6
  43. package/src/tools/computer-use-tool.ts +14 -6
  44. package/src/tools/custom-tool.ts +5 -3
  45. package/src/tools/index.ts +49 -12
  46. package/src/tools/memory-tool.ts +12 -6
  47. package/src/tools/text-editor-tool.ts +14 -6
  48. package/src/tools/web-fetch-tool.ts +15 -7
  49. package/src/tools/web-search-tool.ts +17 -7
@@ -1,3 +1,33 @@
1
+ const validateDomains = (tool) => {
2
+ if (tool.allowed_domains && tool.blocked_domains) {
3
+ throw new Error(
4
+ "allowed_domains and blocked_domains cannot be used together."
5
+ );
6
+ }
7
+ };
8
+ const validateUserLocation = (tool) => {
9
+ const userLocation = tool.user_location;
10
+ if (userLocation) {
11
+ if (userLocation.city && (userLocation.city.length < 1 || userLocation.city.length > 255)) {
12
+ throw new Error(
13
+ "user_location.city must be between 1 and 255 characters."
14
+ );
15
+ }
16
+ if (userLocation.country && userLocation.country.length !== 2) {
17
+ throw new Error("user_location.country must be exactly 2 characters.");
18
+ }
19
+ if (userLocation.region && (userLocation.region.length < 1 || userLocation.region.length > 255)) {
20
+ throw new Error(
21
+ "user_location.region must be between 1 and 255 characters."
22
+ );
23
+ }
24
+ if (userLocation.timezone && (userLocation.timezone.length < 1 || userLocation.timezone.length > 255)) {
25
+ throw new Error(
26
+ "user_location.timezone must be between 1 and 255 characters."
27
+ );
28
+ }
29
+ }
30
+ };
1
31
  function convertWebSearchToolToAdapterFormat(tool) {
2
32
  const metadata = tool.metadata;
3
33
  return {
@@ -10,7 +40,17 @@ function convertWebSearchToolToAdapterFormat(tool) {
10
40
  cache_control: metadata.cacheControl || null
11
41
  };
12
42
  }
43
+ function webSearchTool(config) {
44
+ validateDomains(config);
45
+ validateUserLocation(config);
46
+ return {
47
+ name: "web_search",
48
+ description: "",
49
+ metadata: config
50
+ };
51
+ }
13
52
  export {
14
- convertWebSearchToolToAdapterFormat
53
+ convertWebSearchToolToAdapterFormat,
54
+ webSearchTool
15
55
  };
16
56
  //# sourceMappingURL=web-search-tool.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"web-search-tool.js","sources":["../../../src/tools/web-search-tool.ts"],"sourcesContent":["import type { WebSearchTool20250305 } from '@anthropic-ai/sdk/resources/messages'\nimport type { CacheControl } from '../text/text-provider-options'\nimport type { Tool } from '@tanstack/ai'\n\nexport type WebSearchTool = WebSearchTool20250305\n\nconst validateDomains = (tool: WebSearchTool) => {\n if (tool.allowed_domains && tool.blocked_domains) {\n throw new Error(\n 'allowed_domains and blocked_domains cannot be used together.',\n )\n }\n}\n\nconst validateUserLocation = (tool: WebSearchTool) => {\n const userLocation = tool.user_location\n if (userLocation) {\n if (\n userLocation.city &&\n (userLocation.city.length < 1 || userLocation.city.length > 255)\n ) {\n throw new Error(\n 'user_location.city must be between 1 and 255 characters.',\n )\n }\n if (userLocation.country && userLocation.country.length !== 2) {\n throw new Error('user_location.country must be exactly 2 characters.')\n }\n if (\n userLocation.region &&\n (userLocation.region.length < 1 || userLocation.region.length > 255)\n ) {\n throw new Error(\n 'user_location.region must be between 1 and 255 characters.',\n )\n }\n if (\n userLocation.timezone &&\n (userLocation.timezone.length < 1 || userLocation.timezone.length > 255)\n ) {\n throw new Error(\n 'user_location.timezone must be between 1 and 255 characters.',\n )\n }\n }\n}\n\nexport function convertWebSearchToolToAdapterFormat(tool: Tool): WebSearchTool {\n const metadata = tool.metadata as {\n allowedDomains?: Array<string> | null\n blockedDomains?: Array<string> | null\n maxUses?: number | null\n userLocation?: {\n type: 'approximate'\n city?: string | null\n country?: string | null\n region?: string | null\n timezone?: string | null\n } | null\n cacheControl?: CacheControl | null\n }\n return {\n name: 'web_search',\n type: 'web_search_20250305',\n allowed_domains: metadata.allowedDomains,\n blocked_domains: metadata.blockedDomains,\n max_uses: metadata.maxUses,\n user_location: metadata.userLocation,\n cache_control: metadata.cacheControl || null,\n }\n}\n\nexport function webSearchTool(config: WebSearchTool): Tool {\n validateDomains(config)\n validateUserLocation(config)\n return {\n name: 'web_search',\n description: '',\n metadata: config,\n }\n}\n"],"names":[],"mappings":"AA+CO,SAAS,oCAAoC,MAA2B;AAC7E,QAAM,WAAW,KAAK;AAatB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB,SAAS;AAAA,IAC1B,iBAAiB,SAAS;AAAA,IAC1B,UAAU,SAAS;AAAA,IACnB,eAAe,SAAS;AAAA,IACxB,eAAe,SAAS,gBAAgB;AAAA,EAAA;AAE5C;"}
1
+ {"version":3,"file":"web-search-tool.js","sources":["../../../src/tools/web-search-tool.ts"],"sourcesContent":["import type { WebSearchTool20250305 } from '@anthropic-ai/sdk/resources/messages'\nimport type { CacheControl } from '../text/text-provider-options'\nimport type { ProviderTool, Tool } from '@tanstack/ai'\n\nexport type WebSearchToolConfig = WebSearchTool20250305\n\n/** @deprecated Renamed to `WebSearchToolConfig`. Will be removed in a future release. */\nexport type WebSearchTool = WebSearchToolConfig\n\nexport type AnthropicWebSearchTool = ProviderTool<'anthropic', 'web_search'>\n\nconst validateDomains = (tool: WebSearchToolConfig) => {\n if (tool.allowed_domains && tool.blocked_domains) {\n throw new Error(\n 'allowed_domains and blocked_domains cannot be used together.',\n )\n }\n}\n\nconst validateUserLocation = (tool: WebSearchToolConfig) => {\n const userLocation = tool.user_location\n if (userLocation) {\n if (\n userLocation.city &&\n (userLocation.city.length < 1 || userLocation.city.length > 255)\n ) {\n throw new Error(\n 'user_location.city must be between 1 and 255 characters.',\n )\n }\n if (userLocation.country && userLocation.country.length !== 2) {\n throw new Error('user_location.country must be exactly 2 characters.')\n }\n if (\n userLocation.region &&\n (userLocation.region.length < 1 || userLocation.region.length > 255)\n ) {\n throw new Error(\n 'user_location.region must be between 1 and 255 characters.',\n )\n }\n if (\n userLocation.timezone &&\n (userLocation.timezone.length < 1 || userLocation.timezone.length > 255)\n ) {\n throw new Error(\n 'user_location.timezone must be between 1 and 255 characters.',\n )\n }\n }\n}\n\nexport function convertWebSearchToolToAdapterFormat(\n tool: Tool,\n): WebSearchToolConfig {\n const metadata = tool.metadata as {\n allowedDomains?: Array<string> | null\n blockedDomains?: Array<string> | null\n maxUses?: number | null\n userLocation?: {\n type: 'approximate'\n city?: string | null\n country?: string | null\n region?: string | null\n timezone?: string | null\n } | null\n cacheControl?: CacheControl | null\n }\n return {\n name: 'web_search',\n type: 'web_search_20250305',\n allowed_domains: metadata.allowedDomains,\n blocked_domains: metadata.blockedDomains,\n max_uses: metadata.maxUses,\n user_location: metadata.userLocation,\n cache_control: metadata.cacheControl || null,\n }\n}\n\nexport function webSearchTool(\n config: WebSearchToolConfig,\n): AnthropicWebSearchTool {\n validateDomains(config)\n validateUserLocation(config)\n // Phantom-brand cast: '~provider'/'~toolKind' are type-only and never assigned at runtime.\n return {\n name: 'web_search',\n description: '',\n metadata: config,\n } as unknown as AnthropicWebSearchTool\n}\n"],"names":[],"mappings":"AAWA,MAAM,kBAAkB,CAAC,SAA8B;AACrD,MAAI,KAAK,mBAAmB,KAAK,iBAAiB;AAChD,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACF;AAEA,MAAM,uBAAuB,CAAC,SAA8B;AAC1D,QAAM,eAAe,KAAK;AAC1B,MAAI,cAAc;AAChB,QACE,aAAa,SACZ,aAAa,KAAK,SAAS,KAAK,aAAa,KAAK,SAAS,MAC5D;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AACA,QAAI,aAAa,WAAW,aAAa,QAAQ,WAAW,GAAG;AAC7D,YAAM,IAAI,MAAM,qDAAqD;AAAA,IACvE;AACA,QACE,aAAa,WACZ,aAAa,OAAO,SAAS,KAAK,aAAa,OAAO,SAAS,MAChE;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AACA,QACE,aAAa,aACZ,aAAa,SAAS,SAAS,KAAK,aAAa,SAAS,SAAS,MACpE;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAEO,SAAS,oCACd,MACqB;AACrB,QAAM,WAAW,KAAK;AAatB,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM;AAAA,IACN,iBAAiB,SAAS;AAAA,IAC1B,iBAAiB,SAAS;AAAA,IAC1B,UAAU,SAAS;AAAA,IACnB,eAAe,SAAS;AAAA,IACxB,eAAe,SAAS,gBAAgB;AAAA,EAAA;AAE5C;AAEO,SAAS,cACd,QACwB;AACxB,kBAAgB,MAAM;AACtB,uBAAqB,MAAM;AAE3B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,aAAa;AAAA,IACb,UAAU;AAAA,EAAA;AAEd;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/ai-anthropic",
3
- "version": "0.7.5",
3
+ "version": "0.8.1",
4
4
  "description": "Anthropic Claude adapter for TanStack AI",
5
5
  "author": "",
6
6
  "license": "MIT",
@@ -23,6 +23,10 @@
23
23
  ".": {
24
24
  "types": "./dist/esm/index.d.ts",
25
25
  "import": "./dist/esm/index.js"
26
+ },
27
+ "./tools": {
28
+ "types": "./dist/esm/tools/index.d.ts",
29
+ "import": "./dist/esm/tools/index.js"
26
30
  }
27
31
  },
28
32
  "files": [
@@ -34,12 +38,12 @@
34
38
  },
35
39
  "peerDependencies": {
36
40
  "zod": "^4.0.0",
37
- "@tanstack/ai": "^0.11.0"
41
+ "@tanstack/ai": "^0.13.0"
38
42
  },
39
43
  "devDependencies": {
40
44
  "@vitest/coverage-v8": "4.0.14",
41
45
  "zod": "^4.2.0",
42
- "@tanstack/ai": "0.11.0"
46
+ "@tanstack/ai": "0.13.0"
43
47
  },
44
48
  "scripts": {
45
49
  "build": "vite build",
@@ -54,36 +54,52 @@ export class AnthropicSummarizeAdapter<
54
54
  }
55
55
 
56
56
  async summarize(options: SummarizationOptions): Promise<SummarizationResult> {
57
+ const { logger } = options
57
58
  const systemPrompt = this.buildSummarizationPrompt(options)
58
59
 
59
- const response = await this.client.messages.create({
60
+ logger.request(`activity=summarize provider=anthropic`, {
61
+ provider: 'anthropic',
60
62
  model: options.model,
61
- messages: [{ role: 'user', content: options.text }],
62
- system: systemPrompt,
63
- max_tokens: options.maxLength || 500,
64
- temperature: 0.3,
65
- stream: false,
66
63
  })
67
64
 
68
- const content = response.content
69
- .map((c) => (c.type === 'text' ? c.text : ''))
70
- .join('')
71
-
72
- return {
73
- id: response.id,
74
- model: response.model,
75
- summary: content,
76
- usage: {
77
- promptTokens: response.usage.input_tokens,
78
- completionTokens: response.usage.output_tokens,
79
- totalTokens: response.usage.input_tokens + response.usage.output_tokens,
80
- },
65
+ try {
66
+ const response = await this.client.messages.create({
67
+ model: options.model,
68
+ messages: [{ role: 'user', content: options.text }],
69
+ system: systemPrompt,
70
+ max_tokens: options.maxLength || 500,
71
+ temperature: 0.3,
72
+ stream: false,
73
+ })
74
+
75
+ const content = response.content
76
+ .map((c) => (c.type === 'text' ? c.text : ''))
77
+ .join('')
78
+
79
+ return {
80
+ id: response.id,
81
+ model: response.model,
82
+ summary: content,
83
+ usage: {
84
+ promptTokens: response.usage.input_tokens,
85
+ completionTokens: response.usage.output_tokens,
86
+ totalTokens:
87
+ response.usage.input_tokens + response.usage.output_tokens,
88
+ },
89
+ }
90
+ } catch (error) {
91
+ logger.errors('anthropic.summarize fatal', {
92
+ error,
93
+ source: 'anthropic.summarize',
94
+ })
95
+ throw error
81
96
  }
82
97
  }
83
98
 
84
99
  async *summarizeStream(
85
100
  options: SummarizationOptions,
86
101
  ): AsyncIterable<StreamChunk> {
102
+ const { logger } = options
87
103
  const systemPrompt = this.buildSummarizationPrompt(options)
88
104
  const id = generateId(this.name)
89
105
  const model = options.model
@@ -91,50 +107,68 @@ export class AnthropicSummarizeAdapter<
91
107
  let inputTokens = 0
92
108
  let outputTokens = 0
93
109
 
94
- const stream = await this.client.messages.create({
95
- model: options.model,
96
- messages: [{ role: 'user', content: options.text }],
97
- system: systemPrompt,
98
- max_tokens: options.maxLength || 500,
99
- temperature: 0.3,
110
+ logger.request(`activity=summarize provider=anthropic`, {
111
+ provider: 'anthropic',
112
+ model,
100
113
  stream: true,
101
114
  })
102
115
 
103
- for await (const event of stream) {
104
- if (event.type === 'message_start') {
105
- inputTokens = event.message.usage.input_tokens
106
- } else if (event.type === 'content_block_delta') {
107
- if (event.delta.type === 'text_delta') {
108
- const delta = event.delta.text
109
- accumulatedContent += delta
116
+ try {
117
+ const stream = await this.client.messages.create({
118
+ model: options.model,
119
+ messages: [{ role: 'user', content: options.text }],
120
+ system: systemPrompt,
121
+ max_tokens: options.maxLength || 500,
122
+ temperature: 0.3,
123
+ stream: true,
124
+ })
125
+
126
+ for await (const event of stream) {
127
+ logger.provider(`provider=anthropic type=${event.type}`, {
128
+ chunk: event,
129
+ })
130
+
131
+ if (event.type === 'message_start') {
132
+ inputTokens = event.message.usage.input_tokens
133
+ } else if (event.type === 'content_block_delta') {
134
+ if (event.delta.type === 'text_delta') {
135
+ const delta = event.delta.text
136
+ accumulatedContent += delta
137
+ yield asChunk({
138
+ type: 'TEXT_MESSAGE_CONTENT',
139
+ messageId: id,
140
+ model,
141
+ timestamp: Date.now(),
142
+ delta,
143
+ content: accumulatedContent,
144
+ })
145
+ }
146
+ } else if (event.type === 'message_delta') {
147
+ outputTokens = event.usage.output_tokens
110
148
  yield asChunk({
111
- type: 'TEXT_MESSAGE_CONTENT',
112
- messageId: id,
149
+ type: 'RUN_FINISHED',
150
+ runId: id,
113
151
  model,
114
152
  timestamp: Date.now(),
115
- delta,
116
- content: accumulatedContent,
153
+ finishReason: event.delta.stop_reason as
154
+ | 'stop'
155
+ | 'length'
156
+ | 'content_filter'
157
+ | null,
158
+ usage: {
159
+ promptTokens: inputTokens,
160
+ completionTokens: outputTokens,
161
+ totalTokens: inputTokens + outputTokens,
162
+ },
117
163
  })
118
164
  }
119
- } else if (event.type === 'message_delta') {
120
- outputTokens = event.usage.output_tokens
121
- yield asChunk({
122
- type: 'RUN_FINISHED',
123
- runId: id,
124
- model,
125
- timestamp: Date.now(),
126
- finishReason: event.delta.stop_reason as
127
- | 'stop'
128
- | 'length'
129
- | 'content_filter'
130
- | null,
131
- usage: {
132
- promptTokens: inputTokens,
133
- completionTokens: outputTokens,
134
- totalTokens: inputTokens + outputTokens,
135
- },
136
- })
137
165
  }
166
+ } catch (error) {
167
+ logger.errors('anthropic.summarize fatal', {
168
+ error,
169
+ source: 'anthropic.summarize',
170
+ })
171
+ throw error
138
172
  }
139
173
  }
140
174
 
@@ -9,12 +9,14 @@ import {
9
9
  import type {
10
10
  ANTHROPIC_MODELS,
11
11
  AnthropicChatModelProviderOptionsByName,
12
+ AnthropicChatModelToolCapabilitiesByName,
12
13
  AnthropicModelInputModalitiesByName,
13
14
  } from '../model-meta'
14
15
  import type {
15
16
  StructuredOutputOptions,
16
17
  StructuredOutputResult,
17
18
  } from '@tanstack/ai/adapters'
19
+ import type { InternalLogger } from '@tanstack/ai/adapter-internals'
18
20
  import type {
19
21
  Base64ImageSource,
20
22
  Base64PDFSource,
@@ -89,6 +91,11 @@ type ResolveInputModalities<TModel extends string> =
89
91
  ? AnthropicModelInputModalitiesByName[TModel]
90
92
  : readonly ['text', 'image', 'document']
91
93
 
94
+ type ResolveToolCapabilities<TModel extends string> =
95
+ TModel extends keyof AnthropicChatModelToolCapabilitiesByName
96
+ ? NonNullable<AnthropicChatModelToolCapabilitiesByName[TModel]>
97
+ : readonly []
98
+
92
99
  // ===========================
93
100
  // Adapter Implementation
94
101
  // ===========================
@@ -101,14 +108,17 @@ type ResolveInputModalities<TModel extends string> =
101
108
  */
102
109
  export class AnthropicTextAdapter<
103
110
  TModel extends (typeof ANTHROPIC_MODELS)[number],
104
- TProviderOptions extends object = ResolveProviderOptions<TModel>,
111
+ TProviderOptions extends Record<string, any> = ResolveProviderOptions<TModel>,
105
112
  TInputModalities extends ReadonlyArray<Modality> =
106
113
  ResolveInputModalities<TModel>,
114
+ TToolCapabilities extends ReadonlyArray<string> =
115
+ ResolveToolCapabilities<TModel>,
107
116
  > extends BaseTextAdapter<
108
117
  TModel,
109
118
  TProviderOptions,
110
119
  TInputModalities,
111
- AnthropicMessageMetadataByModality
120
+ AnthropicMessageMetadataByModality,
121
+ TToolCapabilities
112
122
  > {
113
123
  readonly kind = 'text' as const
114
124
  readonly name = 'anthropic' as const
@@ -121,11 +131,16 @@ export class AnthropicTextAdapter<
121
131
  }
122
132
 
123
133
  async *chatStream(
124
- options: TextOptions<AnthropicTextProviderOptions>,
134
+ options: TextOptions<TProviderOptions>,
125
135
  ): AsyncIterable<StreamChunk> {
136
+ const { logger } = options
126
137
  try {
127
138
  const requestParams = this.mapCommonOptionsToAnthropic(options)
128
139
 
140
+ logger.request(
141
+ `activity=chat provider=anthropic model=${this.model} messages=${options.messages.length} tools=${options.tools?.length ?? 0} stream=true`,
142
+ { provider: 'anthropic', model: this.model },
143
+ )
129
144
  const stream = await this.client.beta.messages.create(
130
145
  { ...requestParams, stream: true },
131
146
  {
@@ -134,11 +149,18 @@ export class AnthropicTextAdapter<
134
149
  },
135
150
  )
136
151
 
137
- yield* this.processAnthropicStream(stream, options, () =>
138
- generateId(this.name),
152
+ yield* this.processAnthropicStream(
153
+ stream,
154
+ options,
155
+ () => generateId(this.name),
156
+ logger,
139
157
  )
140
158
  } catch (error: unknown) {
141
159
  const err = error as Error & { status?: number; code?: string }
160
+ logger.errors('anthropic.chatStream fatal', {
161
+ error,
162
+ source: 'anthropic.chatStream',
163
+ })
142
164
  yield asChunk({
143
165
  type: 'RUN_ERROR',
144
166
  model: options.model,
@@ -160,9 +182,10 @@ export class AnthropicTextAdapter<
160
182
  * The outputSchema is already JSON Schema (converted in the ai layer).
161
183
  */
162
184
  async structuredOutput(
163
- options: StructuredOutputOptions<AnthropicTextProviderOptions>,
185
+ options: StructuredOutputOptions<TProviderOptions>,
164
186
  ): Promise<StructuredOutputResult<unknown>> {
165
187
  const { chatOptions, outputSchema } = options
188
+ const { logger } = chatOptions
166
189
 
167
190
  const requestParams = this.mapCommonOptionsToAnthropic(chatOptions)
168
191
 
@@ -180,6 +203,10 @@ export class AnthropicTextAdapter<
180
203
  }
181
204
 
182
205
  try {
206
+ logger.request(
207
+ `activity=chat provider=anthropic model=${this.model} messages=${chatOptions.messages.length} tools=${chatOptions.tools?.length ?? 0} stream=false`,
208
+ { provider: 'anthropic', model: this.model },
209
+ )
183
210
  // Make non-streaming request with tool_choice forced to our structured output tool
184
211
  const response = await this.client.messages.create(
185
212
  {
@@ -231,6 +258,10 @@ export class AnthropicTextAdapter<
231
258
  }
232
259
  } catch (error: unknown) {
233
260
  const err = error as Error
261
+ logger.errors('anthropic.structuredOutput fatal', {
262
+ error,
263
+ source: 'anthropic.structuredOutput',
264
+ })
234
265
  throw new Error(
235
266
  `Structured output generation failed: ${err.message || 'Unknown error occurred'}`,
236
267
  )
@@ -532,6 +563,7 @@ export class AnthropicTextAdapter<
532
563
  stream: AsyncIterable<Anthropic_SDK.Beta.BetaRawMessageStreamEvent>,
533
564
  options: TextOptions<AnthropicTextProviderOptions>,
534
565
  genId: () => string,
566
+ logger: InternalLogger,
535
567
  ): AsyncIterable<StreamChunk> {
536
568
  const model = options.model
537
569
  let accumulatedContent = ''
@@ -558,6 +590,9 @@ export class AnthropicTextAdapter<
558
590
 
559
591
  try {
560
592
  for await (const event of stream) {
593
+ logger.provider(`provider=anthropic type=${event.type}`, {
594
+ chunk: event,
595
+ })
561
596
  // Emit RUN_STARTED on first event
562
597
  if (!hasEmittedRunStarted) {
563
598
  hasEmittedRunStarted = true
@@ -868,6 +903,10 @@ export class AnthropicTextAdapter<
868
903
  } catch (error: unknown) {
869
904
  const err = error as Error & { status?: number; code?: string }
870
905
 
906
+ logger.errors('anthropic.processAnthropicStream fatal', {
907
+ error,
908
+ source: 'anthropic.processAnthropicStream',
909
+ })
871
910
  yield asChunk({
872
911
  type: 'RUN_ERROR',
873
912
  runId,
package/src/index.ts CHANGED
@@ -24,9 +24,10 @@ export {
24
24
  // ============================================================================
25
25
 
26
26
  export type {
27
+ AnthropicChatModel,
27
28
  AnthropicChatModelProviderOptionsByName,
29
+ AnthropicChatModelToolCapabilitiesByName,
28
30
  AnthropicModelInputModalitiesByName,
29
- AnthropicChatModel,
30
31
  } from './model-meta'
31
32
  export { ANTHROPIC_MODELS } from './model-meta'
32
33
  export type {
package/src/model-meta.ts CHANGED
@@ -21,6 +21,15 @@ interface ModelMeta<
21
21
  extended_thinking?: boolean
22
22
  adaptive_thinking?: boolean
23
23
  priority_tier?: boolean
24
+ tools?: Array<
25
+ | 'web_search'
26
+ | 'web_fetch'
27
+ | 'code_execution'
28
+ | 'computer_use'
29
+ | 'bash'
30
+ | 'text_editor'
31
+ | 'memory'
32
+ >
24
33
  }
25
34
  context_window?: number
26
35
  max_output_tokens?: number
@@ -66,6 +75,15 @@ const CLAUDE_OPUS_4_6 = {
66
75
  input: ['text', 'image', 'document'],
67
76
  extended_thinking: true,
68
77
  priority_tier: true,
78
+ tools: [
79
+ 'web_search',
80
+ 'web_fetch',
81
+ 'code_execution',
82
+ 'computer_use',
83
+ 'bash',
84
+ 'text_editor',
85
+ 'memory',
86
+ ],
69
87
  },
70
88
  } as const satisfies ModelMeta<
71
89
  AnthropicContainerOptions &
@@ -96,6 +114,15 @@ const CLAUDE_OPUS_4_5 = {
96
114
  input: ['text', 'image', 'document'],
97
115
  extended_thinking: true,
98
116
  priority_tier: true,
117
+ tools: [
118
+ 'web_search',
119
+ 'web_fetch',
120
+ 'code_execution',
121
+ 'computer_use',
122
+ 'bash',
123
+ 'text_editor',
124
+ 'memory',
125
+ ],
99
126
  },
100
127
  } as const satisfies ModelMeta<
101
128
  AnthropicContainerOptions &
@@ -127,6 +154,15 @@ const CLAUDE_SONNET_4_6 = {
127
154
  extended_thinking: true,
128
155
  adaptive_thinking: true,
129
156
  priority_tier: true,
157
+ tools: [
158
+ 'web_search',
159
+ 'web_fetch',
160
+ 'code_execution',
161
+ 'computer_use',
162
+ 'bash',
163
+ 'text_editor',
164
+ 'memory',
165
+ ],
130
166
  },
131
167
  } as const satisfies ModelMeta<
132
168
  AnthropicContainerOptions &
@@ -157,6 +193,15 @@ const CLAUDE_SONNET_4_5 = {
157
193
  input: ['text', 'image', 'document'],
158
194
  extended_thinking: true,
159
195
  priority_tier: true,
196
+ tools: [
197
+ 'web_search',
198
+ 'web_fetch',
199
+ 'code_execution',
200
+ 'computer_use',
201
+ 'bash',
202
+ 'text_editor',
203
+ 'memory',
204
+ ],
160
205
  },
161
206
  } as const satisfies ModelMeta<
162
207
  AnthropicContainerOptions &
@@ -187,6 +232,15 @@ const CLAUDE_HAIKU_4_5 = {
187
232
  input: ['text', 'image', 'document'],
188
233
  extended_thinking: true,
189
234
  priority_tier: true,
235
+ tools: [
236
+ 'web_search',
237
+ 'web_fetch',
238
+ 'code_execution',
239
+ 'computer_use',
240
+ 'bash',
241
+ 'text_editor',
242
+ 'memory',
243
+ ],
190
244
  },
191
245
  } as const satisfies ModelMeta<
192
246
  AnthropicContainerOptions &
@@ -217,6 +271,15 @@ const CLAUDE_OPUS_4_1 = {
217
271
  input: ['text', 'image', 'document'],
218
272
  extended_thinking: true,
219
273
  priority_tier: true,
274
+ tools: [
275
+ 'web_search',
276
+ 'web_fetch',
277
+ 'code_execution',
278
+ 'computer_use',
279
+ 'bash',
280
+ 'text_editor',
281
+ 'memory',
282
+ ],
220
283
  },
221
284
  } as const satisfies ModelMeta<
222
285
  AnthropicContainerOptions &
@@ -247,6 +310,15 @@ const CLAUDE_SONNET_4 = {
247
310
  input: ['text', 'image', 'document'],
248
311
  extended_thinking: true,
249
312
  priority_tier: true,
313
+ tools: [
314
+ 'web_search',
315
+ 'web_fetch',
316
+ 'code_execution',
317
+ 'computer_use',
318
+ 'bash',
319
+ 'text_editor',
320
+ 'memory',
321
+ ],
250
322
  },
251
323
  } as const satisfies ModelMeta<
252
324
  AnthropicContainerOptions &
@@ -276,6 +348,15 @@ const CLAUDE_SONNET_3_7 = {
276
348
  input: ['text', 'image', 'document'],
277
349
  extended_thinking: true,
278
350
  priority_tier: true,
351
+ tools: [
352
+ 'web_search',
353
+ 'web_fetch',
354
+ 'code_execution',
355
+ 'computer_use',
356
+ 'bash',
357
+ 'text_editor',
358
+ 'memory',
359
+ ],
279
360
  },
280
361
  } as const satisfies ModelMeta<
281
362
  AnthropicContainerOptions &
@@ -306,6 +387,15 @@ const CLAUDE_OPUS_4 = {
306
387
  input: ['text', 'image', 'document'],
307
388
  extended_thinking: true,
308
389
  priority_tier: true,
390
+ tools: [
391
+ 'web_search',
392
+ 'web_fetch',
393
+ 'code_execution',
394
+ 'computer_use',
395
+ 'bash',
396
+ 'text_editor',
397
+ 'memory',
398
+ ],
309
399
  },
310
400
  } as const satisfies ModelMeta<
311
401
  AnthropicContainerOptions &
@@ -336,6 +426,7 @@ const CLAUDE_HAIKU_3_5 = {
336
426
  input: ['text', 'image', 'document'],
337
427
  extended_thinking: false,
338
428
  priority_tier: true,
429
+ tools: ['web_search', 'web_fetch'],
339
430
  },
340
431
  } as const satisfies ModelMeta<
341
432
  AnthropicContainerOptions &
@@ -366,6 +457,7 @@ const CLAUDE_HAIKU_3 = {
366
457
  input: ['text', 'image', 'document'],
367
458
  extended_thinking: false,
368
459
  priority_tier: false,
460
+ tools: ['web_search'],
369
461
  },
370
462
  } as const satisfies ModelMeta<
371
463
  AnthropicContainerOptions &
@@ -432,6 +524,15 @@ const CLAUDE_OPUS_4_6_FAST = {
432
524
  input: ['text', 'image'],
433
525
  extended_thinking: true,
434
526
  priority_tier: true,
527
+ tools: [
528
+ 'web_search',
529
+ 'web_fetch',
530
+ 'code_execution',
531
+ 'computer_use',
532
+ 'bash',
533
+ 'text_editor',
534
+ 'memory',
535
+ ],
435
536
  },
436
537
  pricing: {
437
538
  input: {
@@ -462,6 +563,15 @@ const CLAUDE_OPUS_4_7 = {
462
563
  input: ['text', 'image'],
463
564
  extended_thinking: true,
464
565
  priority_tier: true,
566
+ tools: [
567
+ 'web_search',
568
+ 'web_fetch',
569
+ 'code_execution',
570
+ 'computer_use',
571
+ 'bash',
572
+ 'text_editor',
573
+ 'memory',
574
+ ],
465
575
  },
466
576
  pricing: {
467
577
  input: {
@@ -619,6 +729,22 @@ export type AnthropicChatModelProviderOptionsByName = {
619
729
  AnthropicSamplingOptions
620
730
  }
621
731
 
732
+ export type AnthropicChatModelToolCapabilitiesByName = {
733
+ [CLAUDE_OPUS_4_6.id]: typeof CLAUDE_OPUS_4_6.supports.tools
734
+ [CLAUDE_OPUS_4_5.id]: typeof CLAUDE_OPUS_4_5.supports.tools
735
+ [CLAUDE_SONNET_4_6.id]: typeof CLAUDE_SONNET_4_6.supports.tools
736
+ [CLAUDE_SONNET_4_5.id]: typeof CLAUDE_SONNET_4_5.supports.tools
737
+ [CLAUDE_HAIKU_4_5.id]: typeof CLAUDE_HAIKU_4_5.supports.tools
738
+ [CLAUDE_OPUS_4_1.id]: typeof CLAUDE_OPUS_4_1.supports.tools
739
+ [CLAUDE_SONNET_4.id]: typeof CLAUDE_SONNET_4.supports.tools
740
+ [CLAUDE_SONNET_3_7.id]: typeof CLAUDE_SONNET_3_7.supports.tools
741
+ [CLAUDE_OPUS_4.id]: typeof CLAUDE_OPUS_4.supports.tools
742
+ [CLAUDE_HAIKU_3_5.id]: typeof CLAUDE_HAIKU_3_5.supports.tools
743
+ [CLAUDE_HAIKU_3.id]: typeof CLAUDE_HAIKU_3.supports.tools
744
+ [CLAUDE_OPUS_4_6_FAST.id]: typeof CLAUDE_OPUS_4_6_FAST.supports.tools
745
+ [CLAUDE_OPUS_4_7.id]: typeof CLAUDE_OPUS_4_7.supports.tools
746
+ }
747
+
622
748
  /**
623
749
  * Type-only map from chat model name to its supported input modalities.
624
750
  * All Anthropic Claude models support text, image, and document (PDF) input.